antsibull-nox 0.1.0__py3-none-any.whl → 0.3.0__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.
- antsibull_nox/__init__.py +17 -14
- antsibull_nox/_pydantic.py +98 -0
- antsibull_nox/ansible.py +260 -0
- antsibull_nox/cli.py +132 -0
- antsibull_nox/collection/__init__.py +56 -0
- antsibull_nox/collection/data.py +106 -0
- antsibull_nox/collection/extract.py +23 -0
- antsibull_nox/collection/install.py +523 -0
- antsibull_nox/collection/search.py +460 -0
- antsibull_nox/config.py +378 -0
- antsibull_nox/data/action-groups.py +3 -3
- antsibull_nox/data/antsibull-nox-lint-config.py +29 -0
- antsibull_nox/data/antsibull_nox_data_util.py +91 -0
- antsibull_nox/data/license-check.py +6 -2
- antsibull_nox/data/no-unwanted-files.py +5 -1
- antsibull_nox/data/plugin-yamllint.py +247 -0
- antsibull_nox/data_util.py +0 -77
- antsibull_nox/init.py +83 -0
- antsibull_nox/interpret_config.py +244 -0
- antsibull_nox/lint_config.py +113 -0
- antsibull_nox/paths.py +19 -0
- antsibull_nox/python.py +81 -0
- antsibull_nox/sessions/__init__.py +70 -0
- antsibull_nox/sessions/ansible_lint.py +58 -0
- antsibull_nox/sessions/ansible_test.py +568 -0
- antsibull_nox/sessions/build_import_check.py +147 -0
- antsibull_nox/sessions/collections.py +137 -0
- antsibull_nox/sessions/docs_check.py +78 -0
- antsibull_nox/sessions/extra_checks.py +127 -0
- antsibull_nox/sessions/license_check.py +73 -0
- antsibull_nox/sessions/lint.py +627 -0
- antsibull_nox/sessions/utils.py +206 -0
- antsibull_nox/utils.py +85 -0
- {antsibull_nox-0.1.0.dist-info → antsibull_nox-0.3.0.dist-info}/METADATA +4 -2
- antsibull_nox-0.3.0.dist-info/RECORD +40 -0
- antsibull_nox-0.3.0.dist-info/entry_points.txt +2 -0
- antsibull_nox/collection.py +0 -545
- antsibull_nox/sessions.py +0 -840
- antsibull_nox-0.1.0.dist-info/RECORD +0 -14
- {antsibull_nox-0.1.0.dist-info → antsibull_nox-0.3.0.dist-info}/WHEEL +0 -0
- {antsibull_nox-0.1.0.dist-info → antsibull_nox-0.3.0.dist-info}/licenses/LICENSES/GPL-3.0-or-later.txt +0 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
# Author: Felix Fontein <felix@fontein.de>
|
2
|
+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
|
3
|
+
# https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
5
|
+
# SPDX-FileCopyrightText: 2025, Ansible Project
|
6
|
+
|
7
|
+
"""
|
8
|
+
Utils for creating nox sessions.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
import logging
|
14
|
+
import os
|
15
|
+
import sys
|
16
|
+
import typing as t
|
17
|
+
from contextlib import contextmanager
|
18
|
+
from pathlib import Path
|
19
|
+
|
20
|
+
import nox
|
21
|
+
from nox.logger import OUTPUT as nox_OUTPUT
|
22
|
+
|
23
|
+
from ..data_util import prepare_data_script
|
24
|
+
from ..paths import (
|
25
|
+
find_data_directory,
|
26
|
+
list_all_files,
|
27
|
+
)
|
28
|
+
|
29
|
+
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
|
30
|
+
# https://docs.gitlab.com/ci/variables/predefined_variables/#predefined-variables
|
31
|
+
# https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
|
32
|
+
IN_CI = os.environ.get("CI") == "true"
|
33
|
+
IN_GITHUB_ACTIONS = bool(os.environ.get("GITHUB_ACTION"))
|
34
|
+
ALLOW_EDITABLE = os.environ.get("ALLOW_EDITABLE", str(not IN_CI)).lower() in (
|
35
|
+
"1",
|
36
|
+
"true",
|
37
|
+
)
|
38
|
+
|
39
|
+
_SESSIONS: dict[str, list[dict[str, t.Any]]] = {}
|
40
|
+
|
41
|
+
|
42
|
+
def nox_has_verbosity() -> bool:
|
43
|
+
"""
|
44
|
+
Determine whether nox is run with verbosity enabled.
|
45
|
+
"""
|
46
|
+
logger = logging.getLogger()
|
47
|
+
return logger.level <= nox_OUTPUT
|
48
|
+
|
49
|
+
|
50
|
+
@contextmanager
|
51
|
+
def silence_run_verbosity() -> t.Iterator[None]:
|
52
|
+
"""
|
53
|
+
When using session.run() with silent=True, nox will log the output
|
54
|
+
if -v is used. Using this context manager prevents printing the output.
|
55
|
+
"""
|
56
|
+
logger = logging.getLogger()
|
57
|
+
original_level = logger.level
|
58
|
+
try:
|
59
|
+
logger.setLevel(max(nox_OUTPUT + 1, original_level))
|
60
|
+
yield
|
61
|
+
finally:
|
62
|
+
logger.setLevel(original_level)
|
63
|
+
|
64
|
+
|
65
|
+
@contextmanager
|
66
|
+
def ci_group(name: str) -> t.Iterator[tuple[str, bool]]:
|
67
|
+
"""
|
68
|
+
Try to ensure that the output inside the context is printed in a collapsable group.
|
69
|
+
|
70
|
+
This is highly CI system dependent, and currently only works for GitHub Actions.
|
71
|
+
"""
|
72
|
+
is_collapsing = False
|
73
|
+
if IN_GITHUB_ACTIONS:
|
74
|
+
print(f"::group::{name}")
|
75
|
+
sys.stdout.flush()
|
76
|
+
is_collapsing = True
|
77
|
+
yield (" " if is_collapsing else "", is_collapsing)
|
78
|
+
if IN_GITHUB_ACTIONS:
|
79
|
+
print("::endgroup::")
|
80
|
+
sys.stdout.flush()
|
81
|
+
|
82
|
+
|
83
|
+
def register(name: str, data: dict[str, t.Any]) -> None:
|
84
|
+
"""
|
85
|
+
Register a session name for matrix generation with additional data.
|
86
|
+
"""
|
87
|
+
if name not in _SESSIONS:
|
88
|
+
_SESSIONS[name] = []
|
89
|
+
_SESSIONS[name].append(data)
|
90
|
+
|
91
|
+
|
92
|
+
def get_registered_sessions() -> dict[str, list[dict[str, t.Any]]]:
|
93
|
+
"""
|
94
|
+
Return all registered sessions.
|
95
|
+
"""
|
96
|
+
return {
|
97
|
+
name: [session.copy() for session in sessions]
|
98
|
+
for name, sessions in _SESSIONS.items()
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
def install(session: nox.Session, *args: str, editable: bool = False, **kwargs):
|
103
|
+
"""
|
104
|
+
Install Python packages.
|
105
|
+
"""
|
106
|
+
if not args:
|
107
|
+
return
|
108
|
+
# nox --no-venv
|
109
|
+
if isinstance(session.virtualenv, nox.virtualenv.PassthroughEnv):
|
110
|
+
session.warn(f"No venv. Skipping installation of {args}")
|
111
|
+
return
|
112
|
+
# Don't install in editable mode in CI or if it's explicitly disabled.
|
113
|
+
# This ensures that the wheel contains all of the correct files.
|
114
|
+
if editable and ALLOW_EDITABLE:
|
115
|
+
args = ("-e", *args)
|
116
|
+
session.install(*args, "-U", **kwargs)
|
117
|
+
|
118
|
+
|
119
|
+
def run_bare_script(
|
120
|
+
session: nox.Session,
|
121
|
+
/,
|
122
|
+
name: str,
|
123
|
+
*,
|
124
|
+
use_session_python: bool = False,
|
125
|
+
files: list[Path] | None = None,
|
126
|
+
extra_data: dict[str, t.Any] | None = None,
|
127
|
+
) -> None:
|
128
|
+
"""
|
129
|
+
Run a bare script included in antsibull-nox's data directory.
|
130
|
+
"""
|
131
|
+
if files is None:
|
132
|
+
files = list_all_files()
|
133
|
+
data = prepare_data_script(
|
134
|
+
session,
|
135
|
+
base_name=name,
|
136
|
+
paths=files,
|
137
|
+
extra_data=extra_data,
|
138
|
+
)
|
139
|
+
python = sys.executable
|
140
|
+
env = {}
|
141
|
+
if use_session_python:
|
142
|
+
python = "python"
|
143
|
+
env["PYTHONPATH"] = str(find_data_directory())
|
144
|
+
session.run(
|
145
|
+
python,
|
146
|
+
find_data_directory() / f"{name}.py",
|
147
|
+
"--data",
|
148
|
+
data,
|
149
|
+
external=True,
|
150
|
+
env=env,
|
151
|
+
)
|
152
|
+
|
153
|
+
|
154
|
+
def compose_description(
|
155
|
+
*,
|
156
|
+
prefix: str | dict[t.Literal["one", "other"], str] | None = None,
|
157
|
+
programs: dict[str, str | bool | None],
|
158
|
+
) -> str:
|
159
|
+
"""
|
160
|
+
Compose a description for a nox session from several configurable parts.
|
161
|
+
"""
|
162
|
+
parts: list[str] = []
|
163
|
+
|
164
|
+
def add(text: str, *, comma: bool = False) -> None:
|
165
|
+
if parts:
|
166
|
+
if comma:
|
167
|
+
parts.append(", ")
|
168
|
+
else:
|
169
|
+
parts.append(" ")
|
170
|
+
parts.append(text)
|
171
|
+
|
172
|
+
active_programs = [
|
173
|
+
(program, value if isinstance(value, str) else None)
|
174
|
+
for program, value in programs.items()
|
175
|
+
if value not in (False, None)
|
176
|
+
]
|
177
|
+
|
178
|
+
if prefix:
|
179
|
+
if isinstance(prefix, dict):
|
180
|
+
if len(active_programs) == 1 and "one" in prefix:
|
181
|
+
add(prefix["one"])
|
182
|
+
else:
|
183
|
+
add(prefix["other"])
|
184
|
+
else:
|
185
|
+
add(prefix)
|
186
|
+
|
187
|
+
for index, (program, value) in enumerate(active_programs):
|
188
|
+
if index + 1 == len(active_programs) and index > 0:
|
189
|
+
add("and", comma=index > 1)
|
190
|
+
add(program, comma=index > 0 and index + 1 < len(active_programs))
|
191
|
+
if value is not None:
|
192
|
+
add(f"({value})")
|
193
|
+
|
194
|
+
return "".join(parts)
|
195
|
+
|
196
|
+
|
197
|
+
__all__ = [
|
198
|
+
"ci_group",
|
199
|
+
"compose_description",
|
200
|
+
"get_registered_sessions",
|
201
|
+
"install",
|
202
|
+
"nox_has_verbosity",
|
203
|
+
"register",
|
204
|
+
"run_bare_script",
|
205
|
+
"silence_run_verbosity",
|
206
|
+
]
|
antsibull_nox/utils.py
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Author: Felix Fontein <felix@fontein.de>
|
2
|
+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
|
3
|
+
# https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
5
|
+
# SPDX-FileCopyrightText: 2025, Ansible Project
|
6
|
+
|
7
|
+
"""
|
8
|
+
General utilities.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
import typing as t
|
14
|
+
from dataclasses import dataclass
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass(order=True, frozen=True)
|
18
|
+
class Version:
|
19
|
+
"""
|
20
|
+
Models a two-part version (major and minor).
|
21
|
+
"""
|
22
|
+
|
23
|
+
major: int
|
24
|
+
minor: int
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def parse(cls, version_string: str) -> Version:
|
28
|
+
"""
|
29
|
+
Given a version string, parses it into a Version object.
|
30
|
+
Other components than major and minor version are ignored.
|
31
|
+
"""
|
32
|
+
try:
|
33
|
+
major, minor = [int(v) for v in version_string.split(".")[:2]]
|
34
|
+
except (AttributeError, ValueError) as exc:
|
35
|
+
raise ValueError(
|
36
|
+
f"Cannot parse {version_string!r} as version string."
|
37
|
+
) from exc
|
38
|
+
return Version(major=major, minor=minor)
|
39
|
+
|
40
|
+
def __str__(self) -> str:
|
41
|
+
return f"{self.major}.{self.minor}"
|
42
|
+
|
43
|
+
def next_minor_version(self) -> Version:
|
44
|
+
"""
|
45
|
+
Returns the next minor version.
|
46
|
+
|
47
|
+
The major version stays the same, and the minor version is increased by 1.
|
48
|
+
"""
|
49
|
+
return Version(self.major, self.minor + 1)
|
50
|
+
|
51
|
+
def previous_minor_version(self) -> Version:
|
52
|
+
"""
|
53
|
+
Returns the previous minor version.
|
54
|
+
Raises a ValueError if there is none.
|
55
|
+
|
56
|
+
The major version stays the same, and the minor version is decreased by 1.
|
57
|
+
"""
|
58
|
+
if self.minor == 0:
|
59
|
+
raise ValueError("No previous minor version exists")
|
60
|
+
return Version(self.major, self.minor - 1)
|
61
|
+
|
62
|
+
|
63
|
+
def version_range(
|
64
|
+
start: Version, end: Version, *, inclusive: bool
|
65
|
+
) -> t.Generator[Version]:
|
66
|
+
"""
|
67
|
+
Enumerate all versions starting at ``start`` until ``end``.
|
68
|
+
|
69
|
+
Whether ``end`` is included in the range depends on ``inclusive``.
|
70
|
+
"""
|
71
|
+
if start.major != end.major:
|
72
|
+
raise ValueError(
|
73
|
+
f"Cannot list all versions from {start} to {end}"
|
74
|
+
" since they have different major versions"
|
75
|
+
)
|
76
|
+
version = start
|
77
|
+
while (version <= end) if inclusive else (version < end):
|
78
|
+
yield version
|
79
|
+
version = Version(major=version.major, minor=version.minor + 1)
|
80
|
+
|
81
|
+
|
82
|
+
__all__ = [
|
83
|
+
"Version",
|
84
|
+
"version_range",
|
85
|
+
]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: antsibull-nox
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: Changelog tool for Ansible-core and Ansible collections
|
5
5
|
Project-URL: Documentation, https://ansible.readthedocs.io/projects/antsibull-nox/
|
6
6
|
Project-URL: Source code, https://github.com/ansible-community/antsibull-nox/
|
@@ -11,7 +11,7 @@ Author-email: Felix Fontein <felix@fontein.de>, Toshio Kuratomi <a.badger@gmail.
|
|
11
11
|
Maintainer-email: Felix Fontein <felix@fontein.de>, Maxwell G <maxwell@gtmx.me>
|
12
12
|
License-Expression: GPL-3.0-or-later
|
13
13
|
License-File: LICENSES/GPL-3.0-or-later.txt
|
14
|
-
Classifier: Development Status ::
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
15
15
|
Classifier: Framework :: Ansible
|
16
16
|
Classifier: Intended Audience :: Developers
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
@@ -25,8 +25,10 @@ Requires-Python: >=3.9.0
|
|
25
25
|
Requires-Dist: antsibull-fileutils<2.0.0,>=1.2.0
|
26
26
|
Requires-Dist: nox>=2025.2.9
|
27
27
|
Requires-Dist: packaging
|
28
|
+
Requires-Dist: pydantic~=2.0
|
28
29
|
Requires-Dist: pyyaml
|
29
30
|
Requires-Dist: semantic-version
|
31
|
+
Requires-Dist: tomli; python_version < '3.11'
|
30
32
|
Provides-Extra: codeqa
|
31
33
|
Requires-Dist: antsibull-changelog; extra == 'codeqa'
|
32
34
|
Requires-Dist: flake8>=3.8.0; extra == 'codeqa'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
antsibull_nox/__init__.py,sha256=oivrdT1a_loZAmf254Apt3h5_-Gp1ZnJkOXa40tTEc8,845
|
2
|
+
antsibull_nox/_pydantic.py,sha256=VTIh-u0TpWvnY6Dhe4ba8nAmR2tYmz4PXNp2_8Sr9pw,3203
|
3
|
+
antsibull_nox/ansible.py,sha256=_PugP3a6JXijptFwuUki5nq_66VA8e9gVZUpA5HewVg,8891
|
4
|
+
antsibull_nox/cli.py,sha256=NKeRlWc_0taNRcZcdMv9LHZ5nCz8-DEJxBLPxJ9vFYQ,3358
|
5
|
+
antsibull_nox/config.py,sha256=bgvjfB005xS2vXXsGSZPIAs9vxKIg30bleYqm5EwdJM,10916
|
6
|
+
antsibull_nox/data_util.py,sha256=7FVoqESEc-_QdqrQ16K1AHRVHEglNbRCH_mNaYDJ7a4,953
|
7
|
+
antsibull_nox/init.py,sha256=eRltIrS3AcHqEHk2yNAqJXv7kR6m_ysvFxIHpowd-2M,2259
|
8
|
+
antsibull_nox/interpret_config.py,sha256=OjlHtLn22JAZBLycOndIjZVqzx2-bcbJahygJmBg6NQ,10367
|
9
|
+
antsibull_nox/lint_config.py,sha256=ZnsUbX6IdQK_IP9nvs8Kk6jb5lPdiREFSHAUuEGGceU,3848
|
10
|
+
antsibull_nox/paths.py,sha256=86HOynhCMTVop3ml_77JI06vM9nyK7PHMzLP_4M0V88,6317
|
11
|
+
antsibull_nox/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
antsibull_nox/python.py,sha256=0pyGqgwsuyc0BhzoXNZLTLbjaFA4kAYEHrFD6A1o1-o,2113
|
13
|
+
antsibull_nox/utils.py,sha256=lgBNuJ67Agl9YpFNWCjr6TBUcbC1LroZrJkv6S5VuxA,2437
|
14
|
+
antsibull_nox/collection/__init__.py,sha256=2k6uYdiPjAgJgmz5oviYpu2Sh5ZSKSJEJCR01iz-29A,1494
|
15
|
+
antsibull_nox/collection/data.py,sha256=sacVZF517x54B3G6F0hW8fTpEF7zyB8exSQJO9mvhLk,2609
|
16
|
+
antsibull_nox/collection/extract.py,sha256=qNVknQRtRrCt10aawuU1Z6NTs16CA1bUEX3WDGBw68g,684
|
17
|
+
antsibull_nox/collection/install.py,sha256=DbnXUBzQlzW6TzU_TAszuApix2F2n7w1AcUcPZ4Q4G4,17082
|
18
|
+
antsibull_nox/collection/search.py,sha256=peE8JQfHSkHS82NgZfARSF-k_mril_tpwj4QSkdJpOo,15539
|
19
|
+
antsibull_nox/data/action-groups.py,sha256=qLuh2YqIpc6i2jpWFzjtFhZhx6_SDbbvJe6VH7EXrqg,6934
|
20
|
+
antsibull_nox/data/antsibull-nox-lint-config.py,sha256=tXkKd9AqgfDs5w7S6OaBIt9HnT0KSbiQIU9tFxtYE2U,657
|
21
|
+
antsibull_nox/data/antsibull_nox_data_util.py,sha256=D4i_sKxjAeZuDV-z9Ibow0YYIqhXo2V_YC0LONLcEXM,2931
|
22
|
+
antsibull_nox/data/license-check.py,sha256=or3GyQC0WWYMxMqL-369krGsHaySH1vX-2fwpRyJGp0,5665
|
23
|
+
antsibull_nox/data/license-check.py.license,sha256=iPdFtdkeE3E2nCB-M5KHevbz4d5I-6ymOnKNTc954Dw,218
|
24
|
+
antsibull_nox/data/no-unwanted-files.py,sha256=_B3m-XWvWpdzGN-XAP8rLIS_5RMJGntFWL-Jy0WY9Mc,2959
|
25
|
+
antsibull_nox/data/plugin-yamllint.py,sha256=zobRajdVAemXbuAzyIjr485T-EzhbzW1JH--UwUswf0,7436
|
26
|
+
antsibull_nox/sessions/__init__.py,sha256=4wTTO1E6rdCz4pVMuGUeuXi_vqFaH4whAL9qcjfOqto,2022
|
27
|
+
antsibull_nox/sessions/ansible_lint.py,sha256=TzKqxbUHO2nmvJtKqh1hM9HDoCxDr8iNbbSzp8SNkN4,1589
|
28
|
+
antsibull_nox/sessions/ansible_test.py,sha256=hR6T-RH2epWAUUsmX3SIzDhdRa5jsVkaH_r_mbaoIAo,20624
|
29
|
+
antsibull_nox/sessions/build_import_check.py,sha256=kdr_Cqc0jb8XQQ-2QL-g_X7wgezE04oMVFCPr7a34iA,4719
|
30
|
+
antsibull_nox/sessions/collections.py,sha256=t_j1UNgJDvehj7PRQ-67DrmZmauh2uECL8kaRCnKoEY,4324
|
31
|
+
antsibull_nox/sessions/docs_check.py,sha256=mVYt278xy5AVwo5rCf6FLZlhqBiEYgJ3mmWsVBShKD0,2344
|
32
|
+
antsibull_nox/sessions/extra_checks.py,sha256=sBn0YFD8cM3OqeQ4UgYwD0NAcjKnYoy2zd8q-z4Xl2I,4110
|
33
|
+
antsibull_nox/sessions/license_check.py,sha256=t5ut4ZluhFfk-qE6kcU8VNdvIGvzND81N7WCsbA4jLc,1824
|
34
|
+
antsibull_nox/sessions/lint.py,sha256=2a2F-Q3VqCUCxEM4r9YFHZTGwe39VxbPJBQO6HX6hQ0,19298
|
35
|
+
antsibull_nox/sessions/utils.py,sha256=rrQdzmjdrLQula8t-BCTKbO-tAmzfOKHNn1pN2B1QVc,5632
|
36
|
+
antsibull_nox-0.3.0.dist-info/METADATA,sha256=wFsgoXsDAjCLOcSSoPZod69otcwmEnMpsKz5LUn7YUc,7670
|
37
|
+
antsibull_nox-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
38
|
+
antsibull_nox-0.3.0.dist-info/entry_points.txt,sha256=solWA9TCB37UlaGk8sHXxJg-k1HWckfKdncHDBsVSsI,57
|
39
|
+
antsibull_nox-0.3.0.dist-info/licenses/LICENSES/GPL-3.0-or-later.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
40
|
+
antsibull_nox-0.3.0.dist-info/RECORD,,
|