quickpub 1.0.3__tar.gz → 2.0.0__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.
Files changed (58) hide show
  1. {quickpub-1.0.3/quickpub.egg-info → quickpub-2.0.0}/PKG-INFO +12 -7
  2. {quickpub-1.0.3 → quickpub-2.0.0}/README.md +11 -6
  3. {quickpub-1.0.3 → quickpub-2.0.0}/pyproject.toml +2 -2
  4. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/__main__.py +16 -21
  5. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/classifiers.py +16 -0
  6. quickpub-2.0.0/quickpub/enforcers.py +17 -0
  7. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/proxy.py +1 -1
  8. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/qa.py +7 -9
  9. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/strategies/__init__.py +1 -0
  10. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/strategies/build_schema.py +2 -1
  11. quickpub-2.0.0/quickpub/strategies/constraint_enforcer.py +16 -0
  12. quickpub-2.0.0/quickpub/strategies/implementations/__init__.py +5 -0
  13. quickpub-2.0.0/quickpub/strategies/implementations/build_schemas/__init__.py +1 -0
  14. quickpub-2.0.0/quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +27 -0
  15. quickpub-2.0.0/quickpub/strategies/implementations/constraint_enforcers/__init__.py +5 -0
  16. quickpub-2.0.0/quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +17 -0
  17. quickpub-2.0.0/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +58 -0
  18. quickpub-2.0.0/quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +39 -0
  19. quickpub-2.0.0/quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +29 -0
  20. quickpub-2.0.0/quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +20 -0
  21. quickpub-2.0.0/quickpub/strategies/implementations/python_providers/__init__.py +2 -0
  22. quickpub-1.0.3/quickpub/strategies/implementations/conda_python_version_manager_strategy.py → quickpub-2.0.0/quickpub/strategies/implementations/python_providers/conda_python_provider.py +6 -10
  23. quickpub-1.0.3/quickpub/strategies/implementations/system_interpreter.py → quickpub-2.0.0/quickpub/strategies/implementations/python_providers/default_python_provider.py +6 -5
  24. quickpub-2.0.0/quickpub/strategies/implementations/quality_assurance_runners/__init__.py +4 -0
  25. {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.0/quickpub/strategies/implementations/quality_assurance_runners}/mypy_qa_runner.py +17 -9
  26. {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.0/quickpub/strategies/implementations/quality_assurance_runners}/pylint_qa_runner.py +2 -2
  27. {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.0/quickpub/strategies/implementations/quality_assurance_runners}/pytest_qa_runner.py +5 -4
  28. {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.0/quickpub/strategies/implementations/quality_assurance_runners}/unittest_qa_runner.py +21 -18
  29. quickpub-2.0.0/quickpub/strategies/implementations/upload_targets/__init__.py +2 -0
  30. {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.0/quickpub/strategies/implementations/upload_targets}/github_upload_target.py +2 -2
  31. {quickpub-1.0.3/quickpub/strategies/implementations → quickpub-2.0.0/quickpub/strategies/implementations/upload_targets}/pypirc_upload_target.py +2 -2
  32. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/strategies/python_provider.py +12 -1
  33. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/strategies/quickpub_strategy.py +3 -1
  34. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/strategies/upload_target.py +1 -1
  35. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/structures/dependency.py +22 -16
  36. {quickpub-1.0.3 → quickpub-2.0.0/quickpub.egg-info}/PKG-INFO +12 -7
  37. quickpub-2.0.0/quickpub.egg-info/SOURCES.txt +51 -0
  38. quickpub-2.0.0/quickpub.egg-info/requires.txt +2 -0
  39. quickpub-1.0.3/quickpub/enforcers.py +0 -84
  40. quickpub-1.0.3/quickpub/strategies/implementations/__init__.py +0 -9
  41. quickpub-1.0.3/quickpub/strategies/implementations/setuptools_build_schema.py +0 -21
  42. quickpub-1.0.3/quickpub.egg-info/SOURCES.txt +0 -40
  43. quickpub-1.0.3/quickpub.egg-info/requires.txt +0 -1
  44. {quickpub-1.0.3 → quickpub-2.0.0}/LICENSE +0 -0
  45. {quickpub-1.0.3 → quickpub-2.0.0}/MANIFEST.in +0 -0
  46. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/__init__.py +0 -0
  47. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/files.py +0 -0
  48. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/functions.py +0 -0
  49. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/py.typed +0 -0
  50. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/strategies/quality_assurance_runner.py +0 -0
  51. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/structures/__init__.py +0 -0
  52. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/structures/bound.py +0 -0
  53. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/structures/version.py +0 -0
  54. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub/validators.py +0 -0
  55. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub.egg-info/dependency_links.txt +0 -0
  56. {quickpub-1.0.3 → quickpub-2.0.0}/quickpub.egg-info/top_level.txt +0 -0
  57. {quickpub-1.0.3 → quickpub-2.0.0}/setup.cfg +0 -0
  58. {quickpub-1.0.3 → quickpub-2.0.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quickpub
3
- Version: 1.0.3
3
+ Version: 2.0.0
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
- quality_assurance_runners=[
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.90"],
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
- quality_assurance_runners=[
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.90"],
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 = "1.0.3"
7
+ version = "2.0.0"
8
8
  authors = [
9
9
  { name = "danielnachumdev", email = "danielnachumdev@gmail.com" },
10
10
  ]
11
- dependencies = ['danielutils>=0.9.90']
11
+ dependencies = ['danielutils>=0.9.92', '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, DefaultInterpreterProvider
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
- quality_assurance_runners: Optional[List[QualityAssuranceRunner]] = None,
26
- python_interpreter_provider: PythonProvider = DefaultInterpreterProvider(),
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 quality_assurance_runners: Strategies for quality assurance.
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 the current working directory/<name>.
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
- if quality_assurance_runners is None:
80
- quality_assurance_runners = []
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
- quality_assurance_runners,
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 build_strategy in build_schemas:
123
- build_strategy.execute_strategy()
124
- for upload_strategy in upload_targets:
125
- upload_strategy.execute_strategy(name=name, version=version)
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
+ ]
@@ -13,7 +13,7 @@ def os_system(command) -> int:
13
13
  return os.system(command)
14
14
 
15
15
 
16
- def get(*args, **kwargs):
16
+ def get(*args, **kwargs) -> requests.models.Response:
17
17
  return requests.get(*args, **kwargs)
18
18
 
19
19
 
@@ -122,18 +122,16 @@ def create_pool_print_error(pool: ProgressBarPool):
122
122
 
123
123
 
124
124
  def qa(
125
- python_version_manager: PythonProvider,
125
+ python_provider: PythonProvider,
126
126
  quality_assurance_strategies: List[QualityAssuranceRunner],
127
127
  package_name: str,
128
- src_folder_path: Optional[str],
128
+ src_folder_path: str,
129
129
  dependencies: list
130
130
  ) -> bool:
131
- from .strategies import DefaultInterpreterProvider
131
+ from .strategies import DefaultPythonProvider
132
132
  result = True
133
- if python_version_manager is None:
134
- python_version_manager = DefaultInterpreterProvider()
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
136
  with MultiContext(
139
137
  AttrContext(LayeredCommand, 'class_flush_stdout', False),
@@ -147,7 +145,7 @@ def qa(
147
145
  with executor:
148
146
  executor._prev_instance = base
149
147
  try:
150
- validate_dependencies(python_version_manager, dependencies, executor, env_name, pool_err)
148
+ validate_dependencies(python_provider, dependencies, executor, env_name, pool_err)
151
149
  except SystemExit:
152
150
  result = False
153
151
  continue
@@ -177,7 +175,7 @@ def qa(
177
175
  f"Failed running '{runner.__class__.__name__}' on env '{env_name}'. "
178
176
  f"Try manually: '{manual_command}'.")
179
177
  pool.write(f"\tCaused by '{e.__cause__ or e}'")
180
- if python_version_manager.exit_on_fail:
178
+ if python_provider.exit_on_fail:
181
179
  raise RuntimeError() from e
182
180
  return result
183
181
 
@@ -4,3 +4,4 @@ from .quality_assurance_runner import *
4
4
  from .quickpub_strategy import *
5
5
  from .build_schema import *
6
6
  from .python_provider import *
7
+ from .constraint_enforcer import *
@@ -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 execute_strategy(self, *args, **kwargs) -> None: ...
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,5 @@
1
+ from .constraint_enforcers import *
2
+ from .build_schemas import *
3
+ from .quality_assurance_runners import *
4
+ from .upload_targets import *
5
+ from .python_providers import *
@@ -0,0 +1 @@
1
+ from .setuptools_build_schema import *
@@ -0,0 +1,27 @@
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("", instance_flush_stdout=False, instance_flush_stderr=False,
19
+ instance_raise_on_fail=False) as exc:
20
+ ret, stdout, stderr = exc("python " + self._setup_file_path + " sdist")
21
+ if ret != 0:
22
+ raise self.EXCEPTION_TYPE(stderr)
23
+
24
+
25
+ __all__ = [
26
+ "SetuptoolsBuildSchema",
27
+ ]
@@ -0,0 +1,5 @@
1
+ from .local_version_enforcer import *
2
+ from .license_enforcer import *
3
+ from .pypi_remote_version_enforcer import *
4
+ from .pypirc_enforcer import *
5
+ from .readme_enforcer import *
@@ -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
+ ]
@@ -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 pypi\n testpypi\n\n\[pypi\]\n username = __token__\n password = .+\n\n\[testpypi\]\n username = __token__\n 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
+ ]
@@ -0,0 +1,2 @@
1
+ from .conda_python_provider import *
2
+ from .default_python_provider import *
@@ -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 ..python_provider import PythonProvider
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
- def get_available_envs(self) -> Set[str]:
16
- if self._cached_available_envs is not None:
17
- return self._cached_available_envs
18
-
15
+ @classmethod
16
+ def _get_available_envs_impl(cls) -> Set[str]:
19
17
  with LayeredCommand(instance_flush_stdout=False, instance_flush_stderr=False) as base:
20
18
  code, out, err = base("conda env list")
21
- res = set([line.split(' ')[0] for line in out[2:] if len(line.split(' ')) > 1])
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()
@@ -29,7 +24,8 @@ class CondaPythonProvider(PythonProvider):
29
24
  if name not in available_envs:
30
25
  warning(f"Couldn't find env '{name}'")
31
26
  continue
32
- yield name, LayeredCommand(f"conda activate {name}")
27
+ yield name, LayeredCommand(f"conda activate {name}", instance_flush_stdout=False,
28
+ instance_flush_stderr=False)
33
29
 
34
30
 
35
31
  __all__ = [
@@ -3,10 +3,10 @@ from typing import Set, Tuple, Iterator
3
3
 
4
4
  from danielutils import LayeredCommand
5
5
 
6
- from ..python_provider import PythonProvider
6
+ from ...python_provider import PythonProvider
7
7
 
8
8
 
9
- class DefaultInterpreterProvider(PythonProvider):
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
- def get_available_envs(self) -> Set[str]:
19
+ @classmethod
20
+ def _get_available_envs_impl(cls) -> Set[str]:
20
21
  return set("system")
21
22
 
22
23
 
23
24
  __all__ = [
24
- "DefaultInterpreterProvider",
25
+ "DefaultPythonProvider",
25
26
  ]
@@ -0,0 +1,4 @@
1
+ from .mypy_qa_runner import *
2
+ from .pylint_qa_runner import *
3
+ from .pytest_qa_runner import *
4
+ from .unittest_qa_runner import *