Example #1
0
    def loadPackage(self,
                    tex: TeX,
                    file_name: str,
                    options: Optional[Dict] = None) -> bool:
        """
        Load a Python or LaTeX package

        A Python version of the package is searched for first,
        if one cannot be found then a LaTeX version of the package
        is searched for if config['general']['load-tex-packages']
        is True, or the package has been white-listed in
        config['general']['tex-packages'].

        Python versions are first searched for in
        the directories of config['general']['packages-dirs'], then
        in plugins listed in config['general']['plugins'], and then
        among builtin packages.

        Required Arguments:
        tex -- the instance of the TeX engine to use for parsing
            the LaTeX file, if needed.
        file_name -- the name of the file to load

        Keyword Arguments:
        options -- the options given on the macro to pass to the package

        Returns:
        boolean indicating whether or not the package loaded successfully

        """
        config = tex.ownerDocument.config
        working_dir = tex.ownerDocument.userdata.get('working-dir', '')
        options = options or {}
        module = os.path.splitext(file_name)[0]
        # See if it has already been loaded
        if module in self.packages:
            return True

        packagesini = os.path.join(os.path.dirname(plasTeX.Packages.__file__),
                                   os.path.basename(module) + '.ini')

        imported = None
        orig_sys_path = sys.path

        for pkg_dir in config['general']['packages-dirs']:
            if Path(pkg_dir).is_absolute():
                path = Path(pkg_dir)
            else:
                path = (Path(working_dir) / pkg_dir).absolute()

            pypath = (path / module).with_suffix('.py')
            sys.path.insert(0, str(path))
            spec = find_spec(module)
            if spec is None:
                sys.path = orig_sys_path
                continue
            if (module in sys.modules and pypath.exists()):
                if spec.origin != str(pypath):
                    log.warning(
                        'Python has already loaded a module named {} '
                        ' independently from plasTeX, so we cannot load '
                        'it from {}. You can fix this by creating a '
                        'plugin.'.format(module, pkg_dir))
                break
            if spec.origin:
                # python found the module but not in the expected place,
                # so we give up on this package dir.
                continue

            try:
                imported = import_module(module)
            except (ImportError, ModuleNotFoundError) as msg:
                # Error while importing
                sys.path = orig_sys_path
                raise

        if imported is None:
            for plugin in reversed(config['general']['plugins']):
                sys.path = orig_sys_path
                plugin_module = import_module(plugin)
                sys.path.insert(
                    0, str(Path(plugin_module.__file__).parent.parent))
                try:
                    imported = import_module(plugin + '.Packages.' + module)
                    break
                except (ImportError, ModuleNotFoundError) as msg:
                    # No Python module
                    if 'No module' in str(msg) and module in str(msg):
                        pass
                        # Failed to load Python package
                    # Error while importing
                    else:
                        sys.path = orig_sys_path
                        raise

        if imported is None:
            # Now try builtin plasTeX packages
            sys.path.insert(0, str(Path(__file__).parent.parent))
            try:
                imported = import_module('plasTeX.Packages.' + module)
            except (ImportError, ModuleNotFoundError) as msg:
                # No Python module
                if 'No module' in str(msg) and module in str(msg):
                    pass
                    # Failed to load Python package
                # Error while importing
                else:
                    sys.path = orig_sys_path
                    raise

        sys.path = orig_sys_path

        if imported:
            status.info(' (loading package %s ' % imported.__file__)
            if hasattr(imported, 'ProcessOptions'):
                imported.ProcessOptions(options,
                                        tex.ownerDocument)  # type: ignore
            self.importMacros(vars(imported))
            moduleini = os.path.splitext(imported.__file__)[0] + '.ini'
            self.loadINIPackage([packagesini, moduleini])
            self.packages[module] = options
            status.info(' ) ')
            return True

        log.warning('No Python version of %s was found' % file_name)

        # Try to load a LaTeX implementation
        if (config['general']['load-tex-packages']
                or module in config['general']['tex-packages']):
            log.warning('Will now try to load a LaTeX implementation of %s' %
                        file_name)
            result = tex.loadPackage(file_name, options)
            try:
                moduleini = os.path.join(
                    os.path.dirname(tex.kpsewhich(file_name)),
                    os.path.basename(module) + '.ini')
                self.loadINIPackage([packagesini, moduleini])
            except OSError:
                pass
            return result
        return False
Example #2
0
    def loadPackage(self,
                    tex: TeX,
                    file_name: str,
                    options: Optional[Dict] = None) -> bool:
        """
        Load a Python or LaTeX package

        A Python version of the package is searched for first,
        if one cannot be found then a LaTeX version of the package
        is searched for if config['general']['load-tex-packages']
        is True, or the package has been white-listed in
        config['general']['tex-packages'].

        Python versions are first searched for in
        the directories of config['general']['packages-dirs'], then
        in plugins listed in config['general']['plugins'], and this
        among builtin packages.

        Required Arguments:
        tex -- the instance of the TeX engine to use for parsing
            the LaTeX file, if needed.
        file_name -- the name of the file to load

        Keyword Arguments:
        options -- the options given on the macro to pass to the package

        Returns:
        boolean indicating whether or not the package loaded successfully

        """
        config = tex.ownerDocument.config
        working_dir = tex.ownerDocument.userdata.get('working-dir', '')
        options = options or {}
        module = os.path.splitext(file_name)[0]
        # See if it has already been loaded
        if module in self.packages:
            return True

        packagesini = os.path.join(os.path.dirname(plasTeX.Packages.__file__),
                                   os.path.basename(module) + '.ini')

        imported = None
        for pkg_dir in config['general']['packages-dirs']:
            if Path(pkg_dir).is_absolute():
                path = str(Path(pkg_dir))
            else:
                path = str(Path(working_dir) / path)
            sys.path.insert(0, path)
            try:
                imported = import_module(module)
                sys.path.remove(path)
                break
            except ImportError as msg:
                # No Python module
                if 'No module' in str(msg) and module in str(msg):
                    pass
                    # Failed to load Python package
                # Error while importing
                else:
                    sys.path.remove(path)
                    raise
            sys.path.remove(path)

        if imported is None:
            for plugin in reversed(config['general']['plugins']):
                plugin_module = import_module(plugin)
                path = str(Path(plugin_module.__file__).parent / 'Packages')
                sys.path.insert(0, path)
                try:
                    imported = import_module(module)
                    sys.path.remove(path)
                    break
                except ImportError as msg:
                    # No Python module
                    if 'No module' in str(msg) and module in str(msg):
                        pass
                        # Failed to load Python package
                    # Error while importing
                    else:
                        sys.path.remove(path)
                        raise
                sys.path.remove(path)

        if imported is None:
            # Now try builtin plasTeX packages
            path = str(Path(__file__).parent / 'Packages')
            sys.path.insert(0, path)
            try:
                imported = import_module(module)
            except ImportError as msg:
                # No Python module
                if 'No module' in str(msg) and module in str(msg):
                    pass
                    # Failed to load Python package
                # Error while importing
                else:
                    sys.path.remove(path)
                    raise
            sys.path.remove(path)

        if imported:
            status.info(' (loading package %s ' % imported.__file__)
            if hasattr(imported, 'ProcessOptions'):
                imported.ProcessOptions(options,
                                        tex.ownerDocument)  # type: ignore
            self.importMacros(vars(imported))
            moduleini = os.path.splitext(imported.__file__)[0] + '.ini'
            self.loadINIPackage([packagesini, moduleini])
            self.packages[module] = options
            status.info(' ) ')
            return True

        log.warning('No Python version of %s was found' % file_name)

        # Try to load a LaTeX implementation
        if (config['general']['load-tex-packages']
                or module in config['general']['tex-packages']):
            log.warning('Will now try to load a LaTeX implementation of %s' %
                        file_name)
            result = tex.loadPackage(file_name, options)
            try:
                moduleini = os.path.join(
                    os.path.dirname(tex.kpsewhich(file_name)),
                    os.path.basename(module) + '.ini')
                self.loadINIPackage([packagesini, moduleini])
            except OSError:
                pass
            return result
        return False