csspin-python 4.1.0rc2__py3-none-any.whl → 5.0.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.
@@ -0,0 +1,68 @@
1
+ # -*- mode: python; coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2026 CONTACT Software GmbH
4
+ # https://www.contact-software.com/
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ """
19
+ Predict the wheel filename ``setup.py bdist_wheel`` would produce, without
20
+ building it::
21
+
22
+ python _predict_wheel_filename.py /path/to/project NAME VERSION
23
+
24
+ Must run in the subprocess environment to have setuptools in the environment and
25
+ to have the correct Python version and platform tags that the wheel is built
26
+ for.
27
+ """
28
+
29
+ import io
30
+ import os
31
+ import sys
32
+ import warnings
33
+ from contextlib import redirect_stdout
34
+
35
+
36
+ def predict_wheel_filename(project_path: str, name: str, version: str) -> str:
37
+ """Return the predicted wheel filename for ``project_path``."""
38
+ os.chdir(project_path)
39
+
40
+ # setup.py is exec'd here and free to print, so keep its chatter off the
41
+ # stdout the caller parses. stderr stays, so tracebacks still surface.
42
+ with warnings.catch_warnings(), redirect_stdout(io.StringIO()):
43
+ warnings.simplefilter("ignore")
44
+ if os.path.exists("setup.py"):
45
+ from distutils.core import run_setup # pylint: disable=deprecated-module
46
+
47
+ dist = run_setup(
48
+ "setup.py", script_args=["bdist_wheel"], stop_after="commandline"
49
+ )
50
+ else:
51
+ from setuptools.dist import Distribution
52
+
53
+ dist = Distribution({"script_args": ["bdist_wheel"]})
54
+ dist.parse_config_files()
55
+
56
+ dist.metadata.name = name
57
+ dist.metadata.version = version
58
+
59
+ cmd = dist.get_command_obj("bdist_wheel")
60
+ cmd.ensure_finalized()
61
+ python_tag, abi_tag, plat_tag = cmd.get_tag() # type: ignore[attr-defined]
62
+ dist_name = cmd.wheel_dist_name # type: ignore[attr-defined]
63
+
64
+ return f"{dist_name}-{python_tag}-{abi_tag}-{plat_tag}.whl"
65
+
66
+
67
+ if __name__ == "__main__":
68
+ print(predict_wheel_filename(*sys.argv[1:4]))
csspin_python/pytest.py CHANGED
@@ -28,7 +28,6 @@ defaults = config(
28
28
  "--cov-reset",
29
29
  "--cov",
30
30
  "--cov-report=term",
31
- "--cov-report=html",
32
31
  "--cov-report=xml:{pytest.coverage_report}",
33
32
  ],
34
33
  coverage_report="python-pytest-coverage.xml",
csspin_python/python.py CHANGED
@@ -191,7 +191,7 @@ def wheel(
191
191
  for build_path in {Path(path).absolute() for path in search_paths}:
192
192
  try:
193
193
  echo("Building PEP 517-like wheel")
194
- sh(
194
+ cmd = [
195
195
  "python",
196
196
  "-m",
197
197
  "build",
@@ -199,20 +199,28 @@ def wheel(
199
199
  build_path,
200
200
  "-o",
201
201
  "{spin.project_root}/dist",
202
- )
202
+ ]
203
+ if cfg.verbosity <= Verbosity.NORMAL:
204
+ cmd.append("--config-setting=quiet=true")
205
+ sh(*cmd)
203
206
  except Abort:
204
207
  echo("Building does not seem to work, use legacy setup.py style")
205
208
  with cd(build_path):
206
- sh(
209
+ cmd = [
207
210
  "python",
208
211
  "setup.py",
209
- None if cfg.verbosity > Verbosity.NORMAL else "-v" "build",
212
+ ]
213
+ if cfg.verbosity <= Verbosity.NORMAL:
214
+ cmd.append("-q")
215
+ cmd += [
216
+ "build",
210
217
  "-b",
211
218
  "{spin.project_root}/build",
212
219
  "bdist_wheel",
213
220
  "-d",
214
221
  "{spin.project_root}/dist",
215
- )
222
+ ]
223
+ sh(*cmd)
216
224
 
217
225
 
218
226
  @task()
@@ -297,8 +305,13 @@ def _check_venv( # pylint: disable=too-many-return-statements
297
305
  cfg: ConfigTree,
298
306
  ) -> bool:
299
307
  """
300
- Checks whether the venv is actually a venv compatible
301
- with our configuration and not just some dir.
308
+ Checks whether the venv is actually a venv compatible with our configuration
309
+ and not just some dir.
310
+
311
+ Uses absolute interpreter paths and runs *outside* the subprocess environment
312
+ on purpose: it must work while inspecting an existing venv to decide whether a
313
+ ``python.version`` change requires rebuilding it -- i.e. before the new
314
+ interpreter is provisioned.
302
315
  """
303
316
  try:
304
317
  python_version = (
@@ -402,6 +415,11 @@ def configure(cfg: ConfigTree) -> None:
402
415
  if cfg.python.aws_auth.enabled:
403
416
  _check_aws_token_validity(cfg)
404
417
 
418
+ # Provide spin's subprocess environment (see python_env). Registered here
419
+ # rather than in init so it is also available to the provision and
420
+ # finalize_provision hooks of plugins depending on this one.
421
+ cfg.spin.subprocess_environment = lambda: python_env(cfg)
422
+
405
423
 
406
424
  def init(cfg: ConfigTree) -> None:
407
425
  """Initialize the python plugin"""
@@ -413,32 +431,85 @@ def init(cfg: ConfigTree) -> None:
413
431
  " project. You might want to run spin with the 'provision'"
414
432
  " task."
415
433
  )
416
- venv_init(cfg)
417
-
418
-
419
- # We won't activate more than once.
420
- ACTIVATED = False
421
434
 
422
435
 
423
436
  def venv_init(cfg: ConfigTree) -> None:
424
437
  """Activate the virtual environment"""
425
- global ACTIVATED # pylint: disable=global-statement
426
- if os.environ.get("VIRTUAL_ENV", "") != cfg.python.venv and not ACTIVATED:
427
- activate_this = cfg.python.scriptdir / "activate_this.py"
428
- if not exists(activate_this):
429
- die(
430
- f"{cfg.python.venv} does not exist. You may want to provision"
431
- " it using 'spin provision'"
432
- )
433
- if sys.platform == "win32":
434
- echo(f"{cfg.python.scriptdir}\\activate.ps1")
435
- else:
436
- echo(f". {cfg.python.scriptdir}/activate")
437
- with open(activate_this, encoding="utf-8") as file:
438
- exec( # pylint: disable=exec-used # nosec
439
- file.read(), {"__file__": activate_this}
440
- )
441
- ACTIVATED = True
438
+ activate_this = cfg.python.scriptdir / "activate_this.py"
439
+ if not exists(activate_this):
440
+ die(
441
+ f"{cfg.python.venv} does not exist. You may want to provision"
442
+ " it using 'spin provision'"
443
+ )
444
+ if sys.platform == "win32":
445
+ echo(f"{cfg.python.scriptdir}\\activate.ps1")
446
+ else:
447
+ echo(f". {cfg.python.scriptdir}/activate")
448
+ with open(activate_this, encoding="utf-8") as file:
449
+ exec( # pylint: disable=exec-used # nosec
450
+ file.read(), {"__file__": activate_this}
451
+ )
452
+
453
+
454
+ @contextmanager
455
+ def python_env(cfg: ConfigTree) -> Generator:
456
+ """
457
+ Context manager to activate the subprocess/python virtual environment and
458
+ deactivate it once done.
459
+
460
+ Registered as ``spin.subprocess_environment`` by the plugin's
461
+ :py:func:`configure` hook, so ``csspin.sh`` activates the virtual environment
462
+ around the commands it spawns. Activation is conditional: while the virtual
463
+ environment has not been created yet (e.g. early during ``spin provision``)
464
+ this is a no-op and the command runs in spin's in-process environment; once
465
+ the venv exists it is activated. Saving and restoring
466
+ ``os.environ``, ``sys.path`` and the ``sys`` prefix attributes makes nesting
467
+ safe -- an explicit ``with python_env(cfg):`` block whose inner ``csspin.sh``
468
+ re-enters this context.
469
+
470
+ See the "The subprocess environment" section of csspin's plugin development
471
+ guide for the general concept.
472
+ """
473
+ if not exists(cfg.python.scriptdir / "activate_this.py"):
474
+ # The virtual environment has not been provisioned (yet); run the command
475
+ # in spin's in-process environment instead of activating.
476
+ yield
477
+ else:
478
+
479
+ def interpolate_environ_value(value: str) -> str:
480
+ if not value:
481
+ return ""
482
+ value = str(value)
483
+ keys = re.findall(r"{(?P<key>\w+?)}", value)
484
+ for key in keys:
485
+ if key in os.environ:
486
+ value = value.replace(f"{{{key}}}", os.environ[key])
487
+ return value
488
+
489
+ missing = object()
490
+ old_env = os.environ.copy()
491
+ old_sys_path = sys.path.copy()
492
+ old_sys_prefix = sys.prefix
493
+ old_real_prefix = getattr(sys, "real_prefix", missing)
494
+ try:
495
+ for name, value in EXPORTS:
496
+ os.environ[name] = interpolate_environ_value(value)
497
+
498
+ venv_init(cfg)
499
+ yield
500
+ finally:
501
+ echo(f"deactivate {cfg.python.venv}")
502
+ os.environ.clear()
503
+ os.environ.update(old_env)
504
+ sys.path = old_sys_path
505
+ sys.prefix = old_sys_prefix
506
+ # activate_this.py sets sys.real_prefix on activation; restore it (or
507
+ # remove it) so the attribute does not leak past this context.
508
+ if old_real_prefix is missing:
509
+ if hasattr(sys, "real_prefix"):
510
+ delattr(sys, "real_prefix")
511
+ else:
512
+ setattr(sys, "real_prefix", old_real_prefix)
442
513
 
443
514
 
444
515
  class ActivateScriptPatcher(abc.ABC):
@@ -975,7 +1046,6 @@ def venv_provision( # pylint: disable=too-many-branches,missing-function-docstr
975
1046
  info("Provisioning venv '{python.venv}'")
976
1047
  cfg.python.provisioner.provision_venv(cfg)
977
1048
 
978
- # This sets PATH to the venv
979
1049
  init(cfg)
980
1050
 
981
1051
  _configure_pipconf(cfg)
@@ -1085,15 +1155,15 @@ def _check_aws_token_validity( # pylint: disable=too-many-locals
1085
1155
 
1086
1156
  import time
1087
1157
 
1088
- if not (
1089
- client_secret := (
1090
- interpolate1(cfg.python.aws_auth.client_secret)
1091
- or os.getenv("CS_AWS_OIDC_CLIENT_SECRET")
1092
- )
1093
- ):
1158
+ client_secret = interpolate1(cfg.python.aws_auth.client_secret) or os.getenv(
1159
+ "CS_AWS_OIDC_CLIENT_SECRET"
1160
+ )
1161
+ static_oidc = interpolate1(cfg.python.aws_auth.static_oidc).lower() == "true"
1162
+
1163
+ if static_oidc and not client_secret:
1094
1164
  die(
1095
1165
  "Please provide a client secret for CodeArtifact access via"
1096
- " 'python.aws_auth.client_secret'."
1166
+ " 'python.aws_auth.client_secret' when using static OIDC."
1097
1167
  )
1098
1168
 
1099
1169
  current_time = int(time.time())
@@ -1126,8 +1196,7 @@ def _check_aws_token_validity( # pylint: disable=too-many-locals
1126
1196
 
1127
1197
  opts = {
1128
1198
  "client_secret": client_secret,
1129
- "static_oidc": interpolate1(cfg.python.aws_auth.static_oidc).lower()
1130
- == "true",
1199
+ "static_oidc": static_oidc,
1131
1200
  }
1132
1201
  if cfg.python.aws_auth.client_id:
1133
1202
  opts["client_id"] = interpolate1(cfg.python.aws_auth.client_id)
@@ -25,6 +25,7 @@ from importlib.metadata import metadata as _pkg_metadata
25
25
  from importlib.metadata import version as _pkg_version
26
26
  from subprocess import DEVNULL, PIPE
27
27
  from tempfile import TemporaryDirectory
28
+ from urllib.parse import quote, urlencode, urlparse
28
29
 
29
30
  from csspin import (
30
31
  Verbosity,
@@ -40,12 +41,14 @@ from csspin import (
40
41
  )
41
42
  from csspin.tree import ConfigTree
42
43
  from packaging.requirements import Requirement
44
+ from packaging.utils import canonicalize_name
43
45
  from path import Path
44
46
 
45
47
  defaults = config(
46
48
  cyclonedx_bom_version="7.3.0",
47
49
  project_paths=["{spin.project_root}"],
48
- requires=config(spin=["csspin_python.python"]),
50
+ repository_url="{python.index_url}",
51
+ requires=config(spin=["csspin_python.python"], python=["setuptools"]),
49
52
  )
50
53
 
51
54
 
@@ -69,12 +72,23 @@ def sbom(cfg: ConfigTree) -> None:
69
72
 
70
73
  project_path = Path(project_path).absolute()
71
74
  metadata = get_project_metadata(project_path, cfg.python.index_url)
75
+ project_name, project_version = metadata.get("name"), metadata.get("version")
76
+
77
+ file_name = _predict_wheel_filename(
78
+ project_path, project_name, project_version, cfg.python.python
79
+ )
72
80
  third_party_deps = _collect_thirdparty_deps(
73
81
  metadata.get("requires_dist", set()), python_version=cfg.python.version
74
82
  )
75
83
  sbom_json = json.loads(_run_cyclonedx(cfg, third_party_deps, stderr))
76
- _enrich_sbom(sbom_json, metadata, third_party_deps)
77
- _write_sbom(cfg, sbom_json, metadata.get("name"))
84
+ _enrich_sbom(
85
+ sbom_json,
86
+ metadata,
87
+ third_party_deps,
88
+ cfg.python_sbom.repository_url,
89
+ file_name,
90
+ )
91
+ _write_sbom(cfg, sbom_json, project_name)
78
92
 
79
93
 
80
94
  def cleanup(cfg: ConfigTree) -> None:
@@ -114,7 +128,7 @@ def _ensure_cyclonedx_venv(cfg: ConfigTree, binary_dir: str, quiet: str | None)
114
128
  )
115
129
  rmtree(venv_cdx)
116
130
 
117
- sh(cfg.python.interpreter, "-m", "venv", venv_cdx)
131
+ sh(cfg.python.interpreter, "-m", "venv", venv_cdx, use_subprocess_environment=False)
118
132
  sh(
119
133
  interpreter_cdx,
120
134
  "-m",
@@ -125,6 +139,7 @@ def _ensure_cyclonedx_venv(cfg: ConfigTree, binary_dir: str, quiet: str | None)
125
139
  "--index-url",
126
140
  cfg.python.index_url,
127
141
  "cyclonedx-bom==" + requested_version,
142
+ use_subprocess_environment=False,
128
143
  )
129
144
  with memoizer(memo_file) as memo:
130
145
  memo.add(memo_key)
@@ -143,7 +158,7 @@ def _run_cyclonedx(cfg: ConfigTree, third_party_deps: set[str], stderr: int) ->
143
158
  with TemporaryDirectory() as tmp_dir:
144
159
  venv = Path(tmp_dir) / "venv"
145
160
  interpreter = venv / binary_dir / "python" + cfg.platform.exe
146
- sh(cfg.python.interpreter, "-m", "venv", venv)
161
+ sh(cfg.python.interpreter, "-m", "venv", venv, use_subprocess_environment=False)
147
162
  if third_party_deps:
148
163
  sh(
149
164
  interpreter,
@@ -158,10 +173,28 @@ def _run_cyclonedx(cfg: ConfigTree, third_party_deps: set[str], stderr: int) ->
158
173
  for constraint in cfg.python.constraints
159
174
  ],
160
175
  *third_party_deps,
176
+ use_subprocess_environment=False,
161
177
  stderr=stderr,
162
178
  )
163
- sh(interpreter, "-m", "pip", quiet, "uninstall", "-y", "pip")
164
- return backtick(interpreter_cdx, "-m", "cyclonedx_py", "environment", venv, stderr=stderr) # type: ignore[no-any-return] # noqa: E501
179
+ sh(
180
+ interpreter,
181
+ "-m",
182
+ "pip",
183
+ quiet,
184
+ "uninstall",
185
+ "-y",
186
+ "pip",
187
+ use_subprocess_environment=False,
188
+ )
189
+ return backtick( # type: ignore[no-any-return]
190
+ interpreter_cdx,
191
+ "-m",
192
+ "cyclonedx_py",
193
+ "environment",
194
+ venv,
195
+ use_subprocess_environment=False,
196
+ stderr=stderr,
197
+ )
165
198
 
166
199
 
167
200
  def _write_sbom(cfg: ConfigTree, sbom_json: dict, project_name: str) -> None:
@@ -197,7 +230,52 @@ def _parse_authors(author_name: str, author_email: str) -> str:
197
230
  return ", ".join(f"{name} ({addr})" for name, addr in entries)
198
231
 
199
232
 
200
- def _build_primary_component(metadata: dict) -> tuple[dict, str]:
233
+ def _repository_base_url(index_url: str) -> str:
234
+ """Reduce a pip index URL to the repository base URL a purl expects."""
235
+ parsed = urlparse(index_url)
236
+ segments = [segment for segment in parsed.path.split("/") if segment]
237
+ if segments and segments[-1] in ("simple", "+simple"):
238
+ segments.pop()
239
+ return parsed._replace(
240
+ netloc=parsed.netloc.rsplit("@", 1)[-1], # drop basic-auth credentials
241
+ path="/".join([""] + segments),
242
+ ).geturl()
243
+
244
+
245
+ def _build_purl(name: str, version: str, repository_url: str, file_name: str) -> str:
246
+ """Build the pypi Package URL for a project, per tn-0058.
247
+
248
+ ``repository_url`` is only added when the package isn't published to
249
+ public pypi.org, since type, name and version are unique there already.
250
+ """
251
+ purl = f"pkg:pypi/{canonicalize_name(name)}@{quote(version, safe=':')}"
252
+
253
+ qualifiers = {"file_name": file_name}
254
+ if urlparse(repository_url).hostname != "pypi.org":
255
+ qualifiers["repository_url"] = _repository_base_url(repository_url)
256
+
257
+ return f"{purl}?{urlencode(sorted(qualifiers.items()), safe=':')}"
258
+
259
+
260
+ def _predict_wheel_filename(
261
+ project_path: str, name: str, version: str, interpreter: str
262
+ ) -> str:
263
+ """
264
+ Predict the wheel filename ``python:wheel`` will produce for
265
+ ``project_path``, without building it.
266
+
267
+ ``name`` and ``version`` come from :func:`get_project_metadata`.
268
+ ``interpreter`` must be ``project_path``'s own, since it owns the tags.
269
+ Cached since the prediction is identical for every caller within the same
270
+ process.
271
+ """
272
+ script = Path(__file__).parent / "_predict_wheel_filename.py"
273
+ return backtick(interpreter, script, project_path, name, version).strip() # type: ignore[no-any-return] # noqa: E501
274
+
275
+
276
+ def _build_primary_component(
277
+ metadata: dict, repository_url: str, file_name: str
278
+ ) -> tuple[dict, str]:
201
279
  """Build the CycloneDX primary component dict and its bom-ref from project metadata."""
202
280
  if not (name := metadata.get("name")):
203
281
  die("Project metadata is missing 'name'.")
@@ -206,6 +284,7 @@ def _build_primary_component(metadata: dict) -> tuple[dict, str]:
206
284
  if not (license_id := metadata.get("license")):
207
285
  die("Project metadata is missing 'license'.")
208
286
 
287
+ purl = _build_purl(str(name), str(version), repository_url, file_name)
209
288
  authors = _parse_authors(
210
289
  author_name=metadata.get("author", "").strip(),
211
290
  author_email=metadata.get("author_email", "").strip(),
@@ -216,6 +295,7 @@ def _build_primary_component(metadata: dict) -> tuple[dict, str]:
216
295
  "bom-ref": primary_ref,
217
296
  "licenses": [{"expression": license_id}],
218
297
  "name": name,
298
+ "purl": purl,
219
299
  "type": "application",
220
300
  "version": version,
221
301
  }
@@ -226,9 +306,13 @@ def _enrich_sbom(
226
306
  sbom_json: dict,
227
307
  metadata: dict,
228
308
  third_party_deps: set[str],
309
+ repository_url: str,
310
+ file_name: str,
229
311
  ) -> None:
230
312
  """Add the primary component and its dependency entry to the CycloneDX document."""
231
- component, primary_ref = _build_primary_component(metadata)
313
+ component, primary_ref = _build_primary_component(
314
+ metadata, repository_url, file_name
315
+ )
232
316
  existing_metadata = sbom_json.get("metadata", {})
233
317
  existing_tools = existing_metadata.get("tools", {})
234
318
  existing_tool_components = (
@@ -16,3 +16,8 @@ python_sbom:
16
16
  help: |
17
17
  Version of the cyclonedx-bom package to use for generating the
18
18
  SBOM.
19
+ repository_url:
20
+ type: str
21
+ help: |
22
+ URL of the package index used for the primary component's
23
+ Package URL. Defaults to 'python.index_url'.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csspin-python
3
- Version: 4.1.0rc2
3
+ Version: 5.0.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>
@@ -1,3 +1,4 @@
1
+ csspin_python/_predict_wheel_filename.py,sha256=sz09QpDOPQesG-QAANjH607QSkS3PFo0qxPifG1y85o,2399
1
2
  csspin_python/behave.py,sha256=iJZeyIqB7V_NzTdLTZldNY9W_GGwCWkXe6WY69wpDqs,4997
2
3
  csspin_python/behave_schema.yaml,sha256=8qoOCK-uTmwgRRW29urgK0X_kgn0zO0X34v89bvii2w,1241
3
4
  csspin_python/debugpy.py,sha256=v0ZZopv5TNoSaFf2kiePsw9OmhBpjfOBFh0u71jTcnQ,962
@@ -6,20 +7,18 @@ csspin_python/devpi.py,sha256=o7O06Dw-xt47y-M74TShcl9MUMaW78aFmw6i6ISYwoU,2351
6
7
  csspin_python/devpi_schema.yaml,sha256=2gPATWjVcfvCTrGZX2FK6wH8hh9KS0XzZ35JvZeJGEU,487
7
8
  csspin_python/playwright.py,sha256=oFfphLqa4AB6K9vasCUFHN0kFXu63n3ocrsqVuRp4-0,5102
8
9
  csspin_python/playwright_schema.yaml,sha256=TSeR16YHa7m7bfO59F2eMV-jXcglluTJdEpUeL16saY,1178
9
- csspin_python/pytest.py,sha256=N9YaU_ouQab0PFPf46HLE7Vg4JeoZW4dzVD7EevqJ1U,4573
10
+ csspin_python/pytest.py,sha256=vSLtmS-kNO0NPo_tszfEm5zGwJb6Yb5IGS-hA7CNguo,4544
10
11
  csspin_python/pytest_schema.yaml,sha256=tzXtdF6MvGC9v59EVRJFfLeMMHqPsXcFXy2zJtRECBI,1535
11
- csspin_python/python.py,sha256=yqH1eG6mqRJomu-F99cB74PFaUzbSoG_dhpUjYwr1xE,37725
12
- csspin_python/python_sbom.py,sha256=8NVZ6uVUz7rU3VhEoXJ_wHLCdv4BIV6Zjz1ZRa9nQhc,10488
13
- csspin_python/python_sbom_schema.yaml,sha256=VCxY9E2AG_fS00dvIYe6Fbvz1maPjpY0zyZK1Z0wJu4,538
12
+ csspin_python/python.py,sha256=SO5RkNdpZPcux7KnuDHVTaNIVQAmU9xjwTtJG4v97NE,40725
13
+ csspin_python/python_sbom.py,sha256=FYx9DMDSeooB4lXuIisUsMwBMEj1Hl-QVl1AlDdTmZk,13381
14
+ csspin_python/python_sbom_schema.yaml,sha256=QysysjGJ6n1WEoLBqYHcdbojuEomUvL8QG4ZSWQW2_8,739
14
15
  csspin_python/python_schema.yaml,sha256=pgVVjByUYjxQWek7aFmjQzRwmq2ROLvHYgwGPMrT9sM,6351
15
16
  csspin_python/radon.py,sha256=uFqm6FEi5oWj-_XVaAm3s9cam0cUmr1_FwRf40K6xWs,1876
16
17
  csspin_python/radon_schema.yaml,sha256=rlRzXw5z4XbjOVznRiUxWGP4E9hx1Jm-gGw1iQiYzE0,548
17
18
  csspin_python/uv_provisioner.py,sha256=1e-_Sb39JrqNWyaUNeBX59R5tutXLJ1ZsT7urCN1U0I,6044
18
19
  csspin_python/uv_provisioner_schema.yaml,sha256=Y8ZNC2OMnhR8Us3WUXAXK9hMjqGWAKFJB2puX4X5XNQ,727
19
- csspin_python-4.1.0rc2.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
20
- csspin_python-4.1.0rc2.dist-info/METADATA,sha256=v7TeI32ffR2KghQaNg2Yi1nXefTReG2Pr6iI3D8LKM0,5257
21
- csspin_python-4.1.0rc2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
22
- csspin_python-4.1.0rc2.dist-info/scm_file_list.json,sha256=Q47_SEZYR492yYYWq_DlPPp-64AFlvHaOz7APY0i8j0,2902
23
- csspin_python-4.1.0rc2.dist-info/scm_version.json,sha256=r-bmiPdloFp64jPWB2hS3VjSJvF_ljCIL5y985ukLKY,163
24
- csspin_python-4.1.0rc2.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
25
- csspin_python-4.1.0rc2.dist-info/RECORD,,
20
+ csspin_python-5.0.0.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
21
+ csspin_python-5.0.0.dist-info/METADATA,sha256=a4GiqeHydRsKdxlj_uH-B9bAfoCfbQBSowJJrnCMaJI,5254
22
+ csspin_python-5.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
23
+ csspin_python-5.0.0.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
24
+ csspin_python-5.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,78 +0,0 @@
1
- {
2
- "files": [
3
- "spinfile.yaml",
4
- "README.rst",
5
- ".pre-commit-config.yaml",
6
- "CONTRIBUTING.md",
7
- "requirements-dev.txt",
8
- "LICENSE",
9
- ".gitignore",
10
- ".gitlab-ci.yml",
11
- ".prettierrc.yaml",
12
- "pyproject.toml",
13
- ".readthedocs.yaml",
14
- "doc/virtual_environment.rst",
15
- "doc/development.rst",
16
- "doc/relnotes.rst",
17
- "doc/index.rst",
18
- "doc/links.rst",
19
- "doc/conf.py",
20
- "doc/installation.rst",
21
- "doc/plugins/playwright.rst",
22
- "doc/plugins/uv_provisioner.rst",
23
- "doc/plugins/debugpy.rst",
24
- "doc/plugins/devpi.rst",
25
- "doc/plugins/python.rst",
26
- "doc/plugins/python_sbom.rst",
27
- "doc/plugins/pytest.rst",
28
- "doc/plugins/behave.rst",
29
- "doc/plugins/radon.rst",
30
- "tests/conftest.py",
31
- "tests/unit/test_uv_provisioner.py",
32
- "tests/unit/test_python_sbom.py",
33
- "tests/unit/test_python.py",
34
- "tests/integration/test_provisioning.py",
35
- "tests/integration/conftest.py",
36
- "tests/integration/fixtures/activation_script/test_env.ps1",
37
- "tests/integration/fixtures/activation_script/test_env.sh",
38
- "tests/integration/fixtures/activation_script/plugins/dummy.py",
39
- "tests/integration/yamls/pytest.yaml",
40
- "tests/integration/yamls/uv_provisioner_use.yaml",
41
- "tests/integration/yamls/pytest_with_playwright.yaml",
42
- "tests/integration/yamls/python_version.yaml",
43
- "tests/integration/yamls/playwright.yaml",
44
- "tests/integration/yamls/devpi.yaml",
45
- "tests/integration/yamls/python_use.yaml",
46
- "tests/integration/yamls/behave.yaml",
47
- "tests/integration/yamls/radon.yaml",
48
- "tests/integration/yamls/python_activation_scripts.yaml",
49
- "tests/integration/yamls/uv_provisioner.yaml",
50
- "tests/acceptance/test_aws_auth.py",
51
- "tests/acceptance/python_sbom/spinfile.yaml",
52
- "tests/acceptance/python_sbom/test_sbom.py",
53
- "tests/acceptance/python_sbom/pyproject.toml",
54
- "tests/acceptance/python_constraints/test_constraints.py",
55
- "tests/acceptance/python_constraints/spinfile.yaml",
56
- "tests/acceptance/python_constraints/constraints.txt",
57
- "tests/acceptance/yamls/python_aws_auth.yaml",
58
- "src/csspin_python/debugpy_schema.yaml",
59
- "src/csspin_python/devpi_schema.yaml",
60
- "src/csspin_python/uv_provisioner_schema.yaml",
61
- "src/csspin_python/pytest.py",
62
- "src/csspin_python/playwright_schema.yaml",
63
- "src/csspin_python/behave.py",
64
- "src/csspin_python/pytest_schema.yaml",
65
- "src/csspin_python/uv_provisioner.py",
66
- "src/csspin_python/radon.py",
67
- "src/csspin_python/python_sbom_schema.yaml",
68
- "src/csspin_python/python_sbom.py",
69
- "src/csspin_python/devpi.py",
70
- "src/csspin_python/debugpy.py",
71
- "src/csspin_python/playwright.py",
72
- "src/csspin_python/behave_schema.yaml",
73
- "src/csspin_python/python.py",
74
- "src/csspin_python/python_schema.yaml",
75
- "src/csspin_python/radon_schema.yaml",
76
- ".gitlab/merge_request_templates/default.md"
77
- ]
78
- }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "4.1.0rc2",
3
- "distance": 0,
4
- "node": "gcaf72582381496c091ef82854c9703c0c9583259",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-06-23"
8
- }