OLD | NEW |
(Empty) | |
| 1 import sys |
| 2 import imp |
| 3 import marshal |
| 4 from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN |
| 5 from distutils.version import StrictVersion |
| 6 from setuptools import compat |
| 7 |
| 8 __all__ = [ |
| 9 'Require', 'find_module', 'get_module_constant', 'extract_constant' |
| 10 ] |
| 11 |
| 12 class Require: |
| 13 """A prerequisite to building or installing a distribution""" |
| 14 |
| 15 def __init__(self, name, requested_version, module, homepage='', |
| 16 attribute=None, format=None): |
| 17 |
| 18 if format is None and requested_version is not None: |
| 19 format = StrictVersion |
| 20 |
| 21 if format is not None: |
| 22 requested_version = format(requested_version) |
| 23 if attribute is None: |
| 24 attribute = '__version__' |
| 25 |
| 26 self.__dict__.update(locals()) |
| 27 del self.self |
| 28 |
| 29 def full_name(self): |
| 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) |
| 33 return self.name |
| 34 |
| 35 def version_ok(self, version): |
| 36 """Is 'version' sufficiently up-to-date?""" |
| 37 return self.attribute is None or self.format is None or \ |
| 38 str(version) != "unknown" and version >= self.requested_version |
| 39 |
| 40 def get_version(self, paths=None, default="unknown"): |
| 41 |
| 42 """Get version number of installed module, 'None', or 'default' |
| 43 |
| 44 Search 'paths' for module. If not found, return 'None'. If found, |
| 45 return the extracted version attribute, or 'default' if no version |
| 46 attribute was specified, or the value cannot be determined without |
| 47 importing the module. The version is formatted according to the |
| 48 requirement's version format (if any), unless it is 'None' or the |
| 49 supplied 'default'. |
| 50 """ |
| 51 |
| 52 if self.attribute is None: |
| 53 try: |
| 54 f,p,i = find_module(self.module,paths) |
| 55 if f: f.close() |
| 56 return default |
| 57 except ImportError: |
| 58 return None |
| 59 |
| 60 v = get_module_constant(self.module, self.attribute, default, paths) |
| 61 |
| 62 if v is not None and v is not default and self.format is not None: |
| 63 return self.format(v) |
| 64 |
| 65 return v |
| 66 |
| 67 def is_present(self, paths=None): |
| 68 """Return true if dependency is present on 'paths'""" |
| 69 return self.get_version(paths) is not None |
| 70 |
| 71 def is_current(self, paths=None): |
| 72 """Return true if dependency is present and up-to-date on 'paths'""" |
| 73 version = self.get_version(paths) |
| 74 if version is None: |
| 75 return False |
| 76 return self.version_ok(version) |
| 77 |
| 78 |
| 79 def _iter_code(code): |
| 80 |
| 81 """Yield '(op,arg)' pair for each operation in code object 'code'""" |
| 82 |
| 83 from array import array |
| 84 from dis import HAVE_ARGUMENT, EXTENDED_ARG |
| 85 |
| 86 bytes = array('b',code.co_code) |
| 87 eof = len(code.co_code) |
| 88 |
| 89 ptr = 0 |
| 90 extended_arg = 0 |
| 91 |
| 92 while ptr<eof: |
| 93 |
| 94 op = bytes[ptr] |
| 95 |
| 96 if op>=HAVE_ARGUMENT: |
| 97 |
| 98 arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg |
| 99 ptr += 3 |
| 100 |
| 101 if op==EXTENDED_ARG: |
| 102 extended_arg = arg * compat.long_type(65536) |
| 103 continue |
| 104 |
| 105 else: |
| 106 arg = None |
| 107 ptr += 1 |
| 108 |
| 109 yield op,arg |
| 110 |
| 111 |
| 112 def find_module(module, paths=None): |
| 113 """Just like 'imp.find_module()', but with package support""" |
| 114 |
| 115 parts = module.split('.') |
| 116 |
| 117 while parts: |
| 118 part = parts.pop(0) |
| 119 f, path, (suffix,mode,kind) = info = imp.find_module(part, paths) |
| 120 |
| 121 if kind==PKG_DIRECTORY: |
| 122 parts = parts or ['__init__'] |
| 123 paths = [path] |
| 124 |
| 125 elif parts: |
| 126 raise ImportError("Can't find %r in %s" % (parts,module)) |
| 127 |
| 128 return info |
| 129 |
| 130 |
| 131 def get_module_constant(module, symbol, default=-1, paths=None): |
| 132 |
| 133 """Find 'module' by searching 'paths', and extract 'symbol' |
| 134 |
| 135 Return 'None' if 'module' does not exist on 'paths', or it does not define |
| 136 'symbol'. If the module defines 'symbol' as a constant, return the |
| 137 constant. Otherwise, return 'default'.""" |
| 138 |
| 139 try: |
| 140 f, path, (suffix, mode, kind) = find_module(module, paths) |
| 141 except ImportError: |
| 142 # Module doesn't exist |
| 143 return None |
| 144 |
| 145 try: |
| 146 if kind==PY_COMPILED: |
| 147 f.read(8) # skip magic & date |
| 148 code = marshal.load(f) |
| 149 elif kind==PY_FROZEN: |
| 150 code = imp.get_frozen_object(module) |
| 151 elif kind==PY_SOURCE: |
| 152 code = compile(f.read(), path, 'exec') |
| 153 else: |
| 154 # Not something we can parse; we'll have to import it. :( |
| 155 if module not in sys.modules: |
| 156 imp.load_module(module, f, path, (suffix, mode, kind)) |
| 157 return getattr(sys.modules[module], symbol, None) |
| 158 |
| 159 finally: |
| 160 if f: |
| 161 f.close() |
| 162 |
| 163 return extract_constant(code, symbol, default) |
| 164 |
| 165 |
| 166 def extract_constant(code, symbol, default=-1): |
| 167 """Extract the constant value of 'symbol' from 'code' |
| 168 |
| 169 If the name 'symbol' is bound to a constant value by the Python code |
| 170 object 'code', return that value. If 'symbol' is bound to an expression, |
| 171 return 'default'. Otherwise, return 'None'. |
| 172 |
| 173 Return value is based on the first assignment to 'symbol'. 'symbol' must |
| 174 be a global, or at least a non-"fast" local in the code block. That is, |
| 175 only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' |
| 176 must be present in 'code.co_names'. |
| 177 """ |
| 178 |
| 179 if symbol not in code.co_names: |
| 180 # name's not there, can't possibly be an assigment |
| 181 return None |
| 182 |
| 183 name_idx = list(code.co_names).index(symbol) |
| 184 |
| 185 STORE_NAME = 90 |
| 186 STORE_GLOBAL = 97 |
| 187 LOAD_CONST = 100 |
| 188 |
| 189 const = default |
| 190 |
| 191 for op, arg in _iter_code(code): |
| 192 |
| 193 if op==LOAD_CONST: |
| 194 const = code.co_consts[arg] |
| 195 elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL): |
| 196 return const |
| 197 else: |
| 198 const = default |
| 199 |
| 200 |
| 201 def _update_globals(): |
| 202 """ |
| 203 Patch the globals to remove the objects not available on some platforms. |
| 204 |
| 205 XXX it'd be better to test assertions about bytecode instead. |
| 206 """ |
| 207 |
| 208 if not sys.platform.startswith('java') and sys.platform != 'cli': |
| 209 return |
| 210 incompatible = 'extract_constant', 'get_module_constant' |
| 211 for name in incompatible: |
| 212 del globals()[name] |
| 213 __all__.remove(name) |
| 214 |
| 215 _update_globals() |
OLD | NEW |