Cleanup: Move package download/install code out of subproc.py

Instead do such things in bpkg, and only handle interfacing between
blender and bpkg in subproc.py
This commit is contained in:
Ellwood Zwovic
2017-07-26 02:08:01 -07:00
parent 85a61cfead
commit 2f6357e40e
6 changed files with 296 additions and 382 deletions

View File

@@ -30,12 +30,14 @@ if 'bpy' in locals():
return reloaded_mod
subproc = recursive_reload(subproc)
messages = recursive_reload(messages)
utils = recursive_reload(utils)
bpkg = recursive_reload(bpkg)
Package = bpkg.Package
else:
from . import subproc
from . import messages
from . import bpkg
from . import utils
from .bpkg import Package
@@ -114,8 +116,6 @@ class SubprocMixin:
def invoke(self, context, event):
import multiprocessing
self.log.info('Starting')
self.pipe_blender, self.pipe_subproc = multiprocessing.Pipe()
# The subprocess should just be terminated when Blender quits. Without this,
@@ -160,6 +160,7 @@ class SubprocMixin:
if not self.process.is_alive():
self.report_process_died()
self.cancel(context)
self._finish(context)
return {'CANCELLED'}
@@ -172,7 +173,7 @@ class SubprocMixin:
self._abort_timeout = time.time() + 10
self._state = 'ABORTING'
self.pipe_blender.send(subproc.Abort())
self.pipe_blender.send(messages.Abort())
def _finish(self, context):
import multiprocessing
@@ -227,13 +228,16 @@ class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
bl_description = 'Downloads and installs a Blender add-on package'
bl_options = {'REGISTER'}
package_url = bpy.props.StringProperty(name='package_url', description='The URL of the file to download')
package_name = bpy.props.StringProperty(
name='package_name',
description='The name of the package to install'
)
log = logging.getLogger(__name__ + '.PACKAGE_OT_install')
def invoke(self, context, event):
if not self.package_url:
self.report({'ERROR'}, 'Package URL not given')
if not self.package_name:
self.report({'ERROR'}, 'Package name not given')
return {'CANCELLED'}
return super().invoke(context, event)
@@ -249,14 +253,16 @@ class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
import multiprocessing
self.msg_handlers = {
subproc.Progress: self._subproc_progress,
subproc.DownloadError: self._subproc_download_error,
subproc.InstallError: self._subproc_install_error,
subproc.FileConflictError: self._subproc_conflict_error,
subproc.Success: self._subproc_success,
subproc.Aborted: self._subproc_aborted,
messages.Progress: self._subproc_progress,
messages.DownloadError: self._subproc_download_error,
messages.InstallError: self._subproc_install_error,
messages.Success: self._subproc_success,
messages.Aborted: self._subproc_aborted,
}
global _packages
package = _packages[self.package_name].get_latest_version()
import pathlib
# TODO: We need other paths besides this one on subprocess end, so it might be better to pass them all at once.
@@ -265,31 +271,27 @@ class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
self.log.debug("Using %s as install path", install_path)
import addon_utils
proc = multiprocessing.Process(target=subproc.download_and_install,
args=(self.pipe_subproc, self.package_url, install_path, addon_utils.paths()))
proc = multiprocessing.Process(target=messages.download_and_install_package,
args=(self.pipe_subproc, package, install_path))
return proc
def _subproc_progress(self, progress: subproc.Progress):
def _subproc_progress(self, progress: messages.Progress):
self.log.info('Task progress at %i%%', progress.progress * 100)
def _subproc_download_error(self, error: subproc.DownloadError):
def _subproc_download_error(self, error: messages.DownloadError):
self.report({'ERROR'}, 'Unable to download package: %s' % error.description)
self.quit()
def _subproc_install_error(self, error: subproc.InstallError):
def _subproc_install_error(self, error: messages.InstallError):
self.report({'ERROR'}, 'Unable to install package: %s' % error.message)
self.quit()
def _subproc_conflict_error(self, error: subproc.FileConflictError):
self.report({'ERROR'}, 'Unable to install package: %s' % error.message)
self.quit()
def _subproc_success(self, success: subproc.Success):
def _subproc_success(self, success: messages.Success):
self.report({'INFO'}, 'Package installed successfully')
bpy.ops.package.refresh_packages()
self.quit()
def _subproc_aborted(self, aborted: subproc.Aborted):
def _subproc_aborted(self, aborted: messages.Aborted):
self.report({'ERROR'}, 'Package installation aborted per your request')
self.quit()
@@ -326,8 +328,8 @@ class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
import multiprocessing
self.msg_handlers = {
subproc.UninstallError: self._subproc_uninstall_error,
subproc.Success: self._subproc_success,
messages.UninstallError: self._subproc_uninstall_error,
messages.Success: self._subproc_success,
}
import pathlib
@@ -336,16 +338,16 @@ class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
global _packages
package = _packages[self.package_name].get_latest_version()
proc = multiprocessing.Process(target=subproc.uninstall,
proc = multiprocessing.Process(target=subproc.uninstall_package,
args=(self.pipe_subproc, package, install_path))
return proc
def _subproc_uninstall_error(self, error: subproc.InstallError):
def _subproc_uninstall_error(self, error: messages.InstallError):
self.report({'ERROR'}, error.message)
self.quit()
def _subproc_success(self, success: subproc.Success):
def _subproc_success(self, success: messages.Success):
self.report({'INFO'}, 'Package uninstalled successfully')
bpy.ops.package.refresh_packages()
self.quit()
@@ -403,6 +405,11 @@ class PACKAGE_OT_refresh_repositories(SubprocMixin, bpy.types.Operator):
_running = False
def invoke(self, context, event):
self.repolist = bpy.context.user_preferences.addons[__package__].preferences.repositories
if len(self.repolist) == 0:
self.report({'ERROR'}, "No repositories to refresh")
return {'CANCELLED'}
PACKAGE_OT_refresh_repositories._running = True
return super().invoke(context, event)
@@ -426,38 +433,43 @@ class PACKAGE_OT_refresh_repositories(SubprocMixin, bpy.types.Operator):
#TODO: make sure all possible messages are handled
self.msg_handlers = {
subproc.Progress: self._subproc_progress,
subproc.SubprocError: self._subproc_error,
subproc.DownloadError: self._subproc_download_error,
subproc.Success: self._subproc_success,
subproc.RepositoryResult: self._subproc_repository_result,
subproc.Aborted: self._subproc_aborted,
messages.Progress: self._subproc_progress,
messages.SubprocError: self._subproc_error,
messages.DownloadError: self._subproc_download_error,
messages.Success: self._subproc_success,
messages.RepositoryResult: self._subproc_repository_result,
messages.BadRepositoryError: self._subproc_repository_error,
messages.Aborted: self._subproc_aborted,
}
import pathlib
storage_path = pathlib.Path(bpy.utils.user_resource('CONFIG', 'packages', create=True))
repository_url = bpy.context.user_preferences.addons[__package__].preferences.repositories[0].url
repository_url = self.repolist[0].url
proc = multiprocessing.Process(target=subproc.refresh,
proc = multiprocessing.Process(target=subproc.refresh_repository,
args=(self.pipe_subproc, storage_path, repository_url))
return proc
def _subproc_progress(self, progress: subproc.Progress):
def _subproc_progress(self, progress: messages.Progress):
self.log.info('Task progress at %i%%', progress.progress * 100)
def _subproc_error(self, error: subproc.SubprocError):
def _subproc_error(self, error: messages.SubprocError):
self.report({'ERROR'}, 'Unable to refresh package list: %s' % error.message)
self.quit()
def _subproc_download_error(self, error: subproc.DownloadError):
self.report({'ERROR'}, 'Unable to download package list: %s' % error.description)
def _subproc_download_error(self, error: messages.DownloadError):
self.report({'ERROR'}, 'Unable to download package list: %s' % error.message)
self.quit()
def _subproc_success(self, success: subproc.Success):
def _subproc_repository_error(self, error: messages.BadRepositoryError):
self.report({'ERROR'}, str(error.message))
self.quit()
def _subproc_repository_result(self, result: subproc.RepositoryResult):
def _subproc_success(self, success: messages.Success):
self.quit()
def _subproc_repository_result(self, result: messages.RepositoryResult):
available_packages = result.repository.packages
installed_packages = get_packages_from_disk(refresh=False)
@@ -469,16 +481,16 @@ class PACKAGE_OT_refresh_repositories(SubprocMixin, bpy.types.Operator):
_packages = build_composite_packagelist(installed_packages, available_packages)
self.report({'INFO'}, 'Package list retrieved successfully')
def _subproc_aborted(self, aborted: subproc.Aborted):
def _subproc_aborted(self, aborted: messages.Aborted):
self.report({'ERROR'}, 'Package list retrieval aborted per your request')
self.quit()
def report_process_died(self):
if self.process.exitcode:
self.log.error('Process died without telling us! Exit code was %i', self.process.exitcode)
self.log.error('Refresh process died without telling us! Exit code was %i', self.process.exitcode)
self.report({'ERROR'}, 'Error refreshing package lists, exit code %i' % self.process.exitcode)
else:
self.log.error('Process died without telling us! Exit code was 0 though')
self.log.error('Refresh process died without telling us! Exit code was 0 though')
self.report({'WARNING'}, 'Error refreshing package lists, but process finished OK. This is weird.')
#TODO:
@@ -494,68 +506,6 @@ class PACKAGE_OT_refresh(bpy.types.Operator):
# getattr(bpy.ops, __package__).refresh_packages()
return {'FINISHED'}
class PACKAGE_OT_load_repositories(SubprocMixin, bpy.types.Operator):
bl_idname = 'package.load_repositories'
bl_label = 'Load Repositories'
bl_description = 'Load repositories from disk'
bl_options = {'REGISTER'}
log = logging.getLogger(__name__ + '.PACKAGE_OT_load_repositories')
def create_subprocess(self):
"""
Start the load process and register message handlers
"""
import multiprocessing
import pathlib
# TODO: We need other paths besides this one on subprocess end, so it might be better to pass them all at once.
# For now, just pass this one.
storage_path = pathlib.Path(bpy.utils.user_resource('CONFIG', 'packages', create=True))
self.log.debug("Using %s as install path", install_path)
import addon_utils
proc = multiprocessing.Process(
target=subproc.load_repositories,
args=(self.pipe_subproc, self.storage_path)
)
return proc
self.msg_handlers = {
subproc.SubprocError: self._subproc_error,
subproc.RepositoryResult: self._subproc_repository_result,
subproc.Success: self._subproc_success,
subproc.Aborted: self._subproc_aborted,
}
def _subproc_error(self, error: subproc.SubprocError):
self.report({'ERROR'}, 'Failed to load repositories: %s' % error.message)
self.quit()
def _subproc_repository_result(self, result: subproc.RepositoryResult):
bpy.context.user_preferences.addons[__package__].preferences['repo'] = result.repository
self.log.info("Loaded repository %s", result.repository.name)
def _subproc_success(self, success: subproc.Success):
self.log.info("Successfully loaded repositories")
self.quit()
def _subproc_aborted(self, aborted: subproc.Aborted):
self.report({'ERROR'}, 'Package installation aborted per your request')
self.quit()
def report_process_died(self):
if self.process.exitcode:
self.log.error('Process died without telling us! Exit code was %i', self.process.exitcode)
self.report({'ERROR'}, 'Error downloading package, exit code %i' % self.process.exitcode)
else:
self.log.error('Process died without telling us! Exit code was 0 though')
self.report({'WARNING'}, 'Error downloading package, but process finished OK. This is weird.')
class RepositoryProperty(bpy.types.PropertyGroup):
url = bpy.props.StringProperty(name="URL")
status = bpy.props.EnumProperty(name="Status", items=[
@@ -844,7 +794,7 @@ class USERPREF_PT_packages(bpy.types.Panel):
right.operator(PACKAGE_OT_uninstall.bl_idname,
text="Uninstall").package_name=pkg.name
elif pkg.user:
right.label("Installed")
right.label("Installed, but not in repo")
right.scale_y = 2
right.enabled = False
elif not pkg.user:
@@ -854,7 +804,7 @@ class USERPREF_PT_packages(bpy.types.Panel):
else:
if pkg.url:
right.operator(PACKAGE_OT_install.bl_idname,
text="Install").package_url=pkg.url
text="Install").package_name=pkg.name
else:
right.label("Not installed, but no URL?")
@@ -1012,7 +962,6 @@ def register():
bpy.utils.register_class(PACKAGE_OT_refresh_repositories)
bpy.utils.register_class(PACKAGE_OT_refresh_packages)
bpy.utils.register_class(PACKAGE_OT_refresh)
bpy.utils.register_class(PACKAGE_OT_load_repositories)
bpy.utils.register_class(USERPREF_PT_packages)
bpy.utils.register_class(WM_OT_package_toggle_expand)
bpy.types.WindowManager.package_search = bpy.props.StringProperty(
@@ -1043,7 +992,6 @@ def unregister():
bpy.utils.unregister_class(PACKAGE_OT_refresh_repositories)
bpy.utils.unregister_class(PACKAGE_OT_refresh_packages)
bpy.utils.unregister_class(PACKAGE_OT_refresh)
bpy.utils.unregister_class(PACKAGE_OT_load_repositories)
bpy.utils.unregister_class(USERPREF_PT_packages)
bpy.utils.unregister_class(WM_OT_package_toggle_expand)
del bpy.types.WindowManager.package_search