quickpub 4.1.4__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.
- quickpub/__init__.py +10 -0
- quickpub/__main__.py +276 -0
- quickpub/classifiers.py +138 -0
- quickpub/dist_files.py +33 -0
- quickpub/enforcers.py +26 -0
- quickpub/files.py +246 -0
- quickpub/logging_.py +74 -0
- quickpub/package_manager_detection.py +43 -0
- quickpub/proxy.py +35 -0
- quickpub/py.typed +0 -0
- quickpub/qa.py +409 -0
- quickpub/strategies/__init__.py +8 -0
- quickpub/strategies/build_schema.py +21 -0
- quickpub/strategies/constraint_enforcer.py +17 -0
- quickpub/strategies/implementations/__init__.py +6 -0
- quickpub/strategies/implementations/build_schemas/__init__.py +2 -0
- quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +48 -0
- quickpub/strategies/implementations/build_schemas/uv_build_schema.py +50 -0
- quickpub/strategies/implementations/constraint_enforcers/__init__.py +5 -0
- quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +29 -0
- quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +80 -0
- quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +88 -0
- quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +45 -0
- quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +29 -0
- quickpub/strategies/implementations/package_managers/__init__.py +2 -0
- quickpub/strategies/implementations/package_managers/installed_package_parser.py +29 -0
- quickpub/strategies/implementations/package_managers/pip_package_manager.py +31 -0
- quickpub/strategies/implementations/package_managers/uv_package_manager.py +45 -0
- quickpub/strategies/implementations/python_providers/__init__.py +4 -0
- quickpub/strategies/implementations/python_providers/conda_python_provider.py +54 -0
- quickpub/strategies/implementations/python_providers/default_python_provider.py +47 -0
- quickpub/strategies/implementations/python_providers/union_provider.py +52 -0
- quickpub/strategies/implementations/python_providers/uv_python_provider.py +39 -0
- quickpub/strategies/implementations/quality_assurance_runners/__init__.py +4 -0
- quickpub/strategies/implementations/quality_assurance_runners/mypy_qa_runner.py +98 -0
- quickpub/strategies/implementations/quality_assurance_runners/pylint_qa_runner.py +106 -0
- quickpub/strategies/implementations/quality_assurance_runners/pytest_qa_runner.py +188 -0
- quickpub/strategies/implementations/quality_assurance_runners/unittest_qa_runner.py +138 -0
- quickpub/strategies/implementations/upload_targets/__init__.py +2 -0
- quickpub/strategies/implementations/upload_targets/github_upload_target.py +61 -0
- quickpub/strategies/implementations/upload_targets/pypirc_upload_target.py +90 -0
- quickpub/strategies/package_manager.py +54 -0
- quickpub/strategies/python_provider.py +60 -0
- quickpub/strategies/quality_assurance_runner.py +208 -0
- quickpub/strategies/quickpub_strategy.py +14 -0
- quickpub/strategies/upload_target.py +19 -0
- quickpub/structures/__init__.py +3 -0
- quickpub/structures/bound.py +52 -0
- quickpub/structures/dependency.py +82 -0
- quickpub/structures/version.py +41 -0
- quickpub/validators.py +87 -0
- quickpub/worker_pool.py +16 -0
- quickpub-4.1.4.dist-info/METADATA +246 -0
- quickpub-4.1.4.dist-info/RECORD +58 -0
- quickpub-4.1.4.dist-info/WHEEL +5 -0
- quickpub-4.1.4.dist-info/entry_points.txt +2 -0
- quickpub-4.1.4.dist-info/licenses/LICENSE +21 -0
- quickpub-4.1.4.dist-info/top_level.txt +1 -0
quickpub/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .structures import *
|
|
2
|
+
from .strategies import *
|
|
3
|
+
from .classifiers import *
|
|
4
|
+
from .enforcers import ExitEarlyError
|
|
5
|
+
from .qa import SupportsProgress
|
|
6
|
+
from .logging_ import set_log_level
|
|
7
|
+
from .package_manager_detection import detect_package_manager, resolve_publish_environment
|
|
8
|
+
from .__main__ import publish, main
|
|
9
|
+
|
|
10
|
+
__version__ = "4.1.4"
|
quickpub/__main__.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, Union, List, Any, Dict, Callable, Tuple
|
|
6
|
+
|
|
7
|
+
import fire # type: ignore[import-untyped]
|
|
8
|
+
from danielutils import warning, error
|
|
9
|
+
|
|
10
|
+
from quickpub import ExitEarlyError
|
|
11
|
+
from .strategies import (
|
|
12
|
+
BuildSchema,
|
|
13
|
+
ConstraintEnforcer,
|
|
14
|
+
UploadTarget,
|
|
15
|
+
QualityAssuranceRunner,
|
|
16
|
+
PythonProvider,
|
|
17
|
+
PackageManager,
|
|
18
|
+
PipPackageManager,
|
|
19
|
+
)
|
|
20
|
+
from .validators import (
|
|
21
|
+
validate_version,
|
|
22
|
+
validate_python_version,
|
|
23
|
+
validate_keywords,
|
|
24
|
+
validate_dependencies,
|
|
25
|
+
validate_source,
|
|
26
|
+
)
|
|
27
|
+
from .structures import Version, Dependency
|
|
28
|
+
from .files import (
|
|
29
|
+
create_toml,
|
|
30
|
+
create_setup,
|
|
31
|
+
create_manifest,
|
|
32
|
+
add_version_to_init,
|
|
33
|
+
update_pyproject_version,
|
|
34
|
+
)
|
|
35
|
+
from .classifiers import Classifier, default_publish_classifiers
|
|
36
|
+
from .qa import qa, SupportsProgress
|
|
37
|
+
from .logging_ import setup_logging
|
|
38
|
+
from .package_manager_detection import resolve_publish_environment
|
|
39
|
+
|
|
40
|
+
setup_logging()
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _validate_publish_inputs(
|
|
45
|
+
name: str,
|
|
46
|
+
version: Optional[Union[Version, str]],
|
|
47
|
+
explicit_src_folder_path: Optional[str],
|
|
48
|
+
min_python: Optional[Union[Version, str]],
|
|
49
|
+
keywords: Optional[List[str]],
|
|
50
|
+
dependencies: Optional[List[Union[str, Dependency]]],
|
|
51
|
+
) -> Tuple[Version, str, Version, List[str], List[Dependency]]:
|
|
52
|
+
validated_version = validate_version(version)
|
|
53
|
+
validated_src_path = validate_source(name, explicit_src_folder_path)
|
|
54
|
+
if validated_src_path != f"./{name}":
|
|
55
|
+
warning(
|
|
56
|
+
"The source folder's name is different from the package's name. this may not be currently supported correctly"
|
|
57
|
+
)
|
|
58
|
+
converted_min_python: Optional[Version] = None
|
|
59
|
+
if min_python is not None:
|
|
60
|
+
converted_min_python = (
|
|
61
|
+
min_python
|
|
62
|
+
if isinstance(min_python, Version)
|
|
63
|
+
else Version.from_str(min_python)
|
|
64
|
+
)
|
|
65
|
+
validated_min_python = validate_python_version(converted_min_python)
|
|
66
|
+
validated_keywords = validate_keywords(keywords)
|
|
67
|
+
validated_deps = validate_dependencies(dependencies)
|
|
68
|
+
return (
|
|
69
|
+
validated_version,
|
|
70
|
+
validated_src_path,
|
|
71
|
+
validated_min_python,
|
|
72
|
+
validated_keywords,
|
|
73
|
+
validated_deps,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _run_constraint_enforcers(
|
|
78
|
+
enforcers: Optional[List[ConstraintEnforcer]],
|
|
79
|
+
name: str,
|
|
80
|
+
version: Version,
|
|
81
|
+
demo: bool,
|
|
82
|
+
) -> None:
|
|
83
|
+
for enforcer in enforcers or []:
|
|
84
|
+
enforcer.enforce(name=name, version=version, demo=demo)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _run_quality_assurance(
|
|
88
|
+
python_interpreter_provider: PythonProvider,
|
|
89
|
+
global_quality_assurance_runners: Optional[List[QualityAssuranceRunner]],
|
|
90
|
+
name: str,
|
|
91
|
+
explicit_src_folder_path: str,
|
|
92
|
+
validated_dependencies: List[Dependency],
|
|
93
|
+
pbar: Optional[SupportsProgress],
|
|
94
|
+
package_manager: Optional[PackageManager] = None,
|
|
95
|
+
) -> None:
|
|
96
|
+
if package_manager is None:
|
|
97
|
+
package_manager = PipPackageManager()
|
|
98
|
+
try:
|
|
99
|
+
result = asyncio.run(
|
|
100
|
+
qa(
|
|
101
|
+
python_interpreter_provider,
|
|
102
|
+
global_quality_assurance_runners or [],
|
|
103
|
+
name,
|
|
104
|
+
explicit_src_folder_path,
|
|
105
|
+
validated_dependencies,
|
|
106
|
+
pbar=pbar,
|
|
107
|
+
package_manager=package_manager,
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
if not result:
|
|
111
|
+
error(
|
|
112
|
+
f"quickpub.publish exited early as '{name}' "
|
|
113
|
+
"did not pass quality assurance step, see above "
|
|
114
|
+
"logs to pass this step."
|
|
115
|
+
)
|
|
116
|
+
raise ExitEarlyError("QA step Failed")
|
|
117
|
+
except ExitEarlyError as e:
|
|
118
|
+
raise e
|
|
119
|
+
except Exception as e:
|
|
120
|
+
raise RuntimeError("Quality assurance stage has failed", e) from e
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _create_package_files(
|
|
124
|
+
name: str,
|
|
125
|
+
explicit_src_folder_path: str,
|
|
126
|
+
readme_file_path: str,
|
|
127
|
+
license_file_path: str,
|
|
128
|
+
version: Version,
|
|
129
|
+
author: str,
|
|
130
|
+
author_email: str,
|
|
131
|
+
description: str,
|
|
132
|
+
homepage: str,
|
|
133
|
+
keywords: List[str],
|
|
134
|
+
validated_dependencies: List[Dependency],
|
|
135
|
+
min_python: Version,
|
|
136
|
+
scripts: Optional[Dict[str, Callable]],
|
|
137
|
+
classifiers: Optional[List[Classifier]] = None,
|
|
138
|
+
) -> None:
|
|
139
|
+
create_setup()
|
|
140
|
+
create_toml(
|
|
141
|
+
name=name,
|
|
142
|
+
src_folder_path=explicit_src_folder_path,
|
|
143
|
+
readme_file_path=readme_file_path,
|
|
144
|
+
license_file_path=license_file_path,
|
|
145
|
+
version=version,
|
|
146
|
+
author=author,
|
|
147
|
+
author_email=author_email,
|
|
148
|
+
description=description,
|
|
149
|
+
homepage=homepage,
|
|
150
|
+
keywords=keywords,
|
|
151
|
+
dependencies=validated_dependencies,
|
|
152
|
+
classifiers=classifiers or default_publish_classifiers(),
|
|
153
|
+
min_python=min_python,
|
|
154
|
+
scripts=scripts,
|
|
155
|
+
)
|
|
156
|
+
create_manifest(name=name)
|
|
157
|
+
add_version_to_init(src_folder_path=explicit_src_folder_path, version=version)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _sync_existing_package_metadata(
|
|
161
|
+
explicit_src_folder_path: str,
|
|
162
|
+
version: Version,
|
|
163
|
+
pyproject_path: str = "./pyproject.toml",
|
|
164
|
+
) -> None:
|
|
165
|
+
update_pyproject_version(version=version, pyproject_path=pyproject_path)
|
|
166
|
+
add_version_to_init(src_folder_path=explicit_src_folder_path, version=version)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _build_and_upload_packages(
|
|
170
|
+
build_schemas: List[BuildSchema],
|
|
171
|
+
upload_targets: List[UploadTarget],
|
|
172
|
+
name: str,
|
|
173
|
+
version: Version,
|
|
174
|
+
demo: bool,
|
|
175
|
+
) -> None:
|
|
176
|
+
if not demo:
|
|
177
|
+
for schema in build_schemas:
|
|
178
|
+
schema.build()
|
|
179
|
+
for target in upload_targets:
|
|
180
|
+
target.upload(name=name, version=version)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def publish(
|
|
184
|
+
*,
|
|
185
|
+
name: str,
|
|
186
|
+
author: str,
|
|
187
|
+
author_email: str,
|
|
188
|
+
description: str,
|
|
189
|
+
homepage: str,
|
|
190
|
+
build_schemas: List[BuildSchema],
|
|
191
|
+
upload_targets: List[UploadTarget],
|
|
192
|
+
enforcers: Optional[List[ConstraintEnforcer]] = None,
|
|
193
|
+
global_quality_assurance_runners: Optional[List[QualityAssuranceRunner]] = None,
|
|
194
|
+
python_interpreter_provider: Optional[PythonProvider] = None,
|
|
195
|
+
package_manager: Optional[PackageManager] = None,
|
|
196
|
+
project_root: Path = Path("."),
|
|
197
|
+
readme_file_path: str = "./README.md",
|
|
198
|
+
license_file_path: str = "./LICENSE",
|
|
199
|
+
version: Optional[Union[Version, str]] = None,
|
|
200
|
+
min_python: Optional[Union[Version, str]] = None,
|
|
201
|
+
dependencies: Optional[List[Union[str, Dependency]]] = None,
|
|
202
|
+
keywords: Optional[List[str]] = None,
|
|
203
|
+
explicit_src_folder_path: Optional[str] = None,
|
|
204
|
+
scripts: Optional[Dict[str, Callable]] = None,
|
|
205
|
+
classifiers: Optional[List[Classifier]] = None,
|
|
206
|
+
pbar: Optional[SupportsProgress] = None,
|
|
207
|
+
demo: bool = False,
|
|
208
|
+
generate_project_files: bool = True,
|
|
209
|
+
config: Optional[Any] = None,
|
|
210
|
+
) -> None:
|
|
211
|
+
start_time = time.perf_counter()
|
|
212
|
+
success = False
|
|
213
|
+
try:
|
|
214
|
+
(
|
|
215
|
+
validated_version,
|
|
216
|
+
validated_src_path,
|
|
217
|
+
validated_min_python,
|
|
218
|
+
validated_keywords,
|
|
219
|
+
validated_deps,
|
|
220
|
+
) = _validate_publish_inputs(
|
|
221
|
+
name, version, explicit_src_folder_path, min_python, keywords, dependencies
|
|
222
|
+
)
|
|
223
|
+
package_manager, python_interpreter_provider = resolve_publish_environment(
|
|
224
|
+
project_root,
|
|
225
|
+
python_interpreter_provider=python_interpreter_provider,
|
|
226
|
+
package_manager=package_manager,
|
|
227
|
+
)
|
|
228
|
+
_run_constraint_enforcers(enforcers, name, validated_version, demo)
|
|
229
|
+
_run_quality_assurance(
|
|
230
|
+
python_interpreter_provider,
|
|
231
|
+
global_quality_assurance_runners,
|
|
232
|
+
name,
|
|
233
|
+
validated_src_path,
|
|
234
|
+
validated_deps,
|
|
235
|
+
pbar,
|
|
236
|
+
package_manager,
|
|
237
|
+
)
|
|
238
|
+
if generate_project_files:
|
|
239
|
+
_create_package_files(
|
|
240
|
+
name,
|
|
241
|
+
validated_src_path,
|
|
242
|
+
readme_file_path,
|
|
243
|
+
license_file_path,
|
|
244
|
+
validated_version,
|
|
245
|
+
author,
|
|
246
|
+
author_email,
|
|
247
|
+
description,
|
|
248
|
+
homepage,
|
|
249
|
+
validated_keywords,
|
|
250
|
+
validated_deps,
|
|
251
|
+
validated_min_python,
|
|
252
|
+
scripts,
|
|
253
|
+
classifiers,
|
|
254
|
+
)
|
|
255
|
+
else:
|
|
256
|
+
_sync_existing_package_metadata(
|
|
257
|
+
validated_src_path,
|
|
258
|
+
validated_version,
|
|
259
|
+
)
|
|
260
|
+
_build_and_upload_packages(
|
|
261
|
+
build_schemas, upload_targets, name, validated_version, demo
|
|
262
|
+
)
|
|
263
|
+
success = True
|
|
264
|
+
finally:
|
|
265
|
+
elapsed = time.perf_counter() - start_time
|
|
266
|
+
logger.info("Publish finished in %.3fs (success=%s)", elapsed, success)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def main() -> None:
|
|
270
|
+
fire.Fire(publish)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
main()
|
|
275
|
+
|
|
276
|
+
__all__ = ["main", "publish"]
|
quickpub/classifiers.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
_DEVELOPMENT_STATUS_LABELS = {
|
|
8
|
+
1: "Planning",
|
|
9
|
+
2: "Pre-Alpha",
|
|
10
|
+
3: "Alpha",
|
|
11
|
+
4: "Beta",
|
|
12
|
+
5: "Production/Stable",
|
|
13
|
+
6: "Mature",
|
|
14
|
+
7: "Inactive",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Classifier(Enum):
|
|
19
|
+
|
|
20
|
+
def _str(self) -> str:
|
|
21
|
+
return str(self.value)
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def _split_name(name: str) -> str:
|
|
25
|
+
words = []
|
|
26
|
+
current_word = ""
|
|
27
|
+
|
|
28
|
+
for char in name:
|
|
29
|
+
if char.isupper():
|
|
30
|
+
if current_word:
|
|
31
|
+
words.append(current_word)
|
|
32
|
+
current_word = char
|
|
33
|
+
else:
|
|
34
|
+
current_word += char
|
|
35
|
+
|
|
36
|
+
if current_word:
|
|
37
|
+
words.append(current_word)
|
|
38
|
+
|
|
39
|
+
return " ".join(words[:-1])
|
|
40
|
+
|
|
41
|
+
def __str__(self) -> str:
|
|
42
|
+
name = Classifier._split_name(self.__class__.__qualname__)
|
|
43
|
+
value = self._str()
|
|
44
|
+
result = f"{name} :: {value}"
|
|
45
|
+
logger.debug("Classifier string representation: %s", result)
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DevelopmentStatusClassifier(Classifier):
|
|
50
|
+
Planning = 1
|
|
51
|
+
PreAlpha = 2
|
|
52
|
+
Alpha = 3
|
|
53
|
+
Beta = 4
|
|
54
|
+
Production = 5
|
|
55
|
+
Stable = 5
|
|
56
|
+
Mature = 6
|
|
57
|
+
Inactive = 7
|
|
58
|
+
|
|
59
|
+
def _str(self) -> str:
|
|
60
|
+
return f"{self.value} - {_DEVELOPMENT_STATUS_LABELS[self.value]}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class IntendedAudienceClassifier(Classifier):
|
|
64
|
+
CustomerService = "Customer Service"
|
|
65
|
+
Developers = "Developers"
|
|
66
|
+
Education = "Education"
|
|
67
|
+
EndUsersDesktop = "End Users/Desktop"
|
|
68
|
+
InformationTechnology = "Information Technology"
|
|
69
|
+
ScienceResearch = "Science/Research"
|
|
70
|
+
SystemAdministrators = "System Administrators"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ProgrammingLanguageClassifier(Classifier):
|
|
74
|
+
Python = "Python"
|
|
75
|
+
Python3 = "Python :: 3"
|
|
76
|
+
Python3Only = "Python :: 3 :: Only"
|
|
77
|
+
Python38 = "Python :: 3.8"
|
|
78
|
+
Python39 = "Python :: 3.9"
|
|
79
|
+
Python310 = "Python :: 3.10"
|
|
80
|
+
Python311 = "Python :: 3.11"
|
|
81
|
+
Python312 = "Python :: 3.12"
|
|
82
|
+
Python313 = "Python :: 3.13"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class OperatingSystemClassifier(Classifier):
|
|
86
|
+
OSIndependent = "OS Independent"
|
|
87
|
+
MicrosoftWindows = "Microsoft :: Windows"
|
|
88
|
+
POSIX = "POSIX"
|
|
89
|
+
Linux = "POSIX :: Linux"
|
|
90
|
+
MacOS = "MacOS"
|
|
91
|
+
Unix = "Unix"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class LicenseClassifier(Classifier):
|
|
95
|
+
MIT = "OSI Approved :: MIT License"
|
|
96
|
+
Apache2 = "OSI Approved :: Apache Software License"
|
|
97
|
+
BSD = "OSI Approved :: BSD License"
|
|
98
|
+
GPLv3 = "OSI Approved :: GNU General Public License v3 (GPLv3)"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TopicClassifier(Classifier):
|
|
102
|
+
SoftwareDevelopment = "Software Development"
|
|
103
|
+
BuildTools = "Software Development :: Build Tools"
|
|
104
|
+
Libraries = "Software Development :: Libraries"
|
|
105
|
+
PythonModules = "Software Development :: Libraries :: Python Modules"
|
|
106
|
+
QualityAssurance = "Software Development :: Quality Assurance"
|
|
107
|
+
Testing = "Software Development :: Testing"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TypingClassifier(Classifier):
|
|
111
|
+
Typed = "Typed"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class EnvironmentClassifier(Classifier):
|
|
115
|
+
Console = "Console"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def default_publish_classifiers() -> List[Classifier]:
|
|
119
|
+
return [
|
|
120
|
+
DevelopmentStatusClassifier.Alpha,
|
|
121
|
+
IntendedAudienceClassifier.Developers,
|
|
122
|
+
ProgrammingLanguageClassifier.Python3,
|
|
123
|
+
OperatingSystemClassifier.OSIndependent,
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = [
|
|
128
|
+
"Classifier",
|
|
129
|
+
"DevelopmentStatusClassifier",
|
|
130
|
+
"IntendedAudienceClassifier",
|
|
131
|
+
"ProgrammingLanguageClassifier",
|
|
132
|
+
"OperatingSystemClassifier",
|
|
133
|
+
"LicenseClassifier",
|
|
134
|
+
"TopicClassifier",
|
|
135
|
+
"TypingClassifier",
|
|
136
|
+
"EnvironmentClassifier",
|
|
137
|
+
"default_publish_classifiers",
|
|
138
|
+
]
|
quickpub/dist_files.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List, Union
|
|
4
|
+
|
|
5
|
+
from .enforcers import ExitEarlyError
|
|
6
|
+
from .structures import Version
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def normalize_dist_name(name: str) -> str:
|
|
10
|
+
return re.sub(r"[-_.]+", "-", name).lower()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def find_sdist(
|
|
14
|
+
dist_dir: Union[str, Path],
|
|
15
|
+
name: str,
|
|
16
|
+
version: Union[str, Version],
|
|
17
|
+
) -> Path:
|
|
18
|
+
dist_path = Path(dist_dir)
|
|
19
|
+
version_str = str(version)
|
|
20
|
+
candidates: List[Path] = []
|
|
21
|
+
for candidate_name in (name, normalize_dist_name(name)):
|
|
22
|
+
candidate = dist_path / f"{candidate_name}-{version_str}.tar.gz"
|
|
23
|
+
if candidate not in candidates:
|
|
24
|
+
candidates.append(candidate)
|
|
25
|
+
for candidate in candidates:
|
|
26
|
+
if candidate.exists():
|
|
27
|
+
return candidate
|
|
28
|
+
raise ExitEarlyError(
|
|
29
|
+
f"Could not find sdist for '{name}' version '{version_str}' in '{dist_path}'"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = ["normalize_dist_name", "find_sdist"]
|
quickpub/enforcers.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Union, Callable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExitEarlyError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def exit_if(
|
|
13
|
+
predicate: Union[bool, Callable[[], bool]],
|
|
14
|
+
msg: str,
|
|
15
|
+
) -> None:
|
|
16
|
+
if (isinstance(predicate, bool) and predicate) or (
|
|
17
|
+
callable(predicate) and predicate()
|
|
18
|
+
):
|
|
19
|
+
logger.error("Exit condition met: %s", msg)
|
|
20
|
+
raise ExitEarlyError(msg)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"exit_if",
|
|
25
|
+
"ExitEarlyError",
|
|
26
|
+
]
|