annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/setuptools/depends.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 from __future__ import annotations
jpayne@69 2
jpayne@69 3 import contextlib
jpayne@69 4 import dis
jpayne@69 5 import marshal
jpayne@69 6 import sys
jpayne@69 7
jpayne@69 8 from packaging.version import Version
jpayne@69 9
jpayne@69 10 from . import _imp
jpayne@69 11 from ._imp import PY_COMPILED, PY_FROZEN, PY_SOURCE, find_module
jpayne@69 12
jpayne@69 13 __all__ = ['Require', 'find_module']
jpayne@69 14
jpayne@69 15
jpayne@69 16 class Require:
jpayne@69 17 """A prerequisite to building or installing a distribution"""
jpayne@69 18
jpayne@69 19 def __init__(
jpayne@69 20 self,
jpayne@69 21 name,
jpayne@69 22 requested_version,
jpayne@69 23 module,
jpayne@69 24 homepage: str = '',
jpayne@69 25 attribute=None,
jpayne@69 26 format=None,
jpayne@69 27 ):
jpayne@69 28 if format is None and requested_version is not None:
jpayne@69 29 format = Version
jpayne@69 30
jpayne@69 31 if format is not None:
jpayne@69 32 requested_version = format(requested_version)
jpayne@69 33 if attribute is None:
jpayne@69 34 attribute = '__version__'
jpayne@69 35
jpayne@69 36 self.__dict__.update(locals())
jpayne@69 37 del self.self
jpayne@69 38
jpayne@69 39 def full_name(self):
jpayne@69 40 """Return full package/distribution name, w/version"""
jpayne@69 41 if self.requested_version is not None:
jpayne@69 42 return '%s-%s' % (self.name, self.requested_version)
jpayne@69 43 return self.name
jpayne@69 44
jpayne@69 45 def version_ok(self, version):
jpayne@69 46 """Is 'version' sufficiently up-to-date?"""
jpayne@69 47 return (
jpayne@69 48 self.attribute is None
jpayne@69 49 or self.format is None
jpayne@69 50 or str(version) != "unknown"
jpayne@69 51 and self.format(version) >= self.requested_version
jpayne@69 52 )
jpayne@69 53
jpayne@69 54 def get_version(self, paths=None, default: str = "unknown"):
jpayne@69 55 """Get version number of installed module, 'None', or 'default'
jpayne@69 56
jpayne@69 57 Search 'paths' for module. If not found, return 'None'. If found,
jpayne@69 58 return the extracted version attribute, or 'default' if no version
jpayne@69 59 attribute was specified, or the value cannot be determined without
jpayne@69 60 importing the module. The version is formatted according to the
jpayne@69 61 requirement's version format (if any), unless it is 'None' or the
jpayne@69 62 supplied 'default'.
jpayne@69 63 """
jpayne@69 64
jpayne@69 65 if self.attribute is None:
jpayne@69 66 try:
jpayne@69 67 f, p, i = find_module(self.module, paths)
jpayne@69 68 except ImportError:
jpayne@69 69 return None
jpayne@69 70 if f:
jpayne@69 71 f.close()
jpayne@69 72 return default
jpayne@69 73
jpayne@69 74 v = get_module_constant(self.module, self.attribute, default, paths)
jpayne@69 75
jpayne@69 76 if v is not None and v is not default and self.format is not None:
jpayne@69 77 return self.format(v)
jpayne@69 78
jpayne@69 79 return v
jpayne@69 80
jpayne@69 81 def is_present(self, paths=None):
jpayne@69 82 """Return true if dependency is present on 'paths'"""
jpayne@69 83 return self.get_version(paths) is not None
jpayne@69 84
jpayne@69 85 def is_current(self, paths=None):
jpayne@69 86 """Return true if dependency is present and up-to-date on 'paths'"""
jpayne@69 87 version = self.get_version(paths)
jpayne@69 88 if version is None:
jpayne@69 89 return False
jpayne@69 90 return self.version_ok(str(version))
jpayne@69 91
jpayne@69 92
jpayne@69 93 def maybe_close(f):
jpayne@69 94 @contextlib.contextmanager
jpayne@69 95 def empty():
jpayne@69 96 yield
jpayne@69 97 return
jpayne@69 98
jpayne@69 99 if not f:
jpayne@69 100 return empty()
jpayne@69 101
jpayne@69 102 return contextlib.closing(f)
jpayne@69 103
jpayne@69 104
jpayne@69 105 # Some objects are not available on some platforms.
jpayne@69 106 # XXX it'd be better to test assertions about bytecode instead.
jpayne@69 107 if not sys.platform.startswith('java') and sys.platform != 'cli':
jpayne@69 108
jpayne@69 109 def get_module_constant(module, symbol, default: str | int = -1, paths=None):
jpayne@69 110 """Find 'module' by searching 'paths', and extract 'symbol'
jpayne@69 111
jpayne@69 112 Return 'None' if 'module' does not exist on 'paths', or it does not define
jpayne@69 113 'symbol'. If the module defines 'symbol' as a constant, return the
jpayne@69 114 constant. Otherwise, return 'default'."""
jpayne@69 115
jpayne@69 116 try:
jpayne@69 117 f, path, (suffix, mode, kind) = info = find_module(module, paths)
jpayne@69 118 except ImportError:
jpayne@69 119 # Module doesn't exist
jpayne@69 120 return None
jpayne@69 121
jpayne@69 122 with maybe_close(f):
jpayne@69 123 if kind == PY_COMPILED:
jpayne@69 124 f.read(8) # skip magic & date
jpayne@69 125 code = marshal.load(f)
jpayne@69 126 elif kind == PY_FROZEN:
jpayne@69 127 code = _imp.get_frozen_object(module, paths)
jpayne@69 128 elif kind == PY_SOURCE:
jpayne@69 129 code = compile(f.read(), path, 'exec')
jpayne@69 130 else:
jpayne@69 131 # Not something we can parse; we'll have to import it. :(
jpayne@69 132 imported = _imp.get_module(module, paths, info)
jpayne@69 133 return getattr(imported, symbol, None)
jpayne@69 134
jpayne@69 135 return extract_constant(code, symbol, default)
jpayne@69 136
jpayne@69 137 def extract_constant(code, symbol, default: str | int = -1):
jpayne@69 138 """Extract the constant value of 'symbol' from 'code'
jpayne@69 139
jpayne@69 140 If the name 'symbol' is bound to a constant value by the Python code
jpayne@69 141 object 'code', return that value. If 'symbol' is bound to an expression,
jpayne@69 142 return 'default'. Otherwise, return 'None'.
jpayne@69 143
jpayne@69 144 Return value is based on the first assignment to 'symbol'. 'symbol' must
jpayne@69 145 be a global, or at least a non-"fast" local in the code block. That is,
jpayne@69 146 only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
jpayne@69 147 must be present in 'code.co_names'.
jpayne@69 148 """
jpayne@69 149 if symbol not in code.co_names:
jpayne@69 150 # name's not there, can't possibly be an assignment
jpayne@69 151 return None
jpayne@69 152
jpayne@69 153 name_idx = list(code.co_names).index(symbol)
jpayne@69 154
jpayne@69 155 STORE_NAME = dis.opmap['STORE_NAME']
jpayne@69 156 STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
jpayne@69 157 LOAD_CONST = dis.opmap['LOAD_CONST']
jpayne@69 158
jpayne@69 159 const = default
jpayne@69 160
jpayne@69 161 for byte_code in dis.Bytecode(code):
jpayne@69 162 op = byte_code.opcode
jpayne@69 163 arg = byte_code.arg
jpayne@69 164
jpayne@69 165 if op == LOAD_CONST:
jpayne@69 166 const = code.co_consts[arg]
jpayne@69 167 elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
jpayne@69 168 return const
jpayne@69 169 else:
jpayne@69 170 const = default
jpayne@69 171
jpayne@69 172 return None
jpayne@69 173
jpayne@69 174 __all__ += ['get_module_constant', 'extract_constant']