lib-detect-testenv 3.0.4__tar.gz → 3.0.5__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.
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.github/workflows/default_cicd_public.yml +42 -6
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.gitignore +8 -5
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/CHANGELOG.md +13 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/CLAUDE.md +5 -85
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/Makefile +38 -4
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/PKG-INFO +49 -17
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/README.md +38 -8
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/pyproject.toml +14 -11
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/__init__conf__.py +1 -1
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/safe_console.py +9 -4
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/test_safe_console.py +25 -0
- lib_detect_testenv-3.0.5/uv.lock +1776 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.devcontainer/devcontainer.json +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.devcontainer/settings.json +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.env.example +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.github/actions/extract-metadata/action.yml +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.github/dependabot.yml +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.github/workflows/codeql.yml +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.github/workflows/default_release_public.yml +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.qlty/qlty.toml +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.snyk +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/CONTRIBUTING.md +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/DEVELOPMENT.md +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/INSTALL.md +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/LICENSE +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/codecov.yml +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/concept.md +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/docs/systemdesign/module_reference.md +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/notebooks/Quickstart.ipynb +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/reset_git_history.sh +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/__init__.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/__main__.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/behaviors.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/cli.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/lib_detect_testenv.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/py.typed +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/typed_click.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/conftest.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/test_behaviors.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/test_cli.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/test_lib_detect_testenv.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/test_metadata.py +0 -0
- {lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/tests/test_module_entry.py +0 -0
{lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/.github/workflows/default_cicd_public.yml
RENAMED
|
@@ -124,9 +124,36 @@ jobs:
|
|
|
124
124
|
print("No CI system dependencies configured for macOS.")
|
|
125
125
|
|
|
126
126
|
- name: Install dev deps
|
|
127
|
-
shell:
|
|
127
|
+
shell: python
|
|
128
128
|
run: |
|
|
129
|
-
|
|
129
|
+
import os, subprocess, tempfile
|
|
130
|
+
from pathlib import Path
|
|
131
|
+
try:
|
|
132
|
+
import tomllib
|
|
133
|
+
except ModuleNotFoundError:
|
|
134
|
+
import pip._vendor.tomli as tomllib
|
|
135
|
+
|
|
136
|
+
# Push and pull_request runs install exactly the tree uv.lock pins - versions
|
|
137
|
+
# plus file hashes, which uv verifies - so CI tests what the lock ships and no
|
|
138
|
+
# release published upstream since the last lock refresh can enter the build.
|
|
139
|
+
# The nightly schedule run resolves afresh instead (still inside the project's
|
|
140
|
+
# [tool.uv] exclude-newer cooldown, which uv honours here): it is the canary
|
|
141
|
+
# that reports upstream breakage before a lock refresh adopts it. A project
|
|
142
|
+
# without a uv.lock keeps the unlocked install.
|
|
143
|
+
with open("pyproject.toml", "rb") as f:
|
|
144
|
+
data = tomllib.load(f)
|
|
145
|
+
# Asking uv for an extra the project does not declare is a hard error.
|
|
146
|
+
extra = ["--extra", "dev"] if "dev" in data.get("project", {}).get("optional-dependencies", {}) else []
|
|
147
|
+
target = ["--system"] # setup-python's interpreter; the job runs no venv
|
|
148
|
+
locked = Path("uv.lock").is_file() and os.environ["GITHUB_EVENT_NAME"] != "schedule"
|
|
149
|
+
if locked:
|
|
150
|
+
req = Path(tempfile.mkdtemp()) / "locked-requirements.txt"
|
|
151
|
+
# --locked fails when pyproject.toml changed without a lock refresh.
|
|
152
|
+
subprocess.check_call(["uv", "export", "--locked", "--no-emit-project", *extra, "-o", str(req), "--quiet"])
|
|
153
|
+
subprocess.check_call(["uv", "pip", "install", *target, "-r", str(req)])
|
|
154
|
+
subprocess.check_call(["uv", "pip", "install", *target, "--no-deps", "-e", "."])
|
|
155
|
+
else:
|
|
156
|
+
subprocess.check_call(["uv", "pip", "install", *target, "-e", ".[dev]" if extra else "."])
|
|
130
157
|
|
|
131
158
|
- name: Install CI system dependencies (Windows/choco)
|
|
132
159
|
if: runner.os == 'Windows'
|
|
@@ -236,7 +263,7 @@ jobs:
|
|
|
236
263
|
continue-on-error: true
|
|
237
264
|
shell: python
|
|
238
265
|
run: |
|
|
239
|
-
import subprocess, sys, tempfile
|
|
266
|
+
import os, subprocess, sys, tempfile
|
|
240
267
|
import rtoml
|
|
241
268
|
from pathlib import Path
|
|
242
269
|
|
|
@@ -263,9 +290,18 @@ jobs:
|
|
|
263
290
|
|
|
264
291
|
tmp = Path(tempfile.mkdtemp())
|
|
265
292
|
req = tmp / "compiled-requirements.txt"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
293
|
+
# With a uv.lock the install step put exactly the locked tree on the
|
|
294
|
+
# runner, so audit that tree; the schedule canary and lock-less
|
|
295
|
+
# projects resolve pyproject.toml afresh, as their install did.
|
|
296
|
+
if Path("uv.lock").is_file() and os.environ["GITHUB_EVENT_NAME"] != "schedule":
|
|
297
|
+
subprocess.check_call(
|
|
298
|
+
["uv", "export", "--locked", "--no-emit-project", "--no-hashes", *selector,
|
|
299
|
+
"-o", str(req), "--quiet"]
|
|
300
|
+
)
|
|
301
|
+
else:
|
|
302
|
+
subprocess.check_call(
|
|
303
|
+
["uv", "pip", "compile", *selector, "pyproject.toml", "-o", str(req), "--quiet"]
|
|
304
|
+
)
|
|
269
305
|
|
|
270
306
|
# Drop direct URL references ("pkg @ git+https://...") before auditing.
|
|
271
307
|
# pip-audit cannot audit those (they are not on PyPI) but, given a
|
|
@@ -47,7 +47,8 @@ tmp_*
|
|
|
47
47
|
# *.py[cod]: catches .pyc, .pyo, .pyd
|
|
48
48
|
__pycache__/
|
|
49
49
|
*.py[cod]
|
|
50
|
-
|
|
50
|
+
# other compiled artifacts
|
|
51
|
+
*$py.class
|
|
51
52
|
|
|
52
53
|
# pytest, coverage, MyPy, Hypothesis
|
|
53
54
|
.pytest_cache/
|
|
@@ -73,12 +74,15 @@ coverage.xml
|
|
|
73
74
|
# source/package builds
|
|
74
75
|
build/
|
|
75
76
|
dist/
|
|
76
|
-
|
|
77
|
+
# metadata for pip installs
|
|
78
|
+
*.egg-info/
|
|
77
79
|
*.egg
|
|
78
80
|
*.whl
|
|
79
81
|
pip-wheel-metadata/
|
|
80
|
-
|
|
81
|
-
*.
|
|
82
|
+
# PyInstaller metadata
|
|
83
|
+
*.manifest
|
|
84
|
+
# PyInstaller spec files
|
|
85
|
+
*.spec
|
|
82
86
|
|
|
83
87
|
# Eggs, parts, wheels, sdist, etc.
|
|
84
88
|
develop-eggs/
|
|
@@ -178,7 +182,6 @@ result
|
|
|
178
182
|
# Optional lockfiles (for alternative managers)
|
|
179
183
|
poetry.lock
|
|
180
184
|
pdm.lock
|
|
181
|
-
uv.lock
|
|
182
185
|
|
|
183
186
|
# Local, per-developer settings (Claude Code etc.) - never commit
|
|
184
187
|
*.local.json
|
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.0.5] 2026-10-15 14:11:33
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Python 3.15 is a supported version: declared in the classifiers, so CI and `make test-all` run it.
|
|
14
|
+
- `uv.lock` is committed, and `[tool.uv] exclude-newer` sets a cooldown that keeps releases younger
|
|
15
|
+
than it out of the lock. CI installs exactly the locked tree on push and pull request; the nightly
|
|
16
|
+
scheduled run still resolves afresh to catch upstream breakage early.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- The install section explains why installing with uv protects against supply-chain attacks and why
|
|
20
|
+
a plain `pip install` from PyPI does not.
|
|
21
|
+
- `rtoml` floor raised to 0.14.0, the first release with Python 3.15 wheels.
|
|
22
|
+
|
|
10
23
|
## [3.0.4] 2026-08-01 00:14:59
|
|
11
24
|
### Fixed
|
|
12
25
|
- **Console output no longer crashes on a legacy codepage.** A Windows console at codepage 1252
|
|
@@ -1,36 +1,5 @@
|
|
|
1
1
|
# Claude Code Guidelines for lib_detect_testenv
|
|
2
2
|
|
|
3
|
-
## Session Initialization
|
|
4
|
-
|
|
5
|
-
When starting a new session, read and apply the following system prompt files from `/media/srv-main-softdev/projects/softwarestack/systemprompts`:
|
|
6
|
-
|
|
7
|
-
### Core Guidelines (Always Apply)
|
|
8
|
-
- `core_programming_solid.md`
|
|
9
|
-
|
|
10
|
-
### Bash-Specific Guidelines
|
|
11
|
-
When working with Bash scripts:
|
|
12
|
-
- use skill `bitranox:bash-reference` when in doubt of bash features or syntax
|
|
13
|
-
- `core_programming_solid.md`
|
|
14
|
-
- use skill `bitranox:bash-clean-architecture`
|
|
15
|
-
- `bash_clean_code.md`
|
|
16
|
-
- `bash_small_functions.md`
|
|
17
|
-
|
|
18
|
-
### Python-Specific Guidelines
|
|
19
|
-
When working with Python code:
|
|
20
|
-
- `core_programming_solid.md`
|
|
21
|
-
- `python_solid_architecture_enforcer.md`
|
|
22
|
-
- use skill `bitranox:python-clean-architecture`
|
|
23
|
-
- `python_clean_code.md`
|
|
24
|
-
- `python_small_functions_style.md`
|
|
25
|
-
- use skill `bitranox:python-use-modern-libraries`
|
|
26
|
-
- `python_structure_template.md`
|
|
27
|
-
|
|
28
|
-
### Additional Guidelines
|
|
29
|
-
- `self_documenting.md`
|
|
30
|
-
- `self_documenting_template.md`
|
|
31
|
-
- `python_jupyter_notebooks.md`
|
|
32
|
-
- `python_testing.md`
|
|
33
|
-
|
|
34
3
|
## Project Structure
|
|
35
4
|
|
|
36
5
|
```
|
|
@@ -98,32 +67,14 @@ lib_detect_testenv/
|
|
|
98
67
|
- **Single Source of Truth**: Package version is in `pyproject.toml` (`[project].version`)
|
|
99
68
|
- **Version Bumps**: update `pyproject.toml`, `CHANGELOG.md` and update the constants in `src/lib_detect_testenv/__init__conf__.py` according to `pyproject.toml`
|
|
100
69
|
- Automation rewrites `src/lib_detect_testenv/__init__conf__.py` from `pyproject.toml`, so runtime code imports generated constants instead of querying `importlib.metadata`.
|
|
101
|
-
-
|
|
70
|
+
- bmk regenerates that metadata module itself - it runs as a stage of `make bump`, and again before every `make commit` / `make push`. `make test` does not regenerate it, and the generated constants are never hand-edited.
|
|
102
71
|
- **Release Tags**: Format is `vX.Y.Z` (push tags for CI to build and publish)
|
|
103
72
|
|
|
104
|
-
##
|
|
73
|
+
## Make targets specific to this repo
|
|
105
74
|
|
|
106
|
-
|
|
107
|
-
|-------------------|---------------------------------------------------------------------------------|
|
|
108
|
-
| `build` | Build wheel/sdist artifacts |
|
|
109
|
-
| `bump` | Bump version (VERSION=X.Y.Z or PART=major\|minor\|patch) and update changelog |
|
|
110
|
-
| `bump-major` | Increment major version ((X+1).0.0) |
|
|
111
|
-
| `bump-minor` | Increment minor version (X.Y.Z → X.(Y+1).0) |
|
|
112
|
-
| `bump-patch` | Increment patch version (X.Y.Z → X.Y.(Z+1)) |
|
|
113
|
-
| `clean` | Remove caches, coverage, and build artifacts (includes `dist/` and `build/`) |
|
|
114
|
-
| `dev` | Install package with dev extras |
|
|
115
|
-
| `help` | Show make targets |
|
|
116
|
-
| `install` | Editable install |
|
|
117
|
-
| `menu` | Interactive TUI menu |
|
|
118
|
-
| `push` | Commit changes and push to GitHub (no CI monitoring) |
|
|
119
|
-
| `release` | Tag vX.Y.Z, push, sync packaging, run gh release if available |
|
|
120
|
-
| `run` | Run module entry (`python -m ... --help`) |
|
|
121
|
-
| `test` | Lint, format, type-check, run tests with coverage, upload to Codecov |
|
|
122
|
-
| `version-current` | Print current version from `pyproject.toml` |
|
|
75
|
+
- `make run` - Run module entry (`python -m ... --help`)
|
|
123
76
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Follow the guidelines in `python_clean_code.md` for all Python code.
|
|
77
|
+
Everything else is the generated bmk list: run `make help`.
|
|
127
78
|
|
|
128
79
|
## Architecture Overview
|
|
129
80
|
|
|
@@ -137,41 +88,10 @@ Import rules (enforced by import-linter):
|
|
|
137
88
|
- `cli` depends on `lib_detect_testenv` and `behaviors`
|
|
138
89
|
- Core detection functions have no internal dependencies
|
|
139
90
|
|
|
140
|
-
Apply principles use skill `bitranox:python-clean-architecture` when designing and implementing features.
|
|
141
|
-
|
|
142
|
-
## Security & Configuration
|
|
143
|
-
|
|
144
|
-
- `.env` files are for local tooling only (CodeCov tokens, etc.)
|
|
145
|
-
- **NEVER** commit secrets to version control
|
|
146
|
-
- Rich logging should sanitize payloads before rendering
|
|
147
|
-
|
|
148
|
-
## Documentation & Translations
|
|
149
|
-
|
|
150
|
-
### Web Documentation
|
|
151
|
-
- Update only English docs under `/website/docs`
|
|
152
|
-
- Other languages are translated automatically
|
|
153
|
-
- When in doubt, ask before modifying non-English documentation
|
|
154
|
-
|
|
155
|
-
### App UI Strings (i18n)
|
|
156
|
-
- Update only `sources/_locales/en` for string changes
|
|
157
|
-
- Other languages are translated automatically
|
|
158
|
-
- When in doubt, ask before modifying non-English locales
|
|
91
|
+
Apply principles use skill `bitranox:coding-python-clean-architecture` when designing and implementing features.
|
|
159
92
|
|
|
160
93
|
## Commit & Push Policy
|
|
161
94
|
|
|
162
95
|
### Pre-Push Requirements
|
|
163
96
|
- **Always run `make test` before pushing** to avoid lint/test breakage
|
|
164
97
|
- Ensure all tests pass and code is properly formatted
|
|
165
|
-
|
|
166
|
-
### Post-Push Monitoring
|
|
167
|
-
- Monitor GitHub Actions for errors after pushing
|
|
168
|
-
- Attempt to correct any CI/CD errors that appear
|
|
169
|
-
|
|
170
|
-
## Claude Code Workflow
|
|
171
|
-
|
|
172
|
-
When working on this project:
|
|
173
|
-
1. Read relevant system prompts at session start
|
|
174
|
-
2. Apply appropriate coding guidelines based on file type
|
|
175
|
-
3. Run `make test` before commits
|
|
176
|
-
4. Follow versioning guidelines for releases
|
|
177
|
-
5. Monitor CI after pushing changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# BMK MAKEFILE 3.
|
|
1
|
+
# BMK MAKEFILE 3.21.1
|
|
2
2
|
# do not alter this file - it might be overwritten on new versions of BMK
|
|
3
3
|
# if You want to alter it, remove the first line # BMK MAKEFILE 1.0 - then it is a custom makefile and will not be overwritten
|
|
4
4
|
# bmk Makefile - thin wrapper using `uv tool upgrade` for persistent bmk
|
|
@@ -186,16 +186,50 @@ endif
|
|
|
186
186
|
# BMK_MIN floors the FALLBACK install. The upgrade path does not consult it: uv re-resolves
|
|
187
187
|
# the requirement recorded at install time, which is itself a `bmk>=...`, so it always lands
|
|
188
188
|
# on the newest release and clears the floor anyway.
|
|
189
|
-
BMK_MIN := 3.
|
|
189
|
+
BMK_MIN := 3.21.1
|
|
190
190
|
|
|
191
191
|
# Absent env, missing check, or damaged env - all three mean "do not upgrade, rebuild".
|
|
192
192
|
# `test -x` keeps a fresh machine quiet: there is no interpreter to run yet.
|
|
193
193
|
BMK_INTACT := $(if $(BMK_PY),test -x "$(BMK_PY)" && "$(BMK_PY)" -m bmk_selfcheck,false)
|
|
194
194
|
|
|
195
|
+
# The env this recipe mutates is shared by EVERY repo on the machine, so the mutation has to
|
|
196
|
+
# wait for the bmk processes running out of it. Every bmk started from that env holds a
|
|
197
|
+
# SHARED lock on <uv tool dir>/.bmk-tool.lock for its whole lifetime; these wrappers take
|
|
198
|
+
# the same lock EXCLUSIVE for the length of the upgrade. Without it, a make here deleted the
|
|
199
|
+
# site-packages out from under a bmk minutes into a test suite in an UNRELATED repo, and it
|
|
200
|
+
# surfaced as an ImportError inside bmk's OWN dependencies that cleared on a re-run - so it
|
|
201
|
+
# read as a flake rather than as the environment vanishing mid-gate.
|
|
202
|
+
#
|
|
203
|
+
# `bmk_toollock` is a second stdlib-only TOP-LEVEL module bmk ships, run on the tool env's
|
|
204
|
+
# own interpreter. That is not circular: a uv tool env's bin/python is a SYMLINK to a base
|
|
205
|
+
# interpreter OUTSIDE the tool dir, so the stdlib the guard runs on is not part of the tree
|
|
206
|
+
# uv is about to replace, and the guard is read and compiled before uv is ever spawned.
|
|
207
|
+
#
|
|
208
|
+
# NOT uv's own <uv tool dir>/.lock, which uv does honour: `uv tool upgrade` and `uv tool
|
|
209
|
+
# list` block on it too (measured), and the upgrade runs before every target - so a
|
|
210
|
+
# gate-lifetime shared lock there would make every make on the machine, and every unrelated
|
|
211
|
+
# `uv tool install`, wait for the longest-running gate.
|
|
212
|
+
#
|
|
213
|
+
# $(wildcard) rather than a shell test: on a machine with no tool env yet these expand to
|
|
214
|
+
# NOTHING and the install runs bare, which is exactly what a first install needs.
|
|
215
|
+
#
|
|
216
|
+
# The bounded wait is load-bearing in both directions. An unbounded one would serialise
|
|
217
|
+
# every repo's make behind the longest gate on the machine. A SKIPPED upgrade costs nothing
|
|
218
|
+
# (the next make picks it up), so the upgrade skips. The REPAIR may not skip: proceeding
|
|
219
|
+
# would leave a damaged env in place, so it fails and drops through to the retry.
|
|
220
|
+
#
|
|
221
|
+
# The LAST attempt is deliberately NEVER guarded, and a test pins that. The guard lives
|
|
222
|
+
# inside the env being repaired, so on the path where that env is absent or broken the guard
|
|
223
|
+
# may be broken too; an unguarded last resort is what stops a defective guard from making
|
|
224
|
+
# `make` unrunnable in every repo at once. If bmk_toollock ever falls out of the wheel, every
|
|
225
|
+
# make everywhere drops to a full reinstall - there is a packaging test for exactly that.
|
|
226
|
+
BMK_LOCK := $(if $(wildcard $(BMK_PY)),"$(BMK_PY)" -m bmk_toollock --exclusive --timeout 10 --on-timeout skip --,)
|
|
227
|
+
BMK_LOCK_REPAIR := $(if $(wildcard $(BMK_PY)),"$(BMK_PY)" -m bmk_toollock --exclusive --timeout 60 --on-timeout fail --,)
|
|
228
|
+
|
|
195
229
|
.PHONY: _ensure_bmk
|
|
196
230
|
_ensure_bmk:
|
|
197
|
-
@$(BMK_INTACT) && uv tool upgrade bmk \
|
|
198
|
-
|| uv tool install --reinstall --force "bmk>=$(BMK_MIN)" \
|
|
231
|
+
@$(BMK_INTACT) && $(BMK_LOCK) uv tool upgrade bmk \
|
|
232
|
+
|| $(BMK_LOCK_REPAIR) uv tool install --reinstall --force "bmk>=$(BMK_MIN)" \
|
|
199
233
|
|| uv tool install --reinstall --force "bmk>=$(BMK_MIN)"
|
|
200
234
|
|
|
201
235
|
# ──────────────────────────────────────────────────────────────
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lib_detect_testenv
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.5
|
|
4
4
|
Summary: Detect test environment - pytest, doctest, unittest, or regular execution
|
|
5
5
|
Project-URL: Homepage, https://github.com/bitranox/lib_detect_testenv
|
|
6
6
|
Project-URL: Repository, https://github.com/bitranox/lib_detect_testenv.git
|
|
@@ -18,24 +18,26 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
23
|
Classifier: Typing :: Typed
|
|
22
24
|
Requires-Python: >=3.10
|
|
23
|
-
Requires-Dist: rich-click>=1.9.
|
|
25
|
+
Requires-Dist: rich-click>=1.9.9
|
|
24
26
|
Provides-Extra: dev
|
|
25
27
|
Requires-Dist: bandit<1.9,>=1.8.6; (python_version < '3.10') and extra == 'dev'
|
|
26
28
|
Requires-Dist: bandit>=1.9.4; (python_version >= '3.10') and extra == 'dev'
|
|
27
|
-
Requires-Dist: build>=1.
|
|
28
|
-
Requires-Dist: click>=8.
|
|
29
|
+
Requires-Dist: build>=1.6.1; extra == 'dev'
|
|
30
|
+
Requires-Dist: click>=8.5.0; extra == 'dev'
|
|
29
31
|
Requires-Dist: import-linter<2.6,>=2.5.2; (python_version < '3.10') and extra == 'dev'
|
|
30
|
-
Requires-Dist: import-linter>=2.
|
|
32
|
+
Requires-Dist: import-linter>=2.15; (python_version >= '3.10') and extra == 'dev'
|
|
31
33
|
Requires-Dist: pip-audit>=2.10.1; extra == 'dev'
|
|
32
|
-
Requires-Dist: pyright[nodejs]>=1.1.
|
|
34
|
+
Requires-Dist: pyright[nodejs]>=1.1.414; extra == 'dev'
|
|
33
35
|
Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
|
|
34
36
|
Requires-Dist: pytest<9,>=8.4.2; (python_version < '3.10') and extra == 'dev'
|
|
35
37
|
Requires-Dist: pytest>=9.1.1; (python_version >= '3.10') and extra == 'dev'
|
|
36
|
-
Requires-Dist: rtoml>=0.
|
|
37
|
-
Requires-Dist: ruff>=0.
|
|
38
|
-
Requires-Dist: setuptools>=
|
|
38
|
+
Requires-Dist: rtoml>=0.14.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.17.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: setuptools>=84.0.0; extra == 'dev'
|
|
39
41
|
Requires-Dist: textual>=8.2.8; extra == 'dev'
|
|
40
42
|
Requires-Dist: twine>=7.0.0; extra == 'dev'
|
|
41
43
|
Description-Content-Type: text/markdown
|
|
@@ -72,7 +74,7 @@ This library provides utility functions to detect whether your code is running i
|
|
|
72
74
|
|
|
73
75
|
### Recommended: Install via UV
|
|
74
76
|
|
|
75
|
-
UV is an ultrafast Python package installer written in Rust (10
|
|
77
|
+
UV is an ultrafast Python package installer written in Rust (10-20× faster than pip/poetry):
|
|
76
78
|
|
|
77
79
|
```bash
|
|
78
80
|
# Install uv
|
|
@@ -107,9 +109,39 @@ For more installation methods (pipx, source builds, etc.), see [INSTALL.md](INST
|
|
|
107
109
|
|
|
108
110
|
### Python Version Requirements
|
|
109
111
|
|
|
110
|
-
- **Python 3.
|
|
112
|
+
- **Python 3.10+** is required
|
|
111
113
|
- Tested on Linux, macOS, and Windows
|
|
112
|
-
- CI runs on CPython 3.
|
|
114
|
+
- CI runs on CPython 3.10 to 3.15
|
|
115
|
+
|
|
116
|
+
### Why we recommend uv: supply-chain protection
|
|
117
|
+
|
|
118
|
+
This repository commits a `uv.lock`. It pins every package the project needs, transitive
|
|
119
|
+
dependencies included, to an exact version and to the hashes of that version's files. The
|
|
120
|
+
`exclude-newer` setting under `[tool.uv]` in `pyproject.toml` adds a cooldown: uv leaves any
|
|
121
|
+
release younger than that out of the lock. CI installs from the lock, so a hijacked release that
|
|
122
|
+
gets pulled again within the cooldown never reaches our builds, and a file whose hash does not
|
|
123
|
+
match is refused.
|
|
124
|
+
|
|
125
|
+
That protection stays in this repository. The installable package declares only lower bounds
|
|
126
|
+
(`>=`) for its dependencies, so a plain `pip install` takes the newest release of every transitive
|
|
127
|
+
dependency at the moment you run it. If one of them was hijacked an hour earlier, you get the
|
|
128
|
+
hijacked version. `pipx install` and `uv tool install` without options do the same.
|
|
129
|
+
|
|
130
|
+
To install the command line tool with a cooldown of your own (uv then skips any release younger
|
|
131
|
+
than 7 days, transitive ones included), or to run the exact tree our CI tests:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
uv tool install --exclude-newer "7 days" lib_detect_testenv
|
|
135
|
+
git clone https://github.com/bitranox/lib_detect_testenv.git && cd lib_detect_testenv && uv sync --locked && uv run lib_detect_testenv --help
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
If you depend on lib_detect_testenv in your own project, give that project the same cooldown (7 days here, as
|
|
139
|
+
an example) and commit the `uv.lock` that `uv add lib_detect_testenv` writes:
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[tool.uv]
|
|
143
|
+
exclude-newer = "7 days"
|
|
144
|
+
```
|
|
113
145
|
|
|
114
146
|
## Usage
|
|
115
147
|
|
|
@@ -121,11 +153,11 @@ The package provides a CLI for shell scripting and CI/CD integration. All comman
|
|
|
121
153
|
|
|
122
154
|
All detection commands use a **3-level exit code system**:
|
|
123
155
|
|
|
124
|
-
| Code
|
|
125
|
-
|
|
126
|
-
| **0** | Test environment **detected**
|
|
127
|
-
| **1** | Test environment **not detected** | Normal negative result (not an error)
|
|
128
|
-
| **2** | **Error occurred**
|
|
156
|
+
| Code | Meaning | Use Case |
|
|
157
|
+
|-------|-----------------------------------|-------------------------------------------|
|
|
158
|
+
| **0** | Test environment **detected** | Success - test env found |
|
|
159
|
+
| **1** | Test environment **not detected** | Normal negative result (not an error) |
|
|
160
|
+
| **2** | **Error occurred** | Command failed (bad arguments, exception) |
|
|
129
161
|
|
|
130
162
|
This design follows Unix conventions and makes shell scripting intuitive:
|
|
131
163
|
|
|
@@ -30,7 +30,7 @@ This library provides utility functions to detect whether your code is running i
|
|
|
30
30
|
|
|
31
31
|
### Recommended: Install via UV
|
|
32
32
|
|
|
33
|
-
UV is an ultrafast Python package installer written in Rust (10
|
|
33
|
+
UV is an ultrafast Python package installer written in Rust (10-20× faster than pip/poetry):
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
# Install uv
|
|
@@ -65,9 +65,39 @@ For more installation methods (pipx, source builds, etc.), see [INSTALL.md](INST
|
|
|
65
65
|
|
|
66
66
|
### Python Version Requirements
|
|
67
67
|
|
|
68
|
-
- **Python 3.
|
|
68
|
+
- **Python 3.10+** is required
|
|
69
69
|
- Tested on Linux, macOS, and Windows
|
|
70
|
-
- CI runs on CPython 3.
|
|
70
|
+
- CI runs on CPython 3.10 to 3.15
|
|
71
|
+
|
|
72
|
+
### Why we recommend uv: supply-chain protection
|
|
73
|
+
|
|
74
|
+
This repository commits a `uv.lock`. It pins every package the project needs, transitive
|
|
75
|
+
dependencies included, to an exact version and to the hashes of that version's files. The
|
|
76
|
+
`exclude-newer` setting under `[tool.uv]` in `pyproject.toml` adds a cooldown: uv leaves any
|
|
77
|
+
release younger than that out of the lock. CI installs from the lock, so a hijacked release that
|
|
78
|
+
gets pulled again within the cooldown never reaches our builds, and a file whose hash does not
|
|
79
|
+
match is refused.
|
|
80
|
+
|
|
81
|
+
That protection stays in this repository. The installable package declares only lower bounds
|
|
82
|
+
(`>=`) for its dependencies, so a plain `pip install` takes the newest release of every transitive
|
|
83
|
+
dependency at the moment you run it. If one of them was hijacked an hour earlier, you get the
|
|
84
|
+
hijacked version. `pipx install` and `uv tool install` without options do the same.
|
|
85
|
+
|
|
86
|
+
To install the command line tool with a cooldown of your own (uv then skips any release younger
|
|
87
|
+
than 7 days, transitive ones included), or to run the exact tree our CI tests:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv tool install --exclude-newer "7 days" lib_detect_testenv
|
|
91
|
+
git clone https://github.com/bitranox/lib_detect_testenv.git && cd lib_detect_testenv && uv sync --locked && uv run lib_detect_testenv --help
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If you depend on lib_detect_testenv in your own project, give that project the same cooldown (7 days here, as
|
|
95
|
+
an example) and commit the `uv.lock` that `uv add lib_detect_testenv` writes:
|
|
96
|
+
|
|
97
|
+
```toml
|
|
98
|
+
[tool.uv]
|
|
99
|
+
exclude-newer = "7 days"
|
|
100
|
+
```
|
|
71
101
|
|
|
72
102
|
## Usage
|
|
73
103
|
|
|
@@ -79,11 +109,11 @@ The package provides a CLI for shell scripting and CI/CD integration. All comman
|
|
|
79
109
|
|
|
80
110
|
All detection commands use a **3-level exit code system**:
|
|
81
111
|
|
|
82
|
-
| Code
|
|
83
|
-
|
|
84
|
-
| **0** | Test environment **detected**
|
|
85
|
-
| **1** | Test environment **not detected** | Normal negative result (not an error)
|
|
86
|
-
| **2** | **Error occurred**
|
|
112
|
+
| Code | Meaning | Use Case |
|
|
113
|
+
|-------|-----------------------------------|-------------------------------------------|
|
|
114
|
+
| **0** | Test environment **detected** | Success - test env found |
|
|
115
|
+
| **1** | Test environment **not detected** | Normal negative result (not an error) |
|
|
116
|
+
| **2** | **Error occurred** | Command failed (bad arguments, exception) |
|
|
87
117
|
|
|
88
118
|
This design follows Unix conventions and makes shell scripting intuitive:
|
|
89
119
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lib_detect_testenv"
|
|
3
|
-
version = "3.0.
|
|
3
|
+
version = "3.0.5"
|
|
4
4
|
description = "Detect test environment - pytest, doctest, unittest, or regular execution"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"rich-click>=1.9.
|
|
8
|
+
"rich-click>=1.9.9",
|
|
9
9
|
]
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
authors = [{ name = "bitranox", email = "bitranox@gmail.com" }]
|
|
@@ -17,6 +17,8 @@ classifiers = [
|
|
|
17
17
|
"Programming Language :: Python :: 3.11",
|
|
18
18
|
"Programming Language :: Python :: 3.12",
|
|
19
19
|
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: 3.15",
|
|
20
22
|
"License :: OSI Approved :: MIT License",
|
|
21
23
|
"Environment :: Console",
|
|
22
24
|
"Operating System :: OS Independent",
|
|
@@ -33,13 +35,11 @@ dev = [
|
|
|
33
35
|
"pytest>=8.4.2,<9; python_version<'3.10'",
|
|
34
36
|
"pytest>=9.1.1; python_version>='3.10'",
|
|
35
37
|
"pytest-cov>=7.1.0",
|
|
36
|
-
"ruff>=0.
|
|
37
|
-
"pyright[nodejs]>=1.1.
|
|
38
|
+
"ruff>=0.17.0",
|
|
39
|
+
"pyright[nodejs]>=1.1.414",
|
|
38
40
|
"bandit>=1.8.6,<1.9; python_version<'3.10'",
|
|
39
41
|
"bandit>=1.9.4; python_version>='3.10'",
|
|
40
|
-
|
|
41
|
-
# release; a >=1.5.1 floor has ZERO candidates and makes this extra unresolvable.
|
|
42
|
-
"build>=1.5.0",
|
|
42
|
+
"build>=1.6.1",
|
|
43
43
|
"twine>=7.0.0",
|
|
44
44
|
# codecov-cli is disabled, not dropped: it requires click<8.3.0, which drags click back to
|
|
45
45
|
# 8.2.1 - the version with CVE-2026-7246 (command injection in click.edit(), fixed in 8.3.3).
|
|
@@ -49,13 +49,13 @@ dev = [
|
|
|
49
49
|
# Re-enable once codecov-cli relaxes its click<8.3.0 pin.
|
|
50
50
|
# "codecov-cli>=11.2.8",
|
|
51
51
|
# Transitive CVE pins
|
|
52
|
-
"click>=8.
|
|
52
|
+
"click>=8.5.0", # CVE-2026-7246
|
|
53
53
|
"pip-audit>=2.10.1",
|
|
54
54
|
"textual>=8.2.8",
|
|
55
55
|
"import-linter>=2.5.2,<2.6; python_version<'3.10'",
|
|
56
|
-
"import-linter>=2.
|
|
57
|
-
"setuptools>=
|
|
58
|
-
"rtoml>=0.
|
|
56
|
+
"import-linter>=2.15; python_version>='3.10'",
|
|
57
|
+
"setuptools>=84.0.0",
|
|
58
|
+
"rtoml>=0.14.0",
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
[project.scripts]
|
|
@@ -200,3 +200,6 @@ ignore-vulns = ["GHSA-4xh5-x5gv-qwph", "PYSEC-2022-42969", "CVE-2026-25990", "CV
|
|
|
200
200
|
[tool.bashate]
|
|
201
201
|
max-line-length = 120
|
|
202
202
|
ignores = ["E003"]
|
|
203
|
+
|
|
204
|
+
[tool.uv]
|
|
205
|
+
exclude-newer = "1 day"
|
{lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/__init__conf__.py
RENAMED
|
@@ -26,7 +26,7 @@ name = "lib_detect_testenv"
|
|
|
26
26
|
#: Human-readable summary shown in CLI help output.
|
|
27
27
|
title = "Detect test environment - pytest, doctest, unittest, or regular execution"
|
|
28
28
|
#: Current release version pulled from ``pyproject.toml`` by automation.
|
|
29
|
-
version = "3.0.
|
|
29
|
+
version = "3.0.5"
|
|
30
30
|
#: Repository homepage presented to users.
|
|
31
31
|
homepage = "https://github.com/bitranox/lib_detect_testenv"
|
|
32
32
|
#: Author attribution surfaced in CLI output.
|
{lib_detect_testenv-3.0.4 → lib_detect_testenv-3.0.5}/src/lib_detect_testenv/safe_console.py
RENAMED
|
@@ -64,13 +64,19 @@ ASCII_FALLBACKS: Final[dict[str, str]] = {
|
|
|
64
64
|
_UNIVERSAL_ENCODINGS: Final[frozenset[str]] = frozenset({"utf-8", "utf8", "utf-16", "utf16", "utf-32", "utf32"})
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def _stream_encoding(file: IO[Any] | None) -> str | None:
|
|
67
|
+
def _stream_encoding(file: IO[Any] | None, *, err: bool = False) -> str | None:
|
|
68
68
|
"""Return the target stream's encoding, or None when it cannot be determined.
|
|
69
69
|
|
|
70
70
|
An unknown encoding means the caller gets the original text: guessing would
|
|
71
71
|
degrade output that may well have been fine.
|
|
72
|
+
|
|
73
|
+
With no explicit `file` the answer comes from ``sys.stdout``/``sys.stderr``,
|
|
74
|
+
which is what :func:`click.echo` resolves its own default target from. click
|
|
75
|
+
exposes no supported way to ask for that stream: ``get_text_stream`` was
|
|
76
|
+
deprecated in click 8.5.0 and is removed in 9.0, its documented replacement
|
|
77
|
+
being to let ``echo`` resolve the stream itself.
|
|
72
78
|
"""
|
|
73
|
-
stream = file if file is not None else
|
|
79
|
+
stream = file if file is not None else (sys.stderr if err else sys.stdout)
|
|
74
80
|
encoding = getattr(stream, "encoding", None)
|
|
75
81
|
return encoding if isinstance(encoding, str) else None
|
|
76
82
|
|
|
@@ -135,8 +141,7 @@ def echo(message: object = "", *, file: IO[Any] | None = None, err: bool = False
|
|
|
135
141
|
Writes to the given stream.
|
|
136
142
|
"""
|
|
137
143
|
text = message if isinstance(message, str) else str(message)
|
|
138
|
-
|
|
139
|
-
click.echo(encode_safe(text, _stream_encoding(target)), file=target, nl=nl)
|
|
144
|
+
click.echo(encode_safe(text, _stream_encoding(file, err=err)), file=file, err=err, nl=nl)
|
|
140
145
|
|
|
141
146
|
|
|
142
147
|
class _SafeWriter:
|
|
@@ -8,6 +8,7 @@ keeps the next output line someone adds covered by construction.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import io
|
|
11
|
+
import sys
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
|
|
13
14
|
import pytest
|
|
@@ -78,6 +79,30 @@ class TestEchoOnAUtf8Console:
|
|
|
78
79
|
assert "\u2713" in buffer.getvalue().decode("utf-8")
|
|
79
80
|
|
|
80
81
|
|
|
82
|
+
class TestTheDefaultTargetFollowsTheStreamEchoWritesTo:
|
|
83
|
+
"""With no `file`, the encoding must come from the stream `echo` lands on.
|
|
84
|
+
|
|
85
|
+
click resolves that target itself from ``sys.stdout``/``sys.stderr``. Judge
|
|
86
|
+
the wrong stream and a legacy-codepage console gets exactly the crash this
|
|
87
|
+
module exists to prevent, while every test passing an explicit `file` stays
|
|
88
|
+
green and says nothing about it.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def test_a_cp1252_stdout_degrades_the_glyph(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
92
|
+
stream = _cp1252_stream()
|
|
93
|
+
monkeypatch.setattr(sys, "stdout", stream)
|
|
94
|
+
safe_console.echo("✓ deployed")
|
|
95
|
+
assert "[OK]" in _read_back(stream)
|
|
96
|
+
|
|
97
|
+
def test_err_is_judged_against_stderr_not_stdout(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
98
|
+
"""A stdout that could take the glyph must not excuse a cp1252 stderr."""
|
|
99
|
+
stream = _cp1252_stream()
|
|
100
|
+
monkeypatch.setattr(sys, "stdout", io.TextIOWrapper(io.BytesIO(), encoding="utf-8", newline=""))
|
|
101
|
+
monkeypatch.setattr(sys, "stderr", stream)
|
|
102
|
+
safe_console.echo("✓ deployed", err=True)
|
|
103
|
+
assert "[OK]" in _read_back(stream)
|
|
104
|
+
|
|
105
|
+
|
|
81
106
|
class TestSafeStreamProtectsRich:
|
|
82
107
|
"""Rich raises on a legacy codepage too; it renders through its own writer."""
|
|
83
108
|
|