Some odd tweaks and repo management code
This commit is contained in:
109
bpkg/__init__.py
109
bpkg/__init__.py
@@ -126,7 +126,7 @@ class SubprocMixin:
|
||||
def handle_received_data(self):
|
||||
recvd = self.pipe_blender.recv()
|
||||
|
||||
self.log.info('Received message from subprocess: %s', recvd)
|
||||
self.log.debug('Received message from subprocess: %s', recvd)
|
||||
try:
|
||||
handler = self.msg_handlers[type(recvd)]
|
||||
except KeyError:
|
||||
@@ -261,7 +261,7 @@ class BPKG_OT_refresh(SubprocMixin, bpy.types.Operator):
|
||||
|
||||
import pathlib
|
||||
|
||||
storage_path = pathlib.Path(bpy.utils.user_resource('CONFIG', 'addons', create=True))
|
||||
storage_path = pathlib.Path(bpy.utils.user_resource('CONFIG', 'packages', create=True))
|
||||
repository_url = bpy.context.user_preferences.addons[__package__].preferences.repository_url
|
||||
|
||||
proc = multiprocessing.Process(target=subproc.refresh,
|
||||
@@ -326,6 +326,68 @@ class BPKG_OT_hang(SubprocMixin, bpy.types.Operator):
|
||||
def report_process_died(self):
|
||||
self.report({'ERROR'}, 'Process died, exit code %s' % self.process.exitcode)
|
||||
|
||||
class BPKG_OT_load_repositories(SubprocMixin, bpy.types.Operator):
|
||||
bl_idname = 'bpkg.load_repositories'
|
||||
bl_label = 'Load Repositories'
|
||||
bl_description = 'Load repositories from disk'
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
log = logging.getLogger(__name__ + '.BPKG_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 USERPREF_PT_packages(bpy.types.Panel):
|
||||
bl_label = "Package Management"
|
||||
bl_space_type = 'USER_PREFERENCES'
|
||||
@@ -340,12 +402,6 @@ class USERPREF_PT_packages(bpy.types.Panel):
|
||||
return (userpref.active_section == 'PACKAGES')
|
||||
|
||||
def draw(self, context):
|
||||
try:
|
||||
repo = context.user_preferences.addons[__package__].preferences['repo']
|
||||
except KeyError:
|
||||
# HACK:
|
||||
# If no repositories are initialized, we should try to refresh them. If that doesn't work, display a message
|
||||
repo = {'packages': []}
|
||||
|
||||
layout = self.layout
|
||||
|
||||
@@ -365,7 +421,7 @@ class USERPREF_PT_packages(bpy.types.Panel):
|
||||
|
||||
#TODO: more advanced filter/sorting; sort matches which match the filter string from the start higher
|
||||
#Also some caching of this would be nice, this only needs to be re-run when any of the filters change.
|
||||
def filter_package(package):
|
||||
def filter_package(package):# {{{
|
||||
"""Returns true if the given package matches all filters"""
|
||||
filterstr = bpy.context.window_manager.package_search
|
||||
category = bpy.context.window_manager.addon_filter
|
||||
@@ -390,9 +446,9 @@ class USERPREF_PT_packages(bpy.types.Panel):
|
||||
if match_search() and match_category():
|
||||
return True
|
||||
|
||||
return False
|
||||
return False# }}}
|
||||
|
||||
def draw_package(pkg, layout):
|
||||
def draw_package(pkg, layout):# {{{
|
||||
"""Draws the given package"""
|
||||
pkgbox = layout.box()
|
||||
spl = pkgbox.split(.8)
|
||||
@@ -461,12 +517,35 @@ class USERPREF_PT_packages(bpy.types.Panel):
|
||||
spl.label("{}:".format(prop.title()))
|
||||
spl.label(str(blinfo[prop]))
|
||||
|
||||
|
||||
|
||||
if pkg.get('expand'):
|
||||
expanded()
|
||||
else:
|
||||
collapsed()
|
||||
collapsed()# }}}
|
||||
|
||||
def center_message(layout, msg: str):
|
||||
"""draw a label in the center of an extra-tall row"""
|
||||
row = layout.row()
|
||||
row.label(text=msg)
|
||||
row.alignment='CENTER'
|
||||
row.scale_y = 10
|
||||
|
||||
try:
|
||||
#TODO either store repos in windowmanager and reload from disk every time, or store them in the .blend. Not both
|
||||
repo = context.user_preferences.addons[__package__].preferences['repo']
|
||||
except KeyError:
|
||||
center_message("Loading Repositories...")
|
||||
|
||||
import pathlib
|
||||
# TODO: read repository synchronously for now; can't run an operator to do async monitoring from draw code
|
||||
prefs = bpy.context.user_preferences.addons[__package__].preferences
|
||||
storage_path = pathlib.Path(bpy.utils.user_resource('CONFIG', 'packages', create=True))
|
||||
res = subproc._load_repo(storage_path)
|
||||
prefs['repo'] = res.to_dict(sort=True, ids=True)
|
||||
return
|
||||
|
||||
if repo is None:
|
||||
center_message("No repository found. Add one in the addon preferences.")
|
||||
return
|
||||
|
||||
|
||||
for pkg in repo['packages']:
|
||||
@@ -527,6 +606,7 @@ class PackageManagerPreferences(bpy.types.AddonPreferences):
|
||||
def register():
|
||||
bpy.utils.register_class(BPKG_OT_install)
|
||||
bpy.utils.register_class(BPKG_OT_refresh)
|
||||
bpy.utils.register_class(BPKG_OT_load_repositories)
|
||||
bpy.utils.register_class(BPKG_OT_hang)
|
||||
bpy.utils.register_class(USERPREF_PT_packages)
|
||||
bpy.utils.register_class(WM_OT_package_toggle_expand)
|
||||
@@ -549,6 +629,7 @@ def register():
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(BPKG_OT_install)
|
||||
bpy.utils.unregister_class(BPKG_OT_refresh)
|
||||
bpy.utils.unregister_class(BPKG_OT_load_repositories)
|
||||
bpy.utils.unregister_class(BPKG_OT_hang)
|
||||
bpy.utils.unregister_class(USERPREF_PT_packages)
|
||||
bpy.utils.unregister_class(WM_OT_package_toggle_expand)
|
||||
|
@@ -178,8 +178,6 @@ class Repository:
|
||||
if url is None:
|
||||
url = ""
|
||||
self.set_from_dict({'url': url})
|
||||
self.log.debug("Initializing repository: %s", self.to_dict())
|
||||
self.log.debug("Own URL is '%s'", self.url)
|
||||
|
||||
# def cleanse_packagelist(self):
|
||||
# """Remove empty packages (no bl_info), packages with no name"""
|
||||
@@ -255,7 +253,6 @@ class Repository:
|
||||
|
||||
if ids:
|
||||
for pkg in packages:
|
||||
self.log.debug(pkg['url'], pkg['bl_info']['name'], self.name, self.url)
|
||||
# hash may be too big for a C int
|
||||
pkg['id'] = str(hash(pkg['url'] + pkg['bl_info']['name'] + self.name + self.url))
|
||||
|
||||
@@ -389,6 +386,22 @@ def _download(pipe_to_blender, package_url: str, download_dir: pathlib.Path) ->
|
||||
|
||||
return local_fpath
|
||||
|
||||
def _add_to_installed(storage_path: pathlib.Path, pkg: Package):
|
||||
"""Add pkg to local repository"""
|
||||
repo_path = storage_path / 'local.json'
|
||||
if repo_path.exists():
|
||||
repo = Repository.from_file(repo_path)
|
||||
else:
|
||||
repo = Repository()
|
||||
repo.packages.append(pkg)
|
||||
repo.to_file(repo_path)
|
||||
|
||||
def _remove_from_installed(storage_path: pathlib.Path, pkg: Package):
|
||||
"""Remove pkg from local repository"""
|
||||
repo = Repository.from_file(storage_path / 'local.json')
|
||||
#TODO: this won't work, compare by name? (watch out for conflicts though)
|
||||
repo.packages.remove(pkg)
|
||||
|
||||
def _install(pipe_to_blender, pkgpath: pathlib.Path, dest: pathlib.Path, searchpaths: list):
|
||||
"""Extracts/moves package at `pkgpath` to `dest`"""
|
||||
import zipfile
|
||||
@@ -466,7 +479,7 @@ def _install(pipe_to_blender, pkgpath: pathlib.Path, dest: pathlib.Path, searchp
|
||||
return
|
||||
|
||||
|
||||
def download_and_install(pipe_to_blender, package_url: str, install_path: pathlib.Path, search_paths: list):
|
||||
def download_and_install(pipe_to_blender, package: dict, install_path: pathlib.Path, repo_path: pathlib.Path, search_paths: list):
|
||||
"""Downloads and installs the given package."""
|
||||
|
||||
from . import cache
|
||||
@@ -474,16 +487,15 @@ def download_and_install(pipe_to_blender, package_url: str, install_path: pathli
|
||||
log = logging.getLogger('%s.download_and_install' % __name__)
|
||||
|
||||
cache_dir = cache.cache_directory('downloads')
|
||||
downloaded = _download(pipe_to_blender, package_url, cache_dir)
|
||||
downloaded = _download(pipe_to_blender, package.url, cache_dir)
|
||||
|
||||
if not downloaded:
|
||||
log.debug('Download failed/aborted, not going to install anything.')
|
||||
return
|
||||
|
||||
# Only send success if _install doesn't throw an exception
|
||||
# Maybe not the best way to do this? (will also catch exceptions which occur during message sending)
|
||||
try:
|
||||
_install(pipe_to_blender, downloaded, install_path, search_paths)
|
||||
_add_to_installed(repo_path, Package.from_dict(package))
|
||||
pipe_to_blender.send(Success())
|
||||
except InstallException as err:
|
||||
log.exception("Failed to install package: %s", err)
|
||||
@@ -526,9 +538,13 @@ def load(pipe_to_blender, storage_path: pathlib.Path):
|
||||
try:
|
||||
repo = _load_repo(storage_path)
|
||||
pipe_to_blender.send(RepositoryResult(repo.to_dict(sort=True, ids=True)))
|
||||
pipe_to_blender.send(Success())
|
||||
return repo
|
||||
except BadRepository as err:
|
||||
pipe_to_blender.send(SubprocError("Failed to read repository: %s" % err))
|
||||
|
||||
# def load_local(pipe_to_blender
|
||||
|
||||
|
||||
def debug_hang():
|
||||
"""Hangs for an hour. For testing purposes only."""
|
||||
|
Reference in New Issue
Block a user