quickpub 1.0.3__tar.gz → 2.0.1__tar.gz
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-1.0.3/quickpub.egg-info → quickpub-2.0.1}/PKG-INFO +12 -7
- {quickpub-1.0.3 → quickpub-2.0.1}/README.md +11 -6
- {quickpub-1.0.3 → quickpub-2.0.1}/pyproject.toml +2 -2
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/__main__.py +16 -21
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/classifiers.py +16 -0
- quickpub-2.0.1/quickpub/enforcers.py +17 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/proxy.py +1 -1
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/qa.py +9 -16
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/strategies/__init__.py +1 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/strategies/build_schema.py +2 -1
- quickpub-2.0.1/quickpub/strategies/constraint_enforcer.py +16 -0
- quickpub-2.0.1/quickpub/strategies/implementations/__init__.py +5 -0
- quickpub-2.0.1/quickpub/strategies/implementations/build_schemas/__init__.py +1 -0
- quickpub-2.0.1/quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +26 -0
- quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/__init__.py +5 -0
- quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +17 -0
- quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +58 -0
- quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +39 -0
- quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +29 -0
- quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +20 -0
- quickpub-2.0.1/quickpub/strategies/implementations/python_providers/__init__.py +2 -0
- quickpub-1.0.3/quickpub/strategies/implementations/conda_python_version_manager_strategy.py → quickpub-2.0.1/quickpub/strategies/implementations/python_providers/conda_python_provider.py +5 -10
- quickpub-1.0.3/quickpub/strategies/implementations/system_interpreter.py → quickpub-2.0.1/quickpub/strategies/implementations/python_providers/default_python_provider.py +6 -5
- quickpub-2.0.1/quickpub/strategies/implementations/quality_assurance_runners/__init__.py +4 -0
- {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.1/quickpub/strategies/implementations/quality_assurance_runners}/mypy_qa_runner.py +17 -9
- {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.1/quickpub/strategies/implementations/quality_assurance_runners}/pylint_qa_runner.py +2 -2
- {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.1/quickpub/strategies/implementations/quality_assurance_runners}/pytest_qa_runner.py +5 -4
- {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.1/quickpub/strategies/implementations/quality_assurance_runners}/unittest_qa_runner.py +21 -18
- quickpub-2.0.1/quickpub/strategies/implementations/upload_targets/__init__.py +2 -0
- {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.1/quickpub/strategies/implementations/upload_targets}/github_upload_target.py +2 -2
- {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.1/quickpub/strategies/implementations/upload_targets}/pypirc_upload_target.py +4 -4
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/strategies/python_provider.py +12 -1
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/strategies/quickpub_strategy.py +3 -1
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/strategies/upload_target.py +1 -1
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/structures/dependency.py +22 -16
- {quickpub-1.0.3 → quickpub-2.0.1/quickpub.egg-info}/PKG-INFO +12 -7
- quickpub-2.0.1/quickpub.egg-info/SOURCES.txt +51 -0
- quickpub-2.0.1/quickpub.egg-info/requires.txt +2 -0
- quickpub-1.0.3/quickpub/enforcers.py +0 -84
- quickpub-1.0.3/quickpub/strategies/implementations/__init__.py +0 -9
- quickpub-1.0.3/quickpub/strategies/implementations/setuptools_build_schema.py +0 -21
- quickpub-1.0.3/quickpub.egg-info/SOURCES.txt +0 -40
- quickpub-1.0.3/quickpub.egg-info/requires.txt +0 -1
- {quickpub-1.0.3 → quickpub-2.0.1}/LICENSE +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/MANIFEST.in +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/__init__.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/files.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/functions.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/py.typed +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/strategies/quality_assurance_runner.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/structures/__init__.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/structures/bound.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/structures/version.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub/validators.py +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub.egg-info/dependency_links.txt +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/quickpub.egg-info/top_level.txt +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/setup.cfg +0 -0
- {quickpub-1.0.3 → quickpub-2.0.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: quickpub
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: A python package to quickly configure and publish a new package
|
|
5
5
|
Author-email: danielnachumdev <danielnachumdev@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -38,9 +38,11 @@ License-File: LICENSE
|
|
|
38
38
|
**Tested python versions**: `3.8.0`, `3.9.0`, `3.10.13`,
|
|
39
39
|
|
|
40
40
|
Example usage of how this package was published
|
|
41
|
+
|
|
41
42
|
```python
|
|
42
|
-
from quickpub import publish, MypyRunner, PylintRunner, UnittestRunner, CondaPythonProvider,
|
|
43
|
-
PypircUploadTarget, SetuptoolsBuildSchema, GithubUploadTarget
|
|
43
|
+
from quickpub import publish, MypyRunner, PylintRunner, UnittestRunner, CondaPythonProvider,
|
|
44
|
+
PypircUploadTarget, SetuptoolsBuildSchema, GithubUploadTarget, PypircEnforcer, ReadmeEnforcer, LicenseEnforcer,
|
|
45
|
+
PypiRemoteVersionEnforcer, LocalVersionEnforcer
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
def main() -> None:
|
|
@@ -51,21 +53,24 @@ def main() -> None:
|
|
|
51
53
|
author_email="danielnachumdev@gmail.com",
|
|
52
54
|
description="A python package to quickly configure and publish a new package",
|
|
53
55
|
homepage="https://github.com/danielnachumdev/quickpub",
|
|
56
|
+
enforcers=[
|
|
57
|
+
PypircEnforcer(), ReadmeEnforcer(), LicenseEnforcer(),
|
|
58
|
+
LocalVersionEnforcer(), PypiRemoteVersionEnforcer()
|
|
59
|
+
],
|
|
54
60
|
build_schemas=[SetuptoolsBuildSchema()],
|
|
55
61
|
upload_targets=[PypircUploadTarget(), GithubUploadTarget()],
|
|
56
62
|
python_interpreter_provider=CondaPythonProvider(["base", "390", "380"]),
|
|
57
|
-
|
|
63
|
+
global_quality_assurance_runners=[
|
|
58
64
|
MypyRunner(bound="<=15", configuration_path="./mypy.ini"),
|
|
59
65
|
PylintRunner(bound=">=0.8", configuration_path="./.pylintrc"),
|
|
60
66
|
UnittestRunner(bound=">=0.8"),
|
|
61
67
|
],
|
|
62
|
-
dependencies=["danielutils>=0.9.
|
|
68
|
+
dependencies=["danielutils>=0.9.92", "requests"],
|
|
63
69
|
min_python="3.8.0",
|
|
70
|
+
demo=True
|
|
64
71
|
)
|
|
65
72
|
|
|
66
73
|
|
|
67
74
|
if __name__ == '__main__':
|
|
68
75
|
main()
|
|
69
|
-
|
|
70
|
-
|
|
71
76
|
```
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
**Tested python versions**: `3.8.0`, `3.9.0`, `3.10.13`,
|
|
3
3
|
|
|
4
4
|
Example usage of how this package was published
|
|
5
|
+
|
|
5
6
|
```python
|
|
6
|
-
from quickpub import publish, MypyRunner, PylintRunner, UnittestRunner, CondaPythonProvider,
|
|
7
|
-
PypircUploadTarget, SetuptoolsBuildSchema, GithubUploadTarget
|
|
7
|
+
from quickpub import publish, MypyRunner, PylintRunner, UnittestRunner, CondaPythonProvider,
|
|
8
|
+
PypircUploadTarget, SetuptoolsBuildSchema, GithubUploadTarget, PypircEnforcer, ReadmeEnforcer, LicenseEnforcer,
|
|
9
|
+
PypiRemoteVersionEnforcer, LocalVersionEnforcer
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def main() -> None:
|
|
@@ -15,21 +17,24 @@ def main() -> None:
|
|
|
15
17
|
author_email="danielnachumdev@gmail.com",
|
|
16
18
|
description="A python package to quickly configure and publish a new package",
|
|
17
19
|
homepage="https://github.com/danielnachumdev/quickpub",
|
|
20
|
+
enforcers=[
|
|
21
|
+
PypircEnforcer(), ReadmeEnforcer(), LicenseEnforcer(),
|
|
22
|
+
LocalVersionEnforcer(), PypiRemoteVersionEnforcer()
|
|
23
|
+
],
|
|
18
24
|
build_schemas=[SetuptoolsBuildSchema()],
|
|
19
25
|
upload_targets=[PypircUploadTarget(), GithubUploadTarget()],
|
|
20
26
|
python_interpreter_provider=CondaPythonProvider(["base", "390", "380"]),
|
|
21
|
-
|
|
27
|
+
global_quality_assurance_runners=[
|
|
22
28
|
MypyRunner(bound="<=15", configuration_path="./mypy.ini"),
|
|
23
29
|
PylintRunner(bound=">=0.8", configuration_path="./.pylintrc"),
|
|
24
30
|
UnittestRunner(bound=">=0.8"),
|
|
25
31
|
],
|
|
26
|
-
dependencies=["danielutils>=0.9.
|
|
32
|
+
dependencies=["danielutils>=0.9.92", "requests"],
|
|
27
33
|
min_python="3.8.0",
|
|
34
|
+
demo=True
|
|
28
35
|
)
|
|
29
36
|
|
|
30
37
|
|
|
31
38
|
if __name__ == '__main__':
|
|
32
39
|
main()
|
|
33
|
-
|
|
34
|
-
|
|
35
40
|
```
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quickpub"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.1"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "danielnachumdev", email = "danielnachumdev@gmail.com" },
|
|
10
10
|
]
|
|
11
|
-
dependencies = ['danielutils>=0.9.
|
|
11
|
+
dependencies = ['danielutils>=0.9.94', 'requests']
|
|
12
12
|
keywords = []
|
|
13
13
|
license = { "file" = "./LICENSE" }
|
|
14
14
|
description = "A python package to quickly configure and publish a new package"
|
|
@@ -2,13 +2,13 @@ import argparse
|
|
|
2
2
|
from typing import Optional, Union, List, Any
|
|
3
3
|
from danielutils import warning, file_exists, error
|
|
4
4
|
|
|
5
|
-
from .strategies import BuildSchema, UploadTarget, QualityAssuranceRunner, PythonProvider,
|
|
5
|
+
from .strategies import BuildSchema, ConstraintEnforcer, UploadTarget, QualityAssuranceRunner, PythonProvider, \
|
|
6
|
+
DefaultPythonProvider
|
|
6
7
|
from .validators import validate_version, validate_python_version, validate_keywords, validate_dependencies, \
|
|
7
8
|
validate_source
|
|
8
9
|
from .structures import Version, Dependency
|
|
9
10
|
from .files import create_toml, create_setup, create_manifest
|
|
10
11
|
from .classifiers import *
|
|
11
|
-
from .enforcers import enforce_local_correct_version, enforce_pypirc_exists, exit_if, enforce_remote_correct_version
|
|
12
12
|
from .qa import qa
|
|
13
13
|
|
|
14
14
|
|
|
@@ -22,8 +22,9 @@ def publish(
|
|
|
22
22
|
|
|
23
23
|
build_schemas: List[BuildSchema],
|
|
24
24
|
upload_targets: List[UploadTarget],
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
enforcers: Optional[List[ConstraintEnforcer]] = None,
|
|
26
|
+
global_quality_assurance_runners: Optional[List[QualityAssuranceRunner]] = None,
|
|
27
|
+
python_interpreter_provider: PythonProvider = DefaultPythonProvider(),
|
|
27
28
|
|
|
28
29
|
readme_file_path: str = "./README.md",
|
|
29
30
|
license_file_path: str = "./LICENSE",
|
|
@@ -43,11 +44,11 @@ def publish(
|
|
|
43
44
|
:param author_email: The email of the author.
|
|
44
45
|
:param description: A short description of the package.
|
|
45
46
|
:param homepage: The homepage URL for the package (e.g., GitHub repository).
|
|
46
|
-
:param
|
|
47
|
+
:param global_quality_assurance_runners: Strategies for quality assurance. These will run on all Envs supplies by the supplier.
|
|
47
48
|
:param build_schemas: Strategies for building the package.
|
|
48
49
|
:param upload_targets: Strategies for uploading the package.
|
|
49
50
|
:param python_interpreter_provider: Strategy for managing Python versions. Defaults to SystemInterpreter().
|
|
50
|
-
:param explicit_src_folder_path: The path to the source code of the package. Defaults to
|
|
51
|
+
:param explicit_src_folder_path: The path to the source code of the package. Defaults to <current working directory>/<name>.
|
|
51
52
|
:param version: The version for the new distribution. Defaults to "0.0.1".
|
|
52
53
|
:param readme_file_path: The path to the README file. Defaults to "./README.md".
|
|
53
54
|
:param license_file_path: The path to the license file. Defaults to "./LICENSE".
|
|
@@ -60,28 +61,22 @@ def publish(
|
|
|
60
61
|
Returns:
|
|
61
62
|
None
|
|
62
63
|
"""
|
|
63
|
-
|
|
64
|
-
enforce_pypirc_exists()
|
|
64
|
+
version = validate_version(version)
|
|
65
65
|
explicit_src_folder_path = validate_source(name, explicit_src_folder_path)
|
|
66
66
|
if explicit_src_folder_path != f"./{name}":
|
|
67
67
|
warning(
|
|
68
68
|
"The source folder's name is different from the package's name. this may not be currently supported correctly")
|
|
69
|
-
exit_if(not file_exists(readme_file_path), f"Could not find readme file at {readme_file_path}")
|
|
70
|
-
exit_if(not file_exists(license_file_path), f"Could not find license file at {license_file_path}")
|
|
71
|
-
version = validate_version(version)
|
|
72
|
-
if not demo:
|
|
73
|
-
enforce_local_correct_version(name, version)
|
|
74
69
|
min_python = validate_python_version(min_python) # type:ignore
|
|
75
70
|
keywords = validate_keywords(keywords)
|
|
76
71
|
validated_dependencies: List[Dependency] = validate_dependencies(dependencies)
|
|
77
|
-
enforce_remote_correct_version(name, version)
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
for enforcer in enforcers or []:
|
|
74
|
+
enforcer.enforce(name=name, version=version, demo=demo)
|
|
75
|
+
|
|
81
76
|
try:
|
|
82
77
|
res = qa(
|
|
83
78
|
python_interpreter_provider,
|
|
84
|
-
|
|
79
|
+
global_quality_assurance_runners or [],
|
|
85
80
|
name,
|
|
86
81
|
explicit_src_folder_path,
|
|
87
82
|
validated_dependencies
|
|
@@ -119,10 +114,10 @@ def publish(
|
|
|
119
114
|
)
|
|
120
115
|
create_manifest(name=name)
|
|
121
116
|
if not demo:
|
|
122
|
-
for
|
|
123
|
-
|
|
124
|
-
for
|
|
125
|
-
|
|
117
|
+
for schema in build_schemas:
|
|
118
|
+
schema.build()
|
|
119
|
+
for target in upload_targets:
|
|
120
|
+
target.upload(name=name, version=version)
|
|
126
121
|
|
|
127
122
|
|
|
128
123
|
def parse_args():
|
|
@@ -3,6 +3,10 @@ from enum import Enum
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Classifier(Enum):
|
|
6
|
+
"""
|
|
7
|
+
A base enum class for all classifiers.
|
|
8
|
+
"""
|
|
9
|
+
|
|
6
10
|
def _str(self) -> str:
|
|
7
11
|
return str(self.value)
|
|
8
12
|
|
|
@@ -36,6 +40,9 @@ class Classifier(Enum):
|
|
|
36
40
|
|
|
37
41
|
|
|
38
42
|
class DevelopmentStatusClassifier(Classifier):
|
|
43
|
+
"""
|
|
44
|
+
An enum class for specifying the development state of the package
|
|
45
|
+
"""
|
|
39
46
|
# https://pypi.org/classifiers/
|
|
40
47
|
Planning = 1
|
|
41
48
|
PreAlpha = 2
|
|
@@ -51,15 +58,24 @@ class DevelopmentStatusClassifier(Classifier):
|
|
|
51
58
|
|
|
52
59
|
|
|
53
60
|
class IntendedAudienceClassifier(Classifier):
|
|
61
|
+
"""
|
|
62
|
+
An enum class for specifying the intended audience
|
|
63
|
+
"""
|
|
54
64
|
CustomerService = "CustomerService"
|
|
55
65
|
Developers = "Developers"
|
|
56
66
|
|
|
57
67
|
|
|
58
68
|
class ProgrammingLanguageClassifier(Classifier):
|
|
69
|
+
"""
|
|
70
|
+
An enum class for specifying the target language level
|
|
71
|
+
"""
|
|
59
72
|
Python3 = "Python :: 3"
|
|
60
73
|
|
|
61
74
|
|
|
62
75
|
class OperatingSystemClassifier(Classifier):
|
|
76
|
+
"""
|
|
77
|
+
An enum class for specifying the target operating system
|
|
78
|
+
"""
|
|
63
79
|
MicrosoftWindows = "Microsoft :: Windows"
|
|
64
80
|
|
|
65
81
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Union, Callable
|
|
3
|
+
|
|
4
|
+
from danielutils import error
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def exit_if(predicate: Union[bool, Callable[[], bool]], msg: str, *, verbose: bool = True,
|
|
8
|
+
err_func: Callable[[str], None] = error) -> None:
|
|
9
|
+
if (isinstance(predicate, bool) and predicate) or (callable(predicate) and predicate()):
|
|
10
|
+
if verbose:
|
|
11
|
+
err_func(msg)
|
|
12
|
+
sys.exit(1)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"exit_if"
|
|
17
|
+
]
|
|
@@ -42,7 +42,7 @@ def global_import_sanity_check(package_name: str, executor: LayeredCommand, is_s
|
|
|
42
42
|
p = sys.executable if is_system_interpreter else "python"
|
|
43
43
|
file_name = "./__sanity_check_main.py"
|
|
44
44
|
with TemporaryFile(file_name) as f:
|
|
45
|
-
f.
|
|
45
|
+
f.writelines([f"from {package_name} import *"])
|
|
46
46
|
code, _, _ = executor(f"{p} {file_name}")
|
|
47
47
|
exit_if(code != 0,
|
|
48
48
|
f"Env '{env_name}' failed sanity check. "
|
|
@@ -122,32 +122,25 @@ def create_pool_print_error(pool: ProgressBarPool):
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
def qa(
|
|
125
|
-
|
|
125
|
+
python_provider: PythonProvider,
|
|
126
126
|
quality_assurance_strategies: List[QualityAssuranceRunner],
|
|
127
127
|
package_name: str,
|
|
128
|
-
src_folder_path:
|
|
128
|
+
src_folder_path: str,
|
|
129
129
|
dependencies: list
|
|
130
130
|
) -> bool:
|
|
131
|
-
from .strategies import
|
|
131
|
+
from .strategies import DefaultPythonProvider
|
|
132
132
|
result = True
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
is_system_interpreter = isinstance(python_version_manager, DefaultInterpreterProvider)
|
|
136
|
-
pool = create_progress_bar_pool(python_version_manager, quality_assurance_strategies)
|
|
133
|
+
is_system_interpreter = isinstance(python_provider, DefaultPythonProvider)
|
|
134
|
+
pool = create_progress_bar_pool(python_provider, quality_assurance_strategies)
|
|
137
135
|
pool_err = create_pool_print_error(pool)
|
|
138
|
-
with
|
|
139
|
-
AttrContext(LayeredCommand, 'class_flush_stdout', False),
|
|
140
|
-
AttrContext(LayeredCommand, 'class_flush_stderr', False),
|
|
141
|
-
AttrContext(LayeredCommand, 'class_raise_on_fail', False),
|
|
142
|
-
base := LayeredCommand()
|
|
143
|
-
):
|
|
136
|
+
with LayeredCommand() as base:
|
|
144
137
|
for env_name, executor in pool[0]:
|
|
145
138
|
pool[0].desc = f"Env '{env_name}'"
|
|
146
139
|
pool[0].update(0, refresh=True)
|
|
147
140
|
with executor:
|
|
148
141
|
executor._prev_instance = base
|
|
149
142
|
try:
|
|
150
|
-
validate_dependencies(
|
|
143
|
+
validate_dependencies(python_provider, dependencies, executor, env_name, pool_err)
|
|
151
144
|
except SystemExit:
|
|
152
145
|
result = False
|
|
153
146
|
continue
|
|
@@ -177,7 +170,7 @@ def qa(
|
|
|
177
170
|
f"Failed running '{runner.__class__.__name__}' on env '{env_name}'. "
|
|
178
171
|
f"Try manually: '{manual_command}'.")
|
|
179
172
|
pool.write(f"\tCaused by '{e.__cause__ or e}'")
|
|
180
|
-
if
|
|
173
|
+
if python_provider.exit_on_fail:
|
|
181
174
|
raise RuntimeError() from e
|
|
182
175
|
return result
|
|
183
176
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
+
from typing import Type
|
|
2
3
|
|
|
3
4
|
from .quickpub_strategy import QuickpubStrategy
|
|
4
5
|
|
|
@@ -8,7 +9,7 @@ class BuildSchema(QuickpubStrategy):
|
|
|
8
9
|
self.verbose = verbose
|
|
9
10
|
|
|
10
11
|
@abstractmethod
|
|
11
|
-
def
|
|
12
|
+
def build(self, *args, **kwargs) -> None: ...
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
__all__ = [
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import Type
|
|
3
|
+
|
|
4
|
+
from .quickpub_strategy import QuickpubStrategy
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConstraintEnforcer(QuickpubStrategy):
|
|
8
|
+
EXCEPTION_TYPE: Type[BaseException] = SystemExit
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def enforce(self, **kwargs) -> None: ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'ConstraintEnforcer'
|
|
16
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .setuptools_build_schema import *
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from danielutils import info, file_exists, LayeredCommand
|
|
4
|
+
|
|
5
|
+
from ...build_schema import BuildSchema
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SetuptoolsBuildSchema(BuildSchema):
|
|
9
|
+
def __init__(self, setup_file_path: str = "./setup.py", backend: Literal["toml"] = "toml") -> None:
|
|
10
|
+
self._backend = backend
|
|
11
|
+
self._setup_file_path = setup_file_path
|
|
12
|
+
|
|
13
|
+
def build(self, verbose: bool = False, *args, **kwargs) -> None:
|
|
14
|
+
if not file_exists(self._setup_file_path):
|
|
15
|
+
raise self.EXCEPTION_TYPE(f"Could not find {self._setup_file_path} file")
|
|
16
|
+
if verbose:
|
|
17
|
+
info("Creating new distribution...")
|
|
18
|
+
with LayeredCommand() as exc:
|
|
19
|
+
ret, stdout, stderr = exc("python " + self._setup_file_path + " sdist")
|
|
20
|
+
if ret != 0:
|
|
21
|
+
raise self.EXCEPTION_TYPE(stderr)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"SetuptoolsBuildSchema",
|
|
26
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from danielutils import file_exists
|
|
2
|
+
|
|
3
|
+
from ...constraint_enforcer import ConstraintEnforcer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LicenseEnforcer(ConstraintEnforcer):
|
|
7
|
+
def __init__(self, path: str = "./LICENSE") -> None:
|
|
8
|
+
self.path = path
|
|
9
|
+
|
|
10
|
+
def enforce(self, **kwargs) -> None:
|
|
11
|
+
if not file_exists(self.path):
|
|
12
|
+
raise self.EXCEPTION_TYPE(f"Could not find license file at '{self.path}'")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"LicenseEnforcer",
|
|
17
|
+
]
|
quickpub-2.0.1/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from danielutils import directory_exists, get_files, get_python_version
|
|
2
|
+
|
|
3
|
+
from quickpub import Version
|
|
4
|
+
from ...constraint_enforcer import ConstraintEnforcer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _remove_suffix(s: str, suffix: str) -> str:
|
|
8
|
+
"""
|
|
9
|
+
This function is needed because str.removesuffix is not implemented in python == 3.8.0
|
|
10
|
+
:param s: string to remove from
|
|
11
|
+
:param suffix: substring to remove
|
|
12
|
+
:return: modified string
|
|
13
|
+
"""
|
|
14
|
+
if get_python_version() >= (3, 9):
|
|
15
|
+
return s.removesuffix(suffix) # type:ignore
|
|
16
|
+
return _remove_prefix(s[::-1], suffix[::-1])[::-1]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _remove_prefix(s: str, prefix: str) -> str:
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
:param s:
|
|
23
|
+
:param prefix:
|
|
24
|
+
:return:
|
|
25
|
+
"""
|
|
26
|
+
if get_python_version() >= (3, 9):
|
|
27
|
+
return s.removeprefix(prefix) # type:ignore
|
|
28
|
+
|
|
29
|
+
if s.startswith(prefix):
|
|
30
|
+
return s[len(prefix):]
|
|
31
|
+
return s
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LocalVersionEnforcer(ConstraintEnforcer):
|
|
35
|
+
def enforce(self, name: str, version: Version, demo: bool = False, **kwargs) -> None: # type: ignore
|
|
36
|
+
if demo:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
if not directory_exists("./dist"):
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
prev_builds = get_files("./dist")
|
|
43
|
+
if len(prev_builds) == 0:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
max_local_version = Version(0, 0, 0)
|
|
47
|
+
for d in prev_builds:
|
|
48
|
+
d = _remove_suffix(_remove_prefix(d, f"{name}-"), ".tar.gz")
|
|
49
|
+
v: Version = Version.from_str(d)
|
|
50
|
+
max_local_version = max(max_local_version, v)
|
|
51
|
+
if version <= max_local_version:
|
|
52
|
+
raise self.EXCEPTION_TYPE(
|
|
53
|
+
f"Specified version is '{version}' but (locally available) latest existing is '{max_local_version}'")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
'LocalVersionEnforcer'
|
|
58
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from danielutils import RetryExecutor, ExponentialBackOffStrategy, ConstantBackOffStrategy
|
|
2
|
+
from requests import Response
|
|
3
|
+
|
|
4
|
+
from proxy import get # type: ignore
|
|
5
|
+
from quickpub import Version
|
|
6
|
+
from ...constraint_enforcer import ConstraintEnforcer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PypiRemoteVersionEnforcer(ConstraintEnforcer):
|
|
10
|
+
_HTTP_FAILED_MESSAGE: str = "Failed to send http request"
|
|
11
|
+
|
|
12
|
+
def enforce(self, name: str, version: Version, demo: bool = False, **kwargs) -> None: # type: ignore
|
|
13
|
+
if demo:
|
|
14
|
+
return
|
|
15
|
+
url = f"https://pypi.org/project/{name}/"
|
|
16
|
+
|
|
17
|
+
timeout_strategy = ExponentialBackOffStrategy(1.1, 1.5)
|
|
18
|
+
|
|
19
|
+
def wrapper() -> Response:
|
|
20
|
+
return get(url, timeout=timeout_strategy.get_backoff())
|
|
21
|
+
|
|
22
|
+
executor: RetryExecutor[Response] = RetryExecutor(ConstantBackOffStrategy(1))
|
|
23
|
+
response = executor.execute(wrapper, 5)
|
|
24
|
+
if response is None:
|
|
25
|
+
raise SystemExit(self._HTTP_FAILED_MESSAGE)
|
|
26
|
+
html = response.content.decode()
|
|
27
|
+
i = html.index(f" {name} ")
|
|
28
|
+
try:
|
|
29
|
+
remote_version = Version.from_str(html[i:i + 50].splitlines()[0].strip().split()[1])
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise SystemExit(f"{self.__class__.__name__} encountered an unexpected error while parsing.") from e
|
|
32
|
+
if not version > remote_version:
|
|
33
|
+
raise SystemExit(
|
|
34
|
+
f"Specified version is '{version}' but (remotely available) latest existing is '{remote_version}'")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
'PypiRemoteVersionEnforcer'
|
|
39
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from danielutils import file_exists
|
|
4
|
+
|
|
5
|
+
from ...constraint_enforcer import ConstraintEnforcer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PypircEnforcer(ConstraintEnforcer):
|
|
9
|
+
PYPIRC_REGEX: re.Pattern = re.compile(
|
|
10
|
+
r"\[distutils\]\nindex-servers =\n\s*pypi\n\s*testpypi\n\n\[pypi\]\n\s*username = __token__\n\s*password = .+\n\n\[testpypi\]\n\s*username = __token__\n\s*password = .+\n?") # pylint: disable=line-too-long
|
|
11
|
+
|
|
12
|
+
def __init__(self, path: str = "./.pypirc", should_enforce_expected_format: bool = True) -> None:
|
|
13
|
+
self.path = path
|
|
14
|
+
self.should_enforce_expected_format = should_enforce_expected_format
|
|
15
|
+
|
|
16
|
+
def enforce(self, **kwargs) -> None:
|
|
17
|
+
if not file_exists(self.path):
|
|
18
|
+
raise SystemExit(f"Couldn't find '{self.path}'")
|
|
19
|
+
if self.should_enforce_expected_format:
|
|
20
|
+
with open(self.path, "r") as f:
|
|
21
|
+
text = f.read()
|
|
22
|
+
|
|
23
|
+
if not self.PYPIRC_REGEX.match(text):
|
|
24
|
+
raise self.EXCEPTION_TYPE(f"Couldn't enforce '{self.path}'")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"PypircEnforcer",
|
|
29
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
from danielutils import file_exists
|
|
4
|
+
|
|
5
|
+
from ...constraint_enforcer import ConstraintEnforcer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReadmeEnforcer(ConstraintEnforcer):
|
|
9
|
+
|
|
10
|
+
def __init__(self, path: str = "./README.md") -> None:
|
|
11
|
+
self.path = path
|
|
12
|
+
|
|
13
|
+
def enforce(self, **kwargs) -> None:
|
|
14
|
+
if not file_exists(self.path):
|
|
15
|
+
raise self.EXCEPTION_TYPE(f"Could not find readme file at '{self.path}'")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"ReadmeEnforcer",
|
|
20
|
+
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Tuple, Optional, Set, Iterator, List
|
|
2
2
|
from danielutils import LayeredCommand, warning
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from ...python_provider import PythonProvider
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class CondaPythonProvider(PythonProvider):
|
|
@@ -12,16 +12,11 @@ class CondaPythonProvider(PythonProvider):
|
|
|
12
12
|
PythonProvider.__init__(self, requested_envs=env_names, explicit_versions=[], exit_on_fail=True)
|
|
13
13
|
self._cached_available_envs: Optional[Set[str]] = None
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
with LayeredCommand(instance_flush_stdout=False, instance_flush_stderr=False) as base:
|
|
15
|
+
@classmethod
|
|
16
|
+
def _get_available_envs_impl(cls) -> Set[str]:
|
|
17
|
+
with LayeredCommand() as base:
|
|
20
18
|
code, out, err = base("conda env list")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self._cached_available_envs = res
|
|
24
|
-
return res
|
|
19
|
+
return set([line.split(' ')[0] for line in out[2:] if len(line.split(' ')) > 1])
|
|
25
20
|
|
|
26
21
|
def __iter__(self) -> Iterator[Tuple[str, LayeredCommand]]:
|
|
27
22
|
available_envs = self.get_available_envs()
|
|
@@ -3,10 +3,10 @@ from typing import Set, Tuple, Iterator
|
|
|
3
3
|
|
|
4
4
|
from danielutils import LayeredCommand
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from ...python_provider import PythonProvider
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class DefaultPythonProvider(PythonProvider):
|
|
10
10
|
def get_python_executable_name(self) -> str:
|
|
11
11
|
return sys.executable
|
|
12
12
|
|
|
@@ -14,12 +14,13 @@ class DefaultInterpreterProvider(PythonProvider):
|
|
|
14
14
|
PythonProvider.__init__(self, requested_envs=["system"], explicit_versions=[], exit_on_fail=True)
|
|
15
15
|
|
|
16
16
|
def __iter__(self) -> Iterator[Tuple[str, LayeredCommand]]:
|
|
17
|
-
return iter([("system", LayeredCommand(""))])
|
|
17
|
+
return iter([("system", LayeredCommand("", instance_flush_stderr=False, instance_flush_stdout=False))])
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
@classmethod
|
|
20
|
+
def _get_available_envs_impl(cls) -> Set[str]:
|
|
20
21
|
return set("system")
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
__all__ = [
|
|
24
|
-
"
|
|
25
|
+
"DefaultPythonProvider",
|
|
25
26
|
]
|