omdev 0.0.0.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omdev/__about__.py +35 -0
- omdev/__init__.py +0 -0
- omdev/amalg/__init__.py +0 -0
- omdev/amalg/__main__.py +4 -0
- omdev/amalg/amalg.py +513 -0
- omdev/classdot.py +61 -0
- omdev/cmake.py +164 -0
- omdev/exts/__init__.py +0 -0
- omdev/exts/_distutils/__init__.py +10 -0
- omdev/exts/_distutils/build_ext.py +367 -0
- omdev/exts/_distutils/compilers/__init__.py +3 -0
- omdev/exts/_distutils/compilers/ccompiler.py +1032 -0
- omdev/exts/_distutils/compilers/options.py +80 -0
- omdev/exts/_distutils/compilers/unixccompiler.py +385 -0
- omdev/exts/_distutils/dir_util.py +76 -0
- omdev/exts/_distutils/errors.py +62 -0
- omdev/exts/_distutils/extension.py +107 -0
- omdev/exts/_distutils/file_util.py +216 -0
- omdev/exts/_distutils/modified.py +47 -0
- omdev/exts/_distutils/spawn.py +103 -0
- omdev/exts/_distutils/sysconfig.py +349 -0
- omdev/exts/_distutils/util.py +201 -0
- omdev/exts/_distutils/version.py +308 -0
- omdev/exts/build.py +43 -0
- omdev/exts/cmake.py +195 -0
- omdev/exts/importhook.py +88 -0
- omdev/exts/scan.py +74 -0
- omdev/interp/__init__.py +1 -0
- omdev/interp/__main__.py +4 -0
- omdev/interp/cli.py +63 -0
- omdev/interp/inspect.py +105 -0
- omdev/interp/providers.py +67 -0
- omdev/interp/pyenv.py +353 -0
- omdev/interp/resolvers.py +76 -0
- omdev/interp/standalone.py +187 -0
- omdev/interp/system.py +125 -0
- omdev/interp/types.py +92 -0
- omdev/mypy/__init__.py +0 -0
- omdev/mypy/debug.py +86 -0
- omdev/pyproject/__init__.py +1 -0
- omdev/pyproject/__main__.py +4 -0
- omdev/pyproject/cli.py +319 -0
- omdev/pyproject/configs.py +97 -0
- omdev/pyproject/ext.py +107 -0
- omdev/pyproject/pkg.py +196 -0
- omdev/scripts/__init__.py +0 -0
- omdev/scripts/execrss.py +19 -0
- omdev/scripts/findimports.py +62 -0
- omdev/scripts/findmagic.py +70 -0
- omdev/scripts/interp.py +2118 -0
- omdev/scripts/pyproject.py +3584 -0
- omdev/scripts/traceimport.py +502 -0
- omdev/tokens.py +42 -0
- omdev/toml/__init__.py +1 -0
- omdev/toml/parser.py +823 -0
- omdev/toml/writer.py +104 -0
- omdev/tools/__init__.py +0 -0
- omdev/tools/dockertools.py +81 -0
- omdev/tools/sqlrepl.py +193 -0
- omdev/versioning/__init__.py +1 -0
- omdev/versioning/specifiers.py +531 -0
- omdev/versioning/versions.py +416 -0
- omdev-0.0.0.dev7.dist-info/LICENSE +21 -0
- omdev-0.0.0.dev7.dist-info/METADATA +24 -0
- omdev-0.0.0.dev7.dist-info/RECORD +67 -0
- omdev-0.0.0.dev7.dist-info/WHEEL +5 -0
- omdev-0.0.0.dev7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provide access to Python's configuration information. The specific configuration variables available depend heavily on
|
|
3
|
+
the platform and configuration. The values may be retrieved using get_config_var(name), and the list of variables is
|
|
4
|
+
available via get_config_vars().keys(). Additional convenience functions are also available.
|
|
5
|
+
|
|
6
|
+
Written by: Fred L. Drake, Jr.
|
|
7
|
+
Email: <fdrake@acm.org>
|
|
8
|
+
"""
|
|
9
|
+
import functools
|
|
10
|
+
import os
|
|
11
|
+
import pathlib
|
|
12
|
+
import sys
|
|
13
|
+
import sysconfig
|
|
14
|
+
|
|
15
|
+
from .errors import DistutilsPlatformError
|
|
16
|
+
from .util import pass_none
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
IS_PYPY = '__pypy__' in sys.builtin_module_names
|
|
20
|
+
|
|
21
|
+
# These are needed in a couple of spots, so just compute them once.
|
|
22
|
+
PREFIX = os.path.normpath(sys.prefix)
|
|
23
|
+
EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
|
|
24
|
+
BASE_PREFIX = os.path.normpath(sys.base_prefix)
|
|
25
|
+
BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
|
|
26
|
+
|
|
27
|
+
# Path to the base directory of the project. On Windows the binary may live in project/PCbuild/win32 or
|
|
28
|
+
# project/PCbuild/amd64. set for cross builds
|
|
29
|
+
if '_PYTHON_PROJECT_BASE' in os.environ:
|
|
30
|
+
project_base = os.path.abspath(os.environ['_PYTHON_PROJECT_BASE'])
|
|
31
|
+
elif sys.executable:
|
|
32
|
+
project_base = os.path.dirname(os.path.abspath(sys.executable))
|
|
33
|
+
else:
|
|
34
|
+
# sys.executable can be empty if argv[0] has been changed and Python is unable to retrieve the real program name
|
|
35
|
+
project_base = os.getcwd()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _is_python_source_dir(d):
|
|
39
|
+
"""Return True if the target directory appears to point to an un-installed Python."""
|
|
40
|
+
modules = pathlib.Path(d).joinpath('Modules')
|
|
41
|
+
return any(modules.joinpath(fn).is_file() for fn in ('Setup', 'Setup.local'))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_sys_home = getattr(sys, '_home', None)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _is_parent(dir_a, dir_b):
|
|
48
|
+
"""Return True if a is a parent of b."""
|
|
49
|
+
return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _python_build():
|
|
53
|
+
if _sys_home:
|
|
54
|
+
return _is_python_source_dir(_sys_home)
|
|
55
|
+
return _is_python_source_dir(project_base)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
python_build = _python_build()
|
|
59
|
+
|
|
60
|
+
# Calculate the build qualifier flags if they are defined. Adding the flags to the include and lib directories only
|
|
61
|
+
# makes sense for an installation, not an in-source build.
|
|
62
|
+
build_flags = ''
|
|
63
|
+
try:
|
|
64
|
+
if not python_build:
|
|
65
|
+
build_flags = sys.abiflags
|
|
66
|
+
except AttributeError:
|
|
67
|
+
# It's not a configure-based build, so the sys module doesn't have this attribute, which is fine.
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_python_version():
|
|
72
|
+
"""
|
|
73
|
+
Return a string containing the major and minor Python version, leaving off the patchlevel. Sample return values
|
|
74
|
+
could be '1.5' or '2.2'.
|
|
75
|
+
"""
|
|
76
|
+
return '%d.%d' % sys.version_info[:2]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_python_inc(plat_specific=0, prefix=None):
|
|
80
|
+
"""
|
|
81
|
+
Return the directory containing installed Python header files.
|
|
82
|
+
|
|
83
|
+
If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e.
|
|
84
|
+
Python.h and so on; otherwise, this is the path to platform-specific header files (namely pyconfig.h).
|
|
85
|
+
|
|
86
|
+
If 'prefix' is supplied, use it instead of sys.base_prefix or sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
|
|
87
|
+
"""
|
|
88
|
+
default_prefix = BASE_EXEC_PREFIX if plat_specific else BASE_PREFIX
|
|
89
|
+
resolved_prefix = prefix if prefix is not None else default_prefix
|
|
90
|
+
try:
|
|
91
|
+
getter = globals()[f'_get_python_inc_{os.name}']
|
|
92
|
+
except KeyError:
|
|
93
|
+
raise DistutilsPlatformError("I don't know where Python installs its C header files on platform '%s'" % os.name) from None # noqa
|
|
94
|
+
return getter(resolved_prefix, prefix, plat_specific)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@pass_none
|
|
98
|
+
def _extant(path):
|
|
99
|
+
"""Replace path with None if it doesn't exist."""
|
|
100
|
+
return path if os.path.exists(path) else None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _get_python_inc_posix(prefix, spec_prefix, plat_specific):
|
|
104
|
+
if IS_PYPY and sys.version_info < (3, 8):
|
|
105
|
+
return os.path.join(prefix, 'include')
|
|
106
|
+
return (
|
|
107
|
+
_get_python_inc_posix_python(plat_specific)
|
|
108
|
+
or _extant(_get_python_inc_from_config(plat_specific, spec_prefix))
|
|
109
|
+
or _get_python_inc_posix_prefix(prefix)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _get_python_inc_posix_python(plat_specific):
|
|
114
|
+
"""
|
|
115
|
+
Assume the executable is in the build directory. The pyconfig.h file should be in the same directory. Since the
|
|
116
|
+
build directory may not be the source directory, use "srcdir" from the makefile to find the "Include" directory.
|
|
117
|
+
"""
|
|
118
|
+
if not python_build:
|
|
119
|
+
return None
|
|
120
|
+
if plat_specific:
|
|
121
|
+
return _sys_home or project_base
|
|
122
|
+
incdir = os.path.join(get_config_var('srcdir'), 'Include')
|
|
123
|
+
return os.path.normpath(incdir)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _get_python_inc_from_config(plat_specific, spec_prefix):
|
|
127
|
+
"""
|
|
128
|
+
If no prefix was explicitly specified, provide the include directory from the config vars. Useful when
|
|
129
|
+
cross-compiling, since the config vars may come from the host platform Python installation, while the current Python
|
|
130
|
+
executable is from the build platform installation.
|
|
131
|
+
|
|
132
|
+
>>> monkeypatch = getfixture('monkeypatch')
|
|
133
|
+
>>> gpifc = _get_python_inc_from_config
|
|
134
|
+
>>> monkeypatch.setitem(gpifc.__globals__, 'get_config_var', str.lower)
|
|
135
|
+
>>> gpifc(False, '/usr/bin/')
|
|
136
|
+
>>> gpifc(False, '')
|
|
137
|
+
>>> gpifc(False, None)
|
|
138
|
+
'includepy'
|
|
139
|
+
>>> gpifc(True, None)
|
|
140
|
+
'confincludepy'
|
|
141
|
+
"""
|
|
142
|
+
if spec_prefix is None:
|
|
143
|
+
return get_config_var('CONF' * plat_specific + 'INCLUDEPY')
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _get_python_inc_posix_prefix(prefix):
|
|
148
|
+
implementation = 'pypy' if IS_PYPY else 'python'
|
|
149
|
+
python_dir = implementation + get_python_version() + build_flags
|
|
150
|
+
return os.path.join(prefix, 'include', python_dir)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _get_python_inc_nt(prefix, spec_prefix, plat_specific):
|
|
154
|
+
if python_build:
|
|
155
|
+
# Include both include dirs to ensure we can find pyconfig.h
|
|
156
|
+
return (
|
|
157
|
+
os.path.join(prefix, 'include') +
|
|
158
|
+
os.path.pathsep +
|
|
159
|
+
os.path.dirname(sysconfig.get_config_h_filename())
|
|
160
|
+
)
|
|
161
|
+
return os.path.join(prefix, 'include')
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# allow this behavior to be monkey-patched. Ref pypa/distutils#2.
|
|
165
|
+
def _posix_lib(standard_lib, libpython, early_prefix, prefix):
|
|
166
|
+
if standard_lib:
|
|
167
|
+
return libpython
|
|
168
|
+
else:
|
|
169
|
+
return os.path.join(libpython, 'site-packages')
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
|
|
173
|
+
"""
|
|
174
|
+
Return the directory containing the Python library (standard or site additions).
|
|
175
|
+
|
|
176
|
+
If 'plat_specific' is true, return the directory containing platform-specific modules, i.e. any module from a
|
|
177
|
+
non-pure-Python module distribution; otherwise, return the platform-shared library directory. If 'standard_lib' is
|
|
178
|
+
true, return the directory containing standard Python library modules; otherwise, return the directory for
|
|
179
|
+
site-specific modules.
|
|
180
|
+
|
|
181
|
+
If 'prefix' is supplied, use it instead of sys.base_prefix or sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
if IS_PYPY and sys.version_info < (3, 8):
|
|
185
|
+
# PyPy-specific schema
|
|
186
|
+
if prefix is None:
|
|
187
|
+
prefix = PREFIX
|
|
188
|
+
if standard_lib:
|
|
189
|
+
return os.path.join(prefix, 'lib-python', str(sys.version_info[0]))
|
|
190
|
+
return os.path.join(prefix, 'site-packages')
|
|
191
|
+
|
|
192
|
+
early_prefix = prefix
|
|
193
|
+
|
|
194
|
+
if prefix is None:
|
|
195
|
+
if standard_lib:
|
|
196
|
+
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
|
|
197
|
+
else:
|
|
198
|
+
prefix = plat_specific and EXEC_PREFIX or PREFIX
|
|
199
|
+
|
|
200
|
+
if os.name == 'posix':
|
|
201
|
+
if plat_specific or standard_lib:
|
|
202
|
+
# Platform-specific modules (any module from a non-pure-Python module distribution) or standard Python
|
|
203
|
+
# library modules.
|
|
204
|
+
libdir = getattr(sys, 'platlibdir', 'lib')
|
|
205
|
+
else:
|
|
206
|
+
# Pure Python
|
|
207
|
+
libdir = 'lib'
|
|
208
|
+
implementation = 'pypy' if IS_PYPY else 'python'
|
|
209
|
+
libpython = os.path.join(prefix, libdir, implementation + get_python_version())
|
|
210
|
+
return _posix_lib(standard_lib, libpython, early_prefix, prefix)
|
|
211
|
+
else:
|
|
212
|
+
raise DistutilsPlatformError(f"I don't know where Python installs its library on platform '{os.name}'")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@functools.lru_cache
|
|
216
|
+
def _customize_macos():
|
|
217
|
+
"""
|
|
218
|
+
Perform first-time customization of compiler-related config vars on macOS. Use after a compiler is known to be
|
|
219
|
+
needed. This customization exists primarily to support Pythons from binary installers. The kind and paths to build
|
|
220
|
+
tools on the user system may vary significantly from the system that Python itself was built on. Also the user OS
|
|
221
|
+
version and build tools may not support the same set of CPU architectures for universal builds.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
sys.platform == 'darwin' and __import__('_osx_support').customize_compiler(
|
|
225
|
+
get_config_vars(),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def customize_compiler(compiler): # noqa: C901
|
|
230
|
+
"""Do any platform-specific customization of a CCompiler instance.
|
|
231
|
+
|
|
232
|
+
Mainly needed on Unix, so we can plug in the information that varies across Unices and is stored in Python's
|
|
233
|
+
Makefile.
|
|
234
|
+
"""
|
|
235
|
+
if compiler.compiler_type == 'unix':
|
|
236
|
+
_customize_macos()
|
|
237
|
+
|
|
238
|
+
(
|
|
239
|
+
cc,
|
|
240
|
+
cxx,
|
|
241
|
+
cflags,
|
|
242
|
+
ccshared,
|
|
243
|
+
ldshared,
|
|
244
|
+
shlib_suffix,
|
|
245
|
+
ar,
|
|
246
|
+
ar_flags,
|
|
247
|
+
) = get_config_vars(
|
|
248
|
+
'CC',
|
|
249
|
+
'CXX',
|
|
250
|
+
'CFLAGS',
|
|
251
|
+
'CCSHARED',
|
|
252
|
+
'LDSHARED',
|
|
253
|
+
'SHLIB_SUFFIX',
|
|
254
|
+
'AR',
|
|
255
|
+
'ARFLAGS',
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
if 'CC' in os.environ:
|
|
259
|
+
newcc = os.environ['CC']
|
|
260
|
+
if 'LDSHARED' not in os.environ and ldshared.startswith(cc):
|
|
261
|
+
# If CC is overridden, use that as the default
|
|
262
|
+
# command for LDSHARED as well
|
|
263
|
+
ldshared = newcc + ldshared[len(cc):]
|
|
264
|
+
cc = newcc
|
|
265
|
+
if 'CXX' in os.environ:
|
|
266
|
+
cxx = os.environ['CXX']
|
|
267
|
+
if 'LDSHARED' in os.environ:
|
|
268
|
+
ldshared = os.environ['LDSHARED']
|
|
269
|
+
if 'CPP' in os.environ:
|
|
270
|
+
cpp = os.environ['CPP']
|
|
271
|
+
else:
|
|
272
|
+
cpp = cc + ' -E' # not always
|
|
273
|
+
if 'LDFLAGS' in os.environ:
|
|
274
|
+
ldshared = ldshared + ' ' + os.environ['LDFLAGS']
|
|
275
|
+
if 'CFLAGS' in os.environ:
|
|
276
|
+
cflags = cflags + ' ' + os.environ['CFLAGS']
|
|
277
|
+
ldshared = ldshared + ' ' + os.environ['CFLAGS']
|
|
278
|
+
if 'CPPFLAGS' in os.environ:
|
|
279
|
+
cpp = cpp + ' ' + os.environ['CPPFLAGS']
|
|
280
|
+
cflags = cflags + ' ' + os.environ['CPPFLAGS']
|
|
281
|
+
ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
|
|
282
|
+
if 'AR' in os.environ:
|
|
283
|
+
ar = os.environ['AR']
|
|
284
|
+
if 'ARFLAGS' in os.environ:
|
|
285
|
+
archiver = ar + ' ' + os.environ['ARFLAGS']
|
|
286
|
+
else:
|
|
287
|
+
archiver = ar + ' ' + ar_flags
|
|
288
|
+
|
|
289
|
+
cc_cmd = cc + ' ' + cflags
|
|
290
|
+
compiler.set_executables(
|
|
291
|
+
preprocessor=cpp,
|
|
292
|
+
compiler=cc_cmd,
|
|
293
|
+
compiler_so=cc_cmd + ' ' + ccshared,
|
|
294
|
+
compiler_cxx=cxx,
|
|
295
|
+
linker_so=ldshared,
|
|
296
|
+
linker_exe=cc,
|
|
297
|
+
archiver=archiver,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None):
|
|
301
|
+
compiler.set_executables(ranlib=os.environ['RANLIB'])
|
|
302
|
+
|
|
303
|
+
compiler.shared_lib_extension = shlib_suffix
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def get_config_h_filename():
|
|
307
|
+
"""Return full pathname of installed pyconfig.h file."""
|
|
308
|
+
return sysconfig.get_config_h_filename()
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def parse_config_h(fp, g=None):
|
|
312
|
+
"""
|
|
313
|
+
Parse a config.h-style file.
|
|
314
|
+
|
|
315
|
+
A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second
|
|
316
|
+
argument, it is used instead of a new dictionary.
|
|
317
|
+
"""
|
|
318
|
+
return sysconfig.parse_config_h(fp, vars=g)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
_config_vars = None
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def get_config_vars(*args):
|
|
325
|
+
"""
|
|
326
|
+
With no arguments, return a dictionary of all configuration variables relevant for the current platform. Generally
|
|
327
|
+
this includes everything needed to build extensions and install both pure modules and extensions. On Unix, this
|
|
328
|
+
means every variable defined in Python's installed Makefile; on Windows it's a much smaller set.
|
|
329
|
+
|
|
330
|
+
With arguments, return a list of values that result from looking up each argument in the configuration variable
|
|
331
|
+
dictionary.
|
|
332
|
+
"""
|
|
333
|
+
global _config_vars
|
|
334
|
+
if _config_vars is None:
|
|
335
|
+
_config_vars = sysconfig.get_config_vars().copy()
|
|
336
|
+
|
|
337
|
+
return [_config_vars.get(name) for name in args] if args else _config_vars
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def get_config_var(name):
|
|
341
|
+
"""
|
|
342
|
+
Return the value of a single variable using the dictionary returned by 'get_config_vars()'. Equivalent to
|
|
343
|
+
get_config_vars().get(name)
|
|
344
|
+
"""
|
|
345
|
+
if name == 'SO':
|
|
346
|
+
import warnings
|
|
347
|
+
|
|
348
|
+
warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
|
|
349
|
+
return get_config_vars().get(name)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Miscellaneous utility functions -- anything that doesn't fit into one of the other *util.py modules."""
|
|
2
|
+
import functools
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import string
|
|
7
|
+
import sys
|
|
8
|
+
import sysconfig
|
|
9
|
+
import typing as ta
|
|
10
|
+
|
|
11
|
+
from .errors import DistutilsPlatformError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def pass_none(func):
|
|
18
|
+
"""Wrap func so it's not called if its first param is None"""
|
|
19
|
+
|
|
20
|
+
@functools.wraps(func)
|
|
21
|
+
def wrapper(param, *args, **kwargs):
|
|
22
|
+
if param is not None:
|
|
23
|
+
return func(param, *args, **kwargs)
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
return wrapper
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def always_iterable(obj, base_type=(str, bytes)):
|
|
30
|
+
if obj is None:
|
|
31
|
+
return iter(())
|
|
32
|
+
|
|
33
|
+
if (base_type is not None) and isinstance(obj, base_type):
|
|
34
|
+
return iter((obj,))
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
return iter(obj)
|
|
38
|
+
except TypeError:
|
|
39
|
+
return iter((obj,))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_host_platform() -> str:
|
|
43
|
+
"""
|
|
44
|
+
Return a string that identifies the current platform. Use this function to distinguish platform-specific build
|
|
45
|
+
directories and platform-specific built distributions.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
# This function initially exposed platforms as defined in Python 3.9 even with older Python versions when distutils
|
|
49
|
+
# was split out. Now it delegates to stdlib sysconfig, but maintains compatibility.
|
|
50
|
+
|
|
51
|
+
return sysconfig.get_platform()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_syscfg_macosx_ver: ta.Any
|
|
55
|
+
|
|
56
|
+
if sys.platform == 'darwin':
|
|
57
|
+
_syscfg_macosx_ver = None # cache the version pulled from sysconfig
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _clear_cached_macosx_ver() -> None:
|
|
61
|
+
"""For testing only. Do not call."""
|
|
62
|
+
global _syscfg_macosx_ver
|
|
63
|
+
_syscfg_macosx_ver = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET'
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_macosx_target_ver_from_syscfg() -> str:
|
|
70
|
+
"""
|
|
71
|
+
Get the version of macOS latched in the Python interpreter configuration. Returns the version as a string or None
|
|
72
|
+
if can't obtain one. Cached.
|
|
73
|
+
"""
|
|
74
|
+
global _syscfg_macosx_ver
|
|
75
|
+
if _syscfg_macosx_ver is None:
|
|
76
|
+
from . import sysconfig
|
|
77
|
+
|
|
78
|
+
ver = sysconfig.get_config_var(MACOSX_VERSION_VAR) or ''
|
|
79
|
+
if ver:
|
|
80
|
+
_syscfg_macosx_ver = ver
|
|
81
|
+
return _syscfg_macosx_ver
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_macosx_target_ver() -> str:
|
|
85
|
+
"""
|
|
86
|
+
Return the version of macOS for which we are building.
|
|
87
|
+
|
|
88
|
+
The target version defaults to the version in sysconfig latched at time the Python interpreter was built, unless
|
|
89
|
+
overridden by an environment variable. If neither source has a value, then None is returned
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
syscfg_ver = get_macosx_target_ver_from_syscfg()
|
|
93
|
+
env_ver = os.environ.get(MACOSX_VERSION_VAR)
|
|
94
|
+
|
|
95
|
+
if env_ver:
|
|
96
|
+
# Validate overridden version against sysconfig version, if have both. Ensure that the deployment target of the
|
|
97
|
+
# build process is not less than 10.3 if the interpreter was built for 10.3 or later. This ensures extension
|
|
98
|
+
# modules are built with correct compatibility values, specifically LDSHARED which can use '-undefined
|
|
99
|
+
# dynamic_lookup' which only works on >= 10.3.
|
|
100
|
+
if (
|
|
101
|
+
syscfg_ver
|
|
102
|
+
and split_version(syscfg_ver) >= [10, 3] > split_version(env_ver)
|
|
103
|
+
):
|
|
104
|
+
my_msg = ''.join([
|
|
105
|
+
'$',
|
|
106
|
+
MACOSX_VERSION_VAR,
|
|
107
|
+
' mismatch: ',
|
|
108
|
+
f'now "{env_ver}" but "{syscfg_ver}" during configure; ',
|
|
109
|
+
'must use 10.3 or later',
|
|
110
|
+
])
|
|
111
|
+
raise DistutilsPlatformError(my_msg)
|
|
112
|
+
return env_ver
|
|
113
|
+
return syscfg_ver
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def split_version(s) -> list[int]:
|
|
117
|
+
"""Convert a dot-separated string into a list of numbers for comparisons"""
|
|
118
|
+
return [int(n) for n in s.split('.')]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) # noqa
|
|
122
|
+
_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
|
|
123
|
+
_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def split_quoted(s: str) -> list[str]:
|
|
127
|
+
"""
|
|
128
|
+
Split a string up according to Unix shell-like rules for quotes and backslashes. In short: words are delimited by
|
|
129
|
+
spaces, as long as those spaces are not escaped by a backslash, or inside a quoted string. Single and double quotes
|
|
130
|
+
are equivalent, and the quote characters can be backslash-escaped. The backslash is stripped from any two-character
|
|
131
|
+
escape sequence, leaving only the escaped character. The quote characters are stripped from any quoted string.
|
|
132
|
+
Returns a list of words.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
s = s.strip()
|
|
136
|
+
words = []
|
|
137
|
+
pos = 0
|
|
138
|
+
|
|
139
|
+
while s:
|
|
140
|
+
m = _wordchars_re.match(s, pos)
|
|
141
|
+
if m is None:
|
|
142
|
+
raise ValueError(s)
|
|
143
|
+
end = m.end()
|
|
144
|
+
if end == len(s):
|
|
145
|
+
words.append(s[:end])
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
if s[end] in string.whitespace:
|
|
149
|
+
# unescaped, unquoted whitespace: now we definitely have a word delimiter
|
|
150
|
+
words.append(s[:end])
|
|
151
|
+
s = s[end:].lstrip()
|
|
152
|
+
pos = 0
|
|
153
|
+
|
|
154
|
+
elif s[end] == '\\':
|
|
155
|
+
# preserve whatever is being escaped; will become part of the current word
|
|
156
|
+
s = s[:end] + s[end + 1:]
|
|
157
|
+
pos = end + 1
|
|
158
|
+
|
|
159
|
+
else:
|
|
160
|
+
if s[end] == "'": # slurp singly-quoted string
|
|
161
|
+
m = _squote_re.match(s, end)
|
|
162
|
+
elif s[end] == '"': # slurp doubly-quoted string
|
|
163
|
+
m = _dquote_re.match(s, end)
|
|
164
|
+
else:
|
|
165
|
+
raise RuntimeError(f"this can't happen (bad char '{s[end]}')")
|
|
166
|
+
|
|
167
|
+
if m is None:
|
|
168
|
+
raise ValueError(f'bad string (mismatched {s[end]} quotes?)')
|
|
169
|
+
|
|
170
|
+
(beg, end) = m.span()
|
|
171
|
+
s = s[:beg] + s[beg + 1: end - 1] + s[end:]
|
|
172
|
+
pos = m.end() - 2
|
|
173
|
+
|
|
174
|
+
if pos >= len(s):
|
|
175
|
+
words.append(s)
|
|
176
|
+
break
|
|
177
|
+
|
|
178
|
+
return words
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def execute(
|
|
182
|
+
func: ta.Callable,
|
|
183
|
+
args: ta.Iterable,
|
|
184
|
+
msg: str | None = None,
|
|
185
|
+
verbose: int = 0,
|
|
186
|
+
dry_run: bool = False,
|
|
187
|
+
) -> None:
|
|
188
|
+
"""
|
|
189
|
+
Perform some action that affects the outside world (eg. by writing to the filesystem). Such actions are special
|
|
190
|
+
because they are disabled by the 'dry_run' flag. This method takes care of all that bureaucracy for you; all you
|
|
191
|
+
have to do is supply the function to call and an argument tuple for it (to embody the "external action" being
|
|
192
|
+
performed), and an optional message to print.
|
|
193
|
+
"""
|
|
194
|
+
if msg is None:
|
|
195
|
+
msg = f'{func.__name__}{args!r}'
|
|
196
|
+
if msg[-2:] == ',)': # correct for singleton tuple
|
|
197
|
+
msg = msg[0:-2] + ')'
|
|
198
|
+
|
|
199
|
+
log.info(msg)
|
|
200
|
+
if not dry_run:
|
|
201
|
+
func(*args)
|