Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_core_metadata.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_entry_points.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_importlib.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_reqs.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/build_meta.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/discovery.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/installer.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/logging.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/wheel.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/windows_support.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/_requirestxt.py +131 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/bdist_egg.py +464 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/bdist_rpm.py +39 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py +597 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/build_py.py +401 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/develop.py +196 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/dist_info.py +106 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/editable_wheel.py +918 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/egg_info.py +737 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/install_lib.py +126 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/install_scripts.py +68 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/launcher manifest.xml +15 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/register.py +18 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/rotate.py +64 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/sdist.py +204 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/setopt.py +140 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/test.py +250 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/__autotune_main__.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/__init__.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/aoti_eager.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/autotune_process.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/await_utils.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/bounds.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/cache.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comm_analysis.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comm_lowering.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comms.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comms_debug.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/compile_fx_subproc.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/config_comms.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/constant_folding.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/cpp_builder.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/cpu_vec_isa.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/custom_graph_pass.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/debug.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/decomposition.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/dependencies.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/distributed_autotune.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/exc.cpython-312.pyc +0 -0
- URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/extern_node_serializer.cpython-312.pyc +0 -0
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_core_metadata.cpython-312.pyc
ADDED
|
Binary file (13.4 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_entry_points.cpython-312.pyc
ADDED
|
Binary file (4.73 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_importlib.cpython-312.pyc
ADDED
|
Binary file (1.81 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/_reqs.cpython-312.pyc
ADDED
|
Binary file (1.87 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/build_meta.cpython-312.pyc
ADDED
|
Binary file (23.7 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/discovery.cpython-312.pyc
ADDED
|
Binary file (28.4 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/installer.cpython-312.pyc
ADDED
|
Binary file (6.48 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/logging.cpython-312.pyc
ADDED
|
Binary file (2.07 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/wheel.cpython-312.pyc
ADDED
|
Binary file (13.5 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/__pycache__/windows_support.cpython-312.pyc
ADDED
|
Binary file (1.47 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/_requirestxt.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Helper code used to generate ``requires.txt`` files in the egg-info directory.
|
| 2 |
+
|
| 3 |
+
The ``requires.txt`` file has an specific format:
|
| 4 |
+
- Environment markers need to be part of the section headers and
|
| 5 |
+
should not be part of the requirement spec itself.
|
| 6 |
+
|
| 7 |
+
See https://setuptools.pypa.io/en/latest/deprecated/python_eggs.html#requires-txt
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
import io
|
| 13 |
+
from collections import defaultdict
|
| 14 |
+
from itertools import filterfalse
|
| 15 |
+
from typing import Dict, Mapping, TypeVar
|
| 16 |
+
|
| 17 |
+
from .. import _reqs
|
| 18 |
+
from ..extern.jaraco.text import yield_lines
|
| 19 |
+
from ..extern.packaging.requirements import Requirement
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# dict can work as an ordered set
|
| 23 |
+
_T = TypeVar("_T")
|
| 24 |
+
_Ordered = Dict[_T, None]
|
| 25 |
+
_ordered = dict
|
| 26 |
+
_StrOrIter = _reqs._StrOrIter
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _prepare(
|
| 30 |
+
install_requires: _StrOrIter, extras_require: Mapping[str, _StrOrIter]
|
| 31 |
+
) -> tuple[list[str], dict[str, list[str]]]:
|
| 32 |
+
"""Given values for ``install_requires`` and ``extras_require``
|
| 33 |
+
create modified versions in a way that can be written in ``requires.txt``
|
| 34 |
+
"""
|
| 35 |
+
extras = _convert_extras_requirements(extras_require)
|
| 36 |
+
return _move_install_requirements_markers(install_requires, extras)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _convert_extras_requirements(
|
| 40 |
+
extras_require: Mapping[str, _StrOrIter],
|
| 41 |
+
) -> Mapping[str, _Ordered[Requirement]]:
|
| 42 |
+
"""
|
| 43 |
+
Convert requirements in `extras_require` of the form
|
| 44 |
+
`"extra": ["barbazquux; {marker}"]` to
|
| 45 |
+
`"extra:{marker}": ["barbazquux"]`.
|
| 46 |
+
"""
|
| 47 |
+
output: Mapping[str, _Ordered[Requirement]] = defaultdict(dict)
|
| 48 |
+
for section, v in extras_require.items():
|
| 49 |
+
# Do not strip empty sections.
|
| 50 |
+
output[section]
|
| 51 |
+
for r in _reqs.parse(v):
|
| 52 |
+
output[section + _suffix_for(r)].setdefault(r)
|
| 53 |
+
|
| 54 |
+
return output
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def _move_install_requirements_markers(
|
| 58 |
+
install_requires: _StrOrIter, extras_require: Mapping[str, _Ordered[Requirement]]
|
| 59 |
+
) -> tuple[list[str], dict[str, list[str]]]:
|
| 60 |
+
"""
|
| 61 |
+
The ``requires.txt`` file has an specific format:
|
| 62 |
+
- Environment markers need to be part of the section headers and
|
| 63 |
+
should not be part of the requirement spec itself.
|
| 64 |
+
|
| 65 |
+
Move requirements in ``install_requires`` that are using environment
|
| 66 |
+
markers ``extras_require``.
|
| 67 |
+
"""
|
| 68 |
+
|
| 69 |
+
# divide the install_requires into two sets, simple ones still
|
| 70 |
+
# handled by install_requires and more complex ones handled by extras_require.
|
| 71 |
+
|
| 72 |
+
inst_reqs = list(_reqs.parse(install_requires))
|
| 73 |
+
simple_reqs = filter(_no_marker, inst_reqs)
|
| 74 |
+
complex_reqs = filterfalse(_no_marker, inst_reqs)
|
| 75 |
+
simple_install_requires = list(map(str, simple_reqs))
|
| 76 |
+
|
| 77 |
+
for r in complex_reqs:
|
| 78 |
+
extras_require[':' + str(r.marker)].setdefault(r)
|
| 79 |
+
|
| 80 |
+
expanded_extras = dict(
|
| 81 |
+
# list(dict.fromkeys(...)) ensures a list of unique strings
|
| 82 |
+
(k, list(dict.fromkeys(str(r) for r in map(_clean_req, v))))
|
| 83 |
+
for k, v in extras_require.items()
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
return simple_install_requires, expanded_extras
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def _suffix_for(req):
|
| 90 |
+
"""Return the 'extras_require' suffix for a given requirement."""
|
| 91 |
+
return ':' + str(req.marker) if req.marker else ''
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _clean_req(req):
|
| 95 |
+
"""Given a Requirement, remove environment markers and return it"""
|
| 96 |
+
r = Requirement(str(req)) # create a copy before modifying
|
| 97 |
+
r.marker = None
|
| 98 |
+
return r
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def _no_marker(req):
|
| 102 |
+
return not req.marker
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def _write_requirements(stream, reqs):
|
| 106 |
+
lines = yield_lines(reqs or ())
|
| 107 |
+
|
| 108 |
+
def append_cr(line):
|
| 109 |
+
return line + '\n'
|
| 110 |
+
|
| 111 |
+
lines = map(append_cr, lines)
|
| 112 |
+
stream.writelines(lines)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def write_requirements(cmd, basename, filename):
|
| 116 |
+
dist = cmd.distribution
|
| 117 |
+
data = io.StringIO()
|
| 118 |
+
install_requires, extras_require = _prepare(
|
| 119 |
+
dist.install_requires or (), dist.extras_require or {}
|
| 120 |
+
)
|
| 121 |
+
_write_requirements(data, install_requires)
|
| 122 |
+
for extra in sorted(extras_require):
|
| 123 |
+
data.write('\n[{extra}]\n'.format(**vars()))
|
| 124 |
+
_write_requirements(data, extras_require[extra])
|
| 125 |
+
cmd.write_or_delete_file("requirements", filename, data.getvalue())
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def write_setup_requirements(cmd, basename, filename):
|
| 129 |
+
data = io.StringIO()
|
| 130 |
+
_write_requirements(data, cmd.distribution.setup_requires)
|
| 131 |
+
cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/bdist_egg.py
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""setuptools.command.bdist_egg
|
| 2 |
+
|
| 3 |
+
Build .egg distributions"""
|
| 4 |
+
|
| 5 |
+
from distutils.dir_util import remove_tree, mkpath
|
| 6 |
+
from distutils import log
|
| 7 |
+
from types import CodeType
|
| 8 |
+
import sys
|
| 9 |
+
import os
|
| 10 |
+
import re
|
| 11 |
+
import textwrap
|
| 12 |
+
import marshal
|
| 13 |
+
|
| 14 |
+
from setuptools.extension import Library
|
| 15 |
+
from setuptools import Command
|
| 16 |
+
from .._path import ensure_directory
|
| 17 |
+
|
| 18 |
+
from sysconfig import get_path, get_python_version
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _get_purelib():
|
| 22 |
+
return get_path("purelib")
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def strip_module(filename):
|
| 26 |
+
if '.' in filename:
|
| 27 |
+
filename = os.path.splitext(filename)[0]
|
| 28 |
+
if filename.endswith('module'):
|
| 29 |
+
filename = filename[:-6]
|
| 30 |
+
return filename
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def sorted_walk(dir):
|
| 34 |
+
"""Do os.walk in a reproducible way,
|
| 35 |
+
independent of indeterministic filesystem readdir order
|
| 36 |
+
"""
|
| 37 |
+
for base, dirs, files in os.walk(dir):
|
| 38 |
+
dirs.sort()
|
| 39 |
+
files.sort()
|
| 40 |
+
yield base, dirs, files
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def write_stub(resource, pyfile):
|
| 44 |
+
_stub_template = textwrap.dedent(
|
| 45 |
+
"""
|
| 46 |
+
def __bootstrap__():
|
| 47 |
+
global __bootstrap__, __loader__, __file__
|
| 48 |
+
import sys, pkg_resources, importlib.util
|
| 49 |
+
__file__ = pkg_resources.resource_filename(__name__, %r)
|
| 50 |
+
__loader__ = None; del __bootstrap__, __loader__
|
| 51 |
+
spec = importlib.util.spec_from_file_location(__name__,__file__)
|
| 52 |
+
mod = importlib.util.module_from_spec(spec)
|
| 53 |
+
spec.loader.exec_module(mod)
|
| 54 |
+
__bootstrap__()
|
| 55 |
+
"""
|
| 56 |
+
).lstrip()
|
| 57 |
+
with open(pyfile, 'w', encoding="utf-8") as f:
|
| 58 |
+
f.write(_stub_template % resource)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class bdist_egg(Command):
|
| 62 |
+
description = "create an \"egg\" distribution"
|
| 63 |
+
|
| 64 |
+
user_options = [
|
| 65 |
+
('bdist-dir=', 'b', "temporary directory for creating the distribution"),
|
| 66 |
+
(
|
| 67 |
+
'plat-name=',
|
| 68 |
+
'p',
|
| 69 |
+
"platform name to embed in generated filenames "
|
| 70 |
+
"(by default uses `pkg_resources.get_build_platform()`)",
|
| 71 |
+
),
|
| 72 |
+
('exclude-source-files', None, "remove all .py files from the generated egg"),
|
| 73 |
+
(
|
| 74 |
+
'keep-temp',
|
| 75 |
+
'k',
|
| 76 |
+
"keep the pseudo-installation tree around after "
|
| 77 |
+
"creating the distribution archive",
|
| 78 |
+
),
|
| 79 |
+
('dist-dir=', 'd', "directory to put final built distributions in"),
|
| 80 |
+
('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
|
| 81 |
+
]
|
| 82 |
+
|
| 83 |
+
boolean_options = ['keep-temp', 'skip-build', 'exclude-source-files']
|
| 84 |
+
|
| 85 |
+
def initialize_options(self):
|
| 86 |
+
self.bdist_dir = None
|
| 87 |
+
self.plat_name = None
|
| 88 |
+
self.keep_temp = False
|
| 89 |
+
self.dist_dir = None
|
| 90 |
+
self.skip_build = False
|
| 91 |
+
self.egg_output = None
|
| 92 |
+
self.exclude_source_files = None
|
| 93 |
+
|
| 94 |
+
def finalize_options(self):
|
| 95 |
+
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
|
| 96 |
+
self.egg_info = ei_cmd.egg_info
|
| 97 |
+
|
| 98 |
+
if self.bdist_dir is None:
|
| 99 |
+
bdist_base = self.get_finalized_command('bdist').bdist_base
|
| 100 |
+
self.bdist_dir = os.path.join(bdist_base, 'egg')
|
| 101 |
+
|
| 102 |
+
if self.plat_name is None:
|
| 103 |
+
from pkg_resources import get_build_platform
|
| 104 |
+
|
| 105 |
+
self.plat_name = get_build_platform()
|
| 106 |
+
|
| 107 |
+
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
|
| 108 |
+
|
| 109 |
+
if self.egg_output is None:
|
| 110 |
+
# Compute filename of the output egg
|
| 111 |
+
basename = ei_cmd._get_egg_basename(
|
| 112 |
+
py_version=get_python_version(),
|
| 113 |
+
platform=self.distribution.has_ext_modules() and self.plat_name,
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
self.egg_output = os.path.join(self.dist_dir, basename + '.egg')
|
| 117 |
+
|
| 118 |
+
def do_install_data(self):
|
| 119 |
+
# Hack for packages that install data to install's --install-lib
|
| 120 |
+
self.get_finalized_command('install').install_lib = self.bdist_dir
|
| 121 |
+
|
| 122 |
+
site_packages = os.path.normcase(os.path.realpath(_get_purelib()))
|
| 123 |
+
old, self.distribution.data_files = self.distribution.data_files, []
|
| 124 |
+
|
| 125 |
+
for item in old:
|
| 126 |
+
if isinstance(item, tuple) and len(item) == 2:
|
| 127 |
+
if os.path.isabs(item[0]):
|
| 128 |
+
realpath = os.path.realpath(item[0])
|
| 129 |
+
normalized = os.path.normcase(realpath)
|
| 130 |
+
if normalized == site_packages or normalized.startswith(
|
| 131 |
+
site_packages + os.sep
|
| 132 |
+
):
|
| 133 |
+
item = realpath[len(site_packages) + 1 :], item[1]
|
| 134 |
+
# XXX else: raise ???
|
| 135 |
+
self.distribution.data_files.append(item)
|
| 136 |
+
|
| 137 |
+
try:
|
| 138 |
+
log.info("installing package data to %s", self.bdist_dir)
|
| 139 |
+
self.call_command('install_data', force=False, root=None)
|
| 140 |
+
finally:
|
| 141 |
+
self.distribution.data_files = old
|
| 142 |
+
|
| 143 |
+
def get_outputs(self):
|
| 144 |
+
return [self.egg_output]
|
| 145 |
+
|
| 146 |
+
def call_command(self, cmdname, **kw):
|
| 147 |
+
"""Invoke reinitialized command `cmdname` with keyword args"""
|
| 148 |
+
for dirname in INSTALL_DIRECTORY_ATTRS:
|
| 149 |
+
kw.setdefault(dirname, self.bdist_dir)
|
| 150 |
+
kw.setdefault('skip_build', self.skip_build)
|
| 151 |
+
kw.setdefault('dry_run', self.dry_run)
|
| 152 |
+
cmd = self.reinitialize_command(cmdname, **kw)
|
| 153 |
+
self.run_command(cmdname)
|
| 154 |
+
return cmd
|
| 155 |
+
|
| 156 |
+
def run(self): # noqa: C901 # is too complex (14) # FIXME
|
| 157 |
+
# Generate metadata first
|
| 158 |
+
self.run_command("egg_info")
|
| 159 |
+
# We run install_lib before install_data, because some data hacks
|
| 160 |
+
# pull their data path from the install_lib command.
|
| 161 |
+
log.info("installing library code to %s", self.bdist_dir)
|
| 162 |
+
instcmd = self.get_finalized_command('install')
|
| 163 |
+
old_root = instcmd.root
|
| 164 |
+
instcmd.root = None
|
| 165 |
+
if self.distribution.has_c_libraries() and not self.skip_build:
|
| 166 |
+
self.run_command('build_clib')
|
| 167 |
+
cmd = self.call_command('install_lib', warn_dir=False)
|
| 168 |
+
instcmd.root = old_root
|
| 169 |
+
|
| 170 |
+
all_outputs, ext_outputs = self.get_ext_outputs()
|
| 171 |
+
self.stubs = []
|
| 172 |
+
to_compile = []
|
| 173 |
+
for p, ext_name in enumerate(ext_outputs):
|
| 174 |
+
filename, ext = os.path.splitext(ext_name)
|
| 175 |
+
pyfile = os.path.join(self.bdist_dir, strip_module(filename) + '.py')
|
| 176 |
+
self.stubs.append(pyfile)
|
| 177 |
+
log.info("creating stub loader for %s", ext_name)
|
| 178 |
+
if not self.dry_run:
|
| 179 |
+
write_stub(os.path.basename(ext_name), pyfile)
|
| 180 |
+
to_compile.append(pyfile)
|
| 181 |
+
ext_outputs[p] = ext_name.replace(os.sep, '/')
|
| 182 |
+
|
| 183 |
+
if to_compile:
|
| 184 |
+
cmd.byte_compile(to_compile)
|
| 185 |
+
if self.distribution.data_files:
|
| 186 |
+
self.do_install_data()
|
| 187 |
+
|
| 188 |
+
# Make the EGG-INFO directory
|
| 189 |
+
archive_root = self.bdist_dir
|
| 190 |
+
egg_info = os.path.join(archive_root, 'EGG-INFO')
|
| 191 |
+
self.mkpath(egg_info)
|
| 192 |
+
if self.distribution.scripts:
|
| 193 |
+
script_dir = os.path.join(egg_info, 'scripts')
|
| 194 |
+
log.info("installing scripts to %s", script_dir)
|
| 195 |
+
self.call_command('install_scripts', install_dir=script_dir, no_ep=True)
|
| 196 |
+
|
| 197 |
+
self.copy_metadata_to(egg_info)
|
| 198 |
+
native_libs = os.path.join(egg_info, "native_libs.txt")
|
| 199 |
+
if all_outputs:
|
| 200 |
+
log.info("writing %s", native_libs)
|
| 201 |
+
if not self.dry_run:
|
| 202 |
+
ensure_directory(native_libs)
|
| 203 |
+
with open(native_libs, 'wt', encoding="utf-8") as libs_file:
|
| 204 |
+
libs_file.write('\n'.join(all_outputs))
|
| 205 |
+
libs_file.write('\n')
|
| 206 |
+
elif os.path.isfile(native_libs):
|
| 207 |
+
log.info("removing %s", native_libs)
|
| 208 |
+
if not self.dry_run:
|
| 209 |
+
os.unlink(native_libs)
|
| 210 |
+
|
| 211 |
+
write_safety_flag(os.path.join(archive_root, 'EGG-INFO'), self.zip_safe())
|
| 212 |
+
|
| 213 |
+
if os.path.exists(os.path.join(self.egg_info, 'depends.txt')):
|
| 214 |
+
log.warn(
|
| 215 |
+
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
|
| 216 |
+
"Use the install_requires/extras_require setup() args instead."
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
if self.exclude_source_files:
|
| 220 |
+
self.zap_pyfiles()
|
| 221 |
+
|
| 222 |
+
# Make the archive
|
| 223 |
+
make_zipfile(
|
| 224 |
+
self.egg_output,
|
| 225 |
+
archive_root,
|
| 226 |
+
verbose=self.verbose,
|
| 227 |
+
dry_run=self.dry_run,
|
| 228 |
+
mode=self.gen_header(),
|
| 229 |
+
)
|
| 230 |
+
if not self.keep_temp:
|
| 231 |
+
remove_tree(self.bdist_dir, dry_run=self.dry_run)
|
| 232 |
+
|
| 233 |
+
# Add to 'Distribution.dist_files' so that the "upload" command works
|
| 234 |
+
getattr(self.distribution, 'dist_files', []).append((
|
| 235 |
+
'bdist_egg',
|
| 236 |
+
get_python_version(),
|
| 237 |
+
self.egg_output,
|
| 238 |
+
))
|
| 239 |
+
|
| 240 |
+
def zap_pyfiles(self):
|
| 241 |
+
log.info("Removing .py files from temporary directory")
|
| 242 |
+
for base, dirs, files in walk_egg(self.bdist_dir):
|
| 243 |
+
for name in files:
|
| 244 |
+
path = os.path.join(base, name)
|
| 245 |
+
|
| 246 |
+
if name.endswith('.py'):
|
| 247 |
+
log.debug("Deleting %s", path)
|
| 248 |
+
os.unlink(path)
|
| 249 |
+
|
| 250 |
+
if base.endswith('__pycache__'):
|
| 251 |
+
path_old = path
|
| 252 |
+
|
| 253 |
+
pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc'
|
| 254 |
+
m = re.match(pattern, name)
|
| 255 |
+
path_new = os.path.join(base, os.pardir, m.group('name') + '.pyc')
|
| 256 |
+
log.info("Renaming file from [%s] to [%s]" % (path_old, path_new))
|
| 257 |
+
try:
|
| 258 |
+
os.remove(path_new)
|
| 259 |
+
except OSError:
|
| 260 |
+
pass
|
| 261 |
+
os.rename(path_old, path_new)
|
| 262 |
+
|
| 263 |
+
def zip_safe(self):
|
| 264 |
+
safe = getattr(self.distribution, 'zip_safe', None)
|
| 265 |
+
if safe is not None:
|
| 266 |
+
return safe
|
| 267 |
+
log.warn("zip_safe flag not set; analyzing archive contents...")
|
| 268 |
+
return analyze_egg(self.bdist_dir, self.stubs)
|
| 269 |
+
|
| 270 |
+
def gen_header(self):
|
| 271 |
+
return 'w'
|
| 272 |
+
|
| 273 |
+
def copy_metadata_to(self, target_dir):
|
| 274 |
+
"Copy metadata (egg info) to the target_dir"
|
| 275 |
+
# normalize the path (so that a forward-slash in egg_info will
|
| 276 |
+
# match using startswith below)
|
| 277 |
+
norm_egg_info = os.path.normpath(self.egg_info)
|
| 278 |
+
prefix = os.path.join(norm_egg_info, '')
|
| 279 |
+
for path in self.ei_cmd.filelist.files:
|
| 280 |
+
if path.startswith(prefix):
|
| 281 |
+
target = os.path.join(target_dir, path[len(prefix) :])
|
| 282 |
+
ensure_directory(target)
|
| 283 |
+
self.copy_file(path, target)
|
| 284 |
+
|
| 285 |
+
def get_ext_outputs(self):
|
| 286 |
+
"""Get a list of relative paths to C extensions in the output distro"""
|
| 287 |
+
|
| 288 |
+
all_outputs = []
|
| 289 |
+
ext_outputs = []
|
| 290 |
+
|
| 291 |
+
paths = {self.bdist_dir: ''}
|
| 292 |
+
for base, dirs, files in sorted_walk(self.bdist_dir):
|
| 293 |
+
all_outputs.extend(
|
| 294 |
+
paths[base] + filename
|
| 295 |
+
for filename in files
|
| 296 |
+
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS
|
| 297 |
+
)
|
| 298 |
+
for filename in dirs:
|
| 299 |
+
paths[os.path.join(base, filename)] = paths[base] + filename + '/'
|
| 300 |
+
|
| 301 |
+
if self.distribution.has_ext_modules():
|
| 302 |
+
build_cmd = self.get_finalized_command('build_ext')
|
| 303 |
+
for ext in build_cmd.extensions:
|
| 304 |
+
if isinstance(ext, Library):
|
| 305 |
+
continue
|
| 306 |
+
fullname = build_cmd.get_ext_fullname(ext.name)
|
| 307 |
+
filename = build_cmd.get_ext_filename(fullname)
|
| 308 |
+
if not os.path.basename(filename).startswith('dl-'):
|
| 309 |
+
if os.path.exists(os.path.join(self.bdist_dir, filename)):
|
| 310 |
+
ext_outputs.append(filename)
|
| 311 |
+
|
| 312 |
+
return all_outputs, ext_outputs
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def walk_egg(egg_dir):
|
| 319 |
+
"""Walk an unpacked egg's contents, skipping the metadata directory"""
|
| 320 |
+
walker = sorted_walk(egg_dir)
|
| 321 |
+
base, dirs, files = next(walker)
|
| 322 |
+
if 'EGG-INFO' in dirs:
|
| 323 |
+
dirs.remove('EGG-INFO')
|
| 324 |
+
yield base, dirs, files
|
| 325 |
+
yield from walker
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def analyze_egg(egg_dir, stubs):
|
| 329 |
+
# check for existing flag in EGG-INFO
|
| 330 |
+
for flag, fn in safety_flags.items():
|
| 331 |
+
if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)):
|
| 332 |
+
return flag
|
| 333 |
+
if not can_scan():
|
| 334 |
+
return False
|
| 335 |
+
safe = True
|
| 336 |
+
for base, dirs, files in walk_egg(egg_dir):
|
| 337 |
+
for name in files:
|
| 338 |
+
if name.endswith('.py') or name.endswith('.pyw'):
|
| 339 |
+
continue
|
| 340 |
+
elif name.endswith('.pyc') or name.endswith('.pyo'):
|
| 341 |
+
# always scan, even if we already know we're not safe
|
| 342 |
+
safe = scan_module(egg_dir, base, name, stubs) and safe
|
| 343 |
+
return safe
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def write_safety_flag(egg_dir, safe):
|
| 347 |
+
# Write or remove zip safety flag file(s)
|
| 348 |
+
for flag, fn in safety_flags.items():
|
| 349 |
+
fn = os.path.join(egg_dir, fn)
|
| 350 |
+
if os.path.exists(fn):
|
| 351 |
+
if safe is None or bool(safe) != flag:
|
| 352 |
+
os.unlink(fn)
|
| 353 |
+
elif safe is not None and bool(safe) == flag:
|
| 354 |
+
with open(fn, 'wt', encoding="utf-8") as f:
|
| 355 |
+
f.write('\n')
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
safety_flags = {
|
| 359 |
+
True: 'zip-safe',
|
| 360 |
+
False: 'not-zip-safe',
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def scan_module(egg_dir, base, name, stubs):
|
| 365 |
+
"""Check whether module possibly uses unsafe-for-zipfile stuff"""
|
| 366 |
+
|
| 367 |
+
filename = os.path.join(base, name)
|
| 368 |
+
if filename[:-1] in stubs:
|
| 369 |
+
return True # Extension module
|
| 370 |
+
pkg = base[len(egg_dir) + 1 :].replace(os.sep, '.')
|
| 371 |
+
module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
|
| 372 |
+
skip = 16 # skip magic & reserved? & date & file size
|
| 373 |
+
f = open(filename, 'rb')
|
| 374 |
+
f.read(skip)
|
| 375 |
+
code = marshal.load(f)
|
| 376 |
+
f.close()
|
| 377 |
+
safe = True
|
| 378 |
+
symbols = dict.fromkeys(iter_symbols(code))
|
| 379 |
+
for bad in ['__file__', '__path__']:
|
| 380 |
+
if bad in symbols:
|
| 381 |
+
log.warn("%s: module references %s", module, bad)
|
| 382 |
+
safe = False
|
| 383 |
+
if 'inspect' in symbols:
|
| 384 |
+
for bad in [
|
| 385 |
+
'getsource',
|
| 386 |
+
'getabsfile',
|
| 387 |
+
'getfile',
|
| 388 |
+
'getsourcefile',
|
| 389 |
+
'getsourcelines',
|
| 390 |
+
'findsource',
|
| 391 |
+
'getcomments',
|
| 392 |
+
'getframeinfo',
|
| 393 |
+
'getinnerframes',
|
| 394 |
+
'getouterframes',
|
| 395 |
+
'stack',
|
| 396 |
+
'trace',
|
| 397 |
+
]:
|
| 398 |
+
if bad in symbols:
|
| 399 |
+
log.warn("%s: module MAY be using inspect.%s", module, bad)
|
| 400 |
+
safe = False
|
| 401 |
+
return safe
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def iter_symbols(code):
|
| 405 |
+
"""Yield names and strings used by `code` and its nested code objects"""
|
| 406 |
+
yield from code.co_names
|
| 407 |
+
for const in code.co_consts:
|
| 408 |
+
if isinstance(const, str):
|
| 409 |
+
yield const
|
| 410 |
+
elif isinstance(const, CodeType):
|
| 411 |
+
yield from iter_symbols(const)
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
def can_scan():
|
| 415 |
+
if not sys.platform.startswith('java') and sys.platform != 'cli':
|
| 416 |
+
# CPython, PyPy, etc.
|
| 417 |
+
return True
|
| 418 |
+
log.warn("Unable to analyze compiled code on this platform.")
|
| 419 |
+
log.warn(
|
| 420 |
+
"Please ask the author to include a 'zip_safe'"
|
| 421 |
+
" setting (either True or False) in the package's setup.py"
|
| 422 |
+
)
|
| 423 |
+
return False
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
# Attribute names of options for commands that might need to be convinced to
|
| 427 |
+
# install to the egg build directory
|
| 428 |
+
|
| 429 |
+
INSTALL_DIRECTORY_ATTRS = ['install_lib', 'install_dir', 'install_data', 'install_base']
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
def make_zipfile(
|
| 433 |
+
zip_filename, base_dir, verbose=False, dry_run=False, compress=True, mode='w'
|
| 434 |
+
):
|
| 435 |
+
"""Create a zip file from all the files under 'base_dir'. The output
|
| 436 |
+
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
|
| 437 |
+
Python module (if available) or the InfoZIP "zip" utility (if installed
|
| 438 |
+
and found on the default search path). If neither tool is available,
|
| 439 |
+
raises DistutilsExecError. Returns the name of the output zip file.
|
| 440 |
+
"""
|
| 441 |
+
import zipfile
|
| 442 |
+
|
| 443 |
+
mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
|
| 444 |
+
log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
|
| 445 |
+
|
| 446 |
+
def visit(z, dirname, names):
|
| 447 |
+
for name in names:
|
| 448 |
+
path = os.path.normpath(os.path.join(dirname, name))
|
| 449 |
+
if os.path.isfile(path):
|
| 450 |
+
p = path[len(base_dir) + 1 :]
|
| 451 |
+
if not dry_run:
|
| 452 |
+
z.write(path, p)
|
| 453 |
+
log.debug("adding '%s'", p)
|
| 454 |
+
|
| 455 |
+
compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
|
| 456 |
+
if not dry_run:
|
| 457 |
+
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
|
| 458 |
+
for dirname, dirs, files in sorted_walk(base_dir):
|
| 459 |
+
visit(z, dirname, files)
|
| 460 |
+
z.close()
|
| 461 |
+
else:
|
| 462 |
+
for dirname, dirs, files in sorted_walk(base_dir):
|
| 463 |
+
visit(None, dirname, files)
|
| 464 |
+
return zip_filename
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/bdist_rpm.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import distutils.command.bdist_rpm as orig
|
| 2 |
+
|
| 3 |
+
from ..warnings import SetuptoolsDeprecationWarning
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class bdist_rpm(orig.bdist_rpm):
|
| 7 |
+
"""
|
| 8 |
+
Override the default bdist_rpm behavior to do the following:
|
| 9 |
+
|
| 10 |
+
1. Run egg_info to ensure the name and version are properly calculated.
|
| 11 |
+
2. Always run 'install' using --single-version-externally-managed to
|
| 12 |
+
disable eggs in RPM distributions.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
def run(self):
|
| 16 |
+
SetuptoolsDeprecationWarning.emit(
|
| 17 |
+
"Deprecated command",
|
| 18 |
+
"""
|
| 19 |
+
bdist_rpm is deprecated and will be removed in a future version.
|
| 20 |
+
Use bdist_wheel (wheel packages) instead.
|
| 21 |
+
""",
|
| 22 |
+
see_url="https://github.com/pypa/setuptools/issues/1988",
|
| 23 |
+
due_date=(2023, 10, 30), # Deprecation introduced in 22 Oct 2021.
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
# ensure distro name is up-to-date
|
| 27 |
+
self.run_command('egg_info')
|
| 28 |
+
|
| 29 |
+
orig.bdist_rpm.run(self)
|
| 30 |
+
|
| 31 |
+
def _make_spec_file(self):
|
| 32 |
+
spec = orig.bdist_rpm._make_spec_file(self)
|
| 33 |
+
return [
|
| 34 |
+
line.replace(
|
| 35 |
+
"setup.py install ",
|
| 36 |
+
"setup.py install --single-version-externally-managed ",
|
| 37 |
+
).replace("%setup", "%setup -n %{name}-%{unmangled_version}")
|
| 38 |
+
for line in spec
|
| 39 |
+
]
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Create a wheel (.whl) distribution.
|
| 3 |
+
|
| 4 |
+
A wheel is a built archive format.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import os
|
| 10 |
+
import re
|
| 11 |
+
import shutil
|
| 12 |
+
import stat
|
| 13 |
+
import struct
|
| 14 |
+
import sys
|
| 15 |
+
import sysconfig
|
| 16 |
+
import warnings
|
| 17 |
+
from email.generator import BytesGenerator, Generator
|
| 18 |
+
from email.policy import EmailPolicy
|
| 19 |
+
from distutils import log
|
| 20 |
+
from glob import iglob
|
| 21 |
+
from shutil import rmtree
|
| 22 |
+
from typing import TYPE_CHECKING, Callable, Iterable, Literal, Sequence, cast
|
| 23 |
+
from zipfile import ZIP_DEFLATED, ZIP_STORED
|
| 24 |
+
|
| 25 |
+
from .. import Command, __version__
|
| 26 |
+
from ..extern.wheel.metadata import pkginfo_to_metadata
|
| 27 |
+
from ..extern.packaging import tags
|
| 28 |
+
from ..extern.packaging import version as _packaging_version
|
| 29 |
+
from ..extern.wheel.wheelfile import WheelFile
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
import types
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def safe_name(name: str) -> str:
|
| 36 |
+
"""Convert an arbitrary string to a standard distribution name
|
| 37 |
+
Any runs of non-alphanumeric/. characters are replaced with a single '-'.
|
| 38 |
+
"""
|
| 39 |
+
return re.sub("[^A-Za-z0-9.]+", "-", name)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def safe_version(version: str) -> str:
|
| 43 |
+
"""
|
| 44 |
+
Convert an arbitrary string to a standard version string
|
| 45 |
+
"""
|
| 46 |
+
try:
|
| 47 |
+
# normalize the version
|
| 48 |
+
return str(_packaging_version.Version(version))
|
| 49 |
+
except _packaging_version.InvalidVersion:
|
| 50 |
+
version = version.replace(" ", ".")
|
| 51 |
+
return re.sub("[^A-Za-z0-9.]+", "-", version)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
setuptools_major_version = int(__version__.split(".")[0])
|
| 55 |
+
|
| 56 |
+
PY_LIMITED_API_PATTERN = r"cp3\d"
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _is_32bit_interpreter() -> bool:
|
| 60 |
+
return struct.calcsize("P") == 4
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def python_tag() -> str:
|
| 64 |
+
return f"py{sys.version_info[0]}"
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def get_platform(archive_root: str | None) -> str:
|
| 68 |
+
"""Return our platform name 'win32', 'linux_x86_64'"""
|
| 69 |
+
result = sysconfig.get_platform()
|
| 70 |
+
if result.startswith("macosx") and archive_root is not None:
|
| 71 |
+
from ..extern.wheel.macosx_libfile import calculate_macosx_platform_tag
|
| 72 |
+
|
| 73 |
+
result = calculate_macosx_platform_tag(archive_root, result)
|
| 74 |
+
elif _is_32bit_interpreter():
|
| 75 |
+
if result == "linux-x86_64":
|
| 76 |
+
# pip pull request #3497
|
| 77 |
+
result = "linux-i686"
|
| 78 |
+
elif result == "linux-aarch64":
|
| 79 |
+
# packaging pull request #234
|
| 80 |
+
# TODO armv8l, packaging pull request #690 => this did not land
|
| 81 |
+
# in pip/packaging yet
|
| 82 |
+
result = "linux-armv7l"
|
| 83 |
+
|
| 84 |
+
return result.replace("-", "_")
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def get_flag(
|
| 88 |
+
var: str, fallback: bool, expected: bool = True, warn: bool = True
|
| 89 |
+
) -> bool:
|
| 90 |
+
"""Use a fallback value for determining SOABI flags if the needed config
|
| 91 |
+
var is unset or unavailable."""
|
| 92 |
+
val = sysconfig.get_config_var(var)
|
| 93 |
+
if val is None:
|
| 94 |
+
if warn:
|
| 95 |
+
warnings.warn(
|
| 96 |
+
f"Config variable '{var}' is unset, Python ABI tag may be incorrect",
|
| 97 |
+
RuntimeWarning,
|
| 98 |
+
stacklevel=2,
|
| 99 |
+
)
|
| 100 |
+
return fallback
|
| 101 |
+
return val == expected
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def get_abi_tag() -> str | None:
|
| 105 |
+
"""Return the ABI tag based on SOABI (if available) or emulate SOABI (PyPy2)."""
|
| 106 |
+
soabi: str = sysconfig.get_config_var("SOABI")
|
| 107 |
+
impl = tags.interpreter_name()
|
| 108 |
+
if not soabi and impl in ("cp", "pp") and hasattr(sys, "maxunicode"):
|
| 109 |
+
d = ""
|
| 110 |
+
m = ""
|
| 111 |
+
u = ""
|
| 112 |
+
if get_flag("Py_DEBUG", hasattr(sys, "gettotalrefcount"), warn=(impl == "cp")):
|
| 113 |
+
d = "d"
|
| 114 |
+
|
| 115 |
+
if get_flag(
|
| 116 |
+
"WITH_PYMALLOC",
|
| 117 |
+
impl == "cp",
|
| 118 |
+
warn=(impl == "cp" and sys.version_info < (3, 8)),
|
| 119 |
+
) and sys.version_info < (3, 8):
|
| 120 |
+
m = "m"
|
| 121 |
+
|
| 122 |
+
abi = f"{impl}{tags.interpreter_version()}{d}{m}{u}"
|
| 123 |
+
elif soabi and impl == "cp" and soabi.startswith("cpython"):
|
| 124 |
+
# non-Windows
|
| 125 |
+
abi = "cp" + soabi.split("-")[1]
|
| 126 |
+
elif soabi and impl == "cp" and soabi.startswith("cp"):
|
| 127 |
+
# Windows
|
| 128 |
+
abi = soabi.split("-")[0]
|
| 129 |
+
elif soabi and impl == "pp":
|
| 130 |
+
# we want something like pypy36-pp73
|
| 131 |
+
abi = "-".join(soabi.split("-")[:2])
|
| 132 |
+
abi = abi.replace(".", "_").replace("-", "_")
|
| 133 |
+
elif soabi and impl == "graalpy":
|
| 134 |
+
abi = "-".join(soabi.split("-")[:3])
|
| 135 |
+
abi = abi.replace(".", "_").replace("-", "_")
|
| 136 |
+
elif soabi:
|
| 137 |
+
abi = soabi.replace(".", "_").replace("-", "_")
|
| 138 |
+
else:
|
| 139 |
+
abi = None
|
| 140 |
+
|
| 141 |
+
return abi
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def safer_name(name: str) -> str:
|
| 145 |
+
return safe_name(name).replace("-", "_")
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def safer_version(version: str) -> str:
|
| 149 |
+
return safe_version(version).replace("-", "_")
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def remove_readonly(
|
| 153 |
+
func: Callable[..., object],
|
| 154 |
+
path: str,
|
| 155 |
+
excinfo: tuple[type[Exception], Exception, types.TracebackType],
|
| 156 |
+
) -> None:
|
| 157 |
+
remove_readonly_exc(func, path, excinfo[1])
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def remove_readonly_exc(func: Callable[..., object], path: str, exc: Exception) -> None:
|
| 161 |
+
os.chmod(path, stat.S_IWRITE)
|
| 162 |
+
func(path)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class bdist_wheel(Command):
|
| 166 |
+
description = "create a wheel distribution"
|
| 167 |
+
|
| 168 |
+
supported_compressions = {
|
| 169 |
+
"stored": ZIP_STORED,
|
| 170 |
+
"deflated": ZIP_DEFLATED,
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
user_options = [
|
| 174 |
+
("bdist-dir=", "b", "temporary directory for creating the distribution"),
|
| 175 |
+
(
|
| 176 |
+
"plat-name=",
|
| 177 |
+
"p",
|
| 178 |
+
"platform name to embed in generated filenames "
|
| 179 |
+
f"[default: {get_platform(None)}]",
|
| 180 |
+
),
|
| 181 |
+
(
|
| 182 |
+
"keep-temp",
|
| 183 |
+
"k",
|
| 184 |
+
"keep the pseudo-installation tree around after "
|
| 185 |
+
"creating the distribution archive",
|
| 186 |
+
),
|
| 187 |
+
("dist-dir=", "d", "directory to put final built distributions in"),
|
| 188 |
+
("skip-build", None, "skip rebuilding everything (for testing/debugging)"),
|
| 189 |
+
(
|
| 190 |
+
"relative",
|
| 191 |
+
None,
|
| 192 |
+
"build the archive using relative paths [default: false]",
|
| 193 |
+
),
|
| 194 |
+
(
|
| 195 |
+
"owner=",
|
| 196 |
+
"u",
|
| 197 |
+
"Owner name used when creating a tar file [default: current user]",
|
| 198 |
+
),
|
| 199 |
+
(
|
| 200 |
+
"group=",
|
| 201 |
+
"g",
|
| 202 |
+
"Group name used when creating a tar file [default: current group]",
|
| 203 |
+
),
|
| 204 |
+
("universal", None, "make a universal wheel [default: false]"),
|
| 205 |
+
(
|
| 206 |
+
"compression=",
|
| 207 |
+
None,
|
| 208 |
+
"zipfile compression (one of: {}) [default: 'deflated']".format(
|
| 209 |
+
", ".join(supported_compressions)
|
| 210 |
+
),
|
| 211 |
+
),
|
| 212 |
+
(
|
| 213 |
+
"python-tag=",
|
| 214 |
+
None,
|
| 215 |
+
f"Python implementation compatibility tag [default: '{python_tag()}']",
|
| 216 |
+
),
|
| 217 |
+
(
|
| 218 |
+
"build-number=",
|
| 219 |
+
None,
|
| 220 |
+
"Build number for this particular version. "
|
| 221 |
+
"As specified in PEP-0427, this must start with a digit. "
|
| 222 |
+
"[default: None]",
|
| 223 |
+
),
|
| 224 |
+
(
|
| 225 |
+
"py-limited-api=",
|
| 226 |
+
None,
|
| 227 |
+
"Python tag (cp32|cp33|cpNN) for abi3 wheel tag [default: false]",
|
| 228 |
+
),
|
| 229 |
+
]
|
| 230 |
+
|
| 231 |
+
boolean_options = ["keep-temp", "skip-build", "relative", "universal"]
|
| 232 |
+
|
| 233 |
+
def initialize_options(self) -> None:
|
| 234 |
+
self.bdist_dir: str | None = None
|
| 235 |
+
self.data_dir = None
|
| 236 |
+
self.plat_name: str | None = None
|
| 237 |
+
self.plat_tag = None
|
| 238 |
+
self.format = "zip"
|
| 239 |
+
self.keep_temp = False
|
| 240 |
+
self.dist_dir: str | None = None
|
| 241 |
+
self.egginfo_dir = None
|
| 242 |
+
self.root_is_pure: bool | None = None
|
| 243 |
+
self.skip_build = None
|
| 244 |
+
self.relative = False
|
| 245 |
+
self.owner = None
|
| 246 |
+
self.group = None
|
| 247 |
+
self.universal: bool = False
|
| 248 |
+
self.compression: str | int = "deflated"
|
| 249 |
+
self.python_tag: str = python_tag()
|
| 250 |
+
self.build_number: str | None = None
|
| 251 |
+
self.py_limited_api: str | Literal[False] = False
|
| 252 |
+
self.plat_name_supplied = False
|
| 253 |
+
|
| 254 |
+
def finalize_options(self):
|
| 255 |
+
if self.bdist_dir is None:
|
| 256 |
+
bdist_base = self.get_finalized_command("bdist").bdist_base
|
| 257 |
+
self.bdist_dir = os.path.join(bdist_base, "wheel")
|
| 258 |
+
|
| 259 |
+
egg_info = self.distribution.get_command_obj("egg_info")
|
| 260 |
+
egg_info.ensure_finalized() # needed for correct `wheel_dist_name`
|
| 261 |
+
|
| 262 |
+
self.data_dir = self.wheel_dist_name + ".data"
|
| 263 |
+
self.plat_name_supplied = self.plat_name is not None
|
| 264 |
+
|
| 265 |
+
try:
|
| 266 |
+
self.compression = self.supported_compressions[self.compression]
|
| 267 |
+
except KeyError:
|
| 268 |
+
raise ValueError(f"Unsupported compression: {self.compression}") from None
|
| 269 |
+
|
| 270 |
+
need_options = ("dist_dir", "plat_name", "skip_build")
|
| 271 |
+
|
| 272 |
+
self.set_undefined_options("bdist", *zip(need_options, need_options))
|
| 273 |
+
|
| 274 |
+
self.root_is_pure = not (
|
| 275 |
+
self.distribution.has_ext_modules() or self.distribution.has_c_libraries()
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
if self.py_limited_api and not re.match(
|
| 279 |
+
PY_LIMITED_API_PATTERN, self.py_limited_api
|
| 280 |
+
):
|
| 281 |
+
raise ValueError(f"py-limited-api must match '{PY_LIMITED_API_PATTERN}'")
|
| 282 |
+
|
| 283 |
+
# Support legacy [wheel] section for setting universal
|
| 284 |
+
wheel = self.distribution.get_option_dict("wheel")
|
| 285 |
+
if "universal" in wheel:
|
| 286 |
+
# please don't define this in your global configs
|
| 287 |
+
log.warn("The [wheel] section is deprecated. Use [bdist_wheel] instead.")
|
| 288 |
+
val = wheel["universal"][1].strip()
|
| 289 |
+
if val.lower() in ("1", "true", "yes"):
|
| 290 |
+
self.universal = True
|
| 291 |
+
|
| 292 |
+
if self.build_number is not None and not self.build_number[:1].isdigit():
|
| 293 |
+
raise ValueError("Build tag (build-number) must start with a digit.")
|
| 294 |
+
|
| 295 |
+
@property
|
| 296 |
+
def wheel_dist_name(self):
|
| 297 |
+
"""Return distribution full name with - replaced with _"""
|
| 298 |
+
components = (
|
| 299 |
+
safer_name(self.distribution.get_name()),
|
| 300 |
+
safer_version(self.distribution.get_version()),
|
| 301 |
+
)
|
| 302 |
+
if self.build_number:
|
| 303 |
+
components += (self.build_number,)
|
| 304 |
+
return "-".join(components)
|
| 305 |
+
|
| 306 |
+
def get_tag(self) -> tuple[str, str, str]:
|
| 307 |
+
# bdist sets self.plat_name if unset, we should only use it for purepy
|
| 308 |
+
# wheels if the user supplied it.
|
| 309 |
+
if self.plat_name_supplied:
|
| 310 |
+
plat_name = cast(str, self.plat_name)
|
| 311 |
+
elif self.root_is_pure:
|
| 312 |
+
plat_name = "any"
|
| 313 |
+
else:
|
| 314 |
+
# macosx contains system version in platform name so need special handle
|
| 315 |
+
if self.plat_name and not self.plat_name.startswith("macosx"):
|
| 316 |
+
plat_name = self.plat_name
|
| 317 |
+
else:
|
| 318 |
+
# on macosx always limit the platform name to comply with any
|
| 319 |
+
# c-extension modules in bdist_dir, since the user can specify
|
| 320 |
+
# a higher MACOSX_DEPLOYMENT_TARGET via tools like CMake
|
| 321 |
+
|
| 322 |
+
# on other platforms, and on macosx if there are no c-extension
|
| 323 |
+
# modules, use the default platform name.
|
| 324 |
+
plat_name = get_platform(self.bdist_dir)
|
| 325 |
+
|
| 326 |
+
if _is_32bit_interpreter():
|
| 327 |
+
if plat_name in ("linux-x86_64", "linux_x86_64"):
|
| 328 |
+
plat_name = "linux_i686"
|
| 329 |
+
if plat_name in ("linux-aarch64", "linux_aarch64"):
|
| 330 |
+
# TODO armv8l, packaging pull request #690 => this did not land
|
| 331 |
+
# in pip/packaging yet
|
| 332 |
+
plat_name = "linux_armv7l"
|
| 333 |
+
|
| 334 |
+
plat_name = (
|
| 335 |
+
plat_name.lower().replace("-", "_").replace(".", "_").replace(" ", "_")
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
if self.root_is_pure:
|
| 339 |
+
if self.universal:
|
| 340 |
+
impl = "py2.py3"
|
| 341 |
+
else:
|
| 342 |
+
impl = self.python_tag
|
| 343 |
+
tag = (impl, "none", plat_name)
|
| 344 |
+
else:
|
| 345 |
+
impl_name = tags.interpreter_name()
|
| 346 |
+
impl_ver = tags.interpreter_version()
|
| 347 |
+
impl = impl_name + impl_ver
|
| 348 |
+
# We don't work on CPython 3.1, 3.0.
|
| 349 |
+
if self.py_limited_api and (impl_name + impl_ver).startswith("cp3"):
|
| 350 |
+
impl = self.py_limited_api
|
| 351 |
+
abi_tag = "abi3"
|
| 352 |
+
else:
|
| 353 |
+
abi_tag = str(get_abi_tag()).lower()
|
| 354 |
+
tag = (impl, abi_tag, plat_name)
|
| 355 |
+
# issue gh-374: allow overriding plat_name
|
| 356 |
+
supported_tags = [
|
| 357 |
+
(t.interpreter, t.abi, plat_name) for t in tags.sys_tags()
|
| 358 |
+
]
|
| 359 |
+
assert (
|
| 360 |
+
tag in supported_tags
|
| 361 |
+
), f"would build wheel with unsupported tag {tag}"
|
| 362 |
+
return tag
|
| 363 |
+
|
| 364 |
+
def run(self):
|
| 365 |
+
build_scripts = self.reinitialize_command("build_scripts")
|
| 366 |
+
build_scripts.executable = "python"
|
| 367 |
+
build_scripts.force = True
|
| 368 |
+
|
| 369 |
+
build_ext = self.reinitialize_command("build_ext")
|
| 370 |
+
build_ext.inplace = False
|
| 371 |
+
|
| 372 |
+
if not self.skip_build:
|
| 373 |
+
self.run_command("build")
|
| 374 |
+
|
| 375 |
+
install = self.reinitialize_command("install", reinit_subcommands=True)
|
| 376 |
+
install.root = self.bdist_dir
|
| 377 |
+
install.compile = False
|
| 378 |
+
install.skip_build = self.skip_build
|
| 379 |
+
install.warn_dir = False
|
| 380 |
+
|
| 381 |
+
# A wheel without setuptools scripts is more cross-platform.
|
| 382 |
+
# Use the (undocumented) `no_ep` option to setuptools'
|
| 383 |
+
# install_scripts command to avoid creating entry point scripts.
|
| 384 |
+
install_scripts = self.reinitialize_command("install_scripts")
|
| 385 |
+
install_scripts.no_ep = True
|
| 386 |
+
|
| 387 |
+
# Use a custom scheme for the archive, because we have to decide
|
| 388 |
+
# at installation time which scheme to use.
|
| 389 |
+
for key in ("headers", "scripts", "data", "purelib", "platlib"):
|
| 390 |
+
setattr(install, "install_" + key, os.path.join(self.data_dir, key))
|
| 391 |
+
|
| 392 |
+
basedir_observed = ""
|
| 393 |
+
|
| 394 |
+
if os.name == "nt":
|
| 395 |
+
# win32 barfs if any of these are ''; could be '.'?
|
| 396 |
+
# (distutils.command.install:change_roots bug)
|
| 397 |
+
basedir_observed = os.path.normpath(os.path.join(self.data_dir, ".."))
|
| 398 |
+
self.install_libbase = self.install_lib = basedir_observed
|
| 399 |
+
|
| 400 |
+
setattr(
|
| 401 |
+
install,
|
| 402 |
+
"install_purelib" if self.root_is_pure else "install_platlib",
|
| 403 |
+
basedir_observed,
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
+
log.info(f"installing to {self.bdist_dir}")
|
| 407 |
+
|
| 408 |
+
self.run_command("install")
|
| 409 |
+
|
| 410 |
+
impl_tag, abi_tag, plat_tag = self.get_tag()
|
| 411 |
+
archive_basename = f"{self.wheel_dist_name}-{impl_tag}-{abi_tag}-{plat_tag}"
|
| 412 |
+
if not self.relative:
|
| 413 |
+
archive_root = self.bdist_dir
|
| 414 |
+
else:
|
| 415 |
+
archive_root = os.path.join(
|
| 416 |
+
self.bdist_dir, self._ensure_relative(install.install_base)
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
self.set_undefined_options("install_egg_info", ("target", "egginfo_dir"))
|
| 420 |
+
distinfo_dirname = (
|
| 421 |
+
f"{safer_name(self.distribution.get_name())}-"
|
| 422 |
+
f"{safer_version(self.distribution.get_version())}.dist-info"
|
| 423 |
+
)
|
| 424 |
+
distinfo_dir = os.path.join(self.bdist_dir, distinfo_dirname)
|
| 425 |
+
self.egg2dist(self.egginfo_dir, distinfo_dir)
|
| 426 |
+
|
| 427 |
+
self.write_wheelfile(distinfo_dir)
|
| 428 |
+
|
| 429 |
+
# Make the archive
|
| 430 |
+
if not os.path.exists(self.dist_dir):
|
| 431 |
+
os.makedirs(self.dist_dir)
|
| 432 |
+
|
| 433 |
+
wheel_path = os.path.join(self.dist_dir, archive_basename + ".whl")
|
| 434 |
+
with WheelFile(wheel_path, "w", self.compression) as wf:
|
| 435 |
+
wf.write_files(archive_root)
|
| 436 |
+
|
| 437 |
+
# Add to 'Distribution.dist_files' so that the "upload" command works
|
| 438 |
+
getattr(self.distribution, "dist_files", []).append((
|
| 439 |
+
"bdist_wheel",
|
| 440 |
+
"{}.{}".format(*sys.version_info[:2]), # like 3.7
|
| 441 |
+
wheel_path,
|
| 442 |
+
))
|
| 443 |
+
|
| 444 |
+
if not self.keep_temp:
|
| 445 |
+
log.info(f"removing {self.bdist_dir}")
|
| 446 |
+
if not self.dry_run:
|
| 447 |
+
if sys.version_info < (3, 12):
|
| 448 |
+
rmtree(self.bdist_dir, onerror=remove_readonly)
|
| 449 |
+
else:
|
| 450 |
+
rmtree(self.bdist_dir, onexc=remove_readonly_exc)
|
| 451 |
+
|
| 452 |
+
def write_wheelfile(
|
| 453 |
+
self, wheelfile_base: str, generator: str = f"setuptools ({__version__})"
|
| 454 |
+
):
|
| 455 |
+
from email.message import Message
|
| 456 |
+
|
| 457 |
+
msg = Message()
|
| 458 |
+
msg["Wheel-Version"] = "1.0" # of the spec
|
| 459 |
+
msg["Generator"] = generator
|
| 460 |
+
msg["Root-Is-Purelib"] = str(self.root_is_pure).lower()
|
| 461 |
+
if self.build_number is not None:
|
| 462 |
+
msg["Build"] = self.build_number
|
| 463 |
+
|
| 464 |
+
# Doesn't work for bdist_wininst
|
| 465 |
+
impl_tag, abi_tag, plat_tag = self.get_tag()
|
| 466 |
+
for impl in impl_tag.split("."):
|
| 467 |
+
for abi in abi_tag.split("."):
|
| 468 |
+
for plat in plat_tag.split("."):
|
| 469 |
+
msg["Tag"] = "-".join((impl, abi, plat))
|
| 470 |
+
|
| 471 |
+
wheelfile_path = os.path.join(wheelfile_base, "WHEEL")
|
| 472 |
+
log.info(f"creating {wheelfile_path}")
|
| 473 |
+
with open(wheelfile_path, "wb") as f:
|
| 474 |
+
BytesGenerator(f, maxheaderlen=0).flatten(msg)
|
| 475 |
+
|
| 476 |
+
def _ensure_relative(self, path: str) -> str:
|
| 477 |
+
# copied from dir_util, deleted
|
| 478 |
+
drive, path = os.path.splitdrive(path)
|
| 479 |
+
if path[0:1] == os.sep:
|
| 480 |
+
path = drive + path[1:]
|
| 481 |
+
return path
|
| 482 |
+
|
| 483 |
+
@property
|
| 484 |
+
def license_paths(self) -> Iterable[str]:
|
| 485 |
+
if setuptools_major_version >= 57:
|
| 486 |
+
# Setuptools has resolved any patterns to actual file names
|
| 487 |
+
return self.distribution.metadata.license_files or ()
|
| 488 |
+
|
| 489 |
+
files: set[str] = set()
|
| 490 |
+
metadata = self.distribution.get_option_dict("metadata")
|
| 491 |
+
if setuptools_major_version >= 42:
|
| 492 |
+
# Setuptools recognizes the license_files option but does not do globbing
|
| 493 |
+
patterns = cast(Sequence[str], self.distribution.metadata.license_files)
|
| 494 |
+
else:
|
| 495 |
+
# Prior to those, wheel is entirely responsible for handling license files
|
| 496 |
+
if "license_files" in metadata:
|
| 497 |
+
patterns = metadata["license_files"][1].split()
|
| 498 |
+
else:
|
| 499 |
+
patterns = ()
|
| 500 |
+
|
| 501 |
+
if "license_file" in metadata:
|
| 502 |
+
warnings.warn(
|
| 503 |
+
'The "license_file" option is deprecated. Use "license_files" instead.',
|
| 504 |
+
DeprecationWarning,
|
| 505 |
+
stacklevel=2,
|
| 506 |
+
)
|
| 507 |
+
files.add(metadata["license_file"][1])
|
| 508 |
+
|
| 509 |
+
if not files and not patterns and not isinstance(patterns, list):
|
| 510 |
+
patterns = ("LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*")
|
| 511 |
+
|
| 512 |
+
for pattern in patterns:
|
| 513 |
+
for path in iglob(pattern):
|
| 514 |
+
if path.endswith("~"):
|
| 515 |
+
log.debug(
|
| 516 |
+
f'ignoring license file "{path}" as it looks like a backup'
|
| 517 |
+
)
|
| 518 |
+
continue
|
| 519 |
+
|
| 520 |
+
if path not in files and os.path.isfile(path):
|
| 521 |
+
log.info(
|
| 522 |
+
f'adding license file "{path}" (matched pattern "{pattern}")'
|
| 523 |
+
)
|
| 524 |
+
files.add(path)
|
| 525 |
+
|
| 526 |
+
return files
|
| 527 |
+
|
| 528 |
+
def egg2dist(self, egginfo_path: str, distinfo_path: str):
|
| 529 |
+
"""Convert an .egg-info directory into a .dist-info directory"""
|
| 530 |
+
|
| 531 |
+
def adios(p: str) -> None:
|
| 532 |
+
"""Appropriately delete directory, file or link."""
|
| 533 |
+
if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p):
|
| 534 |
+
shutil.rmtree(p)
|
| 535 |
+
elif os.path.exists(p):
|
| 536 |
+
os.unlink(p)
|
| 537 |
+
|
| 538 |
+
adios(distinfo_path)
|
| 539 |
+
|
| 540 |
+
if not os.path.exists(egginfo_path):
|
| 541 |
+
# There is no egg-info. This is probably because the egg-info
|
| 542 |
+
# file/directory is not named matching the distribution name used
|
| 543 |
+
# to name the archive file. Check for this case and report
|
| 544 |
+
# accordingly.
|
| 545 |
+
import glob
|
| 546 |
+
|
| 547 |
+
pat = os.path.join(os.path.dirname(egginfo_path), "*.egg-info")
|
| 548 |
+
possible = glob.glob(pat)
|
| 549 |
+
err = f"Egg metadata expected at {egginfo_path} but not found"
|
| 550 |
+
if possible:
|
| 551 |
+
alt = os.path.basename(possible[0])
|
| 552 |
+
err += f" ({alt} found - possible misnamed archive file?)"
|
| 553 |
+
|
| 554 |
+
raise ValueError(err)
|
| 555 |
+
|
| 556 |
+
if os.path.isfile(egginfo_path):
|
| 557 |
+
# .egg-info is a single file
|
| 558 |
+
pkg_info = pkginfo_to_metadata(egginfo_path, egginfo_path)
|
| 559 |
+
os.mkdir(distinfo_path)
|
| 560 |
+
else:
|
| 561 |
+
# .egg-info is a directory
|
| 562 |
+
pkginfo_path = os.path.join(egginfo_path, "PKG-INFO")
|
| 563 |
+
pkg_info = pkginfo_to_metadata(egginfo_path, pkginfo_path)
|
| 564 |
+
|
| 565 |
+
# ignore common egg metadata that is useless to wheel
|
| 566 |
+
shutil.copytree(
|
| 567 |
+
egginfo_path,
|
| 568 |
+
distinfo_path,
|
| 569 |
+
ignore=lambda x, y: {
|
| 570 |
+
"PKG-INFO",
|
| 571 |
+
"requires.txt",
|
| 572 |
+
"SOURCES.txt",
|
| 573 |
+
"not-zip-safe",
|
| 574 |
+
},
|
| 575 |
+
)
|
| 576 |
+
|
| 577 |
+
# delete dependency_links if it is only whitespace
|
| 578 |
+
dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt")
|
| 579 |
+
with open(dependency_links_path, encoding="utf-8") as dependency_links_file:
|
| 580 |
+
dependency_links = dependency_links_file.read().strip()
|
| 581 |
+
if not dependency_links:
|
| 582 |
+
adios(dependency_links_path)
|
| 583 |
+
|
| 584 |
+
pkg_info_path = os.path.join(distinfo_path, "METADATA")
|
| 585 |
+
serialization_policy = EmailPolicy(
|
| 586 |
+
utf8=True,
|
| 587 |
+
mangle_from_=False,
|
| 588 |
+
max_line_length=0,
|
| 589 |
+
)
|
| 590 |
+
with open(pkg_info_path, "w", encoding="utf-8") as out:
|
| 591 |
+
Generator(out, policy=serialization_policy).flatten(pkg_info)
|
| 592 |
+
|
| 593 |
+
for license_path in self.license_paths:
|
| 594 |
+
filename = os.path.basename(license_path)
|
| 595 |
+
shutil.copy(license_path, os.path.join(distinfo_path, filename))
|
| 596 |
+
|
| 597 |
+
adios(egginfo_path)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/build_py.py
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from functools import partial
|
| 4 |
+
from glob import glob
|
| 5 |
+
from distutils.util import convert_path
|
| 6 |
+
import distutils.command.build_py as orig
|
| 7 |
+
import os
|
| 8 |
+
import fnmatch
|
| 9 |
+
import textwrap
|
| 10 |
+
import distutils.errors
|
| 11 |
+
import itertools
|
| 12 |
+
import stat
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
from typing import Iterable, Iterator
|
| 15 |
+
|
| 16 |
+
from ..extern.more_itertools import unique_everseen
|
| 17 |
+
from ..warnings import SetuptoolsDeprecationWarning
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
_IMPLICIT_DATA_FILES = ('*.pyi', 'py.typed')
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def make_writable(target):
|
| 24 |
+
os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class build_py(orig.build_py):
|
| 28 |
+
"""Enhanced 'build_py' command that includes data files with packages
|
| 29 |
+
|
| 30 |
+
The data files are specified via a 'package_data' argument to 'setup()'.
|
| 31 |
+
See 'setuptools.dist.Distribution' for more details.
|
| 32 |
+
|
| 33 |
+
Also, this version of the 'build_py' command allows you to specify both
|
| 34 |
+
'py_modules' and 'packages' in the same setup operation.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
editable_mode: bool = False
|
| 38 |
+
existing_egg_info_dir: str | None = None #: Private API, internal use only.
|
| 39 |
+
|
| 40 |
+
def finalize_options(self):
|
| 41 |
+
orig.build_py.finalize_options(self)
|
| 42 |
+
self.package_data = self.distribution.package_data
|
| 43 |
+
self.exclude_package_data = self.distribution.exclude_package_data or {}
|
| 44 |
+
if 'data_files' in self.__dict__:
|
| 45 |
+
del self.__dict__['data_files']
|
| 46 |
+
self.__updated_files = []
|
| 47 |
+
|
| 48 |
+
def copy_file(
|
| 49 |
+
self,
|
| 50 |
+
infile,
|
| 51 |
+
outfile,
|
| 52 |
+
preserve_mode=True,
|
| 53 |
+
preserve_times=True,
|
| 54 |
+
link=None,
|
| 55 |
+
level=1,
|
| 56 |
+
):
|
| 57 |
+
# Overwrite base class to allow using links
|
| 58 |
+
if link:
|
| 59 |
+
infile = str(Path(infile).resolve())
|
| 60 |
+
outfile = str(Path(outfile).resolve())
|
| 61 |
+
return super().copy_file(
|
| 62 |
+
infile, outfile, preserve_mode, preserve_times, link, level
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
def run(self):
|
| 66 |
+
"""Build modules, packages, and copy data files to build directory"""
|
| 67 |
+
if not (self.py_modules or self.packages) or self.editable_mode:
|
| 68 |
+
return
|
| 69 |
+
|
| 70 |
+
if self.py_modules:
|
| 71 |
+
self.build_modules()
|
| 72 |
+
|
| 73 |
+
if self.packages:
|
| 74 |
+
self.build_packages()
|
| 75 |
+
self.build_package_data()
|
| 76 |
+
|
| 77 |
+
# Only compile actual .py files, using our base class' idea of what our
|
| 78 |
+
# output files are.
|
| 79 |
+
self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=False))
|
| 80 |
+
|
| 81 |
+
def __getattr__(self, attr):
|
| 82 |
+
"lazily compute data files"
|
| 83 |
+
if attr == 'data_files':
|
| 84 |
+
self.data_files = self._get_data_files()
|
| 85 |
+
return self.data_files
|
| 86 |
+
return orig.build_py.__getattr__(self, attr)
|
| 87 |
+
|
| 88 |
+
def build_module(self, module, module_file, package):
|
| 89 |
+
outfile, copied = orig.build_py.build_module(self, module, module_file, package)
|
| 90 |
+
if copied:
|
| 91 |
+
self.__updated_files.append(outfile)
|
| 92 |
+
return outfile, copied
|
| 93 |
+
|
| 94 |
+
def _get_data_files(self):
|
| 95 |
+
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
|
| 96 |
+
self.analyze_manifest()
|
| 97 |
+
return list(map(self._get_pkg_data_files, self.packages or ()))
|
| 98 |
+
|
| 99 |
+
def get_data_files_without_manifest(self):
|
| 100 |
+
"""
|
| 101 |
+
Generate list of ``(package,src_dir,build_dir,filenames)`` tuples,
|
| 102 |
+
but without triggering any attempt to analyze or build the manifest.
|
| 103 |
+
"""
|
| 104 |
+
# Prevent eventual errors from unset `manifest_files`
|
| 105 |
+
# (that would otherwise be set by `analyze_manifest`)
|
| 106 |
+
self.__dict__.setdefault('manifest_files', {})
|
| 107 |
+
return list(map(self._get_pkg_data_files, self.packages or ()))
|
| 108 |
+
|
| 109 |
+
def _get_pkg_data_files(self, package):
|
| 110 |
+
# Locate package source directory
|
| 111 |
+
src_dir = self.get_package_dir(package)
|
| 112 |
+
|
| 113 |
+
# Compute package build directory
|
| 114 |
+
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
|
| 115 |
+
|
| 116 |
+
# Strip directory from globbed filenames
|
| 117 |
+
filenames = [
|
| 118 |
+
os.path.relpath(file, src_dir)
|
| 119 |
+
for file in self.find_data_files(package, src_dir)
|
| 120 |
+
]
|
| 121 |
+
return package, src_dir, build_dir, filenames
|
| 122 |
+
|
| 123 |
+
def find_data_files(self, package, src_dir):
|
| 124 |
+
"""Return filenames for package's data files in 'src_dir'"""
|
| 125 |
+
patterns = self._get_platform_patterns(
|
| 126 |
+
self.package_data,
|
| 127 |
+
package,
|
| 128 |
+
src_dir,
|
| 129 |
+
extra_patterns=_IMPLICIT_DATA_FILES,
|
| 130 |
+
)
|
| 131 |
+
globs_expanded = map(partial(glob, recursive=True), patterns)
|
| 132 |
+
# flatten the expanded globs into an iterable of matches
|
| 133 |
+
globs_matches = itertools.chain.from_iterable(globs_expanded)
|
| 134 |
+
glob_files = filter(os.path.isfile, globs_matches)
|
| 135 |
+
files = itertools.chain(
|
| 136 |
+
self.manifest_files.get(package, []),
|
| 137 |
+
glob_files,
|
| 138 |
+
)
|
| 139 |
+
return self.exclude_data_files(package, src_dir, files)
|
| 140 |
+
|
| 141 |
+
def get_outputs(self, include_bytecode=True) -> list[str]:
|
| 142 |
+
"""See :class:`setuptools.commands.build.SubCommand`"""
|
| 143 |
+
if self.editable_mode:
|
| 144 |
+
return list(self.get_output_mapping().keys())
|
| 145 |
+
return super().get_outputs(include_bytecode)
|
| 146 |
+
|
| 147 |
+
def get_output_mapping(self) -> dict[str, str]:
|
| 148 |
+
"""See :class:`setuptools.commands.build.SubCommand`"""
|
| 149 |
+
mapping = itertools.chain(
|
| 150 |
+
self._get_package_data_output_mapping(),
|
| 151 |
+
self._get_module_mapping(),
|
| 152 |
+
)
|
| 153 |
+
return dict(sorted(mapping, key=lambda x: x[0]))
|
| 154 |
+
|
| 155 |
+
def _get_module_mapping(self) -> Iterator[tuple[str, str]]:
|
| 156 |
+
"""Iterate over all modules producing (dest, src) pairs."""
|
| 157 |
+
for package, module, module_file in self.find_all_modules():
|
| 158 |
+
package = package.split('.')
|
| 159 |
+
filename = self.get_module_outfile(self.build_lib, package, module)
|
| 160 |
+
yield (filename, module_file)
|
| 161 |
+
|
| 162 |
+
def _get_package_data_output_mapping(self) -> Iterator[tuple[str, str]]:
|
| 163 |
+
"""Iterate over package data producing (dest, src) pairs."""
|
| 164 |
+
for package, src_dir, build_dir, filenames in self.data_files:
|
| 165 |
+
for filename in filenames:
|
| 166 |
+
target = os.path.join(build_dir, filename)
|
| 167 |
+
srcfile = os.path.join(src_dir, filename)
|
| 168 |
+
yield (target, srcfile)
|
| 169 |
+
|
| 170 |
+
def build_package_data(self):
|
| 171 |
+
"""Copy data files into build directory"""
|
| 172 |
+
for target, srcfile in self._get_package_data_output_mapping():
|
| 173 |
+
self.mkpath(os.path.dirname(target))
|
| 174 |
+
_outf, _copied = self.copy_file(srcfile, target)
|
| 175 |
+
make_writable(target)
|
| 176 |
+
|
| 177 |
+
def analyze_manifest(self):
|
| 178 |
+
self.manifest_files = mf = {}
|
| 179 |
+
if not self.distribution.include_package_data:
|
| 180 |
+
return
|
| 181 |
+
src_dirs = {}
|
| 182 |
+
for package in self.packages or ():
|
| 183 |
+
# Locate package source directory
|
| 184 |
+
src_dirs[assert_relative(self.get_package_dir(package))] = package
|
| 185 |
+
|
| 186 |
+
if (
|
| 187 |
+
getattr(self, 'existing_egg_info_dir', None)
|
| 188 |
+
and Path(self.existing_egg_info_dir, "SOURCES.txt").exists()
|
| 189 |
+
):
|
| 190 |
+
egg_info_dir = self.existing_egg_info_dir
|
| 191 |
+
manifest = Path(egg_info_dir, "SOURCES.txt")
|
| 192 |
+
files = manifest.read_text(encoding="utf-8").splitlines()
|
| 193 |
+
else:
|
| 194 |
+
self.run_command('egg_info')
|
| 195 |
+
ei_cmd = self.get_finalized_command('egg_info')
|
| 196 |
+
egg_info_dir = ei_cmd.egg_info
|
| 197 |
+
files = ei_cmd.filelist.files
|
| 198 |
+
|
| 199 |
+
check = _IncludePackageDataAbuse()
|
| 200 |
+
for path in self._filter_build_files(files, egg_info_dir):
|
| 201 |
+
d, f = os.path.split(assert_relative(path))
|
| 202 |
+
prev = None
|
| 203 |
+
oldf = f
|
| 204 |
+
while d and d != prev and d not in src_dirs:
|
| 205 |
+
prev = d
|
| 206 |
+
d, df = os.path.split(d)
|
| 207 |
+
f = os.path.join(df, f)
|
| 208 |
+
if d in src_dirs:
|
| 209 |
+
if f == oldf:
|
| 210 |
+
if check.is_module(f):
|
| 211 |
+
continue # it's a module, not data
|
| 212 |
+
else:
|
| 213 |
+
importable = check.importable_subpackage(src_dirs[d], f)
|
| 214 |
+
if importable:
|
| 215 |
+
check.warn(importable)
|
| 216 |
+
mf.setdefault(src_dirs[d], []).append(path)
|
| 217 |
+
|
| 218 |
+
def _filter_build_files(self, files: Iterable[str], egg_info: str) -> Iterator[str]:
|
| 219 |
+
"""
|
| 220 |
+
``build_meta`` may try to create egg_info outside of the project directory,
|
| 221 |
+
and this can be problematic for certain plugins (reported in issue #3500).
|
| 222 |
+
|
| 223 |
+
Extensions might also include between their sources files created on the
|
| 224 |
+
``build_lib`` and ``build_temp`` directories.
|
| 225 |
+
|
| 226 |
+
This function should filter this case of invalid files out.
|
| 227 |
+
"""
|
| 228 |
+
build = self.get_finalized_command("build")
|
| 229 |
+
build_dirs = (egg_info, self.build_lib, build.build_temp, build.build_base)
|
| 230 |
+
norm_dirs = [os.path.normpath(p) for p in build_dirs if p]
|
| 231 |
+
|
| 232 |
+
for file in files:
|
| 233 |
+
norm_path = os.path.normpath(file)
|
| 234 |
+
if not os.path.isabs(file) or all(d not in norm_path for d in norm_dirs):
|
| 235 |
+
yield file
|
| 236 |
+
|
| 237 |
+
def get_data_files(self):
|
| 238 |
+
pass # Lazily compute data files in _get_data_files() function.
|
| 239 |
+
|
| 240 |
+
def check_package(self, package, package_dir):
|
| 241 |
+
"""Check namespace packages' __init__ for declare_namespace"""
|
| 242 |
+
try:
|
| 243 |
+
return self.packages_checked[package]
|
| 244 |
+
except KeyError:
|
| 245 |
+
pass
|
| 246 |
+
|
| 247 |
+
init_py = orig.build_py.check_package(self, package, package_dir)
|
| 248 |
+
self.packages_checked[package] = init_py
|
| 249 |
+
|
| 250 |
+
if not init_py or not self.distribution.namespace_packages:
|
| 251 |
+
return init_py
|
| 252 |
+
|
| 253 |
+
for pkg in self.distribution.namespace_packages:
|
| 254 |
+
if pkg == package or pkg.startswith(package + '.'):
|
| 255 |
+
break
|
| 256 |
+
else:
|
| 257 |
+
return init_py
|
| 258 |
+
|
| 259 |
+
with open(init_py, 'rb') as f:
|
| 260 |
+
contents = f.read()
|
| 261 |
+
if b'declare_namespace' not in contents:
|
| 262 |
+
raise distutils.errors.DistutilsError(
|
| 263 |
+
"Namespace package problem: %s is a namespace package, but "
|
| 264 |
+
"its\n__init__.py does not call declare_namespace()! Please "
|
| 265 |
+
'fix it.\n(See the setuptools manual under '
|
| 266 |
+
'"Namespace Packages" for details.)\n"' % (package,)
|
| 267 |
+
)
|
| 268 |
+
return init_py
|
| 269 |
+
|
| 270 |
+
def initialize_options(self):
|
| 271 |
+
self.packages_checked = {}
|
| 272 |
+
orig.build_py.initialize_options(self)
|
| 273 |
+
self.editable_mode = False
|
| 274 |
+
self.existing_egg_info_dir = None
|
| 275 |
+
|
| 276 |
+
def get_package_dir(self, package):
|
| 277 |
+
res = orig.build_py.get_package_dir(self, package)
|
| 278 |
+
if self.distribution.src_root is not None:
|
| 279 |
+
return os.path.join(self.distribution.src_root, res)
|
| 280 |
+
return res
|
| 281 |
+
|
| 282 |
+
def exclude_data_files(self, package, src_dir, files):
|
| 283 |
+
"""Filter filenames for package's data files in 'src_dir'"""
|
| 284 |
+
files = list(files)
|
| 285 |
+
patterns = self._get_platform_patterns(
|
| 286 |
+
self.exclude_package_data,
|
| 287 |
+
package,
|
| 288 |
+
src_dir,
|
| 289 |
+
)
|
| 290 |
+
match_groups = (fnmatch.filter(files, pattern) for pattern in patterns)
|
| 291 |
+
# flatten the groups of matches into an iterable of matches
|
| 292 |
+
matches = itertools.chain.from_iterable(match_groups)
|
| 293 |
+
bad = set(matches)
|
| 294 |
+
keepers = (fn for fn in files if fn not in bad)
|
| 295 |
+
# ditch dupes
|
| 296 |
+
return list(unique_everseen(keepers))
|
| 297 |
+
|
| 298 |
+
@staticmethod
|
| 299 |
+
def _get_platform_patterns(spec, package, src_dir, extra_patterns=()):
|
| 300 |
+
"""
|
| 301 |
+
yield platform-specific path patterns (suitable for glob
|
| 302 |
+
or fn_match) from a glob-based spec (such as
|
| 303 |
+
self.package_data or self.exclude_package_data)
|
| 304 |
+
matching package in src_dir.
|
| 305 |
+
"""
|
| 306 |
+
raw_patterns = itertools.chain(
|
| 307 |
+
extra_patterns,
|
| 308 |
+
spec.get('', []),
|
| 309 |
+
spec.get(package, []),
|
| 310 |
+
)
|
| 311 |
+
return (
|
| 312 |
+
# Each pattern has to be converted to a platform-specific path
|
| 313 |
+
os.path.join(src_dir, convert_path(pattern))
|
| 314 |
+
for pattern in raw_patterns
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def assert_relative(path):
|
| 319 |
+
if not os.path.isabs(path):
|
| 320 |
+
return path
|
| 321 |
+
from distutils.errors import DistutilsSetupError
|
| 322 |
+
|
| 323 |
+
msg = (
|
| 324 |
+
textwrap.dedent(
|
| 325 |
+
"""
|
| 326 |
+
Error: setup script specifies an absolute path:
|
| 327 |
+
|
| 328 |
+
%s
|
| 329 |
+
|
| 330 |
+
setup() arguments must *always* be /-separated paths relative to the
|
| 331 |
+
setup.py directory, *never* absolute paths.
|
| 332 |
+
"""
|
| 333 |
+
).lstrip()
|
| 334 |
+
% path
|
| 335 |
+
)
|
| 336 |
+
raise DistutilsSetupError(msg)
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
class _IncludePackageDataAbuse:
|
| 340 |
+
"""Inform users that package or module is included as 'data file'"""
|
| 341 |
+
|
| 342 |
+
class _Warning(SetuptoolsDeprecationWarning):
|
| 343 |
+
_SUMMARY = """
|
| 344 |
+
Package {importable!r} is absent from the `packages` configuration.
|
| 345 |
+
"""
|
| 346 |
+
|
| 347 |
+
_DETAILS = """
|
| 348 |
+
############################
|
| 349 |
+
# Package would be ignored #
|
| 350 |
+
############################
|
| 351 |
+
Python recognizes {importable!r} as an importable package[^1],
|
| 352 |
+
but it is absent from setuptools' `packages` configuration.
|
| 353 |
+
|
| 354 |
+
This leads to an ambiguous overall configuration. If you want to distribute this
|
| 355 |
+
package, please make sure that {importable!r} is explicitly added
|
| 356 |
+
to the `packages` configuration field.
|
| 357 |
+
|
| 358 |
+
Alternatively, you can also rely on setuptools' discovery methods
|
| 359 |
+
(for example by using `find_namespace_packages(...)`/`find_namespace:`
|
| 360 |
+
instead of `find_packages(...)`/`find:`).
|
| 361 |
+
|
| 362 |
+
You can read more about "package discovery" on setuptools documentation page:
|
| 363 |
+
|
| 364 |
+
- https://setuptools.pypa.io/en/latest/userguide/package_discovery.html
|
| 365 |
+
|
| 366 |
+
If you don't want {importable!r} to be distributed and are
|
| 367 |
+
already explicitly excluding {importable!r} via
|
| 368 |
+
`find_namespace_packages(...)/find_namespace` or `find_packages(...)/find`,
|
| 369 |
+
you can try to use `exclude_package_data`, or `include-package-data=False` in
|
| 370 |
+
combination with a more fine grained `package-data` configuration.
|
| 371 |
+
|
| 372 |
+
You can read more about "package data files" on setuptools documentation page:
|
| 373 |
+
|
| 374 |
+
- https://setuptools.pypa.io/en/latest/userguide/datafiles.html
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
[^1]: For Python, any directory (with suitable naming) can be imported,
|
| 378 |
+
even if it does not contain any `.py` files.
|
| 379 |
+
On the other hand, currently there is no concept of package data
|
| 380 |
+
directory, all directories are treated like packages.
|
| 381 |
+
"""
|
| 382 |
+
# _DUE_DATE: still not defined as this is particularly controversial.
|
| 383 |
+
# Warning initially introduced in May 2022. See issue #3340 for discussion.
|
| 384 |
+
|
| 385 |
+
def __init__(self):
|
| 386 |
+
self._already_warned = set()
|
| 387 |
+
|
| 388 |
+
def is_module(self, file):
|
| 389 |
+
return file.endswith(".py") and file[: -len(".py")].isidentifier()
|
| 390 |
+
|
| 391 |
+
def importable_subpackage(self, parent, file):
|
| 392 |
+
pkg = Path(file).parent
|
| 393 |
+
parts = list(itertools.takewhile(str.isidentifier, pkg.parts))
|
| 394 |
+
if parts:
|
| 395 |
+
return ".".join([parent, *parts])
|
| 396 |
+
return None
|
| 397 |
+
|
| 398 |
+
def warn(self, importable):
|
| 399 |
+
if importable not in self._already_warned:
|
| 400 |
+
self._Warning.emit(importable=importable)
|
| 401 |
+
self._already_warned.add(importable)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/develop.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from distutils.util import convert_path
|
| 2 |
+
from distutils import log
|
| 3 |
+
from distutils.errors import DistutilsOptionError
|
| 4 |
+
import os
|
| 5 |
+
import glob
|
| 6 |
+
|
| 7 |
+
from setuptools.command.easy_install import easy_install
|
| 8 |
+
from setuptools import _normalization
|
| 9 |
+
from setuptools import _path
|
| 10 |
+
from setuptools import namespaces
|
| 11 |
+
import setuptools
|
| 12 |
+
|
| 13 |
+
from ..unicode_utils import _read_utf8_with_fallback
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class develop(namespaces.DevelopInstaller, easy_install):
|
| 17 |
+
"""Set up package for development"""
|
| 18 |
+
|
| 19 |
+
description = "install package in 'development mode'"
|
| 20 |
+
|
| 21 |
+
user_options = easy_install.user_options + [
|
| 22 |
+
("uninstall", "u", "Uninstall this source package"),
|
| 23 |
+
("egg-path=", None, "Set the path to be used in the .egg-link file"),
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
boolean_options = easy_install.boolean_options + ['uninstall']
|
| 27 |
+
|
| 28 |
+
command_consumes_arguments = False # override base
|
| 29 |
+
|
| 30 |
+
def run(self):
|
| 31 |
+
if self.uninstall:
|
| 32 |
+
self.multi_version = True
|
| 33 |
+
self.uninstall_link()
|
| 34 |
+
self.uninstall_namespaces()
|
| 35 |
+
else:
|
| 36 |
+
self.install_for_development()
|
| 37 |
+
self.warn_deprecated_options()
|
| 38 |
+
|
| 39 |
+
def initialize_options(self):
|
| 40 |
+
self.uninstall = None
|
| 41 |
+
self.egg_path = None
|
| 42 |
+
easy_install.initialize_options(self)
|
| 43 |
+
self.setup_path = None
|
| 44 |
+
self.always_copy_from = '.' # always copy eggs installed in curdir
|
| 45 |
+
|
| 46 |
+
def finalize_options(self):
|
| 47 |
+
import pkg_resources
|
| 48 |
+
|
| 49 |
+
ei = self.get_finalized_command("egg_info")
|
| 50 |
+
self.args = [ei.egg_name]
|
| 51 |
+
|
| 52 |
+
easy_install.finalize_options(self)
|
| 53 |
+
self.expand_basedirs()
|
| 54 |
+
self.expand_dirs()
|
| 55 |
+
# pick up setup-dir .egg files only: no .egg-info
|
| 56 |
+
self.package_index.scan(glob.glob('*.egg'))
|
| 57 |
+
|
| 58 |
+
egg_link_fn = (
|
| 59 |
+
_normalization.filename_component_broken(ei.egg_name) + '.egg-link'
|
| 60 |
+
)
|
| 61 |
+
self.egg_link = os.path.join(self.install_dir, egg_link_fn)
|
| 62 |
+
self.egg_base = ei.egg_base
|
| 63 |
+
if self.egg_path is None:
|
| 64 |
+
self.egg_path = os.path.abspath(ei.egg_base)
|
| 65 |
+
|
| 66 |
+
target = _path.normpath(self.egg_base)
|
| 67 |
+
egg_path = _path.normpath(os.path.join(self.install_dir, self.egg_path))
|
| 68 |
+
if egg_path != target:
|
| 69 |
+
raise DistutilsOptionError(
|
| 70 |
+
"--egg-path must be a relative path from the install"
|
| 71 |
+
" directory to " + target
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
# Make a distribution for the package's source
|
| 75 |
+
self.dist = pkg_resources.Distribution(
|
| 76 |
+
target,
|
| 77 |
+
pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)),
|
| 78 |
+
project_name=ei.egg_name,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
self.setup_path = self._resolve_setup_path(
|
| 82 |
+
self.egg_base,
|
| 83 |
+
self.install_dir,
|
| 84 |
+
self.egg_path,
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
@staticmethod
|
| 88 |
+
def _resolve_setup_path(egg_base, install_dir, egg_path):
|
| 89 |
+
"""
|
| 90 |
+
Generate a path from egg_base back to '.' where the
|
| 91 |
+
setup script resides and ensure that path points to the
|
| 92 |
+
setup path from $install_dir/$egg_path.
|
| 93 |
+
"""
|
| 94 |
+
path_to_setup = egg_base.replace(os.sep, '/').rstrip('/')
|
| 95 |
+
if path_to_setup != os.curdir:
|
| 96 |
+
path_to_setup = '../' * (path_to_setup.count('/') + 1)
|
| 97 |
+
resolved = _path.normpath(os.path.join(install_dir, egg_path, path_to_setup))
|
| 98 |
+
curdir = _path.normpath(os.curdir)
|
| 99 |
+
if resolved != curdir:
|
| 100 |
+
raise DistutilsOptionError(
|
| 101 |
+
"Can't get a consistent path to setup script from"
|
| 102 |
+
" installation directory",
|
| 103 |
+
resolved,
|
| 104 |
+
curdir,
|
| 105 |
+
)
|
| 106 |
+
return path_to_setup
|
| 107 |
+
|
| 108 |
+
def install_for_development(self):
|
| 109 |
+
self.run_command('egg_info')
|
| 110 |
+
|
| 111 |
+
# Build extensions in-place
|
| 112 |
+
self.reinitialize_command('build_ext', inplace=True)
|
| 113 |
+
self.run_command('build_ext')
|
| 114 |
+
|
| 115 |
+
if setuptools.bootstrap_install_from:
|
| 116 |
+
self.easy_install(setuptools.bootstrap_install_from)
|
| 117 |
+
setuptools.bootstrap_install_from = None
|
| 118 |
+
|
| 119 |
+
self.install_namespaces()
|
| 120 |
+
|
| 121 |
+
# create an .egg-link in the installation dir, pointing to our egg
|
| 122 |
+
log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
|
| 123 |
+
if not self.dry_run:
|
| 124 |
+
with open(self.egg_link, "w", encoding="utf-8") as f:
|
| 125 |
+
f.write(self.egg_path + "\n" + self.setup_path)
|
| 126 |
+
# postprocess the installed distro, fixing up .pth, installing scripts,
|
| 127 |
+
# and handling requirements
|
| 128 |
+
self.process_distribution(None, self.dist, not self.no_deps)
|
| 129 |
+
|
| 130 |
+
def uninstall_link(self):
|
| 131 |
+
if os.path.exists(self.egg_link):
|
| 132 |
+
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
|
| 133 |
+
|
| 134 |
+
contents = [
|
| 135 |
+
line.rstrip()
|
| 136 |
+
for line in _read_utf8_with_fallback(self.egg_link).splitlines()
|
| 137 |
+
]
|
| 138 |
+
|
| 139 |
+
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
|
| 140 |
+
log.warn("Link points to %s: uninstall aborted", contents)
|
| 141 |
+
return
|
| 142 |
+
if not self.dry_run:
|
| 143 |
+
os.unlink(self.egg_link)
|
| 144 |
+
if not self.dry_run:
|
| 145 |
+
self.update_pth(self.dist) # remove any .pth link to us
|
| 146 |
+
if self.distribution.scripts:
|
| 147 |
+
# XXX should also check for entry point scripts!
|
| 148 |
+
log.warn("Note: you must uninstall or replace scripts manually!")
|
| 149 |
+
|
| 150 |
+
def install_egg_scripts(self, dist):
|
| 151 |
+
if dist is not self.dist:
|
| 152 |
+
# Installing a dependency, so fall back to normal behavior
|
| 153 |
+
return easy_install.install_egg_scripts(self, dist)
|
| 154 |
+
|
| 155 |
+
# create wrapper scripts in the script dir, pointing to dist.scripts
|
| 156 |
+
|
| 157 |
+
# new-style...
|
| 158 |
+
self.install_wrapper_scripts(dist)
|
| 159 |
+
|
| 160 |
+
# ...and old-style
|
| 161 |
+
for script_name in self.distribution.scripts or []:
|
| 162 |
+
script_path = os.path.abspath(convert_path(script_name))
|
| 163 |
+
script_name = os.path.basename(script_path)
|
| 164 |
+
script_text = _read_utf8_with_fallback(script_path)
|
| 165 |
+
self.install_script(dist, script_name, script_text, script_path)
|
| 166 |
+
|
| 167 |
+
return None
|
| 168 |
+
|
| 169 |
+
def install_wrapper_scripts(self, dist):
|
| 170 |
+
dist = VersionlessRequirement(dist)
|
| 171 |
+
return easy_install.install_wrapper_scripts(self, dist)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class VersionlessRequirement:
|
| 175 |
+
"""
|
| 176 |
+
Adapt a pkg_resources.Distribution to simply return the project
|
| 177 |
+
name as the 'requirement' so that scripts will work across
|
| 178 |
+
multiple versions.
|
| 179 |
+
|
| 180 |
+
>>> from pkg_resources import Distribution
|
| 181 |
+
>>> dist = Distribution(project_name='foo', version='1.0')
|
| 182 |
+
>>> str(dist.as_requirement())
|
| 183 |
+
'foo==1.0'
|
| 184 |
+
>>> adapted_dist = VersionlessRequirement(dist)
|
| 185 |
+
>>> str(adapted_dist.as_requirement())
|
| 186 |
+
'foo'
|
| 187 |
+
"""
|
| 188 |
+
|
| 189 |
+
def __init__(self, dist):
|
| 190 |
+
self.__dist = dist
|
| 191 |
+
|
| 192 |
+
def __getattr__(self, name):
|
| 193 |
+
return getattr(self.__dist, name)
|
| 194 |
+
|
| 195 |
+
def as_requirement(self):
|
| 196 |
+
return self.project_name
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/dist_info.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Create a dist_info directory
|
| 3 |
+
As defined in the wheel specification
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import shutil
|
| 8 |
+
from contextlib import contextmanager
|
| 9 |
+
from distutils import log
|
| 10 |
+
from distutils.core import Command
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
from typing import cast
|
| 13 |
+
|
| 14 |
+
from .. import _normalization
|
| 15 |
+
from .egg_info import egg_info as egg_info_cls
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class dist_info(Command):
|
| 19 |
+
"""
|
| 20 |
+
This command is private and reserved for internal use of setuptools,
|
| 21 |
+
users should rely on ``setuptools.build_meta`` APIs.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create .dist-info directory"
|
| 25 |
+
|
| 26 |
+
user_options = [
|
| 27 |
+
(
|
| 28 |
+
'output-dir=',
|
| 29 |
+
'o',
|
| 30 |
+
"directory inside of which the .dist-info will be"
|
| 31 |
+
"created [default: top of the source tree]",
|
| 32 |
+
),
|
| 33 |
+
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
|
| 34 |
+
('tag-build=', 'b', "Specify explicit tag to add to version number"),
|
| 35 |
+
('no-date', 'D', "Don't include date stamp [default]"),
|
| 36 |
+
('keep-egg-info', None, "*TRANSITIONAL* will be removed in the future"),
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
+
boolean_options = ['tag-date', 'keep-egg-info']
|
| 40 |
+
negative_opt = {'no-date': 'tag-date'}
|
| 41 |
+
|
| 42 |
+
def initialize_options(self):
|
| 43 |
+
self.output_dir = None
|
| 44 |
+
self.name = None
|
| 45 |
+
self.dist_info_dir = None
|
| 46 |
+
self.tag_date = None
|
| 47 |
+
self.tag_build = None
|
| 48 |
+
self.keep_egg_info = False
|
| 49 |
+
|
| 50 |
+
def finalize_options(self):
|
| 51 |
+
dist = self.distribution
|
| 52 |
+
project_dir = dist.src_root or os.curdir
|
| 53 |
+
self.output_dir = Path(self.output_dir or project_dir)
|
| 54 |
+
|
| 55 |
+
egg_info = cast(egg_info_cls, self.reinitialize_command("egg_info"))
|
| 56 |
+
egg_info.egg_base = str(self.output_dir)
|
| 57 |
+
|
| 58 |
+
if self.tag_date:
|
| 59 |
+
egg_info.tag_date = self.tag_date
|
| 60 |
+
else:
|
| 61 |
+
self.tag_date = egg_info.tag_date
|
| 62 |
+
|
| 63 |
+
if self.tag_build:
|
| 64 |
+
egg_info.tag_build = self.tag_build
|
| 65 |
+
else:
|
| 66 |
+
self.tag_build = egg_info.tag_build
|
| 67 |
+
|
| 68 |
+
egg_info.finalize_options()
|
| 69 |
+
self.egg_info = egg_info
|
| 70 |
+
|
| 71 |
+
name = _normalization.safer_name(dist.get_name())
|
| 72 |
+
version = _normalization.safer_best_effort_version(dist.get_version())
|
| 73 |
+
self.name = f"{name}-{version}"
|
| 74 |
+
self.dist_info_dir = os.path.join(self.output_dir, f"{self.name}.dist-info")
|
| 75 |
+
|
| 76 |
+
@contextmanager
|
| 77 |
+
def _maybe_bkp_dir(self, dir_path: str, requires_bkp: bool):
|
| 78 |
+
if requires_bkp:
|
| 79 |
+
bkp_name = f"{dir_path}.__bkp__"
|
| 80 |
+
_rm(bkp_name, ignore_errors=True)
|
| 81 |
+
shutil.copytree(dir_path, bkp_name, dirs_exist_ok=True, symlinks=True)
|
| 82 |
+
try:
|
| 83 |
+
yield
|
| 84 |
+
finally:
|
| 85 |
+
_rm(dir_path, ignore_errors=True)
|
| 86 |
+
shutil.move(bkp_name, dir_path)
|
| 87 |
+
else:
|
| 88 |
+
yield
|
| 89 |
+
|
| 90 |
+
def run(self):
|
| 91 |
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
| 92 |
+
self.egg_info.run()
|
| 93 |
+
egg_info_dir = self.egg_info.egg_info
|
| 94 |
+
assert os.path.isdir(egg_info_dir), ".egg-info dir should have been created"
|
| 95 |
+
|
| 96 |
+
log.info("creating '{}'".format(os.path.abspath(self.dist_info_dir)))
|
| 97 |
+
bdist_wheel = self.get_finalized_command('bdist_wheel')
|
| 98 |
+
|
| 99 |
+
# TODO: if bdist_wheel if merged into setuptools, just add "keep_egg_info" there
|
| 100 |
+
with self._maybe_bkp_dir(egg_info_dir, self.keep_egg_info):
|
| 101 |
+
bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _rm(dir_name, **opts):
|
| 105 |
+
if os.path.isdir(dir_name):
|
| 106 |
+
shutil.rmtree(dir_name, **opts)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/editable_wheel.py
ADDED
|
@@ -0,0 +1,918 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Create a wheel that, when installed, will make the source package 'editable'
|
| 3 |
+
(add it to the interpreter's path, including metadata) per PEP 660. Replaces
|
| 4 |
+
'setup.py develop'.
|
| 5 |
+
|
| 6 |
+
.. note::
|
| 7 |
+
One of the mechanisms briefly mentioned in PEP 660 to implement editable installs is
|
| 8 |
+
to create a separated directory inside ``build`` and use a .pth file to point to that
|
| 9 |
+
directory. In the context of this file such directory is referred as
|
| 10 |
+
*auxiliary build directory* or ``auxiliary_dir``.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
import logging
|
| 16 |
+
import io
|
| 17 |
+
import os
|
| 18 |
+
import shutil
|
| 19 |
+
import traceback
|
| 20 |
+
from contextlib import suppress
|
| 21 |
+
from enum import Enum
|
| 22 |
+
from inspect import cleandoc
|
| 23 |
+
from itertools import chain, starmap
|
| 24 |
+
from pathlib import Path
|
| 25 |
+
from tempfile import TemporaryDirectory
|
| 26 |
+
from typing import (
|
| 27 |
+
TYPE_CHECKING,
|
| 28 |
+
Iterable,
|
| 29 |
+
Iterator,
|
| 30 |
+
Mapping,
|
| 31 |
+
Protocol,
|
| 32 |
+
TypeVar,
|
| 33 |
+
cast,
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
from .. import (
|
| 37 |
+
Command,
|
| 38 |
+
_normalization,
|
| 39 |
+
_path,
|
| 40 |
+
errors,
|
| 41 |
+
namespaces,
|
| 42 |
+
)
|
| 43 |
+
from .._path import StrPath
|
| 44 |
+
from ..compat import py39
|
| 45 |
+
from ..discovery import find_package_path
|
| 46 |
+
from ..dist import Distribution
|
| 47 |
+
from ..warnings import (
|
| 48 |
+
InformationOnly,
|
| 49 |
+
SetuptoolsDeprecationWarning,
|
| 50 |
+
SetuptoolsWarning,
|
| 51 |
+
)
|
| 52 |
+
from .build import build as build_cls
|
| 53 |
+
from .build_py import build_py as build_py_cls
|
| 54 |
+
from .dist_info import dist_info as dist_info_cls
|
| 55 |
+
from .egg_info import egg_info as egg_info_cls
|
| 56 |
+
from .install import install as install_cls
|
| 57 |
+
from .install_scripts import install_scripts as install_scripts_cls
|
| 58 |
+
|
| 59 |
+
if TYPE_CHECKING:
|
| 60 |
+
from .._vendor.wheel.wheelfile import WheelFile
|
| 61 |
+
|
| 62 |
+
_P = TypeVar("_P", bound=StrPath)
|
| 63 |
+
_logger = logging.getLogger(__name__)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class _EditableMode(Enum):
|
| 67 |
+
"""
|
| 68 |
+
Possible editable installation modes:
|
| 69 |
+
`lenient` (new files automatically added to the package - DEFAULT);
|
| 70 |
+
`strict` (requires a new installation when files are added/removed); or
|
| 71 |
+
`compat` (attempts to emulate `python setup.py develop` - DEPRECATED).
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
STRICT = "strict"
|
| 75 |
+
LENIENT = "lenient"
|
| 76 |
+
COMPAT = "compat" # TODO: Remove `compat` after Dec/2022.
|
| 77 |
+
|
| 78 |
+
@classmethod
|
| 79 |
+
def convert(cls, mode: str | None) -> _EditableMode:
|
| 80 |
+
if not mode:
|
| 81 |
+
return _EditableMode.LENIENT # default
|
| 82 |
+
|
| 83 |
+
_mode = mode.upper()
|
| 84 |
+
if _mode not in _EditableMode.__members__:
|
| 85 |
+
raise errors.OptionError(f"Invalid editable mode: {mode!r}. Try: 'strict'.")
|
| 86 |
+
|
| 87 |
+
if _mode == "COMPAT":
|
| 88 |
+
SetuptoolsDeprecationWarning.emit(
|
| 89 |
+
"Compat editable installs",
|
| 90 |
+
"""
|
| 91 |
+
The 'compat' editable mode is transitional and will be removed
|
| 92 |
+
in future versions of `setuptools`.
|
| 93 |
+
Please adapt your code accordingly to use either the 'strict' or the
|
| 94 |
+
'lenient' modes.
|
| 95 |
+
""",
|
| 96 |
+
see_docs="userguide/development_mode.html",
|
| 97 |
+
# TODO: define due_date
|
| 98 |
+
# There is a series of shortcomings with the available editable install
|
| 99 |
+
# methods, and they are very controversial. This is something that still
|
| 100 |
+
# needs work.
|
| 101 |
+
# Moreover, `pip` is still hiding this warning, so users are not aware.
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
return _EditableMode[_mode]
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
_STRICT_WARNING = """
|
| 108 |
+
New or renamed files may not be automatically picked up without a new installation.
|
| 109 |
+
"""
|
| 110 |
+
|
| 111 |
+
_LENIENT_WARNING = """
|
| 112 |
+
Options like `package-data`, `include/exclude-package-data` or
|
| 113 |
+
`packages.find.exclude/include` may have no effect.
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
class editable_wheel(Command):
|
| 118 |
+
"""Build 'editable' wheel for development.
|
| 119 |
+
This command is private and reserved for internal use of setuptools,
|
| 120 |
+
users should rely on ``setuptools.build_meta`` APIs.
|
| 121 |
+
"""
|
| 122 |
+
|
| 123 |
+
description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create PEP 660 editable wheel"
|
| 124 |
+
|
| 125 |
+
user_options = [
|
| 126 |
+
("dist-dir=", "d", "directory to put final built distributions in"),
|
| 127 |
+
("dist-info-dir=", "I", "path to a pre-build .dist-info directory"),
|
| 128 |
+
("mode=", None, cleandoc(_EditableMode.__doc__ or "")),
|
| 129 |
+
]
|
| 130 |
+
|
| 131 |
+
def initialize_options(self):
|
| 132 |
+
self.dist_dir = None
|
| 133 |
+
self.dist_info_dir = None
|
| 134 |
+
self.project_dir = None
|
| 135 |
+
self.mode = None
|
| 136 |
+
|
| 137 |
+
def finalize_options(self):
|
| 138 |
+
dist = self.distribution
|
| 139 |
+
self.project_dir = dist.src_root or os.curdir
|
| 140 |
+
self.package_dir = dist.package_dir or {}
|
| 141 |
+
self.dist_dir = Path(self.dist_dir or os.path.join(self.project_dir, "dist"))
|
| 142 |
+
|
| 143 |
+
def run(self):
|
| 144 |
+
try:
|
| 145 |
+
self.dist_dir.mkdir(exist_ok=True)
|
| 146 |
+
self._ensure_dist_info()
|
| 147 |
+
|
| 148 |
+
# Add missing dist_info files
|
| 149 |
+
self.reinitialize_command("bdist_wheel")
|
| 150 |
+
bdist_wheel = self.get_finalized_command("bdist_wheel")
|
| 151 |
+
bdist_wheel.write_wheelfile(self.dist_info_dir)
|
| 152 |
+
|
| 153 |
+
self._create_wheel_file(bdist_wheel)
|
| 154 |
+
except Exception:
|
| 155 |
+
traceback.print_exc()
|
| 156 |
+
project = self.distribution.name or self.distribution.get_name()
|
| 157 |
+
_DebuggingTips.emit(project=project)
|
| 158 |
+
raise
|
| 159 |
+
|
| 160 |
+
def _ensure_dist_info(self):
|
| 161 |
+
if self.dist_info_dir is None:
|
| 162 |
+
dist_info = cast(dist_info_cls, self.reinitialize_command("dist_info"))
|
| 163 |
+
dist_info.output_dir = self.dist_dir
|
| 164 |
+
dist_info.ensure_finalized()
|
| 165 |
+
dist_info.run()
|
| 166 |
+
self.dist_info_dir = dist_info.dist_info_dir
|
| 167 |
+
else:
|
| 168 |
+
assert str(self.dist_info_dir).endswith(".dist-info")
|
| 169 |
+
assert Path(self.dist_info_dir, "METADATA").exists()
|
| 170 |
+
|
| 171 |
+
def _install_namespaces(self, installation_dir, pth_prefix):
|
| 172 |
+
# XXX: Only required to support the deprecated namespace practice
|
| 173 |
+
dist = self.distribution
|
| 174 |
+
if not dist.namespace_packages:
|
| 175 |
+
return
|
| 176 |
+
|
| 177 |
+
src_root = Path(self.project_dir, self.package_dir.get("", ".")).resolve()
|
| 178 |
+
installer = _NamespaceInstaller(dist, installation_dir, pth_prefix, src_root)
|
| 179 |
+
installer.install_namespaces()
|
| 180 |
+
|
| 181 |
+
def _find_egg_info_dir(self) -> str | None:
|
| 182 |
+
parent_dir = Path(self.dist_info_dir).parent if self.dist_info_dir else Path()
|
| 183 |
+
candidates = map(str, parent_dir.glob("*.egg-info"))
|
| 184 |
+
return next(candidates, None)
|
| 185 |
+
|
| 186 |
+
def _configure_build(
|
| 187 |
+
self, name: str, unpacked_wheel: StrPath, build_lib: StrPath, tmp_dir: StrPath
|
| 188 |
+
):
|
| 189 |
+
"""Configure commands to behave in the following ways:
|
| 190 |
+
|
| 191 |
+
- Build commands can write to ``build_lib`` if they really want to...
|
| 192 |
+
(but this folder is expected to be ignored and modules are expected to live
|
| 193 |
+
in the project directory...)
|
| 194 |
+
- Binary extensions should be built in-place (editable_mode = True)
|
| 195 |
+
- Data/header/script files are not part of the "editable" specification
|
| 196 |
+
so they are written directly to the unpacked_wheel directory.
|
| 197 |
+
"""
|
| 198 |
+
# Non-editable files (data, headers, scripts) are written directly to the
|
| 199 |
+
# unpacked_wheel
|
| 200 |
+
|
| 201 |
+
dist = self.distribution
|
| 202 |
+
wheel = str(unpacked_wheel)
|
| 203 |
+
build_lib = str(build_lib)
|
| 204 |
+
data = str(Path(unpacked_wheel, f"{name}.data", "data"))
|
| 205 |
+
headers = str(Path(unpacked_wheel, f"{name}.data", "headers"))
|
| 206 |
+
scripts = str(Path(unpacked_wheel, f"{name}.data", "scripts"))
|
| 207 |
+
|
| 208 |
+
# egg-info may be generated again to create a manifest (used for package data)
|
| 209 |
+
egg_info = cast(
|
| 210 |
+
egg_info_cls, dist.reinitialize_command("egg_info", reinit_subcommands=True)
|
| 211 |
+
)
|
| 212 |
+
egg_info.egg_base = str(tmp_dir)
|
| 213 |
+
egg_info.ignore_egg_info_in_manifest = True
|
| 214 |
+
|
| 215 |
+
build = cast(
|
| 216 |
+
build_cls, dist.reinitialize_command("build", reinit_subcommands=True)
|
| 217 |
+
)
|
| 218 |
+
install = cast(
|
| 219 |
+
install_cls, dist.reinitialize_command("install", reinit_subcommands=True)
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
build.build_platlib = build.build_purelib = build.build_lib = build_lib
|
| 223 |
+
install.install_purelib = install.install_platlib = install.install_lib = wheel
|
| 224 |
+
install.install_scripts = build.build_scripts = scripts
|
| 225 |
+
install.install_headers = headers
|
| 226 |
+
install.install_data = data
|
| 227 |
+
|
| 228 |
+
install_scripts = cast(
|
| 229 |
+
install_scripts_cls, dist.get_command_obj("install_scripts")
|
| 230 |
+
)
|
| 231 |
+
install_scripts.no_ep = True
|
| 232 |
+
|
| 233 |
+
build.build_temp = str(tmp_dir)
|
| 234 |
+
|
| 235 |
+
build_py = cast(build_py_cls, dist.get_command_obj("build_py"))
|
| 236 |
+
build_py.compile = False
|
| 237 |
+
build_py.existing_egg_info_dir = self._find_egg_info_dir()
|
| 238 |
+
|
| 239 |
+
self._set_editable_mode()
|
| 240 |
+
|
| 241 |
+
build.ensure_finalized()
|
| 242 |
+
install.ensure_finalized()
|
| 243 |
+
|
| 244 |
+
def _set_editable_mode(self):
|
| 245 |
+
"""Set the ``editable_mode`` flag in the build sub-commands"""
|
| 246 |
+
dist = self.distribution
|
| 247 |
+
build = dist.get_command_obj("build")
|
| 248 |
+
# TODO: Update typeshed distutils stubs to overload non-None return type by default
|
| 249 |
+
for cmd_name in build.get_sub_commands():
|
| 250 |
+
cmd = dist.get_command_obj(cmd_name)
|
| 251 |
+
if hasattr(cmd, "editable_mode"):
|
| 252 |
+
cmd.editable_mode = True
|
| 253 |
+
elif hasattr(cmd, "inplace"):
|
| 254 |
+
cmd.inplace = True # backward compatibility with distutils
|
| 255 |
+
|
| 256 |
+
def _collect_build_outputs(self) -> tuple[list[str], dict[str, str]]:
|
| 257 |
+
files: list[str] = []
|
| 258 |
+
mapping: dict[str, str] = {}
|
| 259 |
+
build = self.get_finalized_command("build")
|
| 260 |
+
|
| 261 |
+
for cmd_name in build.get_sub_commands():
|
| 262 |
+
cmd = self.get_finalized_command(cmd_name)
|
| 263 |
+
if hasattr(cmd, "get_outputs"):
|
| 264 |
+
files.extend(cmd.get_outputs() or [])
|
| 265 |
+
if hasattr(cmd, "get_output_mapping"):
|
| 266 |
+
mapping.update(cmd.get_output_mapping() or {})
|
| 267 |
+
|
| 268 |
+
return files, mapping
|
| 269 |
+
|
| 270 |
+
def _run_build_commands(
|
| 271 |
+
self,
|
| 272 |
+
dist_name: str,
|
| 273 |
+
unpacked_wheel: StrPath,
|
| 274 |
+
build_lib: StrPath,
|
| 275 |
+
tmp_dir: StrPath,
|
| 276 |
+
) -> tuple[list[str], dict[str, str]]:
|
| 277 |
+
self._configure_build(dist_name, unpacked_wheel, build_lib, tmp_dir)
|
| 278 |
+
self._run_build_subcommands()
|
| 279 |
+
files, mapping = self._collect_build_outputs()
|
| 280 |
+
self._run_install("headers")
|
| 281 |
+
self._run_install("scripts")
|
| 282 |
+
self._run_install("data")
|
| 283 |
+
return files, mapping
|
| 284 |
+
|
| 285 |
+
def _run_build_subcommands(self) -> None:
|
| 286 |
+
"""
|
| 287 |
+
Issue #3501 indicates that some plugins/customizations might rely on:
|
| 288 |
+
|
| 289 |
+
1. ``build_py`` not running
|
| 290 |
+
2. ``build_py`` always copying files to ``build_lib``
|
| 291 |
+
|
| 292 |
+
However both these assumptions may be false in editable_wheel.
|
| 293 |
+
This method implements a temporary workaround to support the ecosystem
|
| 294 |
+
while the implementations catch up.
|
| 295 |
+
"""
|
| 296 |
+
# TODO: Once plugins/customisations had the chance to catch up, replace
|
| 297 |
+
# `self._run_build_subcommands()` with `self.run_command("build")`.
|
| 298 |
+
# Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023.
|
| 299 |
+
build = self.get_finalized_command("build")
|
| 300 |
+
for name in build.get_sub_commands():
|
| 301 |
+
cmd = self.get_finalized_command(name)
|
| 302 |
+
if name == "build_py" and type(cmd) != build_py_cls:
|
| 303 |
+
self._safely_run(name)
|
| 304 |
+
else:
|
| 305 |
+
self.run_command(name)
|
| 306 |
+
|
| 307 |
+
def _safely_run(self, cmd_name: str):
|
| 308 |
+
try:
|
| 309 |
+
return self.run_command(cmd_name)
|
| 310 |
+
except Exception:
|
| 311 |
+
SetuptoolsDeprecationWarning.emit(
|
| 312 |
+
"Customization incompatible with editable install",
|
| 313 |
+
f"""
|
| 314 |
+
{traceback.format_exc()}
|
| 315 |
+
|
| 316 |
+
If you are seeing this warning it is very likely that a setuptools
|
| 317 |
+
plugin or customization overrides the `{cmd_name}` command, without
|
| 318 |
+
taking into consideration how editable installs run build steps
|
| 319 |
+
starting from setuptools v64.0.0.
|
| 320 |
+
|
| 321 |
+
Plugin authors and developers relying on custom build steps are
|
| 322 |
+
encouraged to update their `{cmd_name}` implementation considering the
|
| 323 |
+
information about editable installs in
|
| 324 |
+
https://setuptools.pypa.io/en/latest/userguide/extension.html.
|
| 325 |
+
|
| 326 |
+
For the time being `setuptools` will silence this error and ignore
|
| 327 |
+
the faulty command, but this behaviour will change in future versions.
|
| 328 |
+
""",
|
| 329 |
+
# TODO: define due_date
|
| 330 |
+
# There is a series of shortcomings with the available editable install
|
| 331 |
+
# methods, and they are very controversial. This is something that still
|
| 332 |
+
# needs work.
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
def _create_wheel_file(self, bdist_wheel):
|
| 336 |
+
from ..extern.wheel.wheelfile import WheelFile
|
| 337 |
+
|
| 338 |
+
dist_info = self.get_finalized_command("dist_info")
|
| 339 |
+
dist_name = dist_info.name
|
| 340 |
+
tag = "-".join(bdist_wheel.get_tag())
|
| 341 |
+
build_tag = "0.editable" # According to PEP 427 needs to start with digit
|
| 342 |
+
archive_name = f"{dist_name}-{build_tag}-{tag}.whl"
|
| 343 |
+
wheel_path = Path(self.dist_dir, archive_name)
|
| 344 |
+
if wheel_path.exists():
|
| 345 |
+
wheel_path.unlink()
|
| 346 |
+
|
| 347 |
+
unpacked_wheel = TemporaryDirectory(suffix=archive_name)
|
| 348 |
+
build_lib = TemporaryDirectory(suffix=".build-lib")
|
| 349 |
+
build_tmp = TemporaryDirectory(suffix=".build-temp")
|
| 350 |
+
|
| 351 |
+
with unpacked_wheel as unpacked, build_lib as lib, build_tmp as tmp:
|
| 352 |
+
unpacked_dist_info = Path(unpacked, Path(self.dist_info_dir).name)
|
| 353 |
+
shutil.copytree(self.dist_info_dir, unpacked_dist_info)
|
| 354 |
+
self._install_namespaces(unpacked, dist_name)
|
| 355 |
+
files, mapping = self._run_build_commands(dist_name, unpacked, lib, tmp)
|
| 356 |
+
strategy = self._select_strategy(dist_name, tag, lib)
|
| 357 |
+
with strategy, WheelFile(wheel_path, "w") as wheel_obj:
|
| 358 |
+
strategy(wheel_obj, files, mapping)
|
| 359 |
+
wheel_obj.write_files(unpacked)
|
| 360 |
+
|
| 361 |
+
return wheel_path
|
| 362 |
+
|
| 363 |
+
def _run_install(self, category: str):
|
| 364 |
+
has_category = getattr(self.distribution, f"has_{category}", None)
|
| 365 |
+
if has_category and has_category():
|
| 366 |
+
_logger.info(f"Installing {category} as non editable")
|
| 367 |
+
self.run_command(f"install_{category}")
|
| 368 |
+
|
| 369 |
+
def _select_strategy(
|
| 370 |
+
self,
|
| 371 |
+
name: str,
|
| 372 |
+
tag: str,
|
| 373 |
+
build_lib: StrPath,
|
| 374 |
+
) -> EditableStrategy:
|
| 375 |
+
"""Decides which strategy to use to implement an editable installation."""
|
| 376 |
+
build_name = f"__editable__.{name}-{tag}"
|
| 377 |
+
project_dir = Path(self.project_dir)
|
| 378 |
+
mode = _EditableMode.convert(self.mode)
|
| 379 |
+
|
| 380 |
+
if mode is _EditableMode.STRICT:
|
| 381 |
+
auxiliary_dir = _empty_dir(Path(self.project_dir, "build", build_name))
|
| 382 |
+
return _LinkTree(self.distribution, name, auxiliary_dir, build_lib)
|
| 383 |
+
|
| 384 |
+
packages = _find_packages(self.distribution)
|
| 385 |
+
has_simple_layout = _simple_layout(packages, self.package_dir, project_dir)
|
| 386 |
+
is_compat_mode = mode is _EditableMode.COMPAT
|
| 387 |
+
if set(self.package_dir) == {""} and has_simple_layout or is_compat_mode:
|
| 388 |
+
# src-layout(ish) is relatively safe for a simple pth file
|
| 389 |
+
src_dir = self.package_dir.get("", ".")
|
| 390 |
+
return _StaticPth(self.distribution, name, [Path(project_dir, src_dir)])
|
| 391 |
+
|
| 392 |
+
# Use a MetaPathFinder to avoid adding accidental top-level packages/modules
|
| 393 |
+
return _TopLevelFinder(self.distribution, name)
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
class EditableStrategy(Protocol):
|
| 397 |
+
def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): ...
|
| 398 |
+
|
| 399 |
+
def __enter__(self): ...
|
| 400 |
+
|
| 401 |
+
def __exit__(self, _exc_type, _exc_value, _traceback): ...
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
class _StaticPth:
|
| 405 |
+
def __init__(self, dist: Distribution, name: str, path_entries: list[Path]):
|
| 406 |
+
self.dist = dist
|
| 407 |
+
self.name = name
|
| 408 |
+
self.path_entries = path_entries
|
| 409 |
+
|
| 410 |
+
def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]):
|
| 411 |
+
entries = "\n".join(str(p.resolve()) for p in self.path_entries)
|
| 412 |
+
contents = _encode_pth(f"{entries}\n")
|
| 413 |
+
wheel.writestr(f"__editable__.{self.name}.pth", contents)
|
| 414 |
+
|
| 415 |
+
def __enter__(self):
|
| 416 |
+
msg = f"""
|
| 417 |
+
Editable install will be performed using .pth file to extend `sys.path` with:
|
| 418 |
+
{list(map(os.fspath, self.path_entries))!r}
|
| 419 |
+
"""
|
| 420 |
+
_logger.warning(msg + _LENIENT_WARNING)
|
| 421 |
+
return self
|
| 422 |
+
|
| 423 |
+
def __exit__(self, _exc_type, _exc_value, _traceback): ...
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
class _LinkTree(_StaticPth):
|
| 427 |
+
"""
|
| 428 |
+
Creates a ``.pth`` file that points to a link tree in the ``auxiliary_dir``.
|
| 429 |
+
|
| 430 |
+
This strategy will only link files (not dirs), so it can be implemented in
|
| 431 |
+
any OS, even if that means using hardlinks instead of symlinks.
|
| 432 |
+
|
| 433 |
+
By collocating ``auxiliary_dir`` and the original source code, limitations
|
| 434 |
+
with hardlinks should be avoided.
|
| 435 |
+
"""
|
| 436 |
+
|
| 437 |
+
def __init__(
|
| 438 |
+
self,
|
| 439 |
+
dist: Distribution,
|
| 440 |
+
name: str,
|
| 441 |
+
auxiliary_dir: StrPath,
|
| 442 |
+
build_lib: StrPath,
|
| 443 |
+
):
|
| 444 |
+
self.auxiliary_dir = Path(auxiliary_dir)
|
| 445 |
+
self.build_lib = Path(build_lib).resolve()
|
| 446 |
+
# TODO: Update typeshed distutils stubs to overload non-None return type by default
|
| 447 |
+
self._file = dist.get_command_obj("build_py").copy_file # type: ignore[union-attr]
|
| 448 |
+
super().__init__(dist, name, [self.auxiliary_dir])
|
| 449 |
+
|
| 450 |
+
def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]):
|
| 451 |
+
self._create_links(files, mapping)
|
| 452 |
+
super().__call__(wheel, files, mapping)
|
| 453 |
+
|
| 454 |
+
def _normalize_output(self, file: str) -> str | None:
|
| 455 |
+
# Files relative to build_lib will be normalized to None
|
| 456 |
+
with suppress(ValueError):
|
| 457 |
+
path = Path(file).resolve().relative_to(self.build_lib)
|
| 458 |
+
return str(path).replace(os.sep, '/')
|
| 459 |
+
return None
|
| 460 |
+
|
| 461 |
+
def _create_file(self, relative_output: str, src_file: str, link=None):
|
| 462 |
+
dest = self.auxiliary_dir / relative_output
|
| 463 |
+
if not dest.parent.is_dir():
|
| 464 |
+
dest.parent.mkdir(parents=True)
|
| 465 |
+
# TODO: Update typeshed distutils stubs so distutils.cmd.Command.copy_file, accepts PathLike
|
| 466 |
+
# same with methods used by copy_file
|
| 467 |
+
self._file(src_file, dest, link=link) # type: ignore[arg-type]
|
| 468 |
+
|
| 469 |
+
def _create_links(self, outputs, output_mapping):
|
| 470 |
+
self.auxiliary_dir.mkdir(parents=True, exist_ok=True)
|
| 471 |
+
link_type = "sym" if _can_symlink_files(self.auxiliary_dir) else "hard"
|
| 472 |
+
mappings = {self._normalize_output(k): v for k, v in output_mapping.items()}
|
| 473 |
+
mappings.pop(None, None) # remove files that are not relative to build_lib
|
| 474 |
+
|
| 475 |
+
for output in outputs:
|
| 476 |
+
relative = self._normalize_output(output)
|
| 477 |
+
if relative and relative not in mappings:
|
| 478 |
+
self._create_file(relative, output)
|
| 479 |
+
|
| 480 |
+
for relative, src in mappings.items():
|
| 481 |
+
self._create_file(relative, src, link=link_type)
|
| 482 |
+
|
| 483 |
+
def __enter__(self):
|
| 484 |
+
msg = "Strict editable install will be performed using a link tree.\n"
|
| 485 |
+
_logger.warning(msg + _STRICT_WARNING)
|
| 486 |
+
return self
|
| 487 |
+
|
| 488 |
+
def __exit__(self, _exc_type, _exc_value, _traceback):
|
| 489 |
+
msg = f"""\n
|
| 490 |
+
Strict editable installation performed using the auxiliary directory:
|
| 491 |
+
{self.auxiliary_dir}
|
| 492 |
+
|
| 493 |
+
Please be careful to not remove this directory, otherwise you might not be able
|
| 494 |
+
to import/use your package.
|
| 495 |
+
"""
|
| 496 |
+
InformationOnly.emit("Editable installation.", msg)
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
class _TopLevelFinder:
|
| 500 |
+
def __init__(self, dist: Distribution, name: str):
|
| 501 |
+
self.dist = dist
|
| 502 |
+
self.name = name
|
| 503 |
+
|
| 504 |
+
def template_vars(self) -> tuple[str, str, dict[str, str], dict[str, list[str]]]:
|
| 505 |
+
src_root = self.dist.src_root or os.curdir
|
| 506 |
+
top_level = chain(_find_packages(self.dist), _find_top_level_modules(self.dist))
|
| 507 |
+
package_dir = self.dist.package_dir or {}
|
| 508 |
+
roots = _find_package_roots(top_level, package_dir, src_root)
|
| 509 |
+
|
| 510 |
+
namespaces_: dict[str, list[str]] = dict(
|
| 511 |
+
chain(
|
| 512 |
+
_find_namespaces(self.dist.packages or [], roots),
|
| 513 |
+
((ns, []) for ns in _find_virtual_namespaces(roots)),
|
| 514 |
+
)
|
| 515 |
+
)
|
| 516 |
+
|
| 517 |
+
legacy_namespaces = {
|
| 518 |
+
pkg: find_package_path(pkg, roots, self.dist.src_root or "")
|
| 519 |
+
for pkg in self.dist.namespace_packages or []
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
+
mapping = {**roots, **legacy_namespaces}
|
| 523 |
+
# ^-- We need to explicitly add the legacy_namespaces to the mapping to be
|
| 524 |
+
# able to import their modules even if another package sharing the same
|
| 525 |
+
# namespace is installed in a conventional (non-editable) way.
|
| 526 |
+
|
| 527 |
+
name = f"__editable__.{self.name}.finder"
|
| 528 |
+
finder = _normalization.safe_identifier(name)
|
| 529 |
+
return finder, name, mapping, namespaces_
|
| 530 |
+
|
| 531 |
+
def get_implementation(self) -> Iterator[tuple[str, bytes]]:
|
| 532 |
+
finder, name, mapping, namespaces_ = self.template_vars()
|
| 533 |
+
|
| 534 |
+
content = bytes(_finder_template(name, mapping, namespaces_), "utf-8")
|
| 535 |
+
yield (f"{finder}.py", content)
|
| 536 |
+
|
| 537 |
+
content = _encode_pth(f"import {finder}; {finder}.install()")
|
| 538 |
+
yield (f"__editable__.{self.name}.pth", content)
|
| 539 |
+
|
| 540 |
+
def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]):
|
| 541 |
+
for file, content in self.get_implementation():
|
| 542 |
+
wheel.writestr(file, content)
|
| 543 |
+
|
| 544 |
+
def __enter__(self):
|
| 545 |
+
msg = "Editable install will be performed using a meta path finder.\n"
|
| 546 |
+
_logger.warning(msg + _LENIENT_WARNING)
|
| 547 |
+
return self
|
| 548 |
+
|
| 549 |
+
def __exit__(self, _exc_type, _exc_value, _traceback):
|
| 550 |
+
msg = """\n
|
| 551 |
+
Please be careful with folders in your working directory with the same
|
| 552 |
+
name as your package as they may take precedence during imports.
|
| 553 |
+
"""
|
| 554 |
+
InformationOnly.emit("Editable installation.", msg)
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
def _encode_pth(content: str) -> bytes:
|
| 558 |
+
""".pth files are always read with 'locale' encoding, the recommendation
|
| 559 |
+
from the cpython core developers is to write them as ``open(path, "w")``
|
| 560 |
+
and ignore warnings (see python/cpython#77102, pypa/setuptools#3937).
|
| 561 |
+
This function tries to simulate this behaviour without having to create an
|
| 562 |
+
actual file, in a way that supports a range of active Python versions.
|
| 563 |
+
(There seems to be some variety in the way different version of Python handle
|
| 564 |
+
``encoding=None``, not all of them use ``locale.getpreferredencoding(False)``
|
| 565 |
+
or ``locale.getencoding()``).
|
| 566 |
+
"""
|
| 567 |
+
with io.BytesIO() as buffer:
|
| 568 |
+
wrapper = io.TextIOWrapper(buffer, encoding=py39.LOCALE_ENCODING)
|
| 569 |
+
wrapper.write(content)
|
| 570 |
+
wrapper.flush()
|
| 571 |
+
buffer.seek(0)
|
| 572 |
+
return buffer.read()
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
def _can_symlink_files(base_dir: Path) -> bool:
|
| 576 |
+
with TemporaryDirectory(dir=str(base_dir.resolve())) as tmp:
|
| 577 |
+
path1, path2 = Path(tmp, "file1.txt"), Path(tmp, "file2.txt")
|
| 578 |
+
path1.write_text("file1", encoding="utf-8")
|
| 579 |
+
with suppress(AttributeError, NotImplementedError, OSError):
|
| 580 |
+
os.symlink(path1, path2)
|
| 581 |
+
if path2.is_symlink() and path2.read_text(encoding="utf-8") == "file1":
|
| 582 |
+
return True
|
| 583 |
+
|
| 584 |
+
try:
|
| 585 |
+
os.link(path1, path2) # Ensure hard links can be created
|
| 586 |
+
except Exception as ex:
|
| 587 |
+
msg = (
|
| 588 |
+
"File system does not seem to support either symlinks or hard links. "
|
| 589 |
+
"Strict editable installs require one of them to be supported."
|
| 590 |
+
)
|
| 591 |
+
raise LinksNotSupported(msg) from ex
|
| 592 |
+
return False
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
def _simple_layout(
|
| 596 |
+
packages: Iterable[str], package_dir: dict[str, str], project_dir: StrPath
|
| 597 |
+
) -> bool:
|
| 598 |
+
"""Return ``True`` if:
|
| 599 |
+
- all packages are contained by the same parent directory, **and**
|
| 600 |
+
- all packages become importable if the parent directory is added to ``sys.path``.
|
| 601 |
+
|
| 602 |
+
>>> _simple_layout(['a'], {"": "src"}, "/tmp/myproj")
|
| 603 |
+
True
|
| 604 |
+
>>> _simple_layout(['a', 'a.b'], {"": "src"}, "/tmp/myproj")
|
| 605 |
+
True
|
| 606 |
+
>>> _simple_layout(['a', 'a.b'], {}, "/tmp/myproj")
|
| 607 |
+
True
|
| 608 |
+
>>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"": "src"}, "/tmp/myproj")
|
| 609 |
+
True
|
| 610 |
+
>>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "a", "b": "b"}, ".")
|
| 611 |
+
True
|
| 612 |
+
>>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "_a", "b": "_b"}, ".")
|
| 613 |
+
False
|
| 614 |
+
>>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "_a"}, "/tmp/myproj")
|
| 615 |
+
False
|
| 616 |
+
>>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a.a1.a2": "_a2"}, ".")
|
| 617 |
+
False
|
| 618 |
+
>>> _simple_layout(['a', 'a.b'], {"": "src", "a.b": "_ab"}, "/tmp/myproj")
|
| 619 |
+
False
|
| 620 |
+
>>> # Special cases, no packages yet:
|
| 621 |
+
>>> _simple_layout([], {"": "src"}, "/tmp/myproj")
|
| 622 |
+
True
|
| 623 |
+
>>> _simple_layout([], {"a": "_a", "": "src"}, "/tmp/myproj")
|
| 624 |
+
False
|
| 625 |
+
"""
|
| 626 |
+
layout = {pkg: find_package_path(pkg, package_dir, project_dir) for pkg in packages}
|
| 627 |
+
if not layout:
|
| 628 |
+
return set(package_dir) in ({}, {""})
|
| 629 |
+
parent = os.path.commonpath(starmap(_parent_path, layout.items()))
|
| 630 |
+
return all(
|
| 631 |
+
_path.same_path(Path(parent, *key.split('.')), value)
|
| 632 |
+
for key, value in layout.items()
|
| 633 |
+
)
|
| 634 |
+
|
| 635 |
+
|
| 636 |
+
def _parent_path(pkg, pkg_path):
|
| 637 |
+
"""Infer the parent path containing a package, that if added to ``sys.path`` would
|
| 638 |
+
allow importing that package.
|
| 639 |
+
When ``pkg`` is directly mapped into a directory with a different name, return its
|
| 640 |
+
own path.
|
| 641 |
+
>>> _parent_path("a", "src/a")
|
| 642 |
+
'src'
|
| 643 |
+
>>> _parent_path("b", "src/c")
|
| 644 |
+
'src/c'
|
| 645 |
+
"""
|
| 646 |
+
parent = pkg_path[: -len(pkg)] if pkg_path.endswith(pkg) else pkg_path
|
| 647 |
+
return parent.rstrip("/" + os.sep)
|
| 648 |
+
|
| 649 |
+
|
| 650 |
+
def _find_packages(dist: Distribution) -> Iterator[str]:
|
| 651 |
+
yield from iter(dist.packages or [])
|
| 652 |
+
|
| 653 |
+
py_modules = dist.py_modules or []
|
| 654 |
+
nested_modules = [mod for mod in py_modules if "." in mod]
|
| 655 |
+
if dist.ext_package:
|
| 656 |
+
yield dist.ext_package
|
| 657 |
+
else:
|
| 658 |
+
ext_modules = dist.ext_modules or []
|
| 659 |
+
nested_modules += [x.name for x in ext_modules if "." in x.name]
|
| 660 |
+
|
| 661 |
+
for module in nested_modules:
|
| 662 |
+
package, _, _ = module.rpartition(".")
|
| 663 |
+
yield package
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
def _find_top_level_modules(dist: Distribution) -> Iterator[str]:
|
| 667 |
+
py_modules = dist.py_modules or []
|
| 668 |
+
yield from (mod for mod in py_modules if "." not in mod)
|
| 669 |
+
|
| 670 |
+
if not dist.ext_package:
|
| 671 |
+
ext_modules = dist.ext_modules or []
|
| 672 |
+
yield from (x.name for x in ext_modules if "." not in x.name)
|
| 673 |
+
|
| 674 |
+
|
| 675 |
+
def _find_package_roots(
|
| 676 |
+
packages: Iterable[str],
|
| 677 |
+
package_dir: Mapping[str, str],
|
| 678 |
+
src_root: StrPath,
|
| 679 |
+
) -> dict[str, str]:
|
| 680 |
+
pkg_roots: dict[str, str] = {
|
| 681 |
+
pkg: _absolute_root(find_package_path(pkg, package_dir, src_root))
|
| 682 |
+
for pkg in sorted(packages)
|
| 683 |
+
}
|
| 684 |
+
|
| 685 |
+
return _remove_nested(pkg_roots)
|
| 686 |
+
|
| 687 |
+
|
| 688 |
+
def _absolute_root(path: StrPath) -> str:
|
| 689 |
+
"""Works for packages and top-level modules"""
|
| 690 |
+
path_ = Path(path)
|
| 691 |
+
parent = path_.parent
|
| 692 |
+
|
| 693 |
+
if path_.exists():
|
| 694 |
+
return str(path_.resolve())
|
| 695 |
+
else:
|
| 696 |
+
return str(parent.resolve() / path_.name)
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
def _find_virtual_namespaces(pkg_roots: dict[str, str]) -> Iterator[str]:
|
| 700 |
+
"""By carefully designing ``package_dir``, it is possible to implement the logical
|
| 701 |
+
structure of PEP 420 in a package without the corresponding directories.
|
| 702 |
+
|
| 703 |
+
Moreover a parent package can be purposefully/accidentally skipped in the discovery
|
| 704 |
+
phase (e.g. ``find_packages(include=["mypkg.*"])``, when ``mypkg.foo`` is included
|
| 705 |
+
by ``mypkg`` itself is not).
|
| 706 |
+
We consider this case to also be a virtual namespace (ignoring the original
|
| 707 |
+
directory) to emulate a non-editable installation.
|
| 708 |
+
|
| 709 |
+
This function will try to find these kinds of namespaces.
|
| 710 |
+
"""
|
| 711 |
+
for pkg in pkg_roots:
|
| 712 |
+
if "." not in pkg:
|
| 713 |
+
continue
|
| 714 |
+
parts = pkg.split(".")
|
| 715 |
+
for i in range(len(parts) - 1, 0, -1):
|
| 716 |
+
partial_name = ".".join(parts[:i])
|
| 717 |
+
path = Path(find_package_path(partial_name, pkg_roots, ""))
|
| 718 |
+
if not path.exists() or partial_name not in pkg_roots:
|
| 719 |
+
# partial_name not in pkg_roots ==> purposefully/accidentally skipped
|
| 720 |
+
yield partial_name
|
| 721 |
+
|
| 722 |
+
|
| 723 |
+
def _find_namespaces(
|
| 724 |
+
packages: list[str], pkg_roots: dict[str, str]
|
| 725 |
+
) -> Iterator[tuple[str, list[str]]]:
|
| 726 |
+
for pkg in packages:
|
| 727 |
+
path = find_package_path(pkg, pkg_roots, "")
|
| 728 |
+
if Path(path).exists() and not Path(path, "__init__.py").exists():
|
| 729 |
+
yield (pkg, [path])
|
| 730 |
+
|
| 731 |
+
|
| 732 |
+
def _remove_nested(pkg_roots: dict[str, str]) -> dict[str, str]:
|
| 733 |
+
output = dict(pkg_roots.copy())
|
| 734 |
+
|
| 735 |
+
for pkg, path in reversed(list(pkg_roots.items())):
|
| 736 |
+
if any(
|
| 737 |
+
pkg != other and _is_nested(pkg, path, other, other_path)
|
| 738 |
+
for other, other_path in pkg_roots.items()
|
| 739 |
+
):
|
| 740 |
+
output.pop(pkg)
|
| 741 |
+
|
| 742 |
+
return output
|
| 743 |
+
|
| 744 |
+
|
| 745 |
+
def _is_nested(pkg: str, pkg_path: str, parent: str, parent_path: str) -> bool:
|
| 746 |
+
"""
|
| 747 |
+
Return ``True`` if ``pkg`` is nested inside ``parent`` both logically and in the
|
| 748 |
+
file system.
|
| 749 |
+
>>> _is_nested("a.b", "path/a/b", "a", "path/a")
|
| 750 |
+
True
|
| 751 |
+
>>> _is_nested("a.b", "path/a/b", "a", "otherpath/a")
|
| 752 |
+
False
|
| 753 |
+
>>> _is_nested("a.b", "path/a/b", "c", "path/c")
|
| 754 |
+
False
|
| 755 |
+
>>> _is_nested("a.a", "path/a/a", "a", "path/a")
|
| 756 |
+
True
|
| 757 |
+
>>> _is_nested("b.a", "path/b/a", "a", "path/a")
|
| 758 |
+
False
|
| 759 |
+
"""
|
| 760 |
+
norm_pkg_path = _path.normpath(pkg_path)
|
| 761 |
+
rest = pkg.replace(parent, "", 1).strip(".").split(".")
|
| 762 |
+
return pkg.startswith(parent) and norm_pkg_path == _path.normpath(
|
| 763 |
+
Path(parent_path, *rest)
|
| 764 |
+
)
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
def _empty_dir(dir_: _P) -> _P:
|
| 768 |
+
"""Create a directory ensured to be empty. Existing files may be removed."""
|
| 769 |
+
shutil.rmtree(dir_, ignore_errors=True)
|
| 770 |
+
os.makedirs(dir_)
|
| 771 |
+
return dir_
|
| 772 |
+
|
| 773 |
+
|
| 774 |
+
class _NamespaceInstaller(namespaces.Installer):
|
| 775 |
+
def __init__(self, distribution, installation_dir, editable_name, src_root):
|
| 776 |
+
self.distribution = distribution
|
| 777 |
+
self.src_root = src_root
|
| 778 |
+
self.installation_dir = installation_dir
|
| 779 |
+
self.editable_name = editable_name
|
| 780 |
+
self.outputs = []
|
| 781 |
+
self.dry_run = False
|
| 782 |
+
|
| 783 |
+
def _get_nspkg_file(self):
|
| 784 |
+
"""Installation target."""
|
| 785 |
+
return os.path.join(self.installation_dir, self.editable_name + self.nspkg_ext)
|
| 786 |
+
|
| 787 |
+
def _get_root(self):
|
| 788 |
+
"""Where the modules/packages should be loaded from."""
|
| 789 |
+
return repr(str(self.src_root))
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
_FINDER_TEMPLATE = """\
|
| 793 |
+
from __future__ import annotations
|
| 794 |
+
import sys
|
| 795 |
+
from importlib.machinery import ModuleSpec, PathFinder
|
| 796 |
+
from importlib.machinery import all_suffixes as module_suffixes
|
| 797 |
+
from importlib.util import spec_from_file_location
|
| 798 |
+
from itertools import chain
|
| 799 |
+
from pathlib import Path
|
| 800 |
+
|
| 801 |
+
MAPPING: dict[str, str] = {mapping!r}
|
| 802 |
+
NAMESPACES: dict[str, list[str]] = {namespaces!r}
|
| 803 |
+
PATH_PLACEHOLDER = {name!r} + ".__path_hook__"
|
| 804 |
+
|
| 805 |
+
|
| 806 |
+
class _EditableFinder: # MetaPathFinder
|
| 807 |
+
@classmethod
|
| 808 |
+
def find_spec(cls, fullname: str, _path=None, _target=None) -> ModuleSpec | None:
|
| 809 |
+
# Top-level packages and modules (we know these exist in the FS)
|
| 810 |
+
if fullname in MAPPING:
|
| 811 |
+
pkg_path = MAPPING[fullname]
|
| 812 |
+
return cls._find_spec(fullname, Path(pkg_path))
|
| 813 |
+
|
| 814 |
+
# Handle immediate children modules (required for namespaces to work)
|
| 815 |
+
# To avoid problems with case sensitivity in the file system we delegate
|
| 816 |
+
# to the importlib.machinery implementation.
|
| 817 |
+
parent, _, child = fullname.rpartition(".")
|
| 818 |
+
if parent and parent in MAPPING:
|
| 819 |
+
return PathFinder.find_spec(fullname, path=[MAPPING[parent]])
|
| 820 |
+
|
| 821 |
+
# Other levels of nesting should be handled automatically by importlib
|
| 822 |
+
# using the parent path.
|
| 823 |
+
return None
|
| 824 |
+
|
| 825 |
+
@classmethod
|
| 826 |
+
def _find_spec(cls, fullname: str, candidate_path: Path) -> ModuleSpec | None:
|
| 827 |
+
init = candidate_path / "__init__.py"
|
| 828 |
+
candidates = (candidate_path.with_suffix(x) for x in module_suffixes())
|
| 829 |
+
for candidate in chain([init], candidates):
|
| 830 |
+
if candidate.exists():
|
| 831 |
+
return spec_from_file_location(fullname, candidate)
|
| 832 |
+
return None
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
class _EditableNamespaceFinder: # PathEntryFinder
|
| 836 |
+
@classmethod
|
| 837 |
+
def _path_hook(cls, path) -> type[_EditableNamespaceFinder]:
|
| 838 |
+
if path == PATH_PLACEHOLDER:
|
| 839 |
+
return cls
|
| 840 |
+
raise ImportError
|
| 841 |
+
|
| 842 |
+
@classmethod
|
| 843 |
+
def _paths(cls, fullname: str) -> list[str]:
|
| 844 |
+
paths = NAMESPACES[fullname]
|
| 845 |
+
if not paths and fullname in MAPPING:
|
| 846 |
+
paths = [MAPPING[fullname]]
|
| 847 |
+
# Always add placeholder, for 2 reasons:
|
| 848 |
+
# 1. __path__ cannot be empty for the spec to be considered namespace.
|
| 849 |
+
# 2. In the case of nested namespaces, we need to force
|
| 850 |
+
# import machinery to query _EditableNamespaceFinder again.
|
| 851 |
+
return [*paths, PATH_PLACEHOLDER]
|
| 852 |
+
|
| 853 |
+
@classmethod
|
| 854 |
+
def find_spec(cls, fullname: str, _target=None) -> ModuleSpec | None:
|
| 855 |
+
if fullname in NAMESPACES:
|
| 856 |
+
spec = ModuleSpec(fullname, None, is_package=True)
|
| 857 |
+
spec.submodule_search_locations = cls._paths(fullname)
|
| 858 |
+
return spec
|
| 859 |
+
return None
|
| 860 |
+
|
| 861 |
+
@classmethod
|
| 862 |
+
def find_module(cls, _fullname) -> None:
|
| 863 |
+
return None
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
def install():
|
| 867 |
+
if not any(finder == _EditableFinder for finder in sys.meta_path):
|
| 868 |
+
sys.meta_path.append(_EditableFinder)
|
| 869 |
+
|
| 870 |
+
if not NAMESPACES:
|
| 871 |
+
return
|
| 872 |
+
|
| 873 |
+
if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks):
|
| 874 |
+
# PathEntryFinder is needed to create NamespaceSpec without private APIS
|
| 875 |
+
sys.path_hooks.append(_EditableNamespaceFinder._path_hook)
|
| 876 |
+
if PATH_PLACEHOLDER not in sys.path:
|
| 877 |
+
sys.path.append(PATH_PLACEHOLDER) # Used just to trigger the path hook
|
| 878 |
+
"""
|
| 879 |
+
|
| 880 |
+
|
| 881 |
+
def _finder_template(
|
| 882 |
+
name: str, mapping: Mapping[str, str], namespaces: dict[str, list[str]]
|
| 883 |
+
) -> str:
|
| 884 |
+
"""Create a string containing the code for the``MetaPathFinder`` and
|
| 885 |
+
``PathEntryFinder``.
|
| 886 |
+
"""
|
| 887 |
+
mapping = dict(sorted(mapping.items(), key=lambda p: p[0]))
|
| 888 |
+
return _FINDER_TEMPLATE.format(name=name, mapping=mapping, namespaces=namespaces)
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
class LinksNotSupported(errors.FileError):
|
| 892 |
+
"""File system does not seem to support either symlinks or hard links."""
|
| 893 |
+
|
| 894 |
+
|
| 895 |
+
class _DebuggingTips(SetuptoolsWarning):
|
| 896 |
+
_SUMMARY = "Problem in editable installation."
|
| 897 |
+
_DETAILS = """
|
| 898 |
+
An error happened while installing `{project}` in editable mode.
|
| 899 |
+
|
| 900 |
+
The following steps are recommended to help debug this problem:
|
| 901 |
+
|
| 902 |
+
- Try to install the project normally, without using the editable mode.
|
| 903 |
+
Does the error still persist?
|
| 904 |
+
(If it does, try fixing the problem before attempting the editable mode).
|
| 905 |
+
- If you are using binary extensions, make sure you have all OS-level
|
| 906 |
+
dependencies installed (e.g. compilers, toolchains, binary libraries, ...).
|
| 907 |
+
- Try the latest version of setuptools (maybe the error was already fixed).
|
| 908 |
+
- If you (or your project dependencies) are using any setuptools extension
|
| 909 |
+
or customization, make sure they support the editable mode.
|
| 910 |
+
|
| 911 |
+
After following the steps above, if the problem still persists and
|
| 912 |
+
you think this is related to how setuptools handles editable installations,
|
| 913 |
+
please submit a reproducible example
|
| 914 |
+
(see https://stackoverflow.com/help/minimal-reproducible-example) to:
|
| 915 |
+
|
| 916 |
+
https://github.com/pypa/setuptools/issues
|
| 917 |
+
"""
|
| 918 |
+
_SEE_DOCS = "userguide/development_mode.html"
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/egg_info.py
ADDED
|
@@ -0,0 +1,737 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""setuptools.command.egg_info
|
| 2 |
+
|
| 3 |
+
Create a distribution's .egg-info directory and contents"""
|
| 4 |
+
|
| 5 |
+
from distutils.filelist import FileList as _FileList
|
| 6 |
+
from distutils.errors import DistutilsInternalError
|
| 7 |
+
from distutils.util import convert_path
|
| 8 |
+
from distutils import log
|
| 9 |
+
import distutils.errors
|
| 10 |
+
import distutils.filelist
|
| 11 |
+
import functools
|
| 12 |
+
import os
|
| 13 |
+
import re
|
| 14 |
+
import sys
|
| 15 |
+
import time
|
| 16 |
+
import collections
|
| 17 |
+
|
| 18 |
+
from .._importlib import metadata
|
| 19 |
+
from .. import _entry_points, _normalization
|
| 20 |
+
from . import _requirestxt
|
| 21 |
+
|
| 22 |
+
from setuptools import Command
|
| 23 |
+
from setuptools.command.sdist import sdist
|
| 24 |
+
from setuptools.command.sdist import walk_revctrl
|
| 25 |
+
from setuptools.command.setopt import edit_config
|
| 26 |
+
from setuptools.command import bdist_egg
|
| 27 |
+
import setuptools.unicode_utils as unicode_utils
|
| 28 |
+
from setuptools.glob import glob
|
| 29 |
+
|
| 30 |
+
from setuptools.extern import packaging
|
| 31 |
+
from ..warnings import SetuptoolsDeprecationWarning
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
PY_MAJOR = '{}.{}'.format(*sys.version_info)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
|
| 38 |
+
"""
|
| 39 |
+
Translate a file path glob like '*.txt' in to a regular expression.
|
| 40 |
+
This differs from fnmatch.translate which allows wildcards to match
|
| 41 |
+
directory separators. It also knows about '**/' which matches any number of
|
| 42 |
+
directories.
|
| 43 |
+
"""
|
| 44 |
+
pat = ''
|
| 45 |
+
|
| 46 |
+
# This will split on '/' within [character classes]. This is deliberate.
|
| 47 |
+
chunks = glob.split(os.path.sep)
|
| 48 |
+
|
| 49 |
+
sep = re.escape(os.sep)
|
| 50 |
+
valid_char = '[^%s]' % (sep,)
|
| 51 |
+
|
| 52 |
+
for c, chunk in enumerate(chunks):
|
| 53 |
+
last_chunk = c == len(chunks) - 1
|
| 54 |
+
|
| 55 |
+
# Chunks that are a literal ** are globstars. They match anything.
|
| 56 |
+
if chunk == '**':
|
| 57 |
+
if last_chunk:
|
| 58 |
+
# Match anything if this is the last component
|
| 59 |
+
pat += '.*'
|
| 60 |
+
else:
|
| 61 |
+
# Match '(name/)*'
|
| 62 |
+
pat += '(?:%s+%s)*' % (valid_char, sep)
|
| 63 |
+
continue # Break here as the whole path component has been handled
|
| 64 |
+
|
| 65 |
+
# Find any special characters in the remainder
|
| 66 |
+
i = 0
|
| 67 |
+
chunk_len = len(chunk)
|
| 68 |
+
while i < chunk_len:
|
| 69 |
+
char = chunk[i]
|
| 70 |
+
if char == '*':
|
| 71 |
+
# Match any number of name characters
|
| 72 |
+
pat += valid_char + '*'
|
| 73 |
+
elif char == '?':
|
| 74 |
+
# Match a name character
|
| 75 |
+
pat += valid_char
|
| 76 |
+
elif char == '[':
|
| 77 |
+
# Character class
|
| 78 |
+
inner_i = i + 1
|
| 79 |
+
# Skip initial !/] chars
|
| 80 |
+
if inner_i < chunk_len and chunk[inner_i] == '!':
|
| 81 |
+
inner_i = inner_i + 1
|
| 82 |
+
if inner_i < chunk_len and chunk[inner_i] == ']':
|
| 83 |
+
inner_i = inner_i + 1
|
| 84 |
+
|
| 85 |
+
# Loop till the closing ] is found
|
| 86 |
+
while inner_i < chunk_len and chunk[inner_i] != ']':
|
| 87 |
+
inner_i = inner_i + 1
|
| 88 |
+
|
| 89 |
+
if inner_i >= chunk_len:
|
| 90 |
+
# Got to the end of the string without finding a closing ]
|
| 91 |
+
# Do not treat this as a matching group, but as a literal [
|
| 92 |
+
pat += re.escape(char)
|
| 93 |
+
else:
|
| 94 |
+
# Grab the insides of the [brackets]
|
| 95 |
+
inner = chunk[i + 1 : inner_i]
|
| 96 |
+
char_class = ''
|
| 97 |
+
|
| 98 |
+
# Class negation
|
| 99 |
+
if inner[0] == '!':
|
| 100 |
+
char_class = '^'
|
| 101 |
+
inner = inner[1:]
|
| 102 |
+
|
| 103 |
+
char_class += re.escape(inner)
|
| 104 |
+
pat += '[%s]' % (char_class,)
|
| 105 |
+
|
| 106 |
+
# Skip to the end ]
|
| 107 |
+
i = inner_i
|
| 108 |
+
else:
|
| 109 |
+
pat += re.escape(char)
|
| 110 |
+
i += 1
|
| 111 |
+
|
| 112 |
+
# Join each chunk with the dir separator
|
| 113 |
+
if not last_chunk:
|
| 114 |
+
pat += sep
|
| 115 |
+
|
| 116 |
+
pat += r'\Z'
|
| 117 |
+
return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class InfoCommon:
|
| 121 |
+
tag_build = None
|
| 122 |
+
tag_date = None
|
| 123 |
+
|
| 124 |
+
@property
|
| 125 |
+
def name(self):
|
| 126 |
+
return _normalization.safe_name(self.distribution.get_name())
|
| 127 |
+
|
| 128 |
+
def tagged_version(self):
|
| 129 |
+
tagged = self._maybe_tag(self.distribution.get_version())
|
| 130 |
+
return _normalization.safe_version(tagged)
|
| 131 |
+
|
| 132 |
+
def _maybe_tag(self, version):
|
| 133 |
+
"""
|
| 134 |
+
egg_info may be called more than once for a distribution,
|
| 135 |
+
in which case the version string already contains all tags.
|
| 136 |
+
"""
|
| 137 |
+
return (
|
| 138 |
+
version
|
| 139 |
+
if self.vtags and self._already_tagged(version)
|
| 140 |
+
else version + self.vtags
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
def _already_tagged(self, version: str) -> bool:
|
| 144 |
+
# Depending on their format, tags may change with version normalization.
|
| 145 |
+
# So in addition the regular tags, we have to search for the normalized ones.
|
| 146 |
+
return version.endswith(self.vtags) or version.endswith(self._safe_tags())
|
| 147 |
+
|
| 148 |
+
def _safe_tags(self) -> str:
|
| 149 |
+
# To implement this we can rely on `safe_version` pretending to be version 0
|
| 150 |
+
# followed by tags. Then we simply discard the starting 0 (fake version number)
|
| 151 |
+
try:
|
| 152 |
+
return _normalization.safe_version(f"0{self.vtags}")[1:]
|
| 153 |
+
except packaging.version.InvalidVersion:
|
| 154 |
+
return _normalization.safe_name(self.vtags.replace(' ', '.'))
|
| 155 |
+
|
| 156 |
+
def tags(self) -> str:
|
| 157 |
+
version = ''
|
| 158 |
+
if self.tag_build:
|
| 159 |
+
version += self.tag_build
|
| 160 |
+
if self.tag_date:
|
| 161 |
+
version += time.strftime("%Y%m%d")
|
| 162 |
+
return version
|
| 163 |
+
|
| 164 |
+
vtags = property(tags)
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class egg_info(InfoCommon, Command):
|
| 168 |
+
description = "create a distribution's .egg-info directory"
|
| 169 |
+
|
| 170 |
+
user_options = [
|
| 171 |
+
(
|
| 172 |
+
'egg-base=',
|
| 173 |
+
'e',
|
| 174 |
+
"directory containing .egg-info directories"
|
| 175 |
+
" [default: top of the source tree]",
|
| 176 |
+
),
|
| 177 |
+
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
|
| 178 |
+
('tag-build=', 'b', "Specify explicit tag to add to version number"),
|
| 179 |
+
('no-date', 'D', "Don't include date stamp [default]"),
|
| 180 |
+
]
|
| 181 |
+
|
| 182 |
+
boolean_options = ['tag-date']
|
| 183 |
+
negative_opt = {
|
| 184 |
+
'no-date': 'tag-date',
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
def initialize_options(self):
|
| 188 |
+
self.egg_base = None
|
| 189 |
+
self.egg_name = None
|
| 190 |
+
self.egg_info = None
|
| 191 |
+
self.egg_version = None
|
| 192 |
+
self.ignore_egg_info_in_manifest = False
|
| 193 |
+
|
| 194 |
+
####################################
|
| 195 |
+
# allow the 'tag_svn_revision' to be detected and
|
| 196 |
+
# set, supporting sdists built on older Setuptools.
|
| 197 |
+
@property
|
| 198 |
+
def tag_svn_revision(self):
|
| 199 |
+
pass
|
| 200 |
+
|
| 201 |
+
@tag_svn_revision.setter
|
| 202 |
+
def tag_svn_revision(self, value):
|
| 203 |
+
pass
|
| 204 |
+
|
| 205 |
+
####################################
|
| 206 |
+
|
| 207 |
+
def save_version_info(self, filename):
|
| 208 |
+
"""
|
| 209 |
+
Materialize the value of date into the
|
| 210 |
+
build tag. Install build keys in a deterministic order
|
| 211 |
+
to avoid arbitrary reordering on subsequent builds.
|
| 212 |
+
"""
|
| 213 |
+
egg_info = collections.OrderedDict()
|
| 214 |
+
# follow the order these keys would have been added
|
| 215 |
+
# when PYTHONHASHSEED=0
|
| 216 |
+
egg_info['tag_build'] = self.tags()
|
| 217 |
+
egg_info['tag_date'] = 0
|
| 218 |
+
edit_config(filename, dict(egg_info=egg_info))
|
| 219 |
+
|
| 220 |
+
def finalize_options(self):
|
| 221 |
+
# Note: we need to capture the current value returned
|
| 222 |
+
# by `self.tagged_version()`, so we can later update
|
| 223 |
+
# `self.distribution.metadata.version` without
|
| 224 |
+
# repercussions.
|
| 225 |
+
self.egg_name = self.name
|
| 226 |
+
self.egg_version = self.tagged_version()
|
| 227 |
+
parsed_version = packaging.version.Version(self.egg_version)
|
| 228 |
+
|
| 229 |
+
try:
|
| 230 |
+
is_version = isinstance(parsed_version, packaging.version.Version)
|
| 231 |
+
spec = "%s==%s" if is_version else "%s===%s"
|
| 232 |
+
packaging.requirements.Requirement(spec % (self.egg_name, self.egg_version))
|
| 233 |
+
except ValueError as e:
|
| 234 |
+
raise distutils.errors.DistutilsOptionError(
|
| 235 |
+
"Invalid distribution name or version syntax: %s-%s"
|
| 236 |
+
% (self.egg_name, self.egg_version)
|
| 237 |
+
) from e
|
| 238 |
+
|
| 239 |
+
if self.egg_base is None:
|
| 240 |
+
dirs = self.distribution.package_dir
|
| 241 |
+
self.egg_base = (dirs or {}).get('', os.curdir)
|
| 242 |
+
|
| 243 |
+
self.ensure_dirname('egg_base')
|
| 244 |
+
self.egg_info = _normalization.filename_component(self.egg_name) + '.egg-info'
|
| 245 |
+
if self.egg_base != os.curdir:
|
| 246 |
+
self.egg_info = os.path.join(self.egg_base, self.egg_info)
|
| 247 |
+
|
| 248 |
+
# Set package version for the benefit of dumber commands
|
| 249 |
+
# (e.g. sdist, bdist_wininst, etc.)
|
| 250 |
+
#
|
| 251 |
+
self.distribution.metadata.version = self.egg_version
|
| 252 |
+
|
| 253 |
+
# If we bootstrapped around the lack of a PKG-INFO, as might be the
|
| 254 |
+
# case in a fresh checkout, make sure that any special tags get added
|
| 255 |
+
# to the version info
|
| 256 |
+
#
|
| 257 |
+
pd = self.distribution._patched_dist
|
| 258 |
+
key = getattr(pd, "key", None) or getattr(pd, "name", None)
|
| 259 |
+
if pd is not None and key == self.egg_name.lower():
|
| 260 |
+
pd._version = self.egg_version
|
| 261 |
+
pd._parsed_version = packaging.version.Version(self.egg_version)
|
| 262 |
+
self.distribution._patched_dist = None
|
| 263 |
+
|
| 264 |
+
def _get_egg_basename(self, py_version=PY_MAJOR, platform=None):
|
| 265 |
+
"""Compute filename of the output egg. Private API."""
|
| 266 |
+
return _egg_basename(self.egg_name, self.egg_version, py_version, platform)
|
| 267 |
+
|
| 268 |
+
def write_or_delete_file(self, what, filename, data, force=False):
|
| 269 |
+
"""Write `data` to `filename` or delete if empty
|
| 270 |
+
|
| 271 |
+
If `data` is non-empty, this routine is the same as ``write_file()``.
|
| 272 |
+
If `data` is empty but not ``None``, this is the same as calling
|
| 273 |
+
``delete_file(filename)`. If `data` is ``None``, then this is a no-op
|
| 274 |
+
unless `filename` exists, in which case a warning is issued about the
|
| 275 |
+
orphaned file (if `force` is false), or deleted (if `force` is true).
|
| 276 |
+
"""
|
| 277 |
+
if data:
|
| 278 |
+
self.write_file(what, filename, data)
|
| 279 |
+
elif os.path.exists(filename):
|
| 280 |
+
if data is None and not force:
|
| 281 |
+
log.warn("%s not set in setup(), but %s exists", what, filename)
|
| 282 |
+
return
|
| 283 |
+
else:
|
| 284 |
+
self.delete_file(filename)
|
| 285 |
+
|
| 286 |
+
def write_file(self, what, filename, data):
|
| 287 |
+
"""Write `data` to `filename` (if not a dry run) after announcing it
|
| 288 |
+
|
| 289 |
+
`what` is used in a log message to identify what is being written
|
| 290 |
+
to the file.
|
| 291 |
+
"""
|
| 292 |
+
log.info("writing %s to %s", what, filename)
|
| 293 |
+
data = data.encode("utf-8")
|
| 294 |
+
if not self.dry_run:
|
| 295 |
+
f = open(filename, 'wb')
|
| 296 |
+
f.write(data)
|
| 297 |
+
f.close()
|
| 298 |
+
|
| 299 |
+
def delete_file(self, filename):
|
| 300 |
+
"""Delete `filename` (if not a dry run) after announcing it"""
|
| 301 |
+
log.info("deleting %s", filename)
|
| 302 |
+
if not self.dry_run:
|
| 303 |
+
os.unlink(filename)
|
| 304 |
+
|
| 305 |
+
def run(self):
|
| 306 |
+
self.mkpath(self.egg_info)
|
| 307 |
+
try:
|
| 308 |
+
os.utime(self.egg_info, None)
|
| 309 |
+
except OSError as e:
|
| 310 |
+
msg = f"Cannot update time stamp of directory '{self.egg_info}'"
|
| 311 |
+
raise distutils.errors.DistutilsFileError(msg) from e
|
| 312 |
+
for ep in metadata.entry_points(group='egg_info.writers'):
|
| 313 |
+
writer = ep.load()
|
| 314 |
+
writer(self, ep.name, os.path.join(self.egg_info, ep.name))
|
| 315 |
+
|
| 316 |
+
# Get rid of native_libs.txt if it was put there by older bdist_egg
|
| 317 |
+
nl = os.path.join(self.egg_info, "native_libs.txt")
|
| 318 |
+
if os.path.exists(nl):
|
| 319 |
+
self.delete_file(nl)
|
| 320 |
+
|
| 321 |
+
self.find_sources()
|
| 322 |
+
|
| 323 |
+
def find_sources(self):
|
| 324 |
+
"""Generate SOURCES.txt manifest file"""
|
| 325 |
+
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
|
| 326 |
+
mm = manifest_maker(self.distribution)
|
| 327 |
+
mm.ignore_egg_info_dir = self.ignore_egg_info_in_manifest
|
| 328 |
+
mm.manifest = manifest_filename
|
| 329 |
+
mm.run()
|
| 330 |
+
self.filelist = mm.filelist
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
class FileList(_FileList):
|
| 334 |
+
# Implementations of the various MANIFEST.in commands
|
| 335 |
+
|
| 336 |
+
def __init__(self, warn=None, debug_print=None, ignore_egg_info_dir=False):
|
| 337 |
+
super().__init__(warn, debug_print)
|
| 338 |
+
self.ignore_egg_info_dir = ignore_egg_info_dir
|
| 339 |
+
|
| 340 |
+
def process_template_line(self, line):
|
| 341 |
+
# Parse the line: split it up, make sure the right number of words
|
| 342 |
+
# is there, and return the relevant words. 'action' is always
|
| 343 |
+
# defined: it's the first word of the line. Which of the other
|
| 344 |
+
# three are defined depends on the action; it'll be either
|
| 345 |
+
# patterns, (dir and patterns), or (dir_pattern).
|
| 346 |
+
(action, patterns, dir, dir_pattern) = self._parse_template_line(line)
|
| 347 |
+
|
| 348 |
+
action_map = {
|
| 349 |
+
'include': self.include,
|
| 350 |
+
'exclude': self.exclude,
|
| 351 |
+
'global-include': self.global_include,
|
| 352 |
+
'global-exclude': self.global_exclude,
|
| 353 |
+
'recursive-include': functools.partial(
|
| 354 |
+
self.recursive_include,
|
| 355 |
+
dir,
|
| 356 |
+
),
|
| 357 |
+
'recursive-exclude': functools.partial(
|
| 358 |
+
self.recursive_exclude,
|
| 359 |
+
dir,
|
| 360 |
+
),
|
| 361 |
+
'graft': self.graft,
|
| 362 |
+
'prune': self.prune,
|
| 363 |
+
}
|
| 364 |
+
log_map = {
|
| 365 |
+
'include': "warning: no files found matching '%s'",
|
| 366 |
+
'exclude': ("warning: no previously-included files found matching '%s'"),
|
| 367 |
+
'global-include': (
|
| 368 |
+
"warning: no files found matching '%s' anywhere in distribution"
|
| 369 |
+
),
|
| 370 |
+
'global-exclude': (
|
| 371 |
+
"warning: no previously-included files matching "
|
| 372 |
+
"'%s' found anywhere in distribution"
|
| 373 |
+
),
|
| 374 |
+
'recursive-include': (
|
| 375 |
+
"warning: no files found matching '%s' under directory '%s'"
|
| 376 |
+
),
|
| 377 |
+
'recursive-exclude': (
|
| 378 |
+
"warning: no previously-included files matching "
|
| 379 |
+
"'%s' found under directory '%s'"
|
| 380 |
+
),
|
| 381 |
+
'graft': "warning: no directories found matching '%s'",
|
| 382 |
+
'prune': "no previously-included directories found matching '%s'",
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
try:
|
| 386 |
+
process_action = action_map[action]
|
| 387 |
+
except KeyError:
|
| 388 |
+
msg = f"Invalid MANIFEST.in: unknown action {action!r} in {line!r}"
|
| 389 |
+
raise DistutilsInternalError(msg) from None
|
| 390 |
+
|
| 391 |
+
# OK, now we know that the action is valid and we have the
|
| 392 |
+
# right number of words on the line for that action -- so we
|
| 393 |
+
# can proceed with minimal error-checking.
|
| 394 |
+
|
| 395 |
+
action_is_recursive = action.startswith('recursive-')
|
| 396 |
+
if action in {'graft', 'prune'}:
|
| 397 |
+
patterns = [dir_pattern]
|
| 398 |
+
extra_log_args = (dir,) if action_is_recursive else ()
|
| 399 |
+
log_tmpl = log_map[action]
|
| 400 |
+
|
| 401 |
+
self.debug_print(
|
| 402 |
+
' '.join(
|
| 403 |
+
[action] + ([dir] if action_is_recursive else []) + patterns,
|
| 404 |
+
)
|
| 405 |
+
)
|
| 406 |
+
for pattern in patterns:
|
| 407 |
+
if not process_action(pattern):
|
| 408 |
+
log.warn(log_tmpl, pattern, *extra_log_args)
|
| 409 |
+
|
| 410 |
+
def _remove_files(self, predicate):
|
| 411 |
+
"""
|
| 412 |
+
Remove all files from the file list that match the predicate.
|
| 413 |
+
Return True if any matching files were removed
|
| 414 |
+
"""
|
| 415 |
+
found = False
|
| 416 |
+
for i in range(len(self.files) - 1, -1, -1):
|
| 417 |
+
if predicate(self.files[i]):
|
| 418 |
+
self.debug_print(" removing " + self.files[i])
|
| 419 |
+
del self.files[i]
|
| 420 |
+
found = True
|
| 421 |
+
return found
|
| 422 |
+
|
| 423 |
+
def include(self, pattern):
|
| 424 |
+
"""Include files that match 'pattern'."""
|
| 425 |
+
found = [f for f in glob(pattern) if not os.path.isdir(f)]
|
| 426 |
+
self.extend(found)
|
| 427 |
+
return bool(found)
|
| 428 |
+
|
| 429 |
+
def exclude(self, pattern):
|
| 430 |
+
"""Exclude files that match 'pattern'."""
|
| 431 |
+
match = translate_pattern(pattern)
|
| 432 |
+
return self._remove_files(match.match)
|
| 433 |
+
|
| 434 |
+
def recursive_include(self, dir, pattern):
|
| 435 |
+
"""
|
| 436 |
+
Include all files anywhere in 'dir/' that match the pattern.
|
| 437 |
+
"""
|
| 438 |
+
full_pattern = os.path.join(dir, '**', pattern)
|
| 439 |
+
found = [f for f in glob(full_pattern, recursive=True) if not os.path.isdir(f)]
|
| 440 |
+
self.extend(found)
|
| 441 |
+
return bool(found)
|
| 442 |
+
|
| 443 |
+
def recursive_exclude(self, dir, pattern):
|
| 444 |
+
"""
|
| 445 |
+
Exclude any file anywhere in 'dir/' that match the pattern.
|
| 446 |
+
"""
|
| 447 |
+
match = translate_pattern(os.path.join(dir, '**', pattern))
|
| 448 |
+
return self._remove_files(match.match)
|
| 449 |
+
|
| 450 |
+
def graft(self, dir):
|
| 451 |
+
"""Include all files from 'dir/'."""
|
| 452 |
+
found = [
|
| 453 |
+
item
|
| 454 |
+
for match_dir in glob(dir)
|
| 455 |
+
for item in distutils.filelist.findall(match_dir)
|
| 456 |
+
]
|
| 457 |
+
self.extend(found)
|
| 458 |
+
return bool(found)
|
| 459 |
+
|
| 460 |
+
def prune(self, dir):
|
| 461 |
+
"""Filter out files from 'dir/'."""
|
| 462 |
+
match = translate_pattern(os.path.join(dir, '**'))
|
| 463 |
+
return self._remove_files(match.match)
|
| 464 |
+
|
| 465 |
+
def global_include(self, pattern):
|
| 466 |
+
"""
|
| 467 |
+
Include all files anywhere in the current directory that match the
|
| 468 |
+
pattern. This is very inefficient on large file trees.
|
| 469 |
+
"""
|
| 470 |
+
if self.allfiles is None:
|
| 471 |
+
self.findall()
|
| 472 |
+
match = translate_pattern(os.path.join('**', pattern))
|
| 473 |
+
found = [f for f in self.allfiles if match.match(f)]
|
| 474 |
+
self.extend(found)
|
| 475 |
+
return bool(found)
|
| 476 |
+
|
| 477 |
+
def global_exclude(self, pattern):
|
| 478 |
+
"""
|
| 479 |
+
Exclude all files anywhere that match the pattern.
|
| 480 |
+
"""
|
| 481 |
+
match = translate_pattern(os.path.join('**', pattern))
|
| 482 |
+
return self._remove_files(match.match)
|
| 483 |
+
|
| 484 |
+
def append(self, item):
|
| 485 |
+
if item.endswith('\r'): # Fix older sdists built on Windows
|
| 486 |
+
item = item[:-1]
|
| 487 |
+
path = convert_path(item)
|
| 488 |
+
|
| 489 |
+
if self._safe_path(path):
|
| 490 |
+
self.files.append(path)
|
| 491 |
+
|
| 492 |
+
def extend(self, paths):
|
| 493 |
+
self.files.extend(filter(self._safe_path, paths))
|
| 494 |
+
|
| 495 |
+
def _repair(self):
|
| 496 |
+
"""
|
| 497 |
+
Replace self.files with only safe paths
|
| 498 |
+
|
| 499 |
+
Because some owners of FileList manipulate the underlying
|
| 500 |
+
``files`` attribute directly, this method must be called to
|
| 501 |
+
repair those paths.
|
| 502 |
+
"""
|
| 503 |
+
self.files = list(filter(self._safe_path, self.files))
|
| 504 |
+
|
| 505 |
+
def _safe_path(self, path):
|
| 506 |
+
enc_warn = "'%s' not %s encodable -- skipping"
|
| 507 |
+
|
| 508 |
+
# To avoid accidental trans-codings errors, first to unicode
|
| 509 |
+
u_path = unicode_utils.filesys_decode(path)
|
| 510 |
+
if u_path is None:
|
| 511 |
+
log.warn("'%s' in unexpected encoding -- skipping" % path)
|
| 512 |
+
return False
|
| 513 |
+
|
| 514 |
+
# Must ensure utf-8 encodability
|
| 515 |
+
utf8_path = unicode_utils.try_encode(u_path, "utf-8")
|
| 516 |
+
if utf8_path is None:
|
| 517 |
+
log.warn(enc_warn, path, 'utf-8')
|
| 518 |
+
return False
|
| 519 |
+
|
| 520 |
+
try:
|
| 521 |
+
# ignore egg-info paths
|
| 522 |
+
is_egg_info = ".egg-info" in u_path or b".egg-info" in utf8_path
|
| 523 |
+
if self.ignore_egg_info_dir and is_egg_info:
|
| 524 |
+
return False
|
| 525 |
+
# accept is either way checks out
|
| 526 |
+
if os.path.exists(u_path) or os.path.exists(utf8_path):
|
| 527 |
+
return True
|
| 528 |
+
# this will catch any encode errors decoding u_path
|
| 529 |
+
except UnicodeEncodeError:
|
| 530 |
+
log.warn(enc_warn, path, sys.getfilesystemencoding())
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
class manifest_maker(sdist):
|
| 534 |
+
template = "MANIFEST.in"
|
| 535 |
+
|
| 536 |
+
def initialize_options(self):
|
| 537 |
+
self.use_defaults = True
|
| 538 |
+
self.prune = True
|
| 539 |
+
self.manifest_only = True
|
| 540 |
+
self.force_manifest = True
|
| 541 |
+
self.ignore_egg_info_dir = False
|
| 542 |
+
|
| 543 |
+
def finalize_options(self):
|
| 544 |
+
pass
|
| 545 |
+
|
| 546 |
+
def run(self):
|
| 547 |
+
self.filelist = FileList(ignore_egg_info_dir=self.ignore_egg_info_dir)
|
| 548 |
+
if not os.path.exists(self.manifest):
|
| 549 |
+
self.write_manifest() # it must exist so it'll get in the list
|
| 550 |
+
self.add_defaults()
|
| 551 |
+
if os.path.exists(self.template):
|
| 552 |
+
self.read_template()
|
| 553 |
+
self.add_license_files()
|
| 554 |
+
self._add_referenced_files()
|
| 555 |
+
self.prune_file_list()
|
| 556 |
+
self.filelist.sort()
|
| 557 |
+
self.filelist.remove_duplicates()
|
| 558 |
+
self.write_manifest()
|
| 559 |
+
|
| 560 |
+
def _manifest_normalize(self, path):
|
| 561 |
+
path = unicode_utils.filesys_decode(path)
|
| 562 |
+
return path.replace(os.sep, '/')
|
| 563 |
+
|
| 564 |
+
def write_manifest(self):
|
| 565 |
+
"""
|
| 566 |
+
Write the file list in 'self.filelist' to the manifest file
|
| 567 |
+
named by 'self.manifest'.
|
| 568 |
+
"""
|
| 569 |
+
self.filelist._repair()
|
| 570 |
+
|
| 571 |
+
# Now _repairs should encodability, but not unicode
|
| 572 |
+
files = [self._manifest_normalize(f) for f in self.filelist.files]
|
| 573 |
+
msg = "writing manifest file '%s'" % self.manifest
|
| 574 |
+
self.execute(write_file, (self.manifest, files), msg)
|
| 575 |
+
|
| 576 |
+
def warn(self, msg):
|
| 577 |
+
if not self._should_suppress_warning(msg):
|
| 578 |
+
sdist.warn(self, msg)
|
| 579 |
+
|
| 580 |
+
@staticmethod
|
| 581 |
+
def _should_suppress_warning(msg):
|
| 582 |
+
"""
|
| 583 |
+
suppress missing-file warnings from sdist
|
| 584 |
+
"""
|
| 585 |
+
return re.match(r"standard file .*not found", msg)
|
| 586 |
+
|
| 587 |
+
def add_defaults(self):
|
| 588 |
+
sdist.add_defaults(self)
|
| 589 |
+
self.filelist.append(self.template)
|
| 590 |
+
self.filelist.append(self.manifest)
|
| 591 |
+
rcfiles = list(walk_revctrl())
|
| 592 |
+
if rcfiles:
|
| 593 |
+
self.filelist.extend(rcfiles)
|
| 594 |
+
elif os.path.exists(self.manifest):
|
| 595 |
+
self.read_manifest()
|
| 596 |
+
|
| 597 |
+
if os.path.exists("setup.py"):
|
| 598 |
+
# setup.py should be included by default, even if it's not
|
| 599 |
+
# the script called to create the sdist
|
| 600 |
+
self.filelist.append("setup.py")
|
| 601 |
+
|
| 602 |
+
ei_cmd = self.get_finalized_command('egg_info')
|
| 603 |
+
self.filelist.graft(ei_cmd.egg_info)
|
| 604 |
+
|
| 605 |
+
def add_license_files(self):
|
| 606 |
+
license_files = self.distribution.metadata.license_files or []
|
| 607 |
+
for lf in license_files:
|
| 608 |
+
log.info("adding license file '%s'", lf)
|
| 609 |
+
self.filelist.extend(license_files)
|
| 610 |
+
|
| 611 |
+
def _add_referenced_files(self):
|
| 612 |
+
"""Add files referenced by the config (e.g. `file:` directive) to filelist"""
|
| 613 |
+
referenced = getattr(self.distribution, '_referenced_files', [])
|
| 614 |
+
# ^-- fallback if dist comes from distutils or is a custom class
|
| 615 |
+
for rf in referenced:
|
| 616 |
+
log.debug("adding file referenced by config '%s'", rf)
|
| 617 |
+
self.filelist.extend(referenced)
|
| 618 |
+
|
| 619 |
+
def prune_file_list(self):
|
| 620 |
+
build = self.get_finalized_command('build')
|
| 621 |
+
base_dir = self.distribution.get_fullname()
|
| 622 |
+
self.filelist.prune(build.build_base)
|
| 623 |
+
self.filelist.prune(base_dir)
|
| 624 |
+
sep = re.escape(os.sep)
|
| 625 |
+
self.filelist.exclude_pattern(
|
| 626 |
+
r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep, is_regex=True
|
| 627 |
+
)
|
| 628 |
+
|
| 629 |
+
def _safe_data_files(self, build_py):
|
| 630 |
+
"""
|
| 631 |
+
The parent class implementation of this method
|
| 632 |
+
(``sdist``) will try to include data files, which
|
| 633 |
+
might cause recursion problems when
|
| 634 |
+
``include_package_data=True``.
|
| 635 |
+
|
| 636 |
+
Therefore, avoid triggering any attempt of
|
| 637 |
+
analyzing/building the manifest again.
|
| 638 |
+
"""
|
| 639 |
+
if hasattr(build_py, 'get_data_files_without_manifest'):
|
| 640 |
+
return build_py.get_data_files_without_manifest()
|
| 641 |
+
|
| 642 |
+
SetuptoolsDeprecationWarning.emit(
|
| 643 |
+
"`build_py` command does not inherit from setuptools' `build_py`.",
|
| 644 |
+
"""
|
| 645 |
+
Custom 'build_py' does not implement 'get_data_files_without_manifest'.
|
| 646 |
+
Please extend command classes from setuptools instead of distutils.
|
| 647 |
+
""",
|
| 648 |
+
see_url="https://peps.python.org/pep-0632/",
|
| 649 |
+
# due_date not defined yet, old projects might still do it?
|
| 650 |
+
)
|
| 651 |
+
return build_py.get_data_files()
|
| 652 |
+
|
| 653 |
+
|
| 654 |
+
def write_file(filename, contents):
|
| 655 |
+
"""Create a file with the specified name and write 'contents' (a
|
| 656 |
+
sequence of strings without line terminators) to it.
|
| 657 |
+
"""
|
| 658 |
+
contents = "\n".join(contents)
|
| 659 |
+
|
| 660 |
+
# assuming the contents has been vetted for utf-8 encoding
|
| 661 |
+
contents = contents.encode("utf-8")
|
| 662 |
+
|
| 663 |
+
with open(filename, "wb") as f: # always write POSIX-style manifest
|
| 664 |
+
f.write(contents)
|
| 665 |
+
|
| 666 |
+
|
| 667 |
+
def write_pkg_info(cmd, basename, filename):
|
| 668 |
+
log.info("writing %s", filename)
|
| 669 |
+
if not cmd.dry_run:
|
| 670 |
+
metadata = cmd.distribution.metadata
|
| 671 |
+
metadata.version, oldver = cmd.egg_version, metadata.version
|
| 672 |
+
metadata.name, oldname = cmd.egg_name, metadata.name
|
| 673 |
+
|
| 674 |
+
try:
|
| 675 |
+
# write unescaped data to PKG-INFO, so older pkg_resources
|
| 676 |
+
# can still parse it
|
| 677 |
+
metadata.write_pkg_info(cmd.egg_info)
|
| 678 |
+
finally:
|
| 679 |
+
metadata.name, metadata.version = oldname, oldver
|
| 680 |
+
|
| 681 |
+
safe = getattr(cmd.distribution, 'zip_safe', None)
|
| 682 |
+
|
| 683 |
+
bdist_egg.write_safety_flag(cmd.egg_info, safe)
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
def warn_depends_obsolete(cmd, basename, filename):
|
| 687 |
+
"""
|
| 688 |
+
Unused: left to avoid errors when updating (from source) from <= 67.8.
|
| 689 |
+
Old installations have a .dist-info directory with the entry-point
|
| 690 |
+
``depends.txt = setuptools.command.egg_info:warn_depends_obsolete``.
|
| 691 |
+
This may trigger errors when running the first egg_info in build_meta.
|
| 692 |
+
TODO: Remove this function in a version sufficiently > 68.
|
| 693 |
+
"""
|
| 694 |
+
|
| 695 |
+
|
| 696 |
+
# Export API used in entry_points
|
| 697 |
+
write_requirements = _requirestxt.write_requirements
|
| 698 |
+
write_setup_requirements = _requirestxt.write_setup_requirements
|
| 699 |
+
|
| 700 |
+
|
| 701 |
+
def write_toplevel_names(cmd, basename, filename):
|
| 702 |
+
pkgs = dict.fromkeys([
|
| 703 |
+
k.split('.', 1)[0] for k in cmd.distribution.iter_distribution_names()
|
| 704 |
+
])
|
| 705 |
+
cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
|
| 706 |
+
|
| 707 |
+
|
| 708 |
+
def overwrite_arg(cmd, basename, filename):
|
| 709 |
+
write_arg(cmd, basename, filename, True)
|
| 710 |
+
|
| 711 |
+
|
| 712 |
+
def write_arg(cmd, basename, filename, force=False):
|
| 713 |
+
argname = os.path.splitext(basename)[0]
|
| 714 |
+
value = getattr(cmd.distribution, argname, None)
|
| 715 |
+
if value is not None:
|
| 716 |
+
value = '\n'.join(value) + '\n'
|
| 717 |
+
cmd.write_or_delete_file(argname, filename, value, force)
|
| 718 |
+
|
| 719 |
+
|
| 720 |
+
def write_entries(cmd, basename, filename):
|
| 721 |
+
eps = _entry_points.load(cmd.distribution.entry_points)
|
| 722 |
+
defn = _entry_points.render(eps)
|
| 723 |
+
cmd.write_or_delete_file('entry points', filename, defn, True)
|
| 724 |
+
|
| 725 |
+
|
| 726 |
+
def _egg_basename(egg_name, egg_version, py_version=None, platform=None):
|
| 727 |
+
"""Compute filename of the output egg. Private API."""
|
| 728 |
+
name = _normalization.filename_component(egg_name)
|
| 729 |
+
version = _normalization.filename_component(egg_version)
|
| 730 |
+
egg = f"{name}-{version}-py{py_version or PY_MAJOR}"
|
| 731 |
+
if platform:
|
| 732 |
+
egg += f"-{platform}"
|
| 733 |
+
return egg
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
|
| 737 |
+
"""Deprecated behavior warning for EggInfo, bypassing suppression."""
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/install_lib.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
from itertools import product, starmap
|
| 4 |
+
import distutils.command.install_lib as orig
|
| 5 |
+
from .._path import StrPath
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class install_lib(orig.install_lib):
|
| 9 |
+
"""Don't add compiled flags to filenames of non-Python files"""
|
| 10 |
+
|
| 11 |
+
def run(self):
|
| 12 |
+
self.build()
|
| 13 |
+
outfiles = self.install()
|
| 14 |
+
if outfiles is not None:
|
| 15 |
+
# always compile, in case we have any extension stubs to deal with
|
| 16 |
+
self.byte_compile(outfiles)
|
| 17 |
+
|
| 18 |
+
def get_exclusions(self):
|
| 19 |
+
"""
|
| 20 |
+
Return a collections.Sized collections.Container of paths to be
|
| 21 |
+
excluded for single_version_externally_managed installations.
|
| 22 |
+
"""
|
| 23 |
+
all_packages = (
|
| 24 |
+
pkg
|
| 25 |
+
for ns_pkg in self._get_SVEM_NSPs()
|
| 26 |
+
for pkg in self._all_packages(ns_pkg)
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
excl_specs = product(all_packages, self._gen_exclusion_paths())
|
| 30 |
+
return set(starmap(self._exclude_pkg_path, excl_specs))
|
| 31 |
+
|
| 32 |
+
def _exclude_pkg_path(self, pkg, exclusion_path):
|
| 33 |
+
"""
|
| 34 |
+
Given a package name and exclusion path within that package,
|
| 35 |
+
compute the full exclusion path.
|
| 36 |
+
"""
|
| 37 |
+
parts = pkg.split('.') + [exclusion_path]
|
| 38 |
+
return os.path.join(self.install_dir, *parts)
|
| 39 |
+
|
| 40 |
+
@staticmethod
|
| 41 |
+
def _all_packages(pkg_name):
|
| 42 |
+
"""
|
| 43 |
+
>>> list(install_lib._all_packages('foo.bar.baz'))
|
| 44 |
+
['foo.bar.baz', 'foo.bar', 'foo']
|
| 45 |
+
"""
|
| 46 |
+
while pkg_name:
|
| 47 |
+
yield pkg_name
|
| 48 |
+
pkg_name, sep, child = pkg_name.rpartition('.')
|
| 49 |
+
|
| 50 |
+
def _get_SVEM_NSPs(self):
|
| 51 |
+
"""
|
| 52 |
+
Get namespace packages (list) but only for
|
| 53 |
+
single_version_externally_managed installations and empty otherwise.
|
| 54 |
+
"""
|
| 55 |
+
# TODO: is it necessary to short-circuit here? i.e. what's the cost
|
| 56 |
+
# if get_finalized_command is called even when namespace_packages is
|
| 57 |
+
# False?
|
| 58 |
+
if not self.distribution.namespace_packages:
|
| 59 |
+
return []
|
| 60 |
+
|
| 61 |
+
install_cmd = self.get_finalized_command('install')
|
| 62 |
+
svem = install_cmd.single_version_externally_managed
|
| 63 |
+
|
| 64 |
+
return self.distribution.namespace_packages if svem else []
|
| 65 |
+
|
| 66 |
+
@staticmethod
|
| 67 |
+
def _gen_exclusion_paths():
|
| 68 |
+
"""
|
| 69 |
+
Generate file paths to be excluded for namespace packages (bytecode
|
| 70 |
+
cache files).
|
| 71 |
+
"""
|
| 72 |
+
# always exclude the package module itself
|
| 73 |
+
yield '__init__.py'
|
| 74 |
+
|
| 75 |
+
yield '__init__.pyc'
|
| 76 |
+
yield '__init__.pyo'
|
| 77 |
+
|
| 78 |
+
if not hasattr(sys, 'implementation'):
|
| 79 |
+
return
|
| 80 |
+
|
| 81 |
+
base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag)
|
| 82 |
+
yield base + '.pyc'
|
| 83 |
+
yield base + '.pyo'
|
| 84 |
+
yield base + '.opt-1.pyc'
|
| 85 |
+
yield base + '.opt-2.pyc'
|
| 86 |
+
|
| 87 |
+
def copy_tree(
|
| 88 |
+
self,
|
| 89 |
+
infile: StrPath,
|
| 90 |
+
outfile: str,
|
| 91 |
+
preserve_mode=True,
|
| 92 |
+
preserve_times=True,
|
| 93 |
+
preserve_symlinks=False,
|
| 94 |
+
level=1,
|
| 95 |
+
):
|
| 96 |
+
assert preserve_mode and preserve_times and not preserve_symlinks
|
| 97 |
+
exclude = self.get_exclusions()
|
| 98 |
+
|
| 99 |
+
if not exclude:
|
| 100 |
+
return orig.install_lib.copy_tree(self, infile, outfile) # type: ignore[arg-type] # Fixed upstream
|
| 101 |
+
|
| 102 |
+
# Exclude namespace package __init__.py* files from the output
|
| 103 |
+
|
| 104 |
+
from setuptools.archive_util import unpack_directory
|
| 105 |
+
from distutils import log
|
| 106 |
+
|
| 107 |
+
outfiles = []
|
| 108 |
+
|
| 109 |
+
def pf(src, dst):
|
| 110 |
+
if dst in exclude:
|
| 111 |
+
log.warn("Skipping installation of %s (namespace package)", dst)
|
| 112 |
+
return False
|
| 113 |
+
|
| 114 |
+
log.info("copying %s -> %s", src, os.path.dirname(dst))
|
| 115 |
+
outfiles.append(dst)
|
| 116 |
+
return dst
|
| 117 |
+
|
| 118 |
+
unpack_directory(infile, outfile, pf)
|
| 119 |
+
return outfiles
|
| 120 |
+
|
| 121 |
+
def get_outputs(self):
|
| 122 |
+
outputs = orig.install_lib.get_outputs(self)
|
| 123 |
+
exclude = self.get_exclusions()
|
| 124 |
+
if exclude:
|
| 125 |
+
return [f for f in outputs if f not in exclude]
|
| 126 |
+
return outputs
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/install_scripts.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from distutils import log
|
| 4 |
+
import distutils.command.install_scripts as orig
|
| 5 |
+
import os
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
from .._path import ensure_directory
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class install_scripts(orig.install_scripts):
|
| 12 |
+
"""Do normal script install, plus any egg_info wrapper scripts"""
|
| 13 |
+
|
| 14 |
+
def initialize_options(self):
|
| 15 |
+
orig.install_scripts.initialize_options(self)
|
| 16 |
+
self.no_ep = False
|
| 17 |
+
|
| 18 |
+
def run(self) -> None:
|
| 19 |
+
self.run_command("egg_info")
|
| 20 |
+
if self.distribution.scripts:
|
| 21 |
+
orig.install_scripts.run(self) # run first to set up self.outfiles
|
| 22 |
+
else:
|
| 23 |
+
self.outfiles: list[str] = []
|
| 24 |
+
if self.no_ep:
|
| 25 |
+
# don't install entry point scripts into .egg file!
|
| 26 |
+
return
|
| 27 |
+
self._install_ep_scripts()
|
| 28 |
+
|
| 29 |
+
def _install_ep_scripts(self):
|
| 30 |
+
# Delay import side-effects
|
| 31 |
+
from pkg_resources import Distribution, PathMetadata
|
| 32 |
+
from . import easy_install as ei
|
| 33 |
+
|
| 34 |
+
ei_cmd = self.get_finalized_command("egg_info")
|
| 35 |
+
dist = Distribution(
|
| 36 |
+
ei_cmd.egg_base,
|
| 37 |
+
PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info),
|
| 38 |
+
ei_cmd.egg_name,
|
| 39 |
+
ei_cmd.egg_version,
|
| 40 |
+
)
|
| 41 |
+
bs_cmd = self.get_finalized_command('build_scripts')
|
| 42 |
+
exec_param = getattr(bs_cmd, 'executable', None)
|
| 43 |
+
writer = ei.ScriptWriter
|
| 44 |
+
if exec_param == sys.executable:
|
| 45 |
+
# In case the path to the Python executable contains a space, wrap
|
| 46 |
+
# it so it's not split up.
|
| 47 |
+
exec_param = [exec_param]
|
| 48 |
+
# resolve the writer to the environment
|
| 49 |
+
writer = writer.best()
|
| 50 |
+
cmd = writer.command_spec_class.best().from_param(exec_param)
|
| 51 |
+
for args in writer.get_args(dist, cmd.as_header()):
|
| 52 |
+
self.write_script(*args)
|
| 53 |
+
|
| 54 |
+
def write_script(self, script_name, contents, mode="t", *ignored):
|
| 55 |
+
"""Write an executable file to the scripts directory"""
|
| 56 |
+
from setuptools.command.easy_install import chmod, current_umask
|
| 57 |
+
|
| 58 |
+
log.info("Installing %s script to %s", script_name, self.install_dir)
|
| 59 |
+
target = os.path.join(self.install_dir, script_name)
|
| 60 |
+
self.outfiles.append(target)
|
| 61 |
+
|
| 62 |
+
encoding = None if "b" in mode else "utf-8"
|
| 63 |
+
mask = current_umask()
|
| 64 |
+
if not self.dry_run:
|
| 65 |
+
ensure_directory(target)
|
| 66 |
+
with open(target, "w" + mode, encoding=encoding) as f:
|
| 67 |
+
f.write(contents)
|
| 68 |
+
chmod(target, 0o777 - mask)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/launcher manifest.xml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
| 2 |
+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
| 3 |
+
<assemblyIdentity version="1.0.0.0"
|
| 4 |
+
processorArchitecture="X86"
|
| 5 |
+
name="%(name)s"
|
| 6 |
+
type="win32"/>
|
| 7 |
+
<!-- Identify the application security requirements. -->
|
| 8 |
+
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
| 9 |
+
<security>
|
| 10 |
+
<requestedPrivileges>
|
| 11 |
+
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
| 12 |
+
</requestedPrivileges>
|
| 13 |
+
</security>
|
| 14 |
+
</trustInfo>
|
| 15 |
+
</assembly>
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/register.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from distutils import log
|
| 2 |
+
import distutils.command.register as orig
|
| 3 |
+
|
| 4 |
+
from setuptools.errors import RemovedCommandError
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class register(orig.register):
|
| 8 |
+
"""Formerly used to register packages on PyPI."""
|
| 9 |
+
|
| 10 |
+
def run(self):
|
| 11 |
+
msg = (
|
| 12 |
+
"The register command has been removed, use twine to upload "
|
| 13 |
+
"instead (https://pypi.org/p/twine)"
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
self.announce("ERROR: " + msg, log.ERROR)
|
| 17 |
+
|
| 18 |
+
raise RemovedCommandError(msg)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/rotate.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from distutils.util import convert_path
|
| 4 |
+
from distutils import log
|
| 5 |
+
from distutils.errors import DistutilsOptionError
|
| 6 |
+
import os
|
| 7 |
+
import shutil
|
| 8 |
+
|
| 9 |
+
from setuptools import Command
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class rotate(Command):
|
| 13 |
+
"""Delete older distributions"""
|
| 14 |
+
|
| 15 |
+
description = "delete older distributions, keeping N newest files"
|
| 16 |
+
user_options = [
|
| 17 |
+
('match=', 'm', "patterns to match (required)"),
|
| 18 |
+
('dist-dir=', 'd', "directory where the distributions are"),
|
| 19 |
+
('keep=', 'k', "number of matching distributions to keep"),
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
+
boolean_options: list[str] = []
|
| 23 |
+
|
| 24 |
+
def initialize_options(self):
|
| 25 |
+
self.match = None
|
| 26 |
+
self.dist_dir = None
|
| 27 |
+
self.keep = None
|
| 28 |
+
|
| 29 |
+
def finalize_options(self):
|
| 30 |
+
if self.match is None:
|
| 31 |
+
raise DistutilsOptionError(
|
| 32 |
+
"Must specify one or more (comma-separated) match patterns "
|
| 33 |
+
"(e.g. '.zip' or '.egg')"
|
| 34 |
+
)
|
| 35 |
+
if self.keep is None:
|
| 36 |
+
raise DistutilsOptionError("Must specify number of files to keep")
|
| 37 |
+
try:
|
| 38 |
+
self.keep = int(self.keep)
|
| 39 |
+
except ValueError as e:
|
| 40 |
+
raise DistutilsOptionError("--keep must be an integer") from e
|
| 41 |
+
if isinstance(self.match, str):
|
| 42 |
+
self.match = [convert_path(p.strip()) for p in self.match.split(',')]
|
| 43 |
+
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
|
| 44 |
+
|
| 45 |
+
def run(self):
|
| 46 |
+
self.run_command("egg_info")
|
| 47 |
+
from glob import glob
|
| 48 |
+
|
| 49 |
+
for pattern in self.match:
|
| 50 |
+
pattern = self.distribution.get_name() + '*' + pattern
|
| 51 |
+
files = glob(os.path.join(self.dist_dir, pattern))
|
| 52 |
+
files = [(os.path.getmtime(f), f) for f in files]
|
| 53 |
+
files.sort()
|
| 54 |
+
files.reverse()
|
| 55 |
+
|
| 56 |
+
log.info("%d file(s) matching %s", len(files), pattern)
|
| 57 |
+
files = files[self.keep :]
|
| 58 |
+
for t, f in files:
|
| 59 |
+
log.info("Deleting %s", f)
|
| 60 |
+
if not self.dry_run:
|
| 61 |
+
if os.path.isdir(f):
|
| 62 |
+
shutil.rmtree(f)
|
| 63 |
+
else:
|
| 64 |
+
os.unlink(f)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/sdist.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from distutils import log
|
| 2 |
+
import distutils.command.sdist as orig
|
| 3 |
+
import os
|
| 4 |
+
import contextlib
|
| 5 |
+
from itertools import chain
|
| 6 |
+
|
| 7 |
+
from .._importlib import metadata
|
| 8 |
+
from .build import _ORIGINAL_SUBCOMMANDS
|
| 9 |
+
|
| 10 |
+
_default_revctrl = list
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def walk_revctrl(dirname=''):
|
| 14 |
+
"""Find all files under revision control"""
|
| 15 |
+
for ep in metadata.entry_points(group='setuptools.file_finders'):
|
| 16 |
+
yield from ep.load()(dirname)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class sdist(orig.sdist):
|
| 20 |
+
"""Smart sdist that finds anything supported by revision control"""
|
| 21 |
+
|
| 22 |
+
user_options = [
|
| 23 |
+
('formats=', None, "formats for source distribution (comma-separated list)"),
|
| 24 |
+
(
|
| 25 |
+
'keep-temp',
|
| 26 |
+
'k',
|
| 27 |
+
"keep the distribution tree around after creating " + "archive file(s)",
|
| 28 |
+
),
|
| 29 |
+
(
|
| 30 |
+
'dist-dir=',
|
| 31 |
+
'd',
|
| 32 |
+
"directory to put the source distribution archive(s) in [default: dist]",
|
| 33 |
+
),
|
| 34 |
+
(
|
| 35 |
+
'owner=',
|
| 36 |
+
'u',
|
| 37 |
+
"Owner name used when creating a tar file [default: current user]",
|
| 38 |
+
),
|
| 39 |
+
(
|
| 40 |
+
'group=',
|
| 41 |
+
'g',
|
| 42 |
+
"Group name used when creating a tar file [default: current group]",
|
| 43 |
+
),
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
negative_opt = {}
|
| 47 |
+
|
| 48 |
+
README_EXTENSIONS = ['', '.rst', '.txt', '.md']
|
| 49 |
+
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
|
| 50 |
+
|
| 51 |
+
def run(self):
|
| 52 |
+
self.run_command('egg_info')
|
| 53 |
+
ei_cmd = self.get_finalized_command('egg_info')
|
| 54 |
+
self.filelist = ei_cmd.filelist
|
| 55 |
+
self.filelist.append(os.path.join(ei_cmd.egg_info, 'SOURCES.txt'))
|
| 56 |
+
self.check_readme()
|
| 57 |
+
|
| 58 |
+
# Run sub commands
|
| 59 |
+
for cmd_name in self.get_sub_commands():
|
| 60 |
+
self.run_command(cmd_name)
|
| 61 |
+
|
| 62 |
+
self.make_distribution()
|
| 63 |
+
|
| 64 |
+
dist_files = getattr(self.distribution, 'dist_files', [])
|
| 65 |
+
for file in self.archive_files:
|
| 66 |
+
data = ('sdist', '', file)
|
| 67 |
+
if data not in dist_files:
|
| 68 |
+
dist_files.append(data)
|
| 69 |
+
|
| 70 |
+
def initialize_options(self):
|
| 71 |
+
orig.sdist.initialize_options(self)
|
| 72 |
+
|
| 73 |
+
def make_distribution(self):
|
| 74 |
+
"""
|
| 75 |
+
Workaround for #516
|
| 76 |
+
"""
|
| 77 |
+
with self._remove_os_link():
|
| 78 |
+
orig.sdist.make_distribution(self)
|
| 79 |
+
|
| 80 |
+
@staticmethod
|
| 81 |
+
@contextlib.contextmanager
|
| 82 |
+
def _remove_os_link():
|
| 83 |
+
"""
|
| 84 |
+
In a context, remove and restore os.link if it exists
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
class NoValue:
|
| 88 |
+
pass
|
| 89 |
+
|
| 90 |
+
orig_val = getattr(os, 'link', NoValue)
|
| 91 |
+
try:
|
| 92 |
+
del os.link
|
| 93 |
+
except Exception:
|
| 94 |
+
pass
|
| 95 |
+
try:
|
| 96 |
+
yield
|
| 97 |
+
finally:
|
| 98 |
+
if orig_val is not NoValue:
|
| 99 |
+
os.link = orig_val
|
| 100 |
+
|
| 101 |
+
def add_defaults(self):
|
| 102 |
+
super().add_defaults()
|
| 103 |
+
self._add_defaults_build_sub_commands()
|
| 104 |
+
|
| 105 |
+
def _add_defaults_optional(self):
|
| 106 |
+
super()._add_defaults_optional()
|
| 107 |
+
if os.path.isfile('pyproject.toml'):
|
| 108 |
+
self.filelist.append('pyproject.toml')
|
| 109 |
+
|
| 110 |
+
def _add_defaults_python(self):
|
| 111 |
+
"""getting python files"""
|
| 112 |
+
if self.distribution.has_pure_modules():
|
| 113 |
+
build_py = self.get_finalized_command('build_py')
|
| 114 |
+
self.filelist.extend(build_py.get_source_files())
|
| 115 |
+
self._add_data_files(self._safe_data_files(build_py))
|
| 116 |
+
|
| 117 |
+
def _add_defaults_build_sub_commands(self):
|
| 118 |
+
build = self.get_finalized_command("build")
|
| 119 |
+
missing_cmds = set(build.get_sub_commands()) - _ORIGINAL_SUBCOMMANDS
|
| 120 |
+
# ^-- the original built-in sub-commands are already handled by default.
|
| 121 |
+
cmds = (self.get_finalized_command(c) for c in missing_cmds)
|
| 122 |
+
files = (c.get_source_files() for c in cmds if hasattr(c, "get_source_files"))
|
| 123 |
+
self.filelist.extend(chain.from_iterable(files))
|
| 124 |
+
|
| 125 |
+
def _safe_data_files(self, build_py):
|
| 126 |
+
"""
|
| 127 |
+
Since the ``sdist`` class is also used to compute the MANIFEST
|
| 128 |
+
(via :obj:`setuptools.command.egg_info.manifest_maker`),
|
| 129 |
+
there might be recursion problems when trying to obtain the list of
|
| 130 |
+
data_files and ``include_package_data=True`` (which in turn depends on
|
| 131 |
+
the files included in the MANIFEST).
|
| 132 |
+
|
| 133 |
+
To avoid that, ``manifest_maker`` should be able to overwrite this
|
| 134 |
+
method and avoid recursive attempts to build/analyze the MANIFEST.
|
| 135 |
+
"""
|
| 136 |
+
return build_py.data_files
|
| 137 |
+
|
| 138 |
+
def _add_data_files(self, data_files):
|
| 139 |
+
"""
|
| 140 |
+
Add data files as found in build_py.data_files.
|
| 141 |
+
"""
|
| 142 |
+
self.filelist.extend(
|
| 143 |
+
os.path.join(src_dir, name)
|
| 144 |
+
for _, src_dir, _, filenames in data_files
|
| 145 |
+
for name in filenames
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
def _add_defaults_data_files(self):
|
| 149 |
+
try:
|
| 150 |
+
super()._add_defaults_data_files()
|
| 151 |
+
except TypeError:
|
| 152 |
+
log.warn("data_files contains unexpected objects")
|
| 153 |
+
|
| 154 |
+
def check_readme(self):
|
| 155 |
+
for f in self.READMES:
|
| 156 |
+
if os.path.exists(f):
|
| 157 |
+
return
|
| 158 |
+
else:
|
| 159 |
+
self.warn(
|
| 160 |
+
"standard file not found: should have one of " + ', '.join(self.READMES)
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
def make_release_tree(self, base_dir, files):
|
| 164 |
+
orig.sdist.make_release_tree(self, base_dir, files)
|
| 165 |
+
|
| 166 |
+
# Save any egg_info command line options used to create this sdist
|
| 167 |
+
dest = os.path.join(base_dir, 'setup.cfg')
|
| 168 |
+
if hasattr(os, 'link') and os.path.exists(dest):
|
| 169 |
+
# unlink and re-copy, since it might be hard-linked, and
|
| 170 |
+
# we don't want to change the source version
|
| 171 |
+
os.unlink(dest)
|
| 172 |
+
self.copy_file('setup.cfg', dest)
|
| 173 |
+
|
| 174 |
+
self.get_finalized_command('egg_info').save_version_info(dest)
|
| 175 |
+
|
| 176 |
+
def _manifest_is_not_generated(self):
|
| 177 |
+
# check for special comment used in 2.7.1 and higher
|
| 178 |
+
if not os.path.isfile(self.manifest):
|
| 179 |
+
return False
|
| 180 |
+
|
| 181 |
+
with open(self.manifest, 'rb') as fp:
|
| 182 |
+
first_line = fp.readline()
|
| 183 |
+
return first_line != b'# file GENERATED by distutils, do NOT edit\n'
|
| 184 |
+
|
| 185 |
+
def read_manifest(self):
|
| 186 |
+
"""Read the manifest file (named by 'self.manifest') and use it to
|
| 187 |
+
fill in 'self.filelist', the list of files to include in the source
|
| 188 |
+
distribution.
|
| 189 |
+
"""
|
| 190 |
+
log.info("reading manifest file '%s'", self.manifest)
|
| 191 |
+
manifest = open(self.manifest, 'rb')
|
| 192 |
+
for line in manifest:
|
| 193 |
+
# The manifest must contain UTF-8. See #303.
|
| 194 |
+
try:
|
| 195 |
+
line = line.decode('UTF-8')
|
| 196 |
+
except UnicodeDecodeError:
|
| 197 |
+
log.warn("%r not UTF-8 decodable -- skipping" % line)
|
| 198 |
+
continue
|
| 199 |
+
# ignore comments and blank lines
|
| 200 |
+
line = line.strip()
|
| 201 |
+
if line.startswith('#') or not line:
|
| 202 |
+
continue
|
| 203 |
+
self.filelist.append(line)
|
| 204 |
+
manifest.close()
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/setopt.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from distutils.util import convert_path
|
| 2 |
+
from distutils import log
|
| 3 |
+
from distutils.errors import DistutilsOptionError
|
| 4 |
+
import distutils
|
| 5 |
+
import os
|
| 6 |
+
import configparser
|
| 7 |
+
|
| 8 |
+
from .. import Command
|
| 9 |
+
from ..unicode_utils import _cfg_read_utf8_with_fallback
|
| 10 |
+
|
| 11 |
+
__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def config_file(kind="local"):
|
| 15 |
+
"""Get the filename of the distutils, local, global, or per-user config
|
| 16 |
+
|
| 17 |
+
`kind` must be one of "local", "global", or "user"
|
| 18 |
+
"""
|
| 19 |
+
if kind == 'local':
|
| 20 |
+
return 'setup.cfg'
|
| 21 |
+
if kind == 'global':
|
| 22 |
+
return os.path.join(os.path.dirname(distutils.__file__), 'distutils.cfg')
|
| 23 |
+
if kind == 'user':
|
| 24 |
+
dot = os.name == 'posix' and '.' or ''
|
| 25 |
+
return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot))
|
| 26 |
+
raise ValueError("config_file() type must be 'local', 'global', or 'user'", kind)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def edit_config(filename, settings, dry_run=False):
|
| 30 |
+
"""Edit a configuration file to include `settings`
|
| 31 |
+
|
| 32 |
+
`settings` is a dictionary of dictionaries or ``None`` values, keyed by
|
| 33 |
+
command/section name. A ``None`` value means to delete the entire section,
|
| 34 |
+
while a dictionary lists settings to be changed or deleted in that section.
|
| 35 |
+
A setting of ``None`` means to delete that setting.
|
| 36 |
+
"""
|
| 37 |
+
log.debug("Reading configuration from %s", filename)
|
| 38 |
+
opts = configparser.RawConfigParser()
|
| 39 |
+
opts.optionxform = lambda x: x
|
| 40 |
+
_cfg_read_utf8_with_fallback(opts, filename)
|
| 41 |
+
|
| 42 |
+
for section, options in settings.items():
|
| 43 |
+
if options is None:
|
| 44 |
+
log.info("Deleting section [%s] from %s", section, filename)
|
| 45 |
+
opts.remove_section(section)
|
| 46 |
+
else:
|
| 47 |
+
if not opts.has_section(section):
|
| 48 |
+
log.debug("Adding new section [%s] to %s", section, filename)
|
| 49 |
+
opts.add_section(section)
|
| 50 |
+
for option, value in options.items():
|
| 51 |
+
if value is None:
|
| 52 |
+
log.debug("Deleting %s.%s from %s", section, option, filename)
|
| 53 |
+
opts.remove_option(section, option)
|
| 54 |
+
if not opts.options(section):
|
| 55 |
+
log.info(
|
| 56 |
+
"Deleting empty [%s] section from %s", section, filename
|
| 57 |
+
)
|
| 58 |
+
opts.remove_section(section)
|
| 59 |
+
else:
|
| 60 |
+
log.debug(
|
| 61 |
+
"Setting %s.%s to %r in %s", section, option, value, filename
|
| 62 |
+
)
|
| 63 |
+
opts.set(section, option, value)
|
| 64 |
+
|
| 65 |
+
log.info("Writing %s", filename)
|
| 66 |
+
if not dry_run:
|
| 67 |
+
with open(filename, 'w', encoding="utf-8") as f:
|
| 68 |
+
opts.write(f)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class option_base(Command):
|
| 72 |
+
"""Abstract base class for commands that mess with config files"""
|
| 73 |
+
|
| 74 |
+
user_options = [
|
| 75 |
+
('global-config', 'g', "save options to the site-wide distutils.cfg file"),
|
| 76 |
+
('user-config', 'u', "save options to the current user's pydistutils.cfg file"),
|
| 77 |
+
('filename=', 'f', "configuration file to use (default=setup.cfg)"),
|
| 78 |
+
]
|
| 79 |
+
|
| 80 |
+
boolean_options = [
|
| 81 |
+
'global-config',
|
| 82 |
+
'user-config',
|
| 83 |
+
]
|
| 84 |
+
|
| 85 |
+
def initialize_options(self):
|
| 86 |
+
self.global_config = None
|
| 87 |
+
self.user_config = None
|
| 88 |
+
self.filename = None
|
| 89 |
+
|
| 90 |
+
def finalize_options(self):
|
| 91 |
+
filenames = []
|
| 92 |
+
if self.global_config:
|
| 93 |
+
filenames.append(config_file('global'))
|
| 94 |
+
if self.user_config:
|
| 95 |
+
filenames.append(config_file('user'))
|
| 96 |
+
if self.filename is not None:
|
| 97 |
+
filenames.append(self.filename)
|
| 98 |
+
if not filenames:
|
| 99 |
+
filenames.append(config_file('local'))
|
| 100 |
+
if len(filenames) > 1:
|
| 101 |
+
raise DistutilsOptionError(
|
| 102 |
+
"Must specify only one configuration file option", filenames
|
| 103 |
+
)
|
| 104 |
+
(self.filename,) = filenames
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class setopt(option_base):
|
| 108 |
+
"""Save command-line options to a file"""
|
| 109 |
+
|
| 110 |
+
description = "set an option in setup.cfg or another config file"
|
| 111 |
+
|
| 112 |
+
user_options = [
|
| 113 |
+
('command=', 'c', 'command to set an option for'),
|
| 114 |
+
('option=', 'o', 'option to set'),
|
| 115 |
+
('set-value=', 's', 'value of the option'),
|
| 116 |
+
('remove', 'r', 'remove (unset) the value'),
|
| 117 |
+
] + option_base.user_options
|
| 118 |
+
|
| 119 |
+
boolean_options = option_base.boolean_options + ['remove']
|
| 120 |
+
|
| 121 |
+
def initialize_options(self):
|
| 122 |
+
option_base.initialize_options(self)
|
| 123 |
+
self.command = None
|
| 124 |
+
self.option = None
|
| 125 |
+
self.set_value = None
|
| 126 |
+
self.remove = None
|
| 127 |
+
|
| 128 |
+
def finalize_options(self):
|
| 129 |
+
option_base.finalize_options(self)
|
| 130 |
+
if self.command is None or self.option is None:
|
| 131 |
+
raise DistutilsOptionError("Must specify --command *and* --option")
|
| 132 |
+
if self.set_value is None and not self.remove:
|
| 133 |
+
raise DistutilsOptionError("Must specify --set-value or --remove")
|
| 134 |
+
|
| 135 |
+
def run(self):
|
| 136 |
+
edit_config(
|
| 137 |
+
self.filename,
|
| 138 |
+
{self.command: {self.option.replace('-', '_'): self.set_value}},
|
| 139 |
+
self.dry_run,
|
| 140 |
+
)
|
URSA/.venv_ursa/lib/python3.12/site-packages/setuptools/command/test.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import operator
|
| 3 |
+
import sys
|
| 4 |
+
import contextlib
|
| 5 |
+
import itertools
|
| 6 |
+
import unittest
|
| 7 |
+
from distutils.errors import DistutilsError, DistutilsOptionError
|
| 8 |
+
from distutils import log
|
| 9 |
+
from unittest import TestLoader
|
| 10 |
+
|
| 11 |
+
from pkg_resources import (
|
| 12 |
+
resource_listdir,
|
| 13 |
+
resource_exists,
|
| 14 |
+
normalize_path,
|
| 15 |
+
working_set,
|
| 16 |
+
evaluate_marker,
|
| 17 |
+
add_activation_listener,
|
| 18 |
+
require,
|
| 19 |
+
)
|
| 20 |
+
from .._importlib import metadata
|
| 21 |
+
from setuptools import Command
|
| 22 |
+
from setuptools.extern.more_itertools import unique_everseen
|
| 23 |
+
from setuptools.extern.jaraco.functools import pass_none
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class ScanningLoader(TestLoader):
|
| 27 |
+
def __init__(self):
|
| 28 |
+
TestLoader.__init__(self)
|
| 29 |
+
self._visited = set()
|
| 30 |
+
|
| 31 |
+
def loadTestsFromModule(self, module, pattern=None):
|
| 32 |
+
"""Return a suite of all tests cases contained in the given module
|
| 33 |
+
|
| 34 |
+
If the module is a package, load tests from all the modules in it.
|
| 35 |
+
If the module has an ``additional_tests`` function, call it and add
|
| 36 |
+
the return value to the tests.
|
| 37 |
+
"""
|
| 38 |
+
if module in self._visited:
|
| 39 |
+
return None
|
| 40 |
+
self._visited.add(module)
|
| 41 |
+
|
| 42 |
+
tests = []
|
| 43 |
+
tests.append(TestLoader.loadTestsFromModule(self, module))
|
| 44 |
+
|
| 45 |
+
if hasattr(module, "additional_tests"):
|
| 46 |
+
tests.append(module.additional_tests())
|
| 47 |
+
|
| 48 |
+
if hasattr(module, '__path__'):
|
| 49 |
+
for file in resource_listdir(module.__name__, ''):
|
| 50 |
+
if file.endswith('.py') and file != '__init__.py':
|
| 51 |
+
submodule = module.__name__ + '.' + file[:-3]
|
| 52 |
+
else:
|
| 53 |
+
if resource_exists(module.__name__, file + '/__init__.py'):
|
| 54 |
+
submodule = module.__name__ + '.' + file
|
| 55 |
+
else:
|
| 56 |
+
continue
|
| 57 |
+
tests.append(self.loadTestsFromName(submodule))
|
| 58 |
+
|
| 59 |
+
if len(tests) != 1:
|
| 60 |
+
return self.suiteClass(tests)
|
| 61 |
+
else:
|
| 62 |
+
return tests[0] # don't create a nested suite for only one return
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
# adapted from jaraco.classes.properties:NonDataProperty
|
| 66 |
+
class NonDataProperty:
|
| 67 |
+
def __init__(self, fget):
|
| 68 |
+
self.fget = fget
|
| 69 |
+
|
| 70 |
+
def __get__(self, obj, objtype=None):
|
| 71 |
+
if obj is None:
|
| 72 |
+
return self
|
| 73 |
+
return self.fget(obj)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class test(Command):
|
| 77 |
+
"""Command to run unit tests after in-place build"""
|
| 78 |
+
|
| 79 |
+
description = "run unit tests after in-place build (deprecated)"
|
| 80 |
+
|
| 81 |
+
user_options = [
|
| 82 |
+
('test-module=', 'm', "Run 'test_suite' in specified module"),
|
| 83 |
+
(
|
| 84 |
+
'test-suite=',
|
| 85 |
+
's',
|
| 86 |
+
"Run single test, case or suite (e.g. 'module.test_suite')",
|
| 87 |
+
),
|
| 88 |
+
('test-runner=', 'r', "Test runner to use"),
|
| 89 |
+
]
|
| 90 |
+
|
| 91 |
+
def initialize_options(self):
|
| 92 |
+
self.test_suite = None
|
| 93 |
+
self.test_module = None
|
| 94 |
+
self.test_loader = None
|
| 95 |
+
self.test_runner = None
|
| 96 |
+
|
| 97 |
+
def finalize_options(self):
|
| 98 |
+
if self.test_suite and self.test_module:
|
| 99 |
+
msg = "You may specify a module or a suite, but not both"
|
| 100 |
+
raise DistutilsOptionError(msg)
|
| 101 |
+
|
| 102 |
+
if self.test_suite is None:
|
| 103 |
+
if self.test_module is None:
|
| 104 |
+
self.test_suite = self.distribution.test_suite
|
| 105 |
+
else:
|
| 106 |
+
self.test_suite = self.test_module + ".test_suite"
|
| 107 |
+
|
| 108 |
+
if self.test_loader is None:
|
| 109 |
+
self.test_loader = getattr(self.distribution, 'test_loader', None)
|
| 110 |
+
if self.test_loader is None:
|
| 111 |
+
self.test_loader = "setuptools.command.test:ScanningLoader"
|
| 112 |
+
if self.test_runner is None:
|
| 113 |
+
self.test_runner = getattr(self.distribution, 'test_runner', None)
|
| 114 |
+
|
| 115 |
+
@NonDataProperty
|
| 116 |
+
def test_args(self):
|
| 117 |
+
return list(self._test_args())
|
| 118 |
+
|
| 119 |
+
def _test_args(self):
|
| 120 |
+
if not self.test_suite:
|
| 121 |
+
yield 'discover'
|
| 122 |
+
if self.verbose:
|
| 123 |
+
yield '--verbose'
|
| 124 |
+
if self.test_suite:
|
| 125 |
+
yield self.test_suite
|
| 126 |
+
|
| 127 |
+
def with_project_on_sys_path(self, func):
|
| 128 |
+
"""
|
| 129 |
+
Backward compatibility for project_on_sys_path context.
|
| 130 |
+
"""
|
| 131 |
+
with self.project_on_sys_path():
|
| 132 |
+
func()
|
| 133 |
+
|
| 134 |
+
@contextlib.contextmanager
|
| 135 |
+
def project_on_sys_path(self, include_dists=()):
|
| 136 |
+
self.run_command('egg_info')
|
| 137 |
+
|
| 138 |
+
# Build extensions in-place
|
| 139 |
+
self.reinitialize_command('build_ext', inplace=True)
|
| 140 |
+
self.run_command('build_ext')
|
| 141 |
+
|
| 142 |
+
ei_cmd = self.get_finalized_command("egg_info")
|
| 143 |
+
|
| 144 |
+
old_path = sys.path[:]
|
| 145 |
+
old_modules = sys.modules.copy()
|
| 146 |
+
|
| 147 |
+
try:
|
| 148 |
+
project_path = normalize_path(ei_cmd.egg_base)
|
| 149 |
+
sys.path.insert(0, project_path)
|
| 150 |
+
working_set.__init__()
|
| 151 |
+
add_activation_listener(lambda dist: dist.activate())
|
| 152 |
+
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
|
| 153 |
+
with self.paths_on_pythonpath([project_path]):
|
| 154 |
+
yield
|
| 155 |
+
finally:
|
| 156 |
+
sys.path[:] = old_path
|
| 157 |
+
sys.modules.clear()
|
| 158 |
+
sys.modules.update(old_modules)
|
| 159 |
+
working_set.__init__()
|
| 160 |
+
|
| 161 |
+
@staticmethod
|
| 162 |
+
@contextlib.contextmanager
|
| 163 |
+
def paths_on_pythonpath(paths):
|
| 164 |
+
"""
|
| 165 |
+
Add the indicated paths to the head of the PYTHONPATH environment
|
| 166 |
+
variable so that subprocesses will also see the packages at
|
| 167 |
+
these paths.
|
| 168 |
+
|
| 169 |
+
Do this in a context that restores the value on exit.
|
| 170 |
+
"""
|
| 171 |
+
nothing = object()
|
| 172 |
+
orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
|
| 173 |
+
current_pythonpath = os.environ.get('PYTHONPATH', '')
|
| 174 |
+
try:
|
| 175 |
+
prefix = os.pathsep.join(unique_everseen(paths))
|
| 176 |
+
to_join = filter(None, [prefix, current_pythonpath])
|
| 177 |
+
new_path = os.pathsep.join(to_join)
|
| 178 |
+
if new_path:
|
| 179 |
+
os.environ['PYTHONPATH'] = new_path
|
| 180 |
+
yield
|
| 181 |
+
finally:
|
| 182 |
+
if orig_pythonpath is nothing:
|
| 183 |
+
os.environ.pop('PYTHONPATH', None)
|
| 184 |
+
else:
|
| 185 |
+
os.environ['PYTHONPATH'] = orig_pythonpath
|
| 186 |
+
|
| 187 |
+
@staticmethod
|
| 188 |
+
def install_dists(dist):
|
| 189 |
+
"""
|
| 190 |
+
Install the requirements indicated by self.distribution and
|
| 191 |
+
return an iterable of the dists that were built.
|
| 192 |
+
"""
|
| 193 |
+
ir_d = dist.fetch_build_eggs(dist.install_requires)
|
| 194 |
+
tr_d = dist.fetch_build_eggs(dist.tests_require or [])
|
| 195 |
+
er_d = dist.fetch_build_eggs(
|
| 196 |
+
v
|
| 197 |
+
for k, v in dist.extras_require.items()
|
| 198 |
+
if k.startswith(':') and evaluate_marker(k[1:])
|
| 199 |
+
)
|
| 200 |
+
return itertools.chain(ir_d, tr_d, er_d)
|
| 201 |
+
|
| 202 |
+
def run(self):
|
| 203 |
+
self.announce(
|
| 204 |
+
"WARNING: Testing via this command is deprecated and will be "
|
| 205 |
+
"removed in a future version. Users looking for a generic test "
|
| 206 |
+
"entry point independent of test runner are encouraged to use "
|
| 207 |
+
"tox.",
|
| 208 |
+
log.WARN,
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
installed_dists = self.install_dists(self.distribution)
|
| 212 |
+
|
| 213 |
+
cmd = ' '.join(self._argv)
|
| 214 |
+
if self.dry_run:
|
| 215 |
+
self.announce('skipping "%s" (dry run)' % cmd)
|
| 216 |
+
return
|
| 217 |
+
|
| 218 |
+
self.announce('running "%s"' % cmd)
|
| 219 |
+
|
| 220 |
+
paths = map(operator.attrgetter('location'), installed_dists)
|
| 221 |
+
with self.paths_on_pythonpath(paths):
|
| 222 |
+
with self.project_on_sys_path():
|
| 223 |
+
self.run_tests()
|
| 224 |
+
|
| 225 |
+
def run_tests(self):
|
| 226 |
+
test = unittest.main(
|
| 227 |
+
None,
|
| 228 |
+
None,
|
| 229 |
+
self._argv,
|
| 230 |
+
testLoader=self._resolve_as_ep(self.test_loader),
|
| 231 |
+
testRunner=self._resolve_as_ep(self.test_runner),
|
| 232 |
+
exit=False,
|
| 233 |
+
)
|
| 234 |
+
if not test.result.wasSuccessful():
|
| 235 |
+
msg = 'Test failed: %s' % test.result
|
| 236 |
+
self.announce(msg, log.ERROR)
|
| 237 |
+
raise DistutilsError(msg)
|
| 238 |
+
|
| 239 |
+
@property
|
| 240 |
+
def _argv(self):
|
| 241 |
+
return ['unittest'] + self.test_args
|
| 242 |
+
|
| 243 |
+
@staticmethod
|
| 244 |
+
@pass_none
|
| 245 |
+
def _resolve_as_ep(val):
|
| 246 |
+
"""
|
| 247 |
+
Load the indicated attribute value, called, as a as if it were
|
| 248 |
+
specified as an entry point.
|
| 249 |
+
"""
|
| 250 |
+
return metadata.EntryPoint(value=val, name=None, group=None).load()()
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/__autotune_main__.cpython-312.pyc
ADDED
|
Binary file (1.87 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (15 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/aoti_eager.cpython-312.pyc
ADDED
|
Binary file (13.9 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/autotune_process.cpython-312.pyc
ADDED
|
Binary file (47.7 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/await_utils.cpython-312.pyc
ADDED
|
Binary file (8.25 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/bounds.cpython-312.pyc
ADDED
|
Binary file (14.3 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/cache.cpython-312.pyc
ADDED
|
Binary file (17.3 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comm_analysis.cpython-312.pyc
ADDED
|
Binary file (18.8 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comm_lowering.cpython-312.pyc
ADDED
|
Binary file (16.4 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comms.cpython-312.pyc
ADDED
|
Binary file (92.5 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/comms_debug.cpython-312.pyc
ADDED
|
Binary file (4.66 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/compile_fx_subproc.cpython-312.pyc
ADDED
|
Binary file (2.99 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/config_comms.cpython-312.pyc
ADDED
|
Binary file (2.17 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/constant_folding.cpython-312.pyc
ADDED
|
Binary file (21.3 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/cpp_builder.cpython-312.pyc
ADDED
|
Binary file (97.3 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/cpu_vec_isa.cpython-312.pyc
ADDED
|
Binary file (21.8 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/custom_graph_pass.cpython-312.pyc
ADDED
|
Binary file (7.97 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/debug.cpython-312.pyc
ADDED
|
Binary file (58.9 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/decomposition.cpython-312.pyc
ADDED
|
Binary file (59.2 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/dependencies.cpython-312.pyc
ADDED
|
Binary file (46.7 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/distributed_autotune.cpython-312.pyc
ADDED
|
Binary file (15.5 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/exc.cpython-312.pyc
ADDED
|
Binary file (8.89 kB). View file
|
|
|
URSA/.venv_ursa/lib/python3.12/site-packages/torch/_inductor/__pycache__/extern_node_serializer.cpython-312.pyc
ADDED
|
Binary file (1.4 kB). View file
|
|
|