Blame numpy/_import_tools.py

Packit 7a8e5e
from __future__ import division, absolute_import, print_function
Packit 7a8e5e
Packit 7a8e5e
import os
Packit 7a8e5e
import sys
Packit 7a8e5e
import warnings
Packit 7a8e5e
Packit 7a8e5e
__all__ = ['PackageLoader']
Packit 7a8e5e
Packit 7a8e5e
class PackageLoader(object):
Packit 7a8e5e
    def __init__(self, verbose=False, infunc=False):
Packit 7a8e5e
        """ Manages loading packages.
Packit 7a8e5e
        """
Packit 7a8e5e
Packit 7a8e5e
        if infunc:
Packit 7a8e5e
            _level = 2
Packit 7a8e5e
        else:
Packit 7a8e5e
            _level = 1
Packit 7a8e5e
        self.parent_frame = frame = sys._getframe(_level)
Packit 7a8e5e
        self.parent_name = eval('__name__', frame.f_globals, frame.f_locals)
Packit 7a8e5e
        parent_path = eval('__path__', frame.f_globals, frame.f_locals)
Packit 7a8e5e
        if isinstance(parent_path, str):
Packit 7a8e5e
            parent_path = [parent_path]
Packit 7a8e5e
        self.parent_path = parent_path
Packit 7a8e5e
        if '__all__' not in frame.f_locals:
Packit 7a8e5e
            exec('__all__ = []', frame.f_globals, frame.f_locals)
Packit 7a8e5e
        self.parent_export_names = eval('__all__', frame.f_globals, frame.f_locals)
Packit 7a8e5e
Packit 7a8e5e
        self.info_modules = {}
Packit 7a8e5e
        self.imported_packages = []
Packit 7a8e5e
        self.verbose = None
Packit 7a8e5e
Packit 7a8e5e
    def _get_info_files(self, package_dir, parent_path, parent_package=None):
Packit 7a8e5e
        """ Return list of (package name,info.py file) from parent_path subdirectories.
Packit 7a8e5e
        """
Packit 7a8e5e
        from glob import glob
Packit 7a8e5e
        files = glob(os.path.join(parent_path, package_dir, 'info.py'))
Packit 7a8e5e
        for info_file in glob(os.path.join(parent_path, package_dir, 'info.pyc')):
Packit 7a8e5e
            if info_file[:-1] not in files:
Packit 7a8e5e
                files.append(info_file)
Packit 7a8e5e
        info_files = []
Packit 7a8e5e
        for info_file in files:
Packit 7a8e5e
            package_name = os.path.dirname(info_file[len(parent_path)+1:])\
Packit 7a8e5e
                           .replace(os.sep, '.')
Packit 7a8e5e
            if parent_package:
Packit 7a8e5e
                package_name = parent_package + '.' + package_name
Packit 7a8e5e
            info_files.append((package_name, info_file))
Packit 7a8e5e
            info_files.extend(self._get_info_files('*',
Packit 7a8e5e
                                                   os.path.dirname(info_file),
Packit 7a8e5e
                                                   package_name))
Packit 7a8e5e
        return info_files
Packit 7a8e5e
Packit 7a8e5e
    def _init_info_modules(self, packages=None):
Packit 7a8e5e
        """Initialize info_modules = {<package_name>: <package info.py module>}.
Packit 7a8e5e
        """
Packit 7a8e5e
        from numpy.compat import npy_load_module
Packit 7a8e5e
        info_files = []
Packit 7a8e5e
        info_modules = self.info_modules
Packit 7a8e5e
Packit 7a8e5e
        if packages is None:
Packit 7a8e5e
            for path in self.parent_path:
Packit 7a8e5e
                info_files.extend(self._get_info_files('*', path))
Packit 7a8e5e
        else:
Packit 7a8e5e
            for package_name in packages:
Packit 7a8e5e
                package_dir = os.path.join(*package_name.split('.'))
Packit 7a8e5e
                for path in self.parent_path:
Packit 7a8e5e
                    names_files = self._get_info_files(package_dir, path)
Packit 7a8e5e
                    if names_files:
Packit 7a8e5e
                        info_files.extend(names_files)
Packit 7a8e5e
                        break
Packit 7a8e5e
                else:
Packit 7a8e5e
                    try:
Packit 7a8e5e
                        exec('import %s.info as info' % (package_name))
Packit 7a8e5e
                        info_modules[package_name] = info
Packit 7a8e5e
                    except ImportError as msg:
Packit 7a8e5e
                        self.warn('No scipy-style subpackage %r found in %s. '\
Packit 7a8e5e
                                  'Ignoring: %s'\
Packit 7a8e5e
                                  % (package_name, ':'.join(self.parent_path), msg))
Packit 7a8e5e
Packit 7a8e5e
        for package_name, info_file in info_files:
Packit 7a8e5e
            if package_name in info_modules:
Packit 7a8e5e
                continue
Packit 7a8e5e
            fullname = self.parent_name +'.'+ package_name
Packit 7a8e5e
            if info_file[-1]=='c':
Packit 7a8e5e
                filedescriptor = ('.pyc', 'rb', 2)
Packit 7a8e5e
            else:
Packit 7a8e5e
                filedescriptor = ('.py', 'U', 1)
Packit 7a8e5e
Packit 7a8e5e
            try:
Packit 7a8e5e
                info_module = npy_load_module(fullname + '.info',
Packit 7a8e5e
                                              info_file,
Packit 7a8e5e
                                              filedescriptor)
Packit 7a8e5e
            except Exception as msg:
Packit 7a8e5e
                self.error(msg)
Packit 7a8e5e
                info_module = None
Packit 7a8e5e
Packit 7a8e5e
            if info_module is None or getattr(info_module, 'ignore', False):
Packit 7a8e5e
                info_modules.pop(package_name, None)
Packit 7a8e5e
            else:
Packit 7a8e5e
                self._init_info_modules(getattr(info_module, 'depends', []))
Packit 7a8e5e
                info_modules[package_name] = info_module
Packit 7a8e5e
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    def _get_sorted_names(self):
Packit 7a8e5e
        """ Return package names sorted in the order as they should be
Packit 7a8e5e
        imported due to dependence relations between packages.
Packit 7a8e5e
        """
Packit 7a8e5e
Packit 7a8e5e
        depend_dict = {}
Packit 7a8e5e
        for name, info_module in self.info_modules.items():
Packit 7a8e5e
            depend_dict[name] = getattr(info_module, 'depends', [])
Packit 7a8e5e
        package_names = []
Packit 7a8e5e
Packit 7a8e5e
        for name in list(depend_dict.keys()):
Packit 7a8e5e
            if not depend_dict[name]:
Packit 7a8e5e
                package_names.append(name)
Packit 7a8e5e
                del depend_dict[name]
Packit 7a8e5e
Packit 7a8e5e
        while depend_dict:
Packit 7a8e5e
            for name, lst in list(depend_dict.items()):
Packit 7a8e5e
                new_lst = [n for n in lst if n in depend_dict]
Packit 7a8e5e
                if not new_lst:
Packit 7a8e5e
                    package_names.append(name)
Packit 7a8e5e
                    del depend_dict[name]
Packit 7a8e5e
                else:
Packit 7a8e5e
                    depend_dict[name] = new_lst
Packit 7a8e5e
Packit 7a8e5e
        return package_names
Packit 7a8e5e
Packit 7a8e5e
    def __call__(self,*packages, **options):
Packit 7a8e5e
        """Load one or more packages into parent package top-level namespace.
Packit 7a8e5e
Packit 7a8e5e
       This function is intended to shorten the need to import many
Packit 7a8e5e
       subpackages, say of scipy, constantly with statements such as
Packit 7a8e5e
Packit 7a8e5e
         import scipy.linalg, scipy.fftpack, scipy.etc...
Packit 7a8e5e
Packit 7a8e5e
       Instead, you can say:
Packit 7a8e5e
Packit 7a8e5e
         import scipy
Packit 7a8e5e
         scipy.pkgload('linalg','fftpack',...)
Packit 7a8e5e
Packit 7a8e5e
       or
Packit 7a8e5e
Packit 7a8e5e
         scipy.pkgload()
Packit 7a8e5e
Packit 7a8e5e
       to load all of them in one call.
Packit 7a8e5e
Packit 7a8e5e
       If a name which doesn't exist in scipy's namespace is
Packit 7a8e5e
       given, a warning is shown.
Packit 7a8e5e
Packit 7a8e5e
       Parameters
Packit 7a8e5e
       ----------
Packit 7a8e5e
        *packages : arg-tuple
Packit 7a8e5e
             the names (one or more strings) of all the modules one
Packit 7a8e5e
             wishes to load into the top-level namespace.
Packit 7a8e5e
        verbose= : integer
Packit 7a8e5e
             verbosity level [default: -1].
Packit 7a8e5e
             verbose=-1 will suspend also warnings.
Packit 7a8e5e
        force= : bool
Packit 7a8e5e
             when True, force reloading loaded packages [default: False].
Packit 7a8e5e
        postpone= : bool
Packit 7a8e5e
             when True, don't load packages [default: False]
Packit 7a8e5e
Packit 7a8e5e
        """
Packit 7a8e5e
        # 2014-10-29, 1.10
Packit 7a8e5e
        warnings.warn('pkgload and PackageLoader are obsolete '
Packit 7a8e5e
                'and will be removed in a future version of numpy',
Packit 7a8e5e
                DeprecationWarning, stacklevel=2)
Packit 7a8e5e
        frame = self.parent_frame
Packit 7a8e5e
        self.info_modules = {}
Packit 7a8e5e
        if options.get('force', False):
Packit 7a8e5e
            self.imported_packages = []
Packit 7a8e5e
        self.verbose = verbose = options.get('verbose', -1)
Packit 7a8e5e
        postpone = options.get('postpone', None)
Packit 7a8e5e
        self._init_info_modules(packages or None)
Packit 7a8e5e
Packit 7a8e5e
        self.log('Imports to %r namespace\n----------------------------'\
Packit 7a8e5e
                 % self.parent_name)
Packit 7a8e5e
Packit 7a8e5e
        for package_name in self._get_sorted_names():
Packit 7a8e5e
            if package_name in self.imported_packages:
Packit 7a8e5e
                continue
Packit 7a8e5e
            info_module = self.info_modules[package_name]
Packit 7a8e5e
            global_symbols = getattr(info_module, 'global_symbols', [])
Packit 7a8e5e
            postpone_import = getattr(info_module, 'postpone_import', False)
Packit 7a8e5e
            if (postpone and not global_symbols) \
Packit 7a8e5e
                   or (postpone_import and postpone is not None):
Packit 7a8e5e
                continue
Packit 7a8e5e
Packit 7a8e5e
            old_object = frame.f_locals.get(package_name, None)
Packit 7a8e5e
Packit 7a8e5e
            cmdstr = 'import '+package_name
Packit 7a8e5e
            if self._execcmd(cmdstr):
Packit 7a8e5e
                continue
Packit 7a8e5e
            self.imported_packages.append(package_name)
Packit 7a8e5e
Packit 7a8e5e
            if verbose!=-1:
Packit 7a8e5e
                new_object = frame.f_locals.get(package_name)
Packit 7a8e5e
                if old_object is not None and old_object is not new_object:
Packit 7a8e5e
                    self.warn('Overwriting %s=%s (was %s)' \
Packit 7a8e5e
                              % (package_name, self._obj2repr(new_object),
Packit 7a8e5e
                                 self._obj2repr(old_object)))
Packit 7a8e5e
Packit 7a8e5e
            if '.' not in package_name:
Packit 7a8e5e
                self.parent_export_names.append(package_name)
Packit 7a8e5e
Packit 7a8e5e
            for symbol in global_symbols:
Packit 7a8e5e
                if symbol=='*':
Packit 7a8e5e
                    symbols = eval('getattr(%s,"__all__",None)'\
Packit 7a8e5e
                                   % (package_name),
Packit 7a8e5e
                                   frame.f_globals, frame.f_locals)
Packit 7a8e5e
                    if symbols is None:
Packit 7a8e5e
                        symbols = eval('dir(%s)' % (package_name),
Packit 7a8e5e
                                       frame.f_globals, frame.f_locals)
Packit 7a8e5e
                        symbols = [s for s in symbols if not s.startswith('_')]
Packit 7a8e5e
                else:
Packit 7a8e5e
                    symbols = [symbol]
Packit 7a8e5e
Packit 7a8e5e
                if verbose!=-1:
Packit 7a8e5e
                    old_objects = {}
Packit 7a8e5e
                    for s in symbols:
Packit 7a8e5e
                        if s in frame.f_locals:
Packit 7a8e5e
                            old_objects[s] = frame.f_locals[s]
Packit 7a8e5e
Packit 7a8e5e
                cmdstr = 'from '+package_name+' import '+symbol
Packit 7a8e5e
                if self._execcmd(cmdstr):
Packit 7a8e5e
                    continue
Packit 7a8e5e
Packit 7a8e5e
                if verbose!=-1:
Packit 7a8e5e
                    for s, old_object in old_objects.items():
Packit 7a8e5e
                        new_object = frame.f_locals[s]
Packit 7a8e5e
                        if new_object is not old_object:
Packit 7a8e5e
                            self.warn('Overwriting %s=%s (was %s)' \
Packit 7a8e5e
                                      % (s, self._obj2repr(new_object),
Packit 7a8e5e
                                         self._obj2repr(old_object)))
Packit 7a8e5e
Packit 7a8e5e
                if symbol=='*':
Packit 7a8e5e
                    self.parent_export_names.extend(symbols)
Packit 7a8e5e
                else:
Packit 7a8e5e
                    self.parent_export_names.append(symbol)
Packit 7a8e5e
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    def _execcmd(self, cmdstr):
Packit 7a8e5e
        """ Execute command in parent_frame."""
Packit 7a8e5e
        frame = self.parent_frame
Packit 7a8e5e
        try:
Packit 7a8e5e
            exec (cmdstr, frame.f_globals, frame.f_locals)
Packit 7a8e5e
        except Exception as msg:
Packit 7a8e5e
            self.error('%s -> failed: %s' % (cmdstr, msg))
Packit 7a8e5e
            return True
Packit 7a8e5e
        else:
Packit 7a8e5e
            self.log('%s -> success' % (cmdstr))
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    def _obj2repr(self, obj):
Packit 7a8e5e
        """ Return repr(obj) with"""
Packit 7a8e5e
        module = getattr(obj, '__module__', None)
Packit 7a8e5e
        file = getattr(obj, '__file__', None)
Packit 7a8e5e
        if module is not None:
Packit 7a8e5e
            return repr(obj) + ' from ' + module
Packit 7a8e5e
        if file is not None:
Packit 7a8e5e
            return repr(obj) + ' from ' + file
Packit 7a8e5e
        return repr(obj)
Packit 7a8e5e
Packit 7a8e5e
    def log(self, mess):
Packit 7a8e5e
        if self.verbose>1:
Packit 7a8e5e
            print(str(mess), file=sys.stderr)
Packit 7a8e5e
    def warn(self, mess):
Packit 7a8e5e
        if self.verbose>=0:
Packit 7a8e5e
            print(str(mess), file=sys.stderr)
Packit 7a8e5e
    def error(self, mess):
Packit 7a8e5e
        if self.verbose!=-1:
Packit 7a8e5e
            print(str(mess), file=sys.stderr)
Packit 7a8e5e
Packit 7a8e5e
    def _get_doc_title(self, info_module):
Packit 7a8e5e
        """ Get the title from a package info.py file.
Packit 7a8e5e
        """
Packit 7a8e5e
        title = getattr(info_module, '__doc_title__', None)
Packit 7a8e5e
        if title is not None:
Packit 7a8e5e
            return title
Packit 7a8e5e
        title = getattr(info_module, '__doc__', None)
Packit 7a8e5e
        if title is not None:
Packit 7a8e5e
            title = title.lstrip().split('\n', 1)[0]
Packit 7a8e5e
            return title
Packit 7a8e5e
        return '* Not Available *'
Packit 7a8e5e
Packit 7a8e5e
    def _format_titles(self,titles,colsep='---'):
Packit 7a8e5e
        display_window_width = 70 # How to determine the correct value in runtime??
Packit 7a8e5e
        lengths = [len(name)-name.find('.')-1 for (name, title) in titles]+[0]
Packit 7a8e5e
        max_length = max(lengths)
Packit 7a8e5e
        lines = []
Packit 7a8e5e
        for (name, title) in titles:
Packit 7a8e5e
            name = name[name.find('.')+1:]
Packit 7a8e5e
            w = max_length - len(name)
Packit 7a8e5e
            words = title.split()
Packit 7a8e5e
            line = '%s%s %s' % (name, w*' ', colsep)
Packit 7a8e5e
            tab = len(line) * ' '
Packit 7a8e5e
            while words:
Packit 7a8e5e
                word = words.pop(0)
Packit 7a8e5e
                if len(line)+len(word)>display_window_width:
Packit 7a8e5e
                    lines.append(line)
Packit 7a8e5e
                    line = tab
Packit 7a8e5e
                line += ' ' + word
Packit 7a8e5e
            lines.append(line)
Packit 7a8e5e
        return '\n'.join(lines)
Packit 7a8e5e
Packit 7a8e5e
    def get_pkgdocs(self):
Packit 7a8e5e
        """ Return documentation summary of subpackages.
Packit 7a8e5e
        """
Packit 7a8e5e
        import sys
Packit 7a8e5e
        self.info_modules = {}
Packit 7a8e5e
        self._init_info_modules(None)
Packit 7a8e5e
Packit 7a8e5e
        titles = []
Packit 7a8e5e
        symbols = []
Packit 7a8e5e
        for package_name, info_module in self.info_modules.items():
Packit 7a8e5e
            global_symbols = getattr(info_module, 'global_symbols', [])
Packit 7a8e5e
            fullname = self.parent_name +'.'+ package_name
Packit 7a8e5e
            note = ''
Packit 7a8e5e
            if fullname not in sys.modules:
Packit 7a8e5e
                note = ' [*]'
Packit 7a8e5e
            titles.append((fullname, self._get_doc_title(info_module) + note))
Packit 7a8e5e
            if global_symbols:
Packit 7a8e5e
                symbols.append((package_name, ', '.join(global_symbols)))
Packit 7a8e5e
Packit 7a8e5e
        retstr = self._format_titles(titles) +\
Packit 7a8e5e
               '\n  [*] - using a package requires explicit import (see pkgload)'
Packit 7a8e5e
Packit 7a8e5e
Packit 7a8e5e
        if symbols:
Packit 7a8e5e
            retstr += """\n\nGlobal symbols from subpackages"""\
Packit 7a8e5e
                      """\n-------------------------------\n""" +\
Packit 7a8e5e
                      self._format_titles(symbols, '-->')
Packit 7a8e5e
Packit 7a8e5e
        return retstr
Packit 7a8e5e
Packit 7a8e5e
class PackageLoaderDebug(PackageLoader):
Packit 7a8e5e
    def _execcmd(self, cmdstr):
Packit 7a8e5e
        """ Execute command in parent_frame."""
Packit 7a8e5e
        frame = self.parent_frame
Packit 7a8e5e
        print('Executing', repr(cmdstr), '...', end=' ')
Packit 7a8e5e
        sys.stdout.flush()
Packit 7a8e5e
        exec (cmdstr, frame.f_globals, frame.f_locals)
Packit 7a8e5e
        print('ok')
Packit 7a8e5e
        sys.stdout.flush()
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
if int(os.environ.get('NUMPY_IMPORT_DEBUG', '0')):
Packit 7a8e5e
    PackageLoader = PackageLoaderDebug