From f411c6811509e76d86b06b24c75be6d4fe039ac2 Mon Sep 17 00:00:00 2001 From: Ellwood Zwovic Date: Sun, 9 Jul 2017 18:59:36 -0700 Subject: [PATCH] Some stuff from old exception-based error handling approach --- blender_common.py | 30 ++++++++++++++++++++---------- bpackage/repository.py | 33 ++++++++++++++++++--------------- subprocess_adapter.py | 12 +++++++++--- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/blender_common.py b/blender_common.py index 6ef87ba..1b124f9 100644 --- a/blender_common.py +++ b/blender_common.py @@ -10,12 +10,17 @@ from . import bpackage as bpkg class RepositoryProperty(PropertyGroup): url = bpy.props.StringProperty(name="URL") - # status = bpy.props.EnumProperty(name="Status") + status = bpy.props.EnumProperty(name="Status", items=[ + ("OK", "Okay", "FILE_TICK"), + ("NOTFOUND", "Not found", "ERROR"), + ("NOCONNECT", "Could not connect", "QUESTION"), + ]) class PACKAGE_UL_repositories(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): split = layout.split(0.3) + split.label(item.status) split.prop(item, "name") split.prop(item, "url") @@ -42,29 +47,34 @@ class PackagePreferences(AddonPreferences): @subprocess_operator class PACKAGE_OT_refresh(Operator): - """ - Operator which checks for updates to known package lists - """ + """Check for new and updated packages""" bl_idname = "package.refresh" - bl_label = "Refresh package list(s)" + bl_label = "Refresh Packages" log = logging.getLogger(__name__) def invoke(self, context, event): prefs = context.user_preferences.addons[__package__].preferences - if 'repositories' not in prefs: + if 'repositories' not in prefs or len(prefs['repositories']) <= 0: + self.log.debug(prefs) + self.report({'WARNING'}, "No respositories to refresh") return {'FINISHED'} - # HACK: just do the active repo for now + # HACK: just use the active repo until we do multi-repo support repo = bpkg.Repository(prefs['repositories'][prefs.active_repository].to_dict()) self.proc_kwargs = {'target': subprocess_function(repo.refresh, pipe=self.pipe)} + + return {'RUNNING_MODAL'} def modal(self, context, event): - # try: - self.poll_subprocess() - # except: + try: + self.poll_subprocess() + except bpkg.repository.MissingURLError: + self.report({'WARNING'}, "No URL specified") + + return {'RUNNING_MODAL'} def handle_response(self, resp): diff --git a/bpackage/repository.py b/bpackage/repository.py index a978c7d..1a6a7e7 100644 --- a/bpackage/repository.py +++ b/bpackage/repository.py @@ -10,7 +10,19 @@ import json import logging log = logging.getLogger(__name__) +# log.level = logging.DEBUG +class RepositoryError(Exception): + """ + Superclass for repository-related exceptions + """ + +class MissingURLError(RepositoryError): + """ + Thrown when the repository needs a URL but doesn't have one + """ + +# class RepositoryNotFound( class Package: """ @@ -53,6 +65,12 @@ class Repository: Requests repo.json from URL and embeds etag/last-modification headers """ + log.debug(self.url) + if self.url is None: + raise MissingURLError("Cannot refresh repository without a URL") + + + log.debug("Refreshing repository from %s", self.url) req_headers = {} # Do things this way to avoid adding empty objects/None to the req_headers dict @@ -110,19 +128,4 @@ class Repository: json.dump(self.to_dict(), repo_file, indent=4, sort_keys=True) log.info("repo.json written to %s" % path) -# def fetch_repo(url: str, repo: Repository) -> dict: -def refresh(url): - # we have to explicitly close the end of the pipe we are NOT using, - # otherwise no exception will be generated when the other process closes its end. - pipe[0].close() - - local_repo_path = Path(__file__).parent / 'packages' - local_repo_path.mkdir(exist_ok=True) - - try: - fetch_repojson(url) - pipe[1].send(re.status_code) - finally: - pipe[1].close() - diff --git a/subprocess_adapter.py b/subprocess_adapter.py index 468d6f7..6720a33 100644 --- a/subprocess_adapter.py +++ b/subprocess_adapter.py @@ -44,7 +44,13 @@ def subprocess_operator(cls: Operator, polling_interval=.01) -> Operator: # and the pipe is needed for the subprocess_function decorator. # TODO: Perhaps this responsibility should be handled here instead (simplifies things but looses some flexibility) - orig_invoke(self, context, event) + orig_ret = orig_invoke(self, context, event) + + # HACK to allow early exits. + # Perhaps subprocess_operators should define separately named methods, to avoid confusing mixing semantics in return types + if not orig_ret.issubset({'RUNNING_MODAL'}): + log.debug('Exiting early') + return orig_ret self.proc = Process( *getattr(self, 'proc_args', []), @@ -66,7 +72,7 @@ def subprocess_operator(cls: Operator, polling_interval=.01) -> Operator: """ Check the pipe for new messages (nonblocking). If there is a new message, call the callback """ - self.log.debug("polling") + log.debug("polling") try: if self.pipe[0].poll(): resp = self.pipe[0].recv() @@ -74,7 +80,7 @@ def subprocess_operator(cls: Operator, polling_interval=.01) -> Operator: else: resp = None except EOFError: - self.log.debug("done polling") + log.debug("done polling") return {'FINISHED'} if resp is not None: