Merge branch 'windows-subprocess-fix'
This commit is contained in:
@@ -12,11 +12,15 @@ bl_info = {
|
|||||||
'category': 'System',
|
'category': 'System',
|
||||||
'support': 'TESTING',
|
'support': 'TESTING',
|
||||||
}
|
}
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
if 'bpy' in locals():
|
try:
|
||||||
|
import bpy
|
||||||
|
except ImportError:
|
||||||
|
from . import subproc
|
||||||
|
else:
|
||||||
|
if 'bpkg' in locals():
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
|
|
||||||
def recursive_reload(mod):
|
def recursive_reload(mod):
|
||||||
@@ -35,7 +39,6 @@ if 'bpy' in locals():
|
|||||||
bpkg = recursive_reload(bpkg)
|
bpkg = recursive_reload(bpkg)
|
||||||
Package = bpkg.types.Package
|
Package = bpkg.types.Package
|
||||||
|
|
||||||
else:
|
|
||||||
from . import subproc
|
from . import subproc
|
||||||
from . import messages
|
from . import messages
|
||||||
from . import bpkg
|
from . import bpkg
|
||||||
@@ -44,18 +47,19 @@ else:
|
|||||||
Package,
|
Package,
|
||||||
ConsolidatedPackage,
|
ConsolidatedPackage,
|
||||||
)
|
)
|
||||||
|
from pathlib import Path
|
||||||
|
from collections import OrderedDict
|
||||||
|
import multiprocessing
|
||||||
|
mp_context = multiprocessing.get_context('spawn')
|
||||||
|
mp_context.set_executable(bpy.app.binary_path_python)
|
||||||
|
|
||||||
import bpy
|
# global list of all known packages, indexed by name
|
||||||
from pathlib import Path
|
_packages = OrderedDict()
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
# global list of all known packages, indexed by name
|
# used for lazy loading
|
||||||
_packages = OrderedDict()
|
_main_has_run = False
|
||||||
|
|
||||||
# used for lazy loading
|
class SubprocMixin:
|
||||||
_main_has_run = False
|
|
||||||
|
|
||||||
class SubprocMixin:
|
|
||||||
"""Mix-in class for things that need to be run in a subprocess."""
|
"""Mix-in class for things that need to be run in a subprocess."""
|
||||||
|
|
||||||
log = logging.getLogger(__name__ + '.SubprocMixin')
|
log = logging.getLogger(__name__ + '.SubprocMixin')
|
||||||
@@ -74,8 +78,6 @@ class SubprocMixin:
|
|||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
import multiprocessing
|
|
||||||
|
|
||||||
self.pipe_blender, self.pipe_subproc = multiprocessing.Pipe()
|
self.pipe_blender, self.pipe_subproc = multiprocessing.Pipe()
|
||||||
|
|
||||||
# The subprocess should just be terminated when Blender quits. Without this,
|
# The subprocess should just be terminated when Blender quits. Without this,
|
||||||
@@ -135,7 +137,6 @@ class SubprocMixin:
|
|||||||
self.pipe_blender.send(messages.Abort())
|
self.pipe_blender.send(messages.Abort())
|
||||||
|
|
||||||
def _finish(self, context):
|
def _finish(self, context):
|
||||||
import multiprocessing
|
|
||||||
try:
|
try:
|
||||||
self.cancel(context)
|
self.cancel(context)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -185,7 +186,7 @@ class SubprocMixin:
|
|||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
|
class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
|
||||||
bl_idname = 'package.install'
|
bl_idname = 'package.install'
|
||||||
bl_label = 'Install package'
|
bl_label = 'Install package'
|
||||||
bl_description = 'Downloads and installs a Blender add-on package'
|
bl_description = 'Downloads and installs a Blender add-on package'
|
||||||
@@ -213,8 +214,6 @@ class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
|
|||||||
:rtype: multiprocessing.Process
|
:rtype: multiprocessing.Process
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import multiprocessing
|
|
||||||
|
|
||||||
self.msg_handlers = {
|
self.msg_handlers = {
|
||||||
messages.Progress: self._subproc_progress,
|
messages.Progress: self._subproc_progress,
|
||||||
messages.DownloadError: self._subproc_download_error,
|
messages.DownloadError: self._subproc_download_error,
|
||||||
@@ -234,7 +233,7 @@ class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
|
|||||||
self.log.debug("Using %s as install path", install_path)
|
self.log.debug("Using %s as install path", install_path)
|
||||||
|
|
||||||
import addon_utils
|
import addon_utils
|
||||||
proc = multiprocessing.Process(target=subproc.download_and_install_package,
|
proc = mp_context.Process(target=subproc.download_and_install_package,
|
||||||
args=(self.pipe_subproc, package, install_path))
|
args=(self.pipe_subproc, package, install_path))
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
@@ -267,7 +266,7 @@ class PACKAGE_OT_install(SubprocMixin, bpy.types.Operator):
|
|||||||
self.log.error('Process died without telling us! Exit code was 0 though')
|
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.')
|
self.report({'WARNING'}, 'Error downloading package, but process finished OK. This is weird.')
|
||||||
|
|
||||||
class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
|
class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
|
||||||
bl_idname = 'package.uninstall'
|
bl_idname = 'package.uninstall'
|
||||||
bl_label = 'Install package'
|
bl_label = 'Install package'
|
||||||
bl_description = "Remove installed package files from filesystem"
|
bl_description = "Remove installed package files from filesystem"
|
||||||
@@ -289,8 +288,6 @@ class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
|
|||||||
:rtype: multiprocessing.Process
|
:rtype: multiprocessing.Process
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import multiprocessing
|
|
||||||
|
|
||||||
self.msg_handlers = {
|
self.msg_handlers = {
|
||||||
messages.UninstallError: self._subproc_uninstall_error,
|
messages.UninstallError: self._subproc_uninstall_error,
|
||||||
messages.Success: self._subproc_success,
|
messages.Success: self._subproc_success,
|
||||||
@@ -302,7 +299,7 @@ class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
|
|||||||
global _packages
|
global _packages
|
||||||
package = _packages[self.package_name].get_latest_version()
|
package = _packages[self.package_name].get_latest_version()
|
||||||
|
|
||||||
proc = multiprocessing.Process(target=subproc.uninstall_package,
|
proc = mp_context.Process(target=subproc.uninstall_package,
|
||||||
args=(self.pipe_subproc, package, install_path))
|
args=(self.pipe_subproc, package, install_path))
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
@@ -325,7 +322,7 @@ class PACKAGE_OT_uninstall(SubprocMixin, bpy.types.Operator):
|
|||||||
self.report({'WARNING'}, 'Error downloading package, but process finished OK. This is weird.')
|
self.report({'WARNING'}, 'Error downloading package, but process finished OK. This is weird.')
|
||||||
|
|
||||||
|
|
||||||
def get_installed_packages(refresh=False) -> list:
|
def get_installed_packages(refresh=False) -> list:
|
||||||
"""Get list of packages installed on disk"""
|
"""Get list of packages installed on disk"""
|
||||||
import addon_utils
|
import addon_utils
|
||||||
installed_pkgs = []
|
installed_pkgs = []
|
||||||
@@ -335,10 +332,10 @@ def get_installed_packages(refresh=False) -> list:
|
|||||||
installed_pkgs.append(pkg)
|
installed_pkgs.append(pkg)
|
||||||
return installed_pkgs
|
return installed_pkgs
|
||||||
|
|
||||||
def get_repo_storage_path() -> Path:
|
def get_repo_storage_path() -> Path:
|
||||||
return Path(bpy.utils.user_resource('CONFIG', 'repositories'))
|
return Path(bpy.utils.user_resource('CONFIG', 'repositories'))
|
||||||
|
|
||||||
def get_repositories() -> list:
|
def get_repositories() -> list:
|
||||||
"""
|
"""
|
||||||
Get list of downloaded repositories and update wm.package_repositories
|
Get list of downloaded repositories and update wm.package_repositories
|
||||||
"""
|
"""
|
||||||
@@ -349,23 +346,23 @@ def get_repositories() -> list:
|
|||||||
|
|
||||||
return repos
|
return repos
|
||||||
|
|
||||||
# class PACKAGE_OT_refresh_packages(bpy.types.Operator):
|
# class PACKAGE_OT_refresh_packages(bpy.types.Operator):
|
||||||
# bl_idname = "package.refresh_packages"
|
# bl_idname = "package.refresh_packages"
|
||||||
# bl_label = "Refresh Packages"
|
# bl_label = "Refresh Packages"
|
||||||
# bl_description = "Scan for packages on disk"
|
# bl_description = "Scan for packages on disk"
|
||||||
#
|
#
|
||||||
# log = logging.getLogger(__name__ + ".PACKAGE_OT_refresh_packages")
|
# log = logging.getLogger(__name__ + ".PACKAGE_OT_refresh_packages")
|
||||||
#
|
#
|
||||||
# def execute(self, context):
|
# def execute(self, context):
|
||||||
# global _packages
|
# global _packages
|
||||||
# installed_packages = get_packages_from_disk(refresh=True)
|
# installed_packages = get_packages_from_disk(refresh=True)
|
||||||
# available_packages = get_packages_from_repo()
|
# available_packages = get_packages_from_repo()
|
||||||
# _packages = build_composite_packagelist(installed_packages, available_packages)
|
# _packages = build_composite_packagelist(installed_packages, available_packages)
|
||||||
# context.area.tag_redraw()
|
# context.area.tag_redraw()
|
||||||
#
|
#
|
||||||
# return {'FINISHED'}
|
# return {'FINISHED'}
|
||||||
|
|
||||||
class PACKAGE_OT_refresh(SubprocMixin, bpy.types.Operator):
|
class PACKAGE_OT_refresh(SubprocMixin, bpy.types.Operator):
|
||||||
bl_idname = "package.refresh"
|
bl_idname = "package.refresh"
|
||||||
bl_label = "Refresh"
|
bl_label = "Refresh"
|
||||||
bl_description = 'Check repositories for new and updated packages'
|
bl_description = 'Check repositories for new and updated packages'
|
||||||
@@ -400,8 +397,6 @@ class PACKAGE_OT_refresh(SubprocMixin, bpy.types.Operator):
|
|||||||
:rtype: multiprocessing.Process
|
:rtype: multiprocessing.Process
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import multiprocessing
|
|
||||||
|
|
||||||
#TODO: make sure all possible messages are handled
|
#TODO: make sure all possible messages are handled
|
||||||
self.msg_handlers = {
|
self.msg_handlers = {
|
||||||
messages.Progress: self._subproc_progress,
|
messages.Progress: self._subproc_progress,
|
||||||
@@ -419,7 +414,7 @@ class PACKAGE_OT_refresh(SubprocMixin, bpy.types.Operator):
|
|||||||
repository_urls = [repo.url for repo in self.repositories]
|
repository_urls = [repo.url for repo in self.repositories]
|
||||||
self.log.debug("Repository urls %s", repository_urls)
|
self.log.debug("Repository urls %s", repository_urls)
|
||||||
|
|
||||||
proc = multiprocessing.Process(target=subproc.refresh_repositories,
|
proc = mp_context.Process(target=subproc.refresh_repositories,
|
||||||
args=(self.pipe_subproc, storage_path, repository_urls))
|
args=(self.pipe_subproc, storage_path, repository_urls))
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
@@ -465,7 +460,7 @@ class PACKAGE_OT_refresh(SubprocMixin, bpy.types.Operator):
|
|||||||
self.log.error('Refresh 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.')
|
self.report({'WARNING'}, 'Error refreshing package lists, but process finished OK. This is weird.')
|
||||||
|
|
||||||
class RepositoryProperty(bpy.types.PropertyGroup):
|
class RepositoryProperty(bpy.types.PropertyGroup):
|
||||||
name = bpy.props.StringProperty(name="Name")
|
name = bpy.props.StringProperty(name="Name")
|
||||||
url = bpy.props.StringProperty(name="URL")
|
url = bpy.props.StringProperty(name="URL")
|
||||||
status = bpy.props.EnumProperty(name="Status", items=[
|
status = bpy.props.EnumProperty(name="Status", items=[
|
||||||
@@ -475,7 +470,7 @@ class RepositoryProperty(bpy.types.PropertyGroup):
|
|||||||
])
|
])
|
||||||
enabled = bpy.props.BoolProperty(name="Enabled")
|
enabled = bpy.props.BoolProperty(name="Enabled")
|
||||||
|
|
||||||
class PACKAGE_UL_repositories(bpy.types.UIList):
|
class PACKAGE_UL_repositories(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
layout.alignment='LEFT'
|
layout.alignment='LEFT'
|
||||||
layout.prop(item, "enabled", text="")
|
layout.prop(item, "enabled", text="")
|
||||||
@@ -484,7 +479,7 @@ class PACKAGE_UL_repositories(bpy.types.UIList):
|
|||||||
else:
|
else:
|
||||||
layout.label(item.name)
|
layout.label(item.name)
|
||||||
|
|
||||||
class PACKAGE_OT_add_repository(bpy.types.Operator):
|
class PACKAGE_OT_add_repository(bpy.types.Operator):
|
||||||
bl_idname = "package.add_repository"
|
bl_idname = "package.add_repository"
|
||||||
bl_label = "Add Repository"
|
bl_label = "Add Repository"
|
||||||
|
|
||||||
@@ -509,7 +504,7 @@ class PACKAGE_OT_add_repository(bpy.types.Operator):
|
|||||||
context.area.tag_redraw()
|
context.area.tag_redraw()
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
class PACKAGE_OT_remove_repository(bpy.types.Operator):
|
class PACKAGE_OT_remove_repository(bpy.types.Operator):
|
||||||
bl_idname = "package.remove_repository"
|
bl_idname = "package.remove_repository"
|
||||||
bl_label = "Remove Repository"
|
bl_label = "Remove Repository"
|
||||||
|
|
||||||
@@ -529,7 +524,7 @@ class PACKAGE_OT_remove_repository(bpy.types.Operator):
|
|||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
class USERPREF_PT_packages(bpy.types.Panel):
|
class USERPREF_PT_packages(bpy.types.Panel):
|
||||||
bl_label = "Package Management"
|
bl_label = "Package Management"
|
||||||
bl_space_type = 'USER_PREFERENCES'
|
bl_space_type = 'USER_PREFERENCES'
|
||||||
bl_region_type = 'WINDOW'
|
bl_region_type = 'WINDOW'
|
||||||
@@ -865,7 +860,7 @@ class USERPREF_PT_packages(bpy.types.Panel):
|
|||||||
draw_package(_packages[pkgname], row)
|
draw_package(_packages[pkgname], row)
|
||||||
|
|
||||||
|
|
||||||
class WM_OT_package_toggle_expand(bpy.types.Operator):# {{{
|
class WM_OT_package_toggle_expand(bpy.types.Operator):# {{{
|
||||||
bl_idname = "wm.package_toggle_expand"
|
bl_idname = "wm.package_toggle_expand"
|
||||||
bl_label = ""
|
bl_label = ""
|
||||||
bl_description = "Toggle display of extended information for given package (hold shift to collapse all other packages)"
|
bl_description = "Toggle display of extended information for given package (hold shift to collapse all other packages)"
|
||||||
@@ -888,7 +883,7 @@ class WM_OT_package_toggle_expand(bpy.types.Operator):# {{{
|
|||||||
|
|
||||||
return {'FINISHED'}# }}}
|
return {'FINISHED'}# }}}
|
||||||
|
|
||||||
class PACKAGE_OT_toggle_enabled(bpy.types.Operator):# {{{
|
class PACKAGE_OT_toggle_enabled(bpy.types.Operator):# {{{
|
||||||
bl_idname = "package.toggle_enabled"
|
bl_idname = "package.toggle_enabled"
|
||||||
bl_label = ""
|
bl_label = ""
|
||||||
bl_description = "Enable given package if it's disabled, and vice versa if it's enabled"
|
bl_description = "Enable given package if it's disabled, and vice versa if it's enabled"
|
||||||
@@ -929,7 +924,7 @@ class PACKAGE_OT_toggle_enabled(bpy.types.Operator):# {{{
|
|||||||
|
|
||||||
return {'FINISHED'}# }}}
|
return {'FINISHED'}# }}}
|
||||||
|
|
||||||
class PACKAGE_OT_disable(bpy.types.Operator):# {{{
|
class PACKAGE_OT_disable(bpy.types.Operator):# {{{
|
||||||
bl_idname = "package.disable"
|
bl_idname = "package.disable"
|
||||||
bl_label = ""
|
bl_label = ""
|
||||||
bl_description = "Disable given package"
|
bl_description = "Disable given package"
|
||||||
@@ -954,16 +949,16 @@ class PACKAGE_OT_disable(bpy.types.Operator):# {{{
|
|||||||
_packages[self.package_name].enabled = False
|
_packages[self.package_name].enabled = False
|
||||||
return ret# }}}
|
return ret# }}}
|
||||||
|
|
||||||
# class PackageManagerPreferences(bpy.types.AddonPreferences):
|
# class PackageManagerPreferences(bpy.types.AddonPreferences):
|
||||||
# bl_idname = __package__
|
# bl_idname = __package__
|
||||||
#
|
#
|
||||||
# repositories = bpy.props.CollectionProperty(
|
# repositories = bpy.props.CollectionProperty(
|
||||||
# type=RepositoryProperty,
|
# type=RepositoryProperty,
|
||||||
# name="Repositories",
|
# name="Repositories",
|
||||||
# )
|
# )
|
||||||
# active_repository = bpy.props.IntProperty()
|
# active_repository = bpy.props.IntProperty()
|
||||||
|
|
||||||
def build_packagelist() -> OrderedDict:# {{{
|
def build_packagelist() -> OrderedDict:# {{{
|
||||||
"""Make an OrderedDict of ConsolidatedPackages from known repositories + installed packages, keyed by package name"""
|
"""Make an OrderedDict of ConsolidatedPackages from known repositories + installed packages, keyed by package name"""
|
||||||
|
|
||||||
log = logging.getLogger(__name__ + ".build_composite_packagelist")
|
log = logging.getLogger(__name__ + ".build_composite_packagelist")
|
||||||
@@ -990,7 +985,7 @@ def build_packagelist() -> OrderedDict:# {{{
|
|||||||
# log.debug(masterlist[None].__dict__)
|
# log.debug(masterlist[None].__dict__)
|
||||||
return OrderedDict(sorted(masterlist.items()))# }}}
|
return OrderedDict(sorted(masterlist.items()))# }}}
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Entry point; performs initial loading of repositories and installed packages"""
|
"""Entry point; performs initial loading of repositories and installed packages"""
|
||||||
global _packages
|
global _packages
|
||||||
global _main_has_run
|
global _main_has_run
|
||||||
@@ -1009,7 +1004,7 @@ def main():
|
|||||||
_main_has_run = True
|
_main_has_run = True
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(PACKAGE_OT_install)
|
bpy.utils.register_class(PACKAGE_OT_install)
|
||||||
bpy.utils.register_class(PACKAGE_OT_uninstall)
|
bpy.utils.register_class(PACKAGE_OT_uninstall)
|
||||||
bpy.utils.register_class(PACKAGE_OT_toggle_enabled)
|
bpy.utils.register_class(PACKAGE_OT_toggle_enabled)
|
||||||
@@ -1046,7 +1041,7 @@ def register():
|
|||||||
# bpy.utils.register_class(PackageManagerPreferences)
|
# bpy.utils.register_class(PackageManagerPreferences)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(PACKAGE_OT_install)
|
bpy.utils.unregister_class(PACKAGE_OT_install)
|
||||||
bpy.utils.unregister_class(PACKAGE_OT_uninstall)
|
bpy.utils.unregister_class(PACKAGE_OT_uninstall)
|
||||||
bpy.utils.unregister_class(PACKAGE_OT_toggle_enabled)
|
bpy.utils.unregister_class(PACKAGE_OT_toggle_enabled)
|
||||||
|
Reference in New Issue
Block a user