csspin-python 3.2.0__py3-none-any.whl → 4.1.0rc1__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/devpi.py CHANGED
@@ -17,7 +17,6 @@
17
17
 
18
18
  """Module implementing the devpi plugin for spin"""
19
19
 
20
-
21
20
  from typing import Iterable
22
21
 
23
22
  from csspin import Command, config, die, exists, readyaml, setenv, sh, task
csspin_python/pytest.py CHANGED
@@ -17,7 +17,6 @@
17
17
 
18
18
  """Module implementing the pytest plugin for spin"""
19
19
 
20
-
21
20
  from typing import Iterable
22
21
 
23
22
  from csspin import Path, Verbosity, config, die, interpolate1, option, setenv, sh, task
csspin_python/python.py CHANGED
@@ -69,13 +69,16 @@ point to the base installation.
69
69
  import abc
70
70
  import configparser
71
71
  import hashlib
72
+ import json
72
73
  import logging
73
74
  import os
74
75
  import re
75
76
  import shutil
77
+ import subprocess
76
78
  import sys
77
79
  from contextlib import contextmanager
78
- from subprocess import check_output
80
+ from functools import cache
81
+ from subprocess import CalledProcessError, check_output
79
82
  from textwrap import dedent, indent
80
83
  from typing import Generator, Iterable, Type, Union
81
84
 
@@ -88,6 +91,7 @@ except ImportError:
88
91
 
89
92
  from click.exceptions import Abort
90
93
  from csspin import (
94
+ CONFIG,
91
95
  EXPORTS,
92
96
  Command,
93
97
  Memoizer,
@@ -289,20 +293,65 @@ def nuget_install(cfg: ConfigTree) -> None:
289
293
  )
290
294
 
291
295
 
292
- def provision(cfg: ConfigTree) -> None:
293
- """Provision the python plugin"""
294
- if not cfg.python.use and exists(cfg.python.python):
296
+ def _check_venv( # pylint: disable=too-many-return-statements
297
+ cfg: ConfigTree,
298
+ ) -> bool:
299
+ """
300
+ Checks whether the venv is actually a venv compatible
301
+ with our configuration and not just some dir.
302
+ """
303
+ try:
295
304
  python_version = (
296
305
  check_output([cfg.python.python, "--version"])
297
306
  .decode()
298
307
  .strip()
299
308
  .replace("Python ", "")
300
309
  )
301
- if python_version and not python_version.startswith(cfg.python.version):
310
+ except CalledProcessError:
311
+ return False
312
+ if not cfg.python.version and not cfg.python.use:
313
+ return False
314
+ if cfg.python.use:
315
+ try:
316
+ use_python_version = (
317
+ check_output([cfg.python.use, "--version"])
318
+ .decode()
319
+ .strip()
320
+ .replace("Python ", "")
321
+ )
322
+ except CalledProcessError:
323
+ return False
324
+ if use_python_version == python_version:
325
+ return True
326
+ else:
327
+ warn(
328
+ "The cfg.python.use version does not match the cfg.python.python "
329
+ "version set in the venv. If you want to update the python version "
330
+ "used in the venv, you have to manually remove it."
331
+ )
332
+ return True
333
+ if python_version.startswith(cfg.python.version):
334
+ return True
335
+ return False
336
+
337
+
338
+ def provision(cfg: ConfigTree) -> None:
339
+ """Provision the python plugin"""
340
+
341
+ info("Checking venv '{python.venv}'")
342
+
343
+ fresh_venv = False
344
+
345
+ if exists("{python.venv}"):
346
+ if not _check_venv(cfg):
302
347
  _cleanup_memoed_provisioners(cfg)
303
348
  rmtree(cfg.python.provisioner_memo)
304
349
  rmtree(cfg.python.aws_auth.memo)
305
350
  rmtree(cfg.python.venv)
351
+ fresh_venv = True
352
+ else:
353
+ fresh_venv = True
354
+
306
355
  with memoizer(cfg.python.provisioner_memo) as memo:
307
356
  if cfg.python.provisioner is None:
308
357
  cfg.python.provisioner = SimpleProvisioner(cfg)
@@ -311,7 +360,7 @@ def provision(cfg: ConfigTree) -> None:
311
360
  if not shutil.which(cfg.python.interpreter):
312
361
  cfg.python.provisioner.provision_python(cfg)
313
362
 
314
- venv_provision(cfg)
363
+ venv_provision(cfg, fresh_venv)
315
364
 
316
365
  cfg.python.site_packages = get_site_packages(interpreter=cfg.python.python)
317
366
 
@@ -468,24 +517,19 @@ class BashActivate(ActivateScriptPatcher):
468
517
  replacements = [
469
518
  ("deactivate", "origdeactivate"),
470
519
  ]
471
- old_env_pattern = dedent(
472
- """
520
+ old_env_pattern = dedent("""
473
521
  if [ -z ${{{name}+x}} ]; then
474
522
  export _OLD_SPIN_UNSET{name}=""
475
523
  else
476
524
  export _OLD_SPIN_VALUE{name}="${name}"
477
525
  fi
478
- """
479
- )
480
- setpattern = dedent(
481
- """
526
+ """)
527
+ setpattern = dedent("""
482
528
  {name}="{value}"
483
529
  export {name}
484
- """
485
- )
530
+ """)
486
531
  resetpattern = indent(
487
- dedent(
488
- """
532
+ dedent("""
489
533
  if ! [ -z "${{_OLD_SPIN_VALUE{name}+_}}" ] ; then
490
534
  {name}="$_OLD_SPIN_VALUE{name}"
491
535
  export {name}
@@ -495,12 +539,10 @@ class BashActivate(ActivateScriptPatcher):
495
539
  unset {name}
496
540
  unset _OLD_SPIN_UNSET{name}
497
541
  fi
498
- """
499
- ),
542
+ """),
500
543
  prefix=" ",
501
544
  )
502
- script = dedent(
503
- """
545
+ script = dedent("""
504
546
  {patchmarker}
505
547
  {original}
506
548
  deactivate () {{
@@ -520,8 +562,7 @@ class BashActivate(ActivateScriptPatcher):
520
562
  # commands. Without forgetting past commands the $PATH changes
521
563
  # we made may not be respected
522
564
  hash -r 2>/dev/null
523
- """
524
- )
565
+ """)
525
566
 
526
567
  @staticmethod
527
568
  def interpolate_environ_value(value: str) -> str:
@@ -543,24 +584,19 @@ class PowershellActivate(ActivateScriptPatcher):
543
584
  old_env_pattern = (
544
585
  "New-Variable -Scope global -Name _OLD_SPIN_{name} -Value $env:{name}"
545
586
  )
546
- setpattern = dedent(
547
- """
587
+ setpattern = dedent("""
548
588
  $env:{name} = "{value}"
549
- """
550
- )
589
+ """)
551
590
  resetpattern = indent(
552
- dedent(
553
- """
591
+ dedent("""
554
592
  if (Test-Path variable:_OLD_SPIN_{name}) {{
555
593
  $env:{name} = $variable:_OLD_SPIN_{name}
556
594
  Remove-Variable "_OLD_SPIN_{name}" -Scope global
557
595
  }}
558
- """
559
- ),
596
+ """),
560
597
  prefix=" ",
561
598
  )
562
- script = dedent(
563
- """
599
+ script = dedent("""
564
600
  {patchmarker}
565
601
  {original}
566
602
  function global:deactivate([switch] $NonDestructive) {{
@@ -574,8 +610,7 @@ class PowershellActivate(ActivateScriptPatcher):
574
610
  deactivate -nondestructive
575
611
  {old_value_setters}
576
612
  {setters}
577
- """
578
- )
613
+ """)
579
614
 
580
615
  @staticmethod
581
616
  def interpolate_environ_value(value: str) -> str:
@@ -592,8 +627,7 @@ class BatchActivate(ActivateScriptPatcher):
592
627
  patchmarker = "\nREM Patched by csspin_python.python\n"
593
628
  activatescript = Path("{python.scriptdir}") / "activate.bat"
594
629
  replacements = []
595
- old_env_pattern = dedent(
596
- """
630
+ old_env_pattern = dedent("""
597
631
  if defined _OLD_SPIN_VALUE_{name} goto ENDIFSPIN{name}1
598
632
  if defined _OLD_SPIN_UNSET_{name} goto ENDIFSPIN{name}2
599
633
  if defined {name} goto ENDIFSPIN{name}3
@@ -613,19 +647,16 @@ class BatchActivate(ActivateScriptPatcher):
613
647
  set "_OLD_SPIN_UNSET_{name}= "
614
648
  goto ENDIFSPIN{name}5
615
649
  :ENDIFSPIN{name}5
616
- """
617
- )
650
+ """)
618
651
  setpattern = 'set "{name}={value}"'
619
652
  resetpattern = ""
620
- script = dedent(
621
- """
653
+ script = dedent("""
622
654
  @echo off
623
655
  {patchmarker}
624
656
  {original}
625
657
  {old_value_setters}
626
658
  {setters}
627
- """
628
- )
659
+ """)
629
660
 
630
661
  @staticmethod
631
662
  def interpolate_environ_value(value: str) -> str:
@@ -644,8 +675,7 @@ class BatchDeactivate(ActivateScriptPatcher):
644
675
  replacements = []
645
676
  old_env_pattern = ""
646
677
  setpattern = ""
647
- resetpattern = dedent(
648
- """
678
+ resetpattern = dedent("""
649
679
  if defined _OLD_SPIN_VALUE_{name} goto ENDIFVSPIN{name}1
650
680
  if defined _OLD_SPIN_UNSET_{name} goto ENDIFVSPIN{name}2
651
681
  :ENDIFVSPIN{name}1
@@ -657,16 +687,13 @@ class BatchDeactivate(ActivateScriptPatcher):
657
687
  set _OLD_SPIN_UNSET_{name}=
658
688
  goto ENDIFVSPIN{name}0
659
689
  :ENDIFVSPIN{name}0
660
- """
661
- )
662
- script = dedent(
663
- """
690
+ """)
691
+ script = dedent("""
664
692
  @echo off
665
693
  {patchmarker}
666
694
  {original}
667
695
  {resetters}
668
- """
669
- )
696
+ """)
670
697
 
671
698
 
672
699
  class PythonActivate(ActivateScriptPatcher):
@@ -676,13 +703,11 @@ class PythonActivate(ActivateScriptPatcher):
676
703
  old_env_pattern = ""
677
704
  setpattern = 'os.environ["{name}"] = fr"{value}"'
678
705
  resetpattern = ""
679
- script = dedent(
680
- """
706
+ script = dedent("""
681
707
  {patchmarker}
682
708
  {original}
683
709
  {setters}
684
- """
685
- )
710
+ """)
686
711
 
687
712
  @staticmethod
688
713
  def interpolate_environ_value(value: str) -> str:
@@ -695,6 +720,30 @@ class PythonActivate(ActivateScriptPatcher):
695
720
  return value
696
721
 
697
722
 
723
+ @cache
724
+ def get_project_metadata(project_path: str, index_url: str) -> dict: # type: ignore[return] # pylint: disable=inconsistent-return-statements # noqa: E501
725
+ """
726
+ Retrieve project metadata of ``project_path`` via ``python -m build
727
+ --metadata``.
728
+
729
+ Cached since ``build --metadata`` is noisy and the output is identical for
730
+ every caller within the same process.
731
+ """
732
+ setenv(PIP_INDEX_URL=index_url)
733
+ kwargs = {}
734
+ if CONFIG.verbosity < Verbosity.INFO:
735
+ kwargs["stderr"] = subprocess.DEVNULL
736
+ raw_metadata = backtick(
737
+ "python", "-m", "build", "--metadata", project_path, **kwargs
738
+ )
739
+ setenv(PIP_INDEX_URL=None)
740
+
741
+ if raw_metadata:
742
+ return json.loads(raw_metadata) # type: ignore[no-any-return]
743
+
744
+ die(f"Could not retrieve project metadata of '{project_path}'.")
745
+
746
+
698
747
  def get_site_packages(interpreter: Path) -> Path:
699
748
  """Return the path to the virtual environments site-packages."""
700
749
  return Path(
@@ -919,15 +968,12 @@ def _req_for_memo(
919
968
 
920
969
 
921
970
  def venv_provision( # pylint: disable=too-many-branches,missing-function-docstring
922
- cfg: ConfigTree,
971
+ cfg: ConfigTree, fresh_venv: bool = False
923
972
  ) -> None:
924
- fresh_env = False
925
- info("Checking venv '{python.venv}'")
926
973
 
927
- if not exists(cfg.python.venv):
974
+ if fresh_venv:
928
975
  info("Provisioning venv '{python.venv}'")
929
976
  cfg.python.provisioner.provision_venv(cfg)
930
- fresh_env = True
931
977
 
932
978
  # This sets PATH to the venv
933
979
  init(cfg)
@@ -935,7 +981,7 @@ def venv_provision( # pylint: disable=too-many-branches,missing-function-docstr
935
981
  _configure_pipconf(cfg)
936
982
 
937
983
  # Establish the prerequisites
938
- if fresh_env:
984
+ if fresh_venv:
939
985
  cfg.python.provisioner.prerequisites(cfg)
940
986
 
941
987
  # Plugins can define a 'venv_hook' function, to give them a
@@ -0,0 +1,196 @@
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
+ """Module implementing the python_sbom plugin for csspin"""
19
+
20
+ import json
21
+ import sys
22
+ from subprocess import DEVNULL, PIPE
23
+ from tempfile import TemporaryDirectory
24
+
25
+ from csspin import (
26
+ Verbosity,
27
+ backtick,
28
+ config,
29
+ die,
30
+ exists,
31
+ info,
32
+ memoizer,
33
+ rmtree,
34
+ sh,
35
+ task,
36
+ )
37
+ from csspin.tree import ConfigTree
38
+ from packaging.requirements import Requirement
39
+ from path import Path
40
+
41
+ defaults = config(
42
+ cyclonedx_bom_version="7.3.0",
43
+ project_paths=["{spin.project_root}"],
44
+ requires=config(spin=["csspin_python.python"]),
45
+ )
46
+
47
+
48
+ @task("python-sbom", when="sbom:build")
49
+ def sbom(cfg: ConfigTree) -> None:
50
+ """
51
+ Create the SBOMs for Python projects defined in via
52
+ 'python_sbom.project_paths'.
53
+
54
+ This task assumes that the current package defines its dependencies that
55
+ are to be included in the SBOM via the "thirdparty" extra.
56
+
57
+ If there is no such extra, the SBOM generation is skipped.
58
+ """
59
+ from csspin_python.python import get_project_metadata
60
+
61
+ stderr = PIPE if cfg.verbosity > Verbosity.NORMAL else DEVNULL
62
+ for project_path in cfg.python_sbom.project_paths:
63
+ if not exists(project_path):
64
+ die(f"Project path '{project_path}' does not exist.")
65
+
66
+ project_path = Path(project_path).absolute()
67
+ metadata = get_project_metadata(project_path, cfg.python.index_url)
68
+ third_party_deps = _collect_thirdparty_deps(
69
+ metadata.get("requires_dist", set()), python_version=cfg.python.version
70
+ )
71
+ sbom_content = _run_cyclonedx(cfg, third_party_deps, stderr)
72
+ _write_sbom(cfg, sbom_content, metadata.get("name"), metadata.get("version"))
73
+
74
+
75
+ def cleanup(cfg: ConfigTree) -> None:
76
+ """Get rid of all generated .cdx.json files and the cyclonedx-bom venv."""
77
+ for cdx_file in cfg.spin.project_root.glob("*.python_sbom.cdx.json"):
78
+ rmtree(cdx_file)
79
+ rmtree(cfg.spin.project_root / ".spin" / "venv_csspin_python__python_sbom")
80
+
81
+
82
+ # ---- Internals ---------------------------------------------------------------
83
+
84
+
85
+ def _ensure_cyclonedx_venv(cfg: ConfigTree, binary_dir: str, quiet: str | None) -> Path:
86
+ """Return the cyclonedx-bom interpreter path, (re)creating the venv if needed.
87
+
88
+ We install cyclonedx-bom into a persistent venv since defining it as a
89
+ dependency of csspin-python itself doesn't work at this moment.
90
+ See https://github.com/CycloneDX/cyclonedx-python/issues/1045
91
+ """
92
+ venv_cdx = cfg.spin.project_root / ".spin" / "venv_csspin_python__python_sbom"
93
+ interpreter_cdx = venv_cdx / binary_dir / "python" + cfg.platform.exe
94
+
95
+ requested_version = cfg.python_sbom.cyclonedx_bom_version
96
+ memo_key = f"cyclonedx-bom=={requested_version}"
97
+ memo_file = venv_cdx / "csspin_python_sbom.memo"
98
+
99
+ if venv_cdx.exists():
100
+ with memoizer(memo_file) as memo:
101
+ if memo.check(memo_key):
102
+ info(
103
+ f"Reusing existing cyclonedx-bom {requested_version} from {venv_cdx}"
104
+ )
105
+ return interpreter_cdx
106
+ info(
107
+ f"cyclonedx-bom version mismatch (wanted={requested_version}), "
108
+ f"recreating {venv_cdx}"
109
+ )
110
+ rmtree(venv_cdx)
111
+
112
+ sh(cfg.python.interpreter, "-m", "venv", venv_cdx)
113
+ sh(
114
+ interpreter_cdx,
115
+ "-m",
116
+ "pip",
117
+ quiet,
118
+ "--disable-pip-version-check",
119
+ "install",
120
+ "--index-url",
121
+ cfg.python.index_url,
122
+ "cyclonedx-bom==" + requested_version,
123
+ )
124
+ with memoizer(memo_file) as memo:
125
+ memo.add(memo_key)
126
+ return interpreter_cdx
127
+
128
+
129
+ def _run_cyclonedx(cfg: ConfigTree, third_party_deps: set[str], stderr: int) -> str:
130
+ """
131
+ Install third-party deps into a temp venv and return the CycloneDX JSON.
132
+ """
133
+
134
+ binary_dir = "Scripts" if sys.platform == "win32" else "bin"
135
+ quiet = None if cfg.verbosity > Verbosity.NORMAL else "-q"
136
+ interpreter_cdx = _ensure_cyclonedx_venv(cfg, binary_dir, quiet)
137
+
138
+ with TemporaryDirectory() as tmp_dir:
139
+ venv = Path(tmp_dir) / "venv"
140
+ interpreter = venv / binary_dir / "python" + cfg.platform.exe
141
+ sh(cfg.python.interpreter, "-m", "venv", venv)
142
+ if third_party_deps:
143
+ sh(
144
+ interpreter,
145
+ "-m",
146
+ "pip",
147
+ quiet,
148
+ "install",
149
+ "--index-url",
150
+ cfg.python.index_url,
151
+ *[
152
+ f"--constraint={constraint}"
153
+ for constraint in cfg.python.constraints
154
+ ],
155
+ *third_party_deps,
156
+ stderr=stderr,
157
+ )
158
+ sh(interpreter, "-m", "pip", quiet, "uninstall", "-y", "pip")
159
+ return backtick(interpreter_cdx, "-m", "cyclonedx_py", "environment", venv, stderr=stderr) # type: ignore[no-any-return] # noqa: E501
160
+
161
+
162
+ def _write_sbom(
163
+ cfg: ConfigTree, content: str, project_name: str, project_version: str
164
+ ) -> None:
165
+ """Inject project metadata into the cyclonedx JSON and write the output file."""
166
+ output_file = cfg.spin.project_root / f"{project_name}.python_sbom.cdx.json"
167
+ # cyclonedx-bom doesn't add primary component name and version when not
168
+ # using pyproject.toml
169
+ sbom_json = json.loads(content)
170
+ sbom_json |= {
171
+ "metadata": {"component": {"name": project_name, "version": project_version}}
172
+ }
173
+ with open(output_file, "w", encoding="utf-8") as f:
174
+ json.dump(sbom_json, f, indent=2, sort_keys=True)
175
+ info(f"Generated Python SBOM successfully ({output_file})")
176
+
177
+
178
+ def _collect_thirdparty_deps(requires_dist: list, python_version: str) -> set[str]:
179
+ """Extract 'thirdparty' dependency specifiers from project metadata."""
180
+
181
+ import platform
182
+
183
+ env = {
184
+ "sys_platform": sys.platform,
185
+ "extra": "thirdparty",
186
+ "platform_system": platform.system(),
187
+ "python_version": python_version,
188
+ }
189
+
190
+ dependencies = set()
191
+
192
+ for require in requires_dist:
193
+ req = Requirement(require)
194
+ if req.marker and req.marker.evaluate(environment=env):
195
+ dependencies.add(req.name + str(req.specifier))
196
+ return dependencies
@@ -0,0 +1,18 @@
1
+ # -*- mode: yaml; coding: utf-8 -*-
2
+ #
3
+ # Schema for the python_sbom plugin for csspin
4
+
5
+ python_sbom:
6
+ type: object
7
+ help: Configuration related to the python_sbom plugin for csspin
8
+ properties:
9
+ project_paths:
10
+ type: list
11
+ help: |
12
+ List of paths to the Python projects for which to generate the
13
+ SBOM.
14
+ cyclonedx_version:
15
+ type: str
16
+ help: |
17
+ Version of the cyclonedx-bom package to use for generating the
18
+ SBOM.
@@ -181,8 +181,9 @@ def _update_index_url_in_toml(cfg: ConfigTree) -> None:
181
181
  Update the index-url in the uv.toml in case it changed.
182
182
  """
183
183
  if (uv_toml_path := interpolate1(Path(cfg.uv_provisioner.uv_toml_path))).exists():
184
- with open(uv_toml_path, mode="r+b") as fd:
184
+ with open(uv_toml_path, mode="rb") as fd:
185
185
  toml_content = tomllib.load(fd)
186
- if toml_content.get("index-url") != cfg.python.index_url:
187
- toml_content["index-url"] = cfg.python.index_url
186
+ if toml_content.get("index-url") != cfg.python.index_url:
187
+ toml_content["index-url"] = cfg.python.index_url
188
+ with open(uv_toml_path, mode="wb") as fd:
188
189
  tomli_w.dump(toml_content, fd)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csspin-python
3
- Version: 3.2.0
3
+ Version: 4.1.0rc1
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>
@@ -15,13 +15,13 @@ Classifier: Development Status :: 5 - Production/Stable
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
22
21
  Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
23
  Classifier: Topic :: Software Development
24
- Requires-Python: >=3.9
24
+ Requires-Python: >=3.10
25
25
  Description-Content-Type: text/x-rst
26
26
  License-File: LICENSE
27
27
  Requires-Dist: platformdirs~=4.3.8
@@ -45,18 +45,21 @@ The following plugins are available:
45
45
  - `csspin_python.debugpy`: A plugin for debugging Python code using `debugpy`_.
46
46
  - `csspin_python.devpi`: A plugin for simplified usage of `devpi`_.
47
47
  - `csspin_python.pytest`: A plugin for running tests using pytest.
48
- - `csspin_python.python`: A plugin for provisioning Python environments and
49
- installing dependencies.
48
+ - `csspin_python.python`: A plugin for provisioning Python environments,
49
+ installing dependencies, and managing the virtual environment.
50
+ - `csspin_python.python_sbom`: A plugin for generating a `CycloneDX`_ Software
51
+ Bill of Materials (SBOM) for Python third-party dependencies.
50
52
  - `csspin_python.radon`: A plugin for running `radon`_ to analyze code
51
53
  complexity.
52
54
  - `csspin_python.sphinx`: A plugin for building Sphinx documentation.
53
55
  - `csspin_python.playwright`: A plugin for running tests using `playwright`_.
54
56
  This plugin is deprecated, use the pytest plugin with the
55
57
  'pytest.playwright.enabled=true' setting instead.
56
- - `csspin_python.uv_provisioner`: A plugin that uses `uv`_ to provision the Python environment.
58
+ - `csspin_python.uv_provisioner`: A plugin that uses `uv`_ to provision the
59
+ Python environment.
57
60
 
58
61
  The package provides an ``aws_auth`` extra, that, if enabled, can authenticate
59
- to `CONTACT Software GmbH`_'s AWS Codeartifact. It also provides an ``uv``
62
+ to `CONTACT Software GmbH`_'s AWS CodeArtifact. It also provides an ``uv``
60
63
  extra, that is necessary for using the ``csspin_python.uv_provisioner`` plugin.
61
64
 
62
65
  Prerequisites
@@ -90,7 +93,7 @@ within the `spinfile.yaml` configuration file of your project.
90
93
  - csspin_python.pytest
91
94
 
92
95
  python:
93
- version: 3.9.8
96
+ version: 3.10.19
94
97
  requirements:
95
98
  - sphinx-click
96
99
  - sphinx-rtd-theme
@@ -120,4 +123,5 @@ tests using ``spin pytest`` and do other great things.
120
123
  .. _`devpi`: https://pypi.org/project/devpi
121
124
  .. _`playwright`: https://pypi.org/project/pytest-playwright
122
125
  .. _`radon`: https://pypi.org/project/radon
126
+ .. _`CycloneDX`: https://cyclonedx.org/
123
127
  .. _`uv`: https://docs.astral.sh/uv/
@@ -2,20 +2,22 @@ csspin_python/behave.py,sha256=iJZeyIqB7V_NzTdLTZldNY9W_GGwCWkXe6WY69wpDqs,4997
2
2
  csspin_python/behave_schema.yaml,sha256=8qoOCK-uTmwgRRW29urgK0X_kgn0zO0X34v89bvii2w,1241
3
3
  csspin_python/debugpy.py,sha256=v0ZZopv5TNoSaFf2kiePsw9OmhBpjfOBFh0u71jTcnQ,962
4
4
  csspin_python/debugpy_schema.yaml,sha256=BeH30nSirDYctkdhS9xMXUG5htj3PED_ZjmxPG5WRUc,364
5
- csspin_python/devpi.py,sha256=C-5O_vA06CwQR4uElOw-2VH2-m001SpxowM_X6RbRwo,2352
5
+ csspin_python/devpi.py,sha256=o7O06Dw-xt47y-M74TShcl9MUMaW78aFmw6i6ISYwoU,2351
6
6
  csspin_python/devpi_schema.yaml,sha256=2gPATWjVcfvCTrGZX2FK6wH8hh9KS0XzZ35JvZeJGEU,487
7
7
  csspin_python/playwright.py,sha256=oFfphLqa4AB6K9vasCUFHN0kFXu63n3ocrsqVuRp4-0,5102
8
8
  csspin_python/playwright_schema.yaml,sha256=TSeR16YHa7m7bfO59F2eMV-jXcglluTJdEpUeL16saY,1178
9
- csspin_python/pytest.py,sha256=pTOb5zFd9RINZwJsHNaRuSGVDkPMABzaAhwpAJo1nQE,4574
9
+ csspin_python/pytest.py,sha256=N9YaU_ouQab0PFPf46HLE7Vg4JeoZW4dzVD7EevqJ1U,4573
10
10
  csspin_python/pytest_schema.yaml,sha256=tzXtdF6MvGC9v59EVRJFfLeMMHqPsXcFXy2zJtRECBI,1535
11
- csspin_python/python.py,sha256=rSdF72FkcxiCrzLWSV7Cr4Thx5OgHGN7b6RKgOaXFEg,35871
11
+ csspin_python/python.py,sha256=yqH1eG6mqRJomu-F99cB74PFaUzbSoG_dhpUjYwr1xE,37725
12
+ csspin_python/python_sbom.py,sha256=RpqobiJ768Q6no7uwYAqvykp1-Bj0bkvED3a3pZbEBI,6817
13
+ csspin_python/python_sbom_schema.yaml,sha256=VCxY9E2AG_fS00dvIYe6Fbvz1maPjpY0zyZK1Z0wJu4,538
12
14
  csspin_python/python_schema.yaml,sha256=pgVVjByUYjxQWek7aFmjQzRwmq2ROLvHYgwGPMrT9sM,6351
13
15
  csspin_python/radon.py,sha256=uFqm6FEi5oWj-_XVaAm3s9cam0cUmr1_FwRf40K6xWs,1876
14
16
  csspin_python/radon_schema.yaml,sha256=rlRzXw5z4XbjOVznRiUxWGP4E9hx1Jm-gGw1iQiYzE0,548
15
- csspin_python/uv_provisioner.py,sha256=MGedx4e286ZE1miwh70y6b3wePw3mmc0m1kG6H_738I,5999
17
+ csspin_python/uv_provisioner.py,sha256=1e-_Sb39JrqNWyaUNeBX59R5tutXLJ1ZsT7urCN1U0I,6044
16
18
  csspin_python/uv_provisioner_schema.yaml,sha256=Y8ZNC2OMnhR8Us3WUXAXK9hMjqGWAKFJB2puX4X5XNQ,727
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,,
19
+ csspin_python-4.1.0rc1.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
20
+ csspin_python-4.1.0rc1.dist-info/METADATA,sha256=kW0dZXu1Wwuhw4EooWroAHOZGMw5wjBso1MFJDzkJX8,5257
21
+ csspin_python-4.1.0rc1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
22
+ csspin_python-4.1.0rc1.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
23
+ csspin_python-4.1.0rc1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (82.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5