csspin-python 3.1.0__py3-none-any.whl → 3.2.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.
csspin_python/python.py CHANGED
@@ -74,9 +74,10 @@ import os
74
74
  import re
75
75
  import shutil
76
76
  import sys
77
+ from contextlib import contextmanager
77
78
  from subprocess import check_output
78
79
  from textwrap import dedent, indent
79
- from typing import Iterable, Type, Union
80
+ from typing import Generator, Iterable, Type, Union
80
81
 
81
82
  try:
82
83
  from typing import Self # type: ignore[attr-defined]
@@ -159,33 +160,13 @@ defaults = config(
159
160
  key_duration=3600 * 10, # 10 hours
160
161
  static_oidc=False,
161
162
  index="16.0/simple",
163
+ # Need to set client secret to empty string, otherwise the string "None"
164
+ # would be handled as secret and obfucsated in logs.
165
+ client_secret="", # nosec: B106
162
166
  ),
163
167
  index_url="https://pypi.org/simple",
164
- requires=config(
165
- python=["build", "wheel"],
166
- system=config(
167
- debian=config(
168
- apt=[
169
- "build-essential",
170
- "curl",
171
- "git",
172
- "libbz2-dev",
173
- "libffi-dev",
174
- "libkrb5-dev",
175
- "liblzma-dev",
176
- "libncursesw5-dev",
177
- "libreadline-dev",
178
- "libsqlite3-dev",
179
- "libssl-dev",
180
- "libxml2-dev",
181
- "libxmlsec1-dev",
182
- "make",
183
- "xz-utils",
184
- "zlib1g-dev",
185
- ]
186
- )
187
- ),
188
- ),
168
+ skip_js_build=None,
169
+ requires=config(python=["build", "wheel"]),
189
170
  )
190
171
 
191
172
 
@@ -310,12 +291,23 @@ def nuget_install(cfg: ConfigTree) -> None:
310
291
 
311
292
  def provision(cfg: ConfigTree) -> None:
312
293
  """Provision the python plugin"""
294
+ if not cfg.python.use and exists(cfg.python.python):
295
+ python_version = (
296
+ check_output([cfg.python.python, "--version"])
297
+ .decode()
298
+ .strip()
299
+ .replace("Python ", "")
300
+ )
301
+ if python_version and not python_version.startswith(cfg.python.version):
302
+ _cleanup_memoed_provisioners(cfg)
303
+ rmtree(cfg.python.provisioner_memo)
304
+ rmtree(cfg.python.aws_auth.memo)
305
+ rmtree(cfg.python.venv)
313
306
  with memoizer(cfg.python.provisioner_memo) as memo:
314
307
  if cfg.python.provisioner is None:
315
308
  cfg.python.provisioner = SimpleProvisioner(cfg)
316
309
  if not memo.check(cfg.python.provisioner):
317
310
  memo.add(cfg.python.provisioner)
318
-
319
311
  if not shutil.which(cfg.python.interpreter):
320
312
  cfg.python.provisioner.provision_python(cfg)
321
313
 
@@ -353,6 +345,10 @@ def configure(cfg: ConfigTree) -> None:
353
345
 
354
346
  if exists(cfg.python.python):
355
347
  cfg.python.site_packages = get_site_packages(interpreter=cfg.python.python)
348
+ # Built JS should still be available in this case
349
+ # Skip only if users hasn't explicitly set it to False
350
+ if cfg.python.skip_js_build is None:
351
+ cfg.python.skip_js_build = True
356
352
 
357
353
  if cfg.python.aws_auth.enabled:
358
354
  _check_aws_token_validity(cfg)
@@ -831,13 +827,31 @@ class SimpleProvisioner(ProvisionerProtocol):
831
827
  )
832
828
 
833
829
  def install(self: Self, cfg: ConfigTree) -> None:
834
- if requirements := self._filter(
835
- self._requirements, self._m, cfg.spin.project_root
836
- ):
837
- self._install_command(*self._split(requirements))
838
- self._m.clear()
839
- for req in requirements:
840
- self._m.add(_req_for_memo(req, cfg.spin.project_root))
830
+ @contextmanager
831
+ def skip_js_build(cfg: ConfigTree) -> Generator[None, None, None]:
832
+ if cfg.python.skip_js_build:
833
+ try:
834
+ setenv(SETUPTOOLS_CE_BUILD_JS_SKIP=1)
835
+ yield
836
+ setenv(SETUPTOOLS_CE_BUILD_JS_SKIP=None)
837
+ finally:
838
+ # Make sure the variable will not be written into the
839
+ # activation scripts
840
+ global EXPORTS # pylint: disable=global-statement
841
+ EXPORTS = [
842
+ (k, v) for k, v in EXPORTS if k != "SETUPTOOLS_CE_BUILD_JS_SKIP"
843
+ ]
844
+ else:
845
+ yield
846
+
847
+ with skip_js_build(cfg):
848
+ if self._m.items():
849
+ self._install_command("--upgrade", *self._split(self._requirements))
850
+ else:
851
+ self._install_command(*self._split(self._requirements))
852
+ self._m.clear()
853
+ for req in self._requirements:
854
+ self._m.add(_req_for_memo(req, cfg.spin.project_root))
841
855
 
842
856
  @staticmethod
843
857
  def _split(requirements: Iterable[str]) -> list[str]:
@@ -847,20 +861,6 @@ class SimpleProvisioner(ProvisionerProtocol):
847
861
  requirement_list.extend(requirement.split())
848
862
  return requirement_list
849
863
 
850
- @staticmethod
851
- def _filter(
852
- requirements: set[str], memo: Memoizer, project_root: Union[Path, str]
853
- ) -> set[str]:
854
- """
855
- We want to filter all requirements prior to installing them, because we
856
- only want to run the install, when there are changes, as it takes pip
857
- quite some time to check, whether it has to do something.
858
- """
859
- if all(memo.check(_req_for_memo(req, project_root)) for req in requirements):
860
- return set()
861
- else:
862
- return requirements
863
-
864
864
 
865
865
  def _file_hash(filename: Union[Path, str]) -> str:
866
866
  """
@@ -922,8 +922,8 @@ def venv_provision( # pylint: disable=too-many-branches,missing-function-docstr
922
922
  cfg: ConfigTree,
923
923
  ) -> None:
924
924
  fresh_env = False
925
-
926
925
  info("Checking venv '{python.venv}'")
926
+
927
927
  if not exists(cfg.python.venv):
928
928
  info("Provisioning venv '{python.venv}'")
929
929
  cfg.python.provisioner.provision_venv(cfg)
@@ -964,6 +964,19 @@ def venv_provision( # pylint: disable=too-many-branches,missing-function-docstr
964
964
 
965
965
  def cleanup(cfg: ConfigTree) -> None:
966
966
  """Remove directories and files generated by the python plugin."""
967
+ _cleanup_memoed_provisioners(cfg)
968
+ rmtree(cfg.python.provisioner_memo)
969
+ rmtree(cfg.python.aws_auth.memo)
970
+ for path in cfg.python.build_wheels:
971
+ current_path = Path(interpolate1(path))
972
+ rmtree(current_path / "build")
973
+ rmtree(current_path / "dist")
974
+ for filename in os.listdir(current_path):
975
+ if filename.endswith(".egg-info") or filename.endswith(".dist-info"):
976
+ rmtree(current_path / filename)
977
+
978
+
979
+ def _cleanup_memoed_provisioners(cfg: ConfigTree) -> None:
967
980
  with memoizer(cfg.python.provisioner_memo) as memo:
968
981
  for provisioner in memo.items():
969
982
  try:
@@ -975,16 +988,6 @@ def cleanup(cfg: ConfigTree) -> None:
975
988
  )
976
989
  memo.clear()
977
990
 
978
- rmtree(cfg.python.provisioner_memo)
979
- rmtree(cfg.python.aws_auth.memo)
980
- for path in cfg.python.build_wheels:
981
- current_path = Path(interpolate1(path))
982
- rmtree(current_path / "build")
983
- rmtree(current_path / "dist")
984
- for filename in os.listdir(current_path):
985
- if filename.endswith(".egg-info") or filename.endswith(".dist-info"):
986
- rmtree(current_path / filename)
987
-
988
991
 
989
992
  def _get_pipconf(cfg: ConfigTree) -> Path:
990
993
  """Retrieve the pipconf configuration file path."""
@@ -1018,7 +1021,9 @@ def _obfuscate_index_url(index_url: str) -> None:
1018
1021
  secrets.add(index_url.split(":")[2].split("@")[0]) # Codeartifact token
1019
1022
 
1020
1023
 
1021
- def _check_aws_token_validity(cfg: ConfigTree) -> None:
1024
+ def _check_aws_token_validity( # pylint: disable=too-many-locals
1025
+ cfg: ConfigTree,
1026
+ ) -> None:
1022
1027
  """
1023
1028
  If csspin-python[aws_auth] is installed, we can use csaccess to get the
1024
1029
  CodeArtifact authentication token.
@@ -1034,6 +1039,17 @@ def _check_aws_token_validity(cfg: ConfigTree) -> None:
1034
1039
 
1035
1040
  import time
1036
1041
 
1042
+ if not (
1043
+ client_secret := (
1044
+ interpolate1(cfg.python.aws_auth.client_secret)
1045
+ or os.getenv("CS_AWS_OIDC_CLIENT_SECRET")
1046
+ )
1047
+ ):
1048
+ die(
1049
+ "Please provide a client secret for CodeArtifact access via"
1050
+ " 'python.aws_auth.client_secret'."
1051
+ )
1052
+
1037
1053
  current_time = int(time.time())
1038
1054
  timestamp_key = "aws_auth_timestamp"
1039
1055
 
@@ -1059,18 +1075,24 @@ def _check_aws_token_validity(cfg: ConfigTree) -> None:
1059
1075
  memo.items().remove(item)
1060
1076
  else:
1061
1077
  info("Updating Codeartifact token.")
1078
+ from urllib.error import HTTPError
1062
1079
  from urllib.parse import urljoin
1063
1080
 
1064
1081
  opts = {
1082
+ "client_secret": client_secret,
1065
1083
  "static_oidc": interpolate1(cfg.python.aws_auth.static_oidc).lower()
1066
- == "true"
1084
+ == "true",
1067
1085
  }
1068
1086
  if cfg.python.aws_auth.client_id:
1069
1087
  opts["client_id"] = interpolate1(cfg.python.aws_auth.client_id)
1070
1088
  if cfg.python.aws_auth.role_arn:
1071
1089
  opts["aws_role_arn"] = interpolate1(cfg.python.aws_auth.role_arn)
1072
1090
 
1073
- index_base_url = get_ca_pypi_url_programmatic(**opts)
1091
+ try:
1092
+ index_base_url = get_ca_pypi_url_programmatic(**opts)
1093
+ except HTTPError as e:
1094
+ die(f"Failed to establish CodeArtifact connection: {e}")
1095
+
1074
1096
  index_url = urljoin(
1075
1097
  index_base_url + "/", interpolate1(cfg.python.aws_auth.index)
1076
1098
  )
@@ -148,10 +148,20 @@ python:
148
148
  CodeArtifact.
149
149
  index:
150
150
  type: str
151
- help: The Codeartifact repository index (e.g. "16.0/simple").
151
+ help: The CodeArtifact repository index (e.g. "16.0/simple").
152
152
  client_id:
153
153
  type: str
154
154
  help: The OIDC client ID to use.
155
+ client_secret:
156
+ type: secret
157
+ help: |
158
+ The OIDC client secret to use, defaults to the
159
+ environment variable 'CS_AWS_OIDC_CLIENT_SECRET'.
155
160
  role_arn:
156
161
  type: str
157
162
  help: The role ARN to assume when authenticating.
163
+ skip_js_build:
164
+ type: bool
165
+ help: |
166
+ If true the build_js part of setuptools_ce will be skipped
167
+ during provision.
@@ -180,7 +180,7 @@ def _update_index_url_in_toml(cfg: ConfigTree) -> None:
180
180
  """
181
181
  Update the index-url in the uv.toml in case it changed.
182
182
  """
183
- if (uv_toml_path := interpolate1(cfg.uv_provisioner.uv_toml_path)).exists():
183
+ if (uv_toml_path := interpolate1(Path(cfg.uv_provisioner.uv_toml_path))).exists():
184
184
  with open(uv_toml_path, mode="r+b") as fd:
185
185
  toml_content = tomllib.load(fd)
186
186
  if toml_content.get("index-url") != cfg.python.index_url:
@@ -1,11 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csspin-python
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Plugin-package for csspin providing Python related plugins
5
5
  Author-email: CONTACT Software GmbH <info@contact-software.com>
6
6
  Maintainer-email: Waleri Enns <waleri.enns@contact-software.com>, Benjamin Thomas Schwertfeger <benjaminthomas.schwertfeger@contact-software.com>, Fabian Hafer <fabian.hafer@contact-software.com>
7
7
  License-Expression: Apache-2.0
8
- Project-URL: Homepage, https://contact-software.com
8
+ Project-URL: CONTACT Software GmbH, https://contact-software.com
9
+ Project-URL: Documentation, https://csspin-python.readthedocs.io/en/stable/
10
+ Project-URL: Issue Tracker, https://github.com/cslab/csspin-python/issues
11
+ Project-URL: Release Notes, https://csspin-python.readthedocs.io/en/stable/relnotes.html
12
+ Project-URL: Repository, https://github.com/cslab/csspin-python
9
13
  Classifier: Environment :: Console
10
14
  Classifier: Development Status :: 5 - Production/Stable
11
15
  Classifier: Intended Audience :: Developers
@@ -8,14 +8,14 @@ csspin_python/playwright.py,sha256=oFfphLqa4AB6K9vasCUFHN0kFXu63n3ocrsqVuRp4-0,5
8
8
  csspin_python/playwright_schema.yaml,sha256=TSeR16YHa7m7bfO59F2eMV-jXcglluTJdEpUeL16saY,1178
9
9
  csspin_python/pytest.py,sha256=pTOb5zFd9RINZwJsHNaRuSGVDkPMABzaAhwpAJo1nQE,4574
10
10
  csspin_python/pytest_schema.yaml,sha256=tzXtdF6MvGC9v59EVRJFfLeMMHqPsXcFXy2zJtRECBI,1535
11
- csspin_python/python.py,sha256=Hht9TcBhIcHU4PdicB5W7s-9OYepUnxK1bdpxzob1Ro,34697
12
- csspin_python/python_schema.yaml,sha256=J9GEr5023cUHvfvSRiD1572eSVvfXt8-XEk9uhowhlc,5939
11
+ csspin_python/python.py,sha256=rSdF72FkcxiCrzLWSV7Cr4Thx5OgHGN7b6RKgOaXFEg,35871
12
+ csspin_python/python_schema.yaml,sha256=pgVVjByUYjxQWek7aFmjQzRwmq2ROLvHYgwGPMrT9sM,6351
13
13
  csspin_python/radon.py,sha256=uFqm6FEi5oWj-_XVaAm3s9cam0cUmr1_FwRf40K6xWs,1876
14
14
  csspin_python/radon_schema.yaml,sha256=rlRzXw5z4XbjOVznRiUxWGP4E9hx1Jm-gGw1iQiYzE0,548
15
- csspin_python/uv_provisioner.py,sha256=8d_24xyofO_6wU7bkomAH1FfY6P_Am0Yy6CXbzLxWN4,5993
15
+ csspin_python/uv_provisioner.py,sha256=MGedx4e286ZE1miwh70y6b3wePw3mmc0m1kG6H_738I,5999
16
16
  csspin_python/uv_provisioner_schema.yaml,sha256=Y8ZNC2OMnhR8Us3WUXAXK9hMjqGWAKFJB2puX4X5XNQ,727
17
- csspin_python-3.1.0.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
18
- csspin_python-3.1.0.dist-info/METADATA,sha256=YIDI67iYHouz4I8dVqCkMtcpXgQTXqmXlz3yUwjbeaM,4715
19
- csspin_python-3.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
- csspin_python-3.1.0.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
21
- csspin_python-3.1.0.dist-info/RECORD,,
17
+ csspin_python-3.2.0.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
18
+ csspin_python-3.2.0.dist-info/METADATA,sha256=54Kz-P7RQC4aHc31QmvyZnY2QSZgrnn9BnIW5ogr8ws,5031
19
+ csspin_python-3.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ csspin_python-3.2.0.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
21
+ csspin_python-3.2.0.dist-info/RECORD,,