1 from __future__ import generators
2 import sys, imp, marshal
3 from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
4 from distutils.version import StrictVersion, LooseVersion
7 'Require', 'find_module', 'get_module_constant', 'extract_constant'
11 """A prerequisite to building or installing a distribution"""
13 def __init__(self,name,requested_version,module,homepage='',
14 attribute=None,format=None
17 if format is None and requested_version is not None:
18 format = StrictVersion
20 if format is not None:
21 requested_version = format(requested_version)
23 attribute = '__version__'
25 self.__dict__.update(locals())
30 """Return full package/distribution name, w/version"""
31 if self.requested_version is not None:
32 return '%s-%s' % (self.name,self.requested_version)
36 def version_ok(self,version):
37 """Is 'version' sufficiently up-to-date?"""
38 return self.attribute is None or self.format is None or \
39 str(version)!="unknown" and version >= self.requested_version
42 def get_version(self, paths=None, default="unknown"):
44 """Get version number of installed module, 'None', or 'default'
46 Search 'paths' for module. If not found, return 'None'. If found,
47 return the extracted version attribute, or 'default' if no version
48 attribute was specified, or the value cannot be determined without
49 importing the module. The version is formatted according to the
50 requirement's version format (if any), unless it is 'None' or the
54 if self.attribute is None:
56 f,p,i = find_module(self.module,paths)
62 v = get_module_constant(self.module,self.attribute,default,paths)
64 if v is not None and v is not default and self.format is not None:
70 def is_present(self,paths=None):
71 """Return true if dependency is present on 'paths'"""
72 return self.get_version(paths) is not None
75 def is_current(self,paths=None):
76 """Return true if dependency is present and up-to-date on 'paths'"""
77 version = self.get_version(paths)
80 return self.version_ok(version)
85 """Yield '(op,arg)' pair for each operation in code object 'code'"""
87 from array import array
88 from dis import HAVE_ARGUMENT, EXTENDED_ARG
90 bytes = array('b',code.co_code)
91 eof = len(code.co_code)
100 if op>=HAVE_ARGUMENT:
102 arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg
106 extended_arg = arg * 65536L
124 def find_module(module, paths=None):
125 """Just like 'imp.find_module()', but with package support"""
127 parts = module.split('.')
131 f, path, (suffix,mode,kind) = info = imp.find_module(part, paths)
133 if kind==PKG_DIRECTORY:
134 parts = parts or ['__init__']
138 raise ImportError("Can't find %r in %s" % (parts,module))
165 def get_module_constant(module, symbol, default=-1, paths=None):
167 """Find 'module' by searching 'paths', and extract 'symbol'
169 Return 'None' if 'module' does not exist on 'paths', or it does not define
170 'symbol'. If the module defines 'symbol' as a constant, return the
171 constant. Otherwise, return 'default'."""
174 f, path, (suffix,mode,kind) = find_module(module,paths)
176 # Module doesn't exist
180 if kind==PY_COMPILED:
181 f.read(8) # skip magic & date
182 code = marshal.load(f)
183 elif kind==PY_FROZEN:
184 code = imp.get_frozen_object(module)
185 elif kind==PY_SOURCE:
186 code = compile(f.read(), path, 'exec')
188 # Not something we can parse; we'll have to import it. :(
189 if module not in sys.modules:
190 imp.load_module(module,f,path,(suffix,mode,kind))
191 return getattr(sys.modules[module],symbol,None)
197 return extract_constant(code,symbol,default)
206 def extract_constant(code,symbol,default=-1):
207 """Extract the constant value of 'symbol' from 'code'
209 If the name 'symbol' is bound to a constant value by the Python code
210 object 'code', return that value. If 'symbol' is bound to an expression,
211 return 'default'. Otherwise, return 'None'.
213 Return value is based on the first assignment to 'symbol'. 'symbol' must
214 be a global, or at least a non-"fast" local in the code block. That is,
215 only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
216 must be present in 'code.co_names'.
219 if symbol not in code.co_names:
220 # name's not there, can't possibly be an assigment
223 name_idx = list(code.co_names).index(symbol)
231 for op, arg in _iter_code(code):
234 const = code.co_consts[arg]
235 elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL):
240 if sys.platform.startswith('java') or sys.platform == 'cli':
241 # XXX it'd be better to test assertions about bytecode instead...
242 del extract_constant, get_module_constant
243 __all__.remove('extract_constant')
244 __all__.remove('get_module_constant')