Пример #1
0
class PackageManager(object):
    # # add_package_menu_signal is emitted with a tuple containing the package
    # # identifier, package name and the menu item
    # add_package_menu_signal = QtCore.SIGNAL("add_package_menu")
    # # remove_package_menu_signal is emitted with the package identifier
    # remove_package_menu_signal = QtCore.SIGNAL("remove_package_menu")
    # # package_error_message_signal is emitted with the package identifier,
    # # package name and the error message
    # package_error_message_signal = QtCore.SIGNAL("package_error_message_signal")
    # # reloading_package_signal is emitted when a package reload has disabled
    # # the packages, but has not yet enabled them
    # reloading_package_signal = QtCore.SIGNAL("reloading_package_signal")

    class DependencyCycle(Exception):
        def __init__(self, p1, p2):
            self._package_1 = p1
            self._package_2 = p2

        def __str__(self):
            return ("Packages '%s' and '%s' have cyclic dependencies" %
                    (self._package_1, self._package_2))

    class MissingPackage(Exception):
        def __init__(self, n):
            self._package_name = n

        def __str__(self):
            return "Package '%s' is missing." % self._package_name

    class PackageInternalError(Exception):
        def __init__(self, n, d):
            self._package_name = n
            self._description = d

        def __str__(self):
            return "Package '%s' has a bug: %s" % (self._package_name,
                                                   self._description)

    def import_packages_module(self):
        """Imports the packages module using path trickery to find it
        in the right place.

        """
        if self._packages is not None:
            return self._packages
        # Imports standard packages directory
        conf = self._configuration
        old_sys_path = copy.copy(sys.path)
        if conf.check('packageDirectory'):
            sys.path.insert(0, conf.packageDirectory)
        try:
            import packages
        except ImportError:
            debug.critical('ImportError: "packages" sys.path: %s' % sys.path)
            raise
        finally:
            sys.path = old_sys_path
        self._packages = packages
        return packages

    def import_user_packages_module(self):
        """Imports the packages module using path trickery to find it
        in the right place.

        """
        if self._userpackages is not None:
            return self._userpackages
        # Imports user packages directory
        conf = self._configuration
        old_sys_path = copy.copy(sys.path)
        if conf.check('userPackageDirectory'):
            sys.path.insert(
                0, os.path.join(conf.userPackageDirectory, os.path.pardir))
        try:
            import userpackages
        except ImportError:
            debug.critical('ImportError: "userpackages" sys.path: %s' %
                           sys.path)
            raise
        finally:
            sys.path = old_sys_path
        self._userpackages = userpackages
        return userpackages

    def __init__(self, configuration):
        """__init__(configuration: ConfigurationObject) -> PackageManager
        configuration is the persistent configuration object of the application.
        
        """
        global _package_manager
        if _package_manager:
            m = "Package manager can only be constructed once."
            raise VistrailsInternalError(m)
        _package_manager = self
        self._configuration = configuration
        self._package_list = {}
        self._package_versions = {}
        self._dependency_graph = core.data_structures.graph.Graph()
        self._registry = None
        self._userpackages = None
        self._packages = None
        self._abstraction_pkg = None

    def init_registry(self, registry_filename=None):
        if registry_filename is not None:
            self._registry = core.db.io.open_registry(registry_filename)
            self._registry.set_global()
        else:
            self._registry = ModuleRegistry()
            self._registry.set_global()

            def setup_basic_package():
                # setup basic package
                basic_package = self.add_package('basic_modules')
                self._registry._default_package = basic_package
                prefix_dictionary = {'basic_modules': 'core.modules.'}
                self.initialize_packages(prefix_dictionary)

            setup_basic_package()

            self._abstraction_pkg = self.add_package('abstraction', False)
            # FIXME need to get this info from the package, but cannot
            # do this since controller isn't imported yet
            self._abstraction_pkg.identifier = 'local.abstractions'
            self._abstraction_pkg.name = 'My SubWorkflows'
            self._abstraction_pkg.version = '1.6'
            self._registry.add_package(self._abstraction_pkg)

    def finalize_packages(self):
        """Finalizes all installed packages. Call this only prior to
exiting VisTrails."""
        for package in self._package_list.itervalues():
            package.finalize()
        self._package_list = {}
        self._package_versions = {}
        global _package_manager
        _package_manager = None

    def add_package(self, codepath, add_to_package_list=True):
        """Adds a new package to the manager. This does not initialize it.
To do so, call initialize_packages()"""
        package = self._registry.create_package(codepath)
        if add_to_package_list:
            self.add_to_package_list(codepath, package)
        return package

    def add_to_package_list(self, codepath, package):
        self._package_list[codepath] = package

    def initialize_abstraction_pkg(self, prefix_dictionary):
        if self._abstraction_pkg is None:
            raise Exception("Subworkflows packages is None")
        self.add_to_package_list(self._abstraction_pkg.codepath,
                                 self._abstraction_pkg)
        self.late_enable_package(self._abstraction_pkg.codepath,
                                 prefix_dictionary, False)

    def remove_package(self, codepath):
        """remove_package(name): Removes a package from the system."""
        pkg = self._package_list[codepath]
        self._dependency_graph.delete_vertex(pkg.identifier)
        del self._package_versions[pkg.identifier][pkg.version]
        if len(self._package_versions[pkg.identifier]) == 0:
            del self._package_versions[pkg.identifier]
        self.remove_menu_items(pkg)
        pkg.finalize()
        del self._package_list[codepath]
        self._registry.remove_package(pkg)
        app = get_vistrails_application()
        app.send_notification("package_removed", codepath)

    def has_package(self, identifier, version=None):
        """has_package(identifer: string) -> Boolean.
Returns true if given package identifier is present."""
        if identifier in self._package_versions:
            return (version is None
                    or version in self._package_versions[identifier])
        return False

    def look_at_available_package(self, codepath):
        """look_at_available_package(codepath: string) -> Package

        Returns a Package object for an uninstalled package. This does
        NOT install a package.
        """
        return self._registry.create_package(codepath, False)

    def get_package(self, identifier, version=None):
        package_versions = self._package_versions[identifier]
        if version is not None:
            return package_versions[version]

        max_version = '0'
        max_pkg = None
        for version, pkg in package_versions.iteritems():
            if versions_increasing(max_version, version):
                max_version = version
                max_pkg = pkg
        return max_pkg

    def get_package_by_codepath(self, codepath):
        """get_package_by_codepath(codepath: string) -> Package.
        Returns a package with given codepath if it is enabled,
        otherwise throws exception
        """
        if codepath not in self._package_list:
            raise self.MissingPackage(codepath)
        else:
            return self._package_list[codepath]

    def get_package_by_identifier(self, identifier):
        """get_package_by_identifier(identifier: string) -> Package.
        Returns a package with given identifier if it is enabled,
        otherwise throws exception
        """
        if identifier not in self._registry.packages:
            raise self.MissingPackage(identifier)
        return self._registry.packages[identifier]

    def get_package_configuration(self, codepath):
        """get_package_configuration(codepath: string) ->
        ConfigurationObject or None

        Returns the configuration object for the package, if existing,
        or None. Throws MissingPackage if package doesn't exist.
        """

        pkg = self.get_package_by_codepath(codepath)

        if not hasattr(pkg.module, 'configuration'):
            return None
        else:
            c = pkg.module.configuration
            if not isinstance(c, ConfigurationObject):
                d = "'configuration' attribute should be a ConfigurationObject"
                raise self.PackageInternalError(codepath, d)
            return c

    def check_dependencies(self, package, deps):
        # want to check that necessary version also exists, if specified
        missing_deps = []
        for dep in deps:
            min_version = None
            max_version = None
            if type(dep) == tuple:
                identifier = dep[0]
                if len(dep) > 1:
                    min_version = dep[1]
                    if len(dep) > 2:
                        max_version = dep[2]
            else:
                identifier = dep

            if identifier not in self._package_versions:
                missing_deps.append((identifier, None, None))
            else:
                if min_version is None and max_version is None:
                    continue
                found_version = False
                for version, pkg in \
                        self._package_versions[identifier].iteritems():
                    if ((min_version is None
                         or versions_increasing(min_version, version)) and
                        (max_version is None
                         or versions_increasing(version, max_version))):
                        found_version = True
                if not found_version:
                    missing_deps.append((identifier, min_version, max_version))

        if len(missing_deps) > 0:
            raise Package.MissingDependency(package, missing_deps)
        return True

    def add_dependencies(self, package):
        """add_dependencies(package) -> None.  Register all
        dependencies a package contains by calling the appropriate
        callback.

        Does not add multiple dependencies - if a dependency is already there,
        add_dependencies ignores it.
        """
        deps = package.dependencies()
        # FIXME don't hardcode this
        from core.modules.basic_modules import identifier as basic_pkg
        if package.identifier != basic_pkg:
            deps.append(basic_pkg)

        self.check_dependencies(package, deps)

        for dep in deps:
            if type(dep) == tuple:
                dep_name = dep[0]
            else:
                dep_name = dep

            if not self._dependency_graph.has_edge(package.identifier,
                                                   dep_name):
                self._dependency_graph.add_edge(package.identifier, dep_name)

    def late_enable_package(self,
                            package_codepath,
                            prefix_dictionary={},
                            needs_add=True):
        """late_enable_package enables a package 'late', that is,
        after VisTrails initialization. All dependencies need to be
        already enabled.
        """
        if needs_add:
            if package_codepath in self._package_list:
                msg = 'duplicate package identifier: %s' % package_codepath
                raise VistrailsInternalError(msg)
            self.add_package(package_codepath)
        pkg = self.get_package_by_codepath(package_codepath)
        try:
            pkg.load(prefix_dictionary.get(pkg.codepath, None))
        except Exception, e:
            # invert self.add_package
            del self._package_list[package_codepath]
            raise
        self._dependency_graph.add_vertex(pkg.identifier)
        if pkg.identifier not in self._package_versions:
            self._package_versions[pkg.identifier] = {}
        self._package_versions[pkg.identifier][pkg.version] = pkg
        try:
            self.add_dependencies(pkg)
            #check_requirements is now called in pkg.initialize()
            #pkg.check_requirements()
            self._registry.initialize_package(pkg)
            self._registry.signals.emit_new_package(pkg.identifier, True)
            app = get_vistrails_application()
            app.send_notification("package_added", package_codepath)
            self.add_menu_items(pkg)
        except Exception, e:
            del self._package_versions[pkg.identifier][pkg.version]
            if len(self._package_versions[pkg.identifier]) == 0:
                del self._package_versions[pkg.identifier]
            self._dependency_graph.delete_vertex(pkg.identifier)
            # invert self.add_package
            del self._package_list[package_codepath]
            # if we adding the package to the registry, make sure we
            # remove it if initialization fails
            try:
                self._registry.remove_package(pkg)
            except MissingPackage:
                pass
            raise e
class PackageManager(object):
    # # add_package_menu_signal is emitted with a tuple containing the package
    # # identifier, package name and the menu item
    # add_package_menu_signal = QtCore.SIGNAL("add_package_menu")
    # # remove_package_menu_signal is emitted with the package identifier
    # remove_package_menu_signal = QtCore.SIGNAL("remove_package_menu")
    # # package_error_message_signal is emitted with the package identifier,
    # # package name and the error message
    # package_error_message_signal = QtCore.SIGNAL("package_error_message_signal")
    # # reloading_package_signal is emitted when a package reload has disabled
    # # the packages, but has not yet enabled them
    # reloading_package_signal = QtCore.SIGNAL("reloading_package_signal")

    class DependencyCycle(Exception):
        def __init__(self, p1, p2):
            self._package_1 = p1
            self._package_2 = p2
        def __str__(self):
            return ("Packages '%s' and '%s' have cyclic dependencies" %
                    (self._package_1,
                     self._package_2))

    class MissingPackage(Exception):
        def __init__(self, n):
            self._package_name = n
        def __str__(self):
            return "Package '%s' is missing." % self._package_name

    class PackageInternalError(Exception):
        def __init__(self, n, d):
            self._package_name = n
            self._description = d
        def __str__(self):
            return "Package '%s' has a bug: %s" % (self._package_name,
                                                   self._description)

    def import_packages_module(self):
        """Imports the packages module using path trickery to find it
        in the right place.

        """
        if self._packages is not None:
            return self._packages
        # Imports standard packages directory
        conf = self._configuration
        old_sys_path = copy.copy(sys.path)
        if conf.check('packageDirectory'):
            sys.path.insert(0, conf.packageDirectory)
        try:
            import packages
        except ImportError:
            debug.critical('ImportError: "packages" sys.path: %s' % sys.path)
            raise
        finally:
            sys.path = old_sys_path
        self._packages = packages
        return packages

    def import_user_packages_module(self):
        """Imports the packages module using path trickery to find it
        in the right place.

        """
        if self._userpackages is not None:
            return self._userpackages
        # Imports user packages directory
        conf = self._configuration
        old_sys_path = copy.copy(sys.path)
        if conf.check('userPackageDirectory'):
            sys.path.insert(0, os.path.join(conf.userPackageDirectory,
                                            os.path.pardir))
        try:
            import userpackages
        except ImportError:
            debug.critical('ImportError: "userpackages" sys.path: %s' % sys.path)
            raise
        finally:
            sys.path = old_sys_path
        self._userpackages = userpackages
        return userpackages

    def __init__(self, configuration):
        """__init__(configuration: ConfigurationObject) -> PackageManager
        configuration is the persistent configuration object of the application.
        
        """
        global _package_manager
        if _package_manager:
            m = "Package manager can only be constructed once."
            raise VistrailsInternalError(m)
        _package_manager = self
        self._configuration = configuration
        self._package_list = {}
        self._package_versions = {}
        self._dependency_graph = core.data_structures.graph.Graph()
        self._registry = None
        self._userpackages = None
        self._packages = None
        self._abstraction_pkg = None

    def init_registry(self, registry_filename=None):
        if registry_filename is not None:
            self._registry = core.db.io.open_registry(registry_filename)
            self._registry.set_global()
        else:
            self._registry = ModuleRegistry()
            self._registry.set_global()

            def setup_basic_package():
                # setup basic package
                basic_package = self.add_package('basic_modules')
                self._registry._default_package = basic_package
                prefix_dictionary = {'basic_modules': 'core.modules.'}
                self.initialize_packages(prefix_dictionary)
            setup_basic_package()

            self._abstraction_pkg = self.add_package('abstraction', False)
            # FIXME need to get this info from the package, but cannot
            # do this since controller isn't imported yet
            self._abstraction_pkg.identifier = 'local.abstractions'
            self._abstraction_pkg.name = 'My SubWorkflows'
            self._abstraction_pkg.version = '1.6'
            self._registry.add_package(self._abstraction_pkg)

    def finalize_packages(self):
        """Finalizes all installed packages. Call this only prior to
exiting VisTrails."""
        for package in self._package_list.itervalues():
            package.finalize()
        self._package_list = {}
        self._package_versions = {}
        global _package_manager
        _package_manager = None

    def add_package(self, codepath, add_to_package_list=True):
        """Adds a new package to the manager. This does not initialize it.
To do so, call initialize_packages()"""
        package = self._registry.create_package(codepath)
        if add_to_package_list:
            self.add_to_package_list(codepath, package)
        app = get_vistrails_application()
        app.send_notification("package_added", codepath)
        return package

    def add_to_package_list(self, codepath, package):
        self._package_list[codepath] = package

    def initialize_abstraction_pkg(self, prefix_dictionary):
        if self._abstraction_pkg is None:
            raise Exception("Subworkflows packages is None")
        self.add_to_package_list(self._abstraction_pkg.codepath,
                                 self._abstraction_pkg)
        self.late_enable_package(self._abstraction_pkg.codepath, 
                                 prefix_dictionary, False)

    def remove_package(self, codepath):
        """remove_package(name): Removes a package from the system."""
        pkg = self._package_list[codepath]
        self._dependency_graph.delete_vertex(pkg.identifier)
        del self._package_versions[pkg.identifier][pkg.version]
        if len(self._package_versions[pkg.identifier]) == 0:
            del self._package_versions[pkg.identifier]
        self.remove_menu_items(pkg)
        pkg.finalize()
        del self._package_list[codepath]
        self._registry.remove_package(pkg)
        app = get_vistrails_application()
        app.send_notification("package_removed", codepath)

    def has_package(self, identifier, version=None):
        """has_package(identifer: string) -> Boolean.
Returns true if given package identifier is present."""
        if identifier in self._package_versions:
            return (version is None or 
                    version in self._package_versions[identifier])
        return False

    def look_at_available_package(self, codepath):
        """look_at_available_package(codepath: string) -> Package

        Returns a Package object for an uninstalled package. This does
        NOT install a package.
        """
        return self._registry.create_package(codepath, False)

    def get_package(self, identifier, version=None):
        package_versions = self._package_versions[identifier]
        if version is not None:
            return package_versions[version]

        max_version = '0'
        max_pkg = None
        for version, pkg in package_versions.iteritems():
            if versions_increasing(max_version, version):
                max_version = version
                max_pkg = pkg
        return max_pkg

    def get_package_by_codepath(self, codepath):
        """get_package_by_codepath(codepath: string) -> Package.
        Returns a package with given codepath if it is enabled,
        otherwise throws exception
        """
        if codepath not in self._package_list:
            raise self.MissingPackage(codepath)
        else:
            return self._package_list[codepath]

    def get_package_by_identifier(self, identifier):
        """get_package_by_identifier(identifier: string) -> Package.
        Returns a package with given identifier if it is enabled,
        otherwise throws exception
        """
        if identifier not in self._registry.packages:
            raise self.MissingPackage(identifier)
        return self._registry.packages[identifier]

    def get_package_configuration(self, codepath):
        """get_package_configuration(codepath: string) ->
        ConfigurationObject or None

        Returns the configuration object for the package, if existing,
        or None. Throws MissingPackage if package doesn't exist.
        """

        pkg = self.get_package_by_codepath(codepath)

        if not hasattr(pkg.module, 'configuration'):
            return None
        else:
            c = pkg.module.configuration
            if not isinstance(c, ConfigurationObject):
                d = "'configuration' attribute should be a ConfigurationObject"
                raise self.PackageInternalError(codepath, d)
            return c

    def check_dependencies(self, package, deps):
        # want to check that necessary version also exists, if specified
        missing_deps = []
        for dep in deps:
            min_version = None
            max_version = None
            if type(dep) == tuple:
                identifier = dep[0]
                if len(dep) > 1:
                    min_version = dep[1]
                    if len(dep) > 2:
                        max_version = dep[2]
            else:
                identifier = dep

            if identifier not in self._package_versions:
                missing_deps.append((identifier, None, None))
            else:
                if min_version is None and max_version is None:
                    continue
                found_version = False
                for version, pkg in \
                        self._package_versions[identifier].iteritems():
                    if ((min_version is None or
                         versions_increasing(min_version, version)) and
                        (max_version is None or
                         versions_increasing(version, max_version))):
                        found_version = True
                if not found_version:
                    missing_deps.append((identifier, min_version, max_version))

        if len(missing_deps) > 0:
            raise Package.MissingDependency(package, missing_deps)
        return True

    def add_dependencies(self, package):
        """add_dependencies(package) -> None.  Register all
        dependencies a package contains by calling the appropriate
        callback.

        Does not add multiple dependencies - if a dependency is already there,
        add_dependencies ignores it.
        """
        deps = package.dependencies()
        # FIXME don't hardcode this
        from core.modules.basic_modules import identifier as basic_pkg
        if package.identifier != basic_pkg:
            deps.append(basic_pkg)

        self.check_dependencies(package, deps)

        for dep in deps:
            if type(dep) == tuple:
                dep_name = dep[0]
            else:
                dep_name = dep

            if not self._dependency_graph.has_edge(package.identifier,
                                                   dep_name):
                self._dependency_graph.add_edge(package.identifier, dep_name)

    def late_enable_package(self, package_codepath, prefix_dictionary={}, 
                            needs_add=True):
        """late_enable_package enables a package 'late', that is,
        after VisTrails initialization. All dependencies need to be
        already enabled.
        """
        if needs_add:
            if package_codepath in self._package_list:
                msg = 'duplicate package identifier: %s' % package_codepath
                raise VistrailsInternalError(msg)
            self.add_package(package_codepath)
        pkg = self.get_package_by_codepath(package_codepath)
        try:
            pkg.load(prefix_dictionary.get(pkg.codepath, None))
        except Exception, e:
            # invert self.add_package
            del self._package_list[package_codepath]
            raise
        self._dependency_graph.add_vertex(pkg.identifier)
        if pkg.identifier not in self._package_versions:
            self._package_versions[pkg.identifier] = {}
        self._package_versions[pkg.identifier][pkg.version] = pkg
        try:
            self.add_dependencies(pkg)
            #check_requirements is now called in pkg.initialize()
            #pkg.check_requirements()
            self._registry.initialize_package(pkg)
            # FIXME Empty packages still need to be added, but currently they are not
            # because newPackage is typically only called for the first module inside
            # a package.
            from core.modules.abstraction import identifier as abstraction_identifier
            if pkg.identifier == abstraction_identifier:
                self._registry.signals.emit_new_package(abstraction_identifier, True)
        except Exception, e:
            del self._package_versions[pkg.identifier][pkg.version]
            if len(self._package_versions[pkg.identifier]) == 0:
                del self._package_versions[pkg.identifier]
            self._dependency_graph.delete_vertex(pkg.identifier)
            # invert self.add_package
            del self._package_list[package_codepath]
            # if we adding the package to the registry, make sure we
            # remove it if initialization fails
            try:
                self._registry.remove_package(pkg)
            except MissingPackage:
                pass
            raise e