mercurial/packagescan.py
changeset 3887 79e5a6e7c451
parent 3884 d83b125b7d7e
parent 3886 abaee83ce0a6
child 3888 3b628b5da9e9
equal deleted inserted replaced
3884:d83b125b7d7e 3887:79e5a6e7c451
     1 # packagescan.py - Helper module for identifing used modules.
       
     2 # Used for the py2exe distutil.
       
     3 # This module must be the first mercurial module imported in setup.py
       
     4 #
       
     5 # Copyright 2005, 2006 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
       
     6 #
       
     7 # This software may be used and distributed according to the terms
       
     8 # of the GNU General Public License, incorporated herein by reference.
       
     9 import glob
       
    10 import os
       
    11 import sys
       
    12 import ihooks
       
    13 import types
       
    14 import string
       
    15 
       
    16 # Install this module as fake demandload module
       
    17 sys.modules['mercurial.demandload'] = sys.modules[__name__]
       
    18 
       
    19 # Requiredmodules contains the modules imported by demandload.
       
    20 # Please note that demandload can be invoked before the
       
    21 # mercurial.packagescan.scan method is invoked in case a mercurial
       
    22 # module is imported.
       
    23 requiredmodules = {}
       
    24 def demandload(scope, modules):
       
    25     """ fake demandload function that collects the required modules
       
    26         foo            import foo
       
    27         foo bar        import foo, bar
       
    28         foo.bar        import foo.bar
       
    29         foo@bar        import foo as bar
       
    30         foo:bar        from foo import bar
       
    31         foo:bar,quux   from foo import bar, quux
       
    32         foo.bar:quux   from foo.bar import quux"""
       
    33 
       
    34     for m in modules.split():
       
    35         mod = None
       
    36         try:
       
    37             module, fromlist = m.split(':')
       
    38             fromlist = fromlist.split(',')
       
    39         except:
       
    40             module = m
       
    41             fromlist = []
       
    42         as_ = None
       
    43         if '@' in module:
       
    44             module, as_ = module.split('@')
       
    45         mod = __import__(module, scope, scope, fromlist)
       
    46         if fromlist == []:
       
    47             # mod is only the top package, but we need all packages
       
    48             comp = module.split('.')
       
    49             i = 1
       
    50             mn = comp[0]
       
    51             while True:
       
    52                 # mn and mod.__name__ might not be the same
       
    53                 if not as_:
       
    54                     as_ = mn
       
    55                 scope[as_] = mod
       
    56                 requiredmodules[mod.__name__] = 1
       
    57                 if len(comp) == i: break
       
    58                 mod = getattr(mod, comp[i])
       
    59                 mn = string.join(comp[:i+1],'.')
       
    60                 i += 1
       
    61         else:
       
    62             # mod is the last package in the component list
       
    63             requiredmodules[mod.__name__] = 1
       
    64             for f in fromlist:
       
    65                 scope[f] = getattr(mod, f)
       
    66                 if type(scope[f]) == types.ModuleType:
       
    67                     requiredmodules[scope[f].__name__] = 1
       
    68 
       
    69 class SkipPackage(Exception):
       
    70     def __init__(self, reason):
       
    71         self.reason = reason
       
    72 
       
    73 scan_in_progress = False
       
    74 
       
    75 def scan(libpath, packagename):
       
    76     """ helper for finding all required modules of package <packagename> """
       
    77     global scan_in_progress
       
    78     scan_in_progress = True
       
    79     # Use the package in the build directory
       
    80     libpath = os.path.abspath(libpath)
       
    81     sys.path.insert(0, libpath)
       
    82     packdir = os.path.join(libpath, packagename.replace('.', '/'))
       
    83     # A normal import would not find the package in
       
    84     # the build directory. ihook is used to force the import.
       
    85     # After the package is imported the import scope for
       
    86     # the following imports is settled.
       
    87     p = importfrom(packdir)
       
    88     globals()[packagename] = p
       
    89     sys.modules[packagename] = p
       
    90     # Fetch the python modules in the package
       
    91     cwd = os.getcwd()
       
    92     os.chdir(packdir)
       
    93     pymodulefiles = glob.glob('*.py')
       
    94     extmodulefiles = glob.glob('*.pyd')
       
    95     os.chdir(cwd)
       
    96     # Import all python modules and by that run the fake demandload
       
    97     for m in pymodulefiles:
       
    98         if m == '__init__.py': continue
       
    99         tmp = {}
       
   100         mname, ext = os.path.splitext(m)
       
   101         fullname = packagename+'.'+mname
       
   102         try:
       
   103             __import__(fullname, tmp, tmp)
       
   104         except SkipPackage, inst:
       
   105             print >> sys.stderr, 'skipping %s: %s' % (fullname, inst.reason)
       
   106             continue
       
   107         requiredmodules[fullname] = 1
       
   108     # Import all extension modules and by that run the fake demandload
       
   109     for m in extmodulefiles:
       
   110         tmp = {}
       
   111         mname, ext = os.path.splitext(m)
       
   112         fullname = packagename+'.'+mname
       
   113         __import__(fullname, tmp, tmp)
       
   114         requiredmodules[fullname] = 1
       
   115 
       
   116 def getmodules():
       
   117     return requiredmodules.keys()
       
   118 
       
   119 def importfrom(filename):
       
   120     """
       
   121     import module/package from a named file and returns the module.
       
   122     It does not check on sys.modules or includes the module in the scope.
       
   123     """
       
   124     loader = ihooks.BasicModuleLoader()
       
   125     path, file = os.path.split(filename)
       
   126     name, ext  = os.path.splitext(file)
       
   127     m = loader.find_module_in_dir(name, path)
       
   128     if not m:
       
   129         raise ImportError, name
       
   130     m = loader.load_module(name, m)
       
   131     return m