nab-python 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nab_python/__init__.py +1 -0
- nab_python/_build/__init__.py +1 -0
- nab_python/_build/env.py +364 -0
- nab_python/_build/errors.py +17 -0
- nab_python/_build/runner.py +254 -0
- nab_python/_lockfile/__init__.py +1 -0
- nab_python/_lockfile/builder.py +339 -0
- nab_python/_lockfile/disjointness.py +207 -0
- nab_python/_lockfile/pylock.py +323 -0
- nab_python/_lockfile/requirements.py +121 -0
- nab_python/_packaging_provider.py +98 -0
- nab_python/_provider/__init__.py +1 -0
- nab_python/_provider/build_remote.py +95 -0
- nab_python/_provider/extras.py +231 -0
- nab_python/_provider/listing.py +442 -0
- nab_python/_provider/lookahead.py +156 -0
- nab_python/_provider/metadata_resolver.py +450 -0
- nab_python/_provider/priority.py +174 -0
- nab_python/_provider/sources.py +215 -0
- nab_python/_testing/__init__.py +1 -0
- nab_python/_testing/coordinator_fake.py +240 -0
- nab_python/_vcs_admission.py +209 -0
- nab_python/_vendor/__init__.py +6 -0
- nab_python/_vendor/packaging/LICENSE +3 -0
- nab_python/_vendor/packaging/LICENSE.APACHE +177 -0
- nab_python/_vendor/packaging/LICENSE.BSD +23 -0
- nab_python/_vendor/packaging/PROVENANCE.md +73 -0
- nab_python/_vendor/packaging/__init__.py +15 -0
- nab_python/_vendor/packaging/_elffile.py +108 -0
- nab_python/_vendor/packaging/_manylinux.py +265 -0
- nab_python/_vendor/packaging/_musllinux.py +88 -0
- nab_python/_vendor/packaging/_parser.py +394 -0
- nab_python/_vendor/packaging/_structures.py +33 -0
- nab_python/_vendor/packaging/_tokenizer.py +196 -0
- nab_python/_vendor/packaging/dependency_groups.py +302 -0
- nab_python/_vendor/packaging/direct_url.py +325 -0
- nab_python/_vendor/packaging/errors.py +94 -0
- nab_python/_vendor/packaging/licenses/__init__.py +186 -0
- nab_python/_vendor/packaging/licenses/_spdx.py +799 -0
- nab_python/_vendor/packaging/markers.py +506 -0
- nab_python/_vendor/packaging/metadata.py +964 -0
- nab_python/_vendor/packaging/py.typed +0 -0
- nab_python/_vendor/packaging/pylock.py +910 -0
- nab_python/_vendor/packaging/ranges.py +1803 -0
- nab_python/_vendor/packaging/requirements.py +132 -0
- nab_python/_vendor/packaging/specifiers.py +1141 -0
- nab_python/_vendor/packaging/tags.py +929 -0
- nab_python/_vendor/packaging/utils.py +296 -0
- nab_python/_vendor/packaging/version.py +1230 -0
- nab_python/build_backend.py +184 -0
- nab_python/config.py +805 -0
- nab_python/download.py +170 -0
- nab_python/fetch.py +827 -0
- nab_python/lockfile.py +238 -0
- nab_python/metadata.py +145 -0
- nab_python/provider.py +1235 -0
- nab_python/py.typed +0 -0
- nab_python/requirements_file.py +180 -0
- nab_python/resolve.py +497 -0
- nab_python/universal/__init__.py +1 -0
- nab_python/universal/matrix.py +235 -0
- nab_python/universal/provider.py +214 -0
- nab_python/universal/reresolve.py +310 -0
- nab_python/universal/resolve.py +508 -0
- nab_python/universal/validate.py +439 -0
- nab_python/universal/wheel_selection.py +327 -0
- nab_python/workspace.py +214 -0
- nab_python-0.0.1.dist-info/METADATA +49 -0
- nab_python-0.0.1.dist-info/RECORD +71 -0
- nab_python-0.0.1.dist-info/WHEEL +4 -0
- nab_python-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Metadata extraction for source trees.
|
|
2
|
+
|
|
3
|
+
Hand a source directory to :func:`extract_metadata` and get back a
|
|
4
|
+
:class:`WheelMetadata`. The static pyproject.toml reader runs first;
|
|
5
|
+
dynamic ``project.dependencies`` fall through to a PEP 517 backend
|
|
6
|
+
invocation inside :class:`~nab_python._build.env.NabBuildEnv`. The
|
|
7
|
+
dynamic path needs a :class:`NabProjectConfig`.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from functools import lru_cache
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
from ._build.errors import (
|
|
17
|
+
BuildBackendError as BuildBackendError, # noqa: PLC0414 (public re-export)
|
|
18
|
+
)
|
|
19
|
+
from ._vendor.packaging.requirements import Requirement
|
|
20
|
+
from ._vendor.packaging.specifiers import SpecifierSet
|
|
21
|
+
from ._vendor.packaging.utils import canonicalize_name
|
|
22
|
+
from ._vendor.packaging.version import InvalidVersion, Version
|
|
23
|
+
from .metadata import WheelMetadata, load_static_project
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
from .config import NabProjectConfig
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"BuildBackendError",
|
|
32
|
+
"extract_metadata",
|
|
33
|
+
"extract_static_metadata",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@lru_cache(maxsize=4096)
|
|
41
|
+
def extract_static_metadata(source_dir: Path) -> WheelMetadata | None:
|
|
42
|
+
"""Build :class:`WheelMetadata` from a directory's static pyproject.toml.
|
|
43
|
+
|
|
44
|
+
Returns ``None`` when ``[project]`` is missing, malformed, or
|
|
45
|
+
``project.dynamic`` includes ``dependencies`` /
|
|
46
|
+
``optional-dependencies`` (the field cannot be trusted as static).
|
|
47
|
+
Returns a :class:`WheelMetadata` shape when the static fields are
|
|
48
|
+
authoritative, populating ``name``, ``version``,
|
|
49
|
+
``requires_python``, ``requires_dist``, and ``provides_extra``.
|
|
50
|
+
|
|
51
|
+
The returned ``provides_extra`` includes both the lower-cased
|
|
52
|
+
keys of ``project.optional-dependencies`` and any extras declared
|
|
53
|
+
via PEP 685 markers in ``requires-dist``. PEP 685 normalisation
|
|
54
|
+
is applied to all extra names.
|
|
55
|
+
|
|
56
|
+
Cached per ``source_dir``: the static fields are deterministic
|
|
57
|
+
derivations of the on-disk ``pyproject.toml``, and a universal
|
|
58
|
+
resolve reads the same workspace member's file once per matrix
|
|
59
|
+
tuple. Callers must treat the returned :class:`WheelMetadata`
|
|
60
|
+
as read-only.
|
|
61
|
+
"""
|
|
62
|
+
pyproject = source_dir / "pyproject.toml"
|
|
63
|
+
if not pyproject.is_file():
|
|
64
|
+
return None
|
|
65
|
+
try:
|
|
66
|
+
text = pyproject.read_text(encoding="utf-8")
|
|
67
|
+
except OSError:
|
|
68
|
+
# ``is_file`` is racy: the file may vanish or become unreadable
|
|
69
|
+
# between the check and the read. Treat it the same as missing.
|
|
70
|
+
return None
|
|
71
|
+
project = load_static_project(text)
|
|
72
|
+
if project is None:
|
|
73
|
+
return None
|
|
74
|
+
return _project_to_metadata(project)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _project_to_metadata(project: dict) -> WheelMetadata | None:
|
|
78
|
+
"""Convert a static ``[project]`` table to :class:`WheelMetadata`."""
|
|
79
|
+
name = project.get("name")
|
|
80
|
+
version_raw = project.get("version")
|
|
81
|
+
if not isinstance(name, str) or not isinstance(version_raw, str):
|
|
82
|
+
return None
|
|
83
|
+
try:
|
|
84
|
+
version = Version(version_raw)
|
|
85
|
+
except InvalidVersion:
|
|
86
|
+
return None
|
|
87
|
+
requires_python_raw = project.get("requires-python")
|
|
88
|
+
requires_python = (
|
|
89
|
+
SpecifierSet(requires_python_raw)
|
|
90
|
+
if isinstance(requires_python_raw, str)
|
|
91
|
+
else None
|
|
92
|
+
)
|
|
93
|
+
return WheelMetadata(
|
|
94
|
+
name=canonicalize_name(name),
|
|
95
|
+
version=version,
|
|
96
|
+
requires_python=requires_python,
|
|
97
|
+
requires_dist=_collect_requires_dist(project),
|
|
98
|
+
provides_extra=sorted(_collect_provides_extra(project)),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _collect_requires_dist(project: dict) -> list[Requirement]:
|
|
103
|
+
"""Concatenate PEP 631 ``dependencies`` + ``optional-dependencies``.
|
|
104
|
+
|
|
105
|
+
Optional-dependencies entries get an ``; extra == "name"`` marker
|
|
106
|
+
appended (combined with any existing marker via ``and``).
|
|
107
|
+
"""
|
|
108
|
+
out: list[Requirement] = []
|
|
109
|
+
_extend_with_dep_strings(out, project.get("dependencies"))
|
|
110
|
+
optional = project.get("optional-dependencies", {})
|
|
111
|
+
if isinstance(optional, dict):
|
|
112
|
+
for raw_extra, deps in sorted(optional.items()):
|
|
113
|
+
extra = canonicalize_name(str(raw_extra))
|
|
114
|
+
_extend_with_dep_strings(out, deps, extra=extra)
|
|
115
|
+
return out
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _extend_with_dep_strings(
|
|
119
|
+
out: list[Requirement],
|
|
120
|
+
raw: object,
|
|
121
|
+
*,
|
|
122
|
+
extra: str | None = None,
|
|
123
|
+
) -> None:
|
|
124
|
+
if not isinstance(raw, list):
|
|
125
|
+
return
|
|
126
|
+
# Late import: ``pypi`` imports this module at module load.
|
|
127
|
+
from .provider import _add_extra_marker # noqa: PLC0415
|
|
128
|
+
|
|
129
|
+
for dep in raw:
|
|
130
|
+
if not isinstance(dep, str):
|
|
131
|
+
continue
|
|
132
|
+
text = _add_extra_marker(dep, extra) if extra is not None else dep
|
|
133
|
+
try:
|
|
134
|
+
out.append(Requirement(text))
|
|
135
|
+
except (ValueError, TypeError):
|
|
136
|
+
logger.warning("skipping unparseable requirement: %s", dep)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _collect_provides_extra(project: dict) -> set[str]:
|
|
140
|
+
optional = project.get("optional-dependencies", {})
|
|
141
|
+
if not isinstance(optional, dict):
|
|
142
|
+
return set()
|
|
143
|
+
return {canonicalize_name(str(extra)) for extra in optional}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def extract_metadata(
|
|
147
|
+
source_dir: Path,
|
|
148
|
+
*,
|
|
149
|
+
config: NabProjectConfig | None = None,
|
|
150
|
+
python_version: str | None = None,
|
|
151
|
+
) -> WheelMetadata:
|
|
152
|
+
"""Extract metadata for a source directory.
|
|
153
|
+
|
|
154
|
+
Tries the static path first (:func:`extract_static_metadata`).
|
|
155
|
+
When that returns ``None``, the dynamic-build path is taken:
|
|
156
|
+
the project's PEP 517 backend is invoked in an isolated venv
|
|
157
|
+
via :func:`nab_python._build.runner.run_build_backend`. That
|
|
158
|
+
needs a :class:`NabProjectConfig`; callers that cannot provide
|
|
159
|
+
one get a :class:`BuildBackendError` instead.
|
|
160
|
+
|
|
161
|
+
The build env owns its own HTTP transport (see
|
|
162
|
+
:class:`~nab_python._build.env.NabBuildEnv` for why); callers
|
|
163
|
+
do not pass one in.
|
|
164
|
+
"""
|
|
165
|
+
static = extract_static_metadata(source_dir)
|
|
166
|
+
if static is not None:
|
|
167
|
+
return static
|
|
168
|
+
if config is None:
|
|
169
|
+
msg = (
|
|
170
|
+
"dynamic-metadata path requires a NabProjectConfig;"
|
|
171
|
+
f" the static reader returned None for {source_dir}."
|
|
172
|
+
" Pass one through ``extract_metadata`` or use a"
|
|
173
|
+
" build-policy that does not enter the dynamic path."
|
|
174
|
+
)
|
|
175
|
+
raise BuildBackendError(msg)
|
|
176
|
+
# Late import: ``_build.runner`` pulls in ``build`` and friends,
|
|
177
|
+
# which we should not pay for in static-only callers.
|
|
178
|
+
from ._build.runner import run_build_backend # noqa: PLC0415
|
|
179
|
+
|
|
180
|
+
return run_build_backend(
|
|
181
|
+
source_dir,
|
|
182
|
+
config=config,
|
|
183
|
+
python_version=python_version,
|
|
184
|
+
)
|