SimPyTick 0.0.1__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.
- simpytick-0.0.1/.github/CI.md +159 -0
- simpytick-0.0.1/.github/actions/install-dependencies/action.yml +46 -0
- simpytick-0.0.1/.github/actions/python-check/action.yml +49 -0
- simpytick-0.0.1/.github/actions/setup-python/action.yml +29 -0
- simpytick-0.0.1/.github/scripts/install_dependencies.py +31 -0
- simpytick-0.0.1/.github/scripts/report_probe.py +34 -0
- simpytick-0.0.1/.github/scripts/run_checks.py +31 -0
- simpytick-0.0.1/.github/scripts/tag_release.sh +41 -0
- simpytick-0.0.1/.github/workflows/build.yml +64 -0
- simpytick-0.0.1/.github/workflows/pylint.yml +24 -0
- simpytick-0.0.1/.github/workflows/pypi_publish.yml +106 -0
- simpytick-0.0.1/.github/workflows/test.yml +47 -0
- simpytick-0.0.1/.github/workflows/verify-workflows.yml +41 -0
- simpytick-0.0.1/.github/workflows/weekly-latest-python.yml +52 -0
- simpytick-0.0.1/.pylintrc +106 -0
- simpytick-0.0.1/LICENSE +28 -0
- simpytick-0.0.1/PKG-INFO +274 -0
- simpytick-0.0.1/README.md +260 -0
- simpytick-0.0.1/pyproject.toml +44 -0
- simpytick-0.0.1/requirements-dev.txt +3 -0
- simpytick-0.0.1/requirements.txt +2 -0
- simpytick-0.0.1/simpytick/__init__.py +11 -0
- simpytick-0.0.1/simpytick/_version.py +3 -0
- simpytick-0.0.1/simpytick/_worker.py +13 -0
- simpytick-0.0.1/simpytick/logger.py +183 -0
- simpytick-0.0.1/simpytick/sleep.py +38 -0
- simpytick-0.0.1/simpytick/sleep_2.py +76 -0
- simpytick-0.0.1/simpytick/timer.py +317 -0
- simpytick-0.0.1/tests/__init__.py +0 -0
- simpytick-0.0.1/tests/test_logger.py +228 -0
- simpytick-0.0.1/tests/test_requirements.py +18 -0
- simpytick-0.0.1/tests/test_sleep.py +61 -0
- simpytick-0.0.1/tests/test_sleep_2.py +77 -0
- simpytick-0.0.1/tests/test_timer.py +80 -0
- simpytick-0.0.1/tests/test_timer_lifecycle.py +174 -0
- simpytick-0.0.1/tests/test_timer_process.py +42 -0
- simpytick-0.0.1/tests/test_timer_worker.py +158 -0
- simpytick-0.0.1/tests/test_version.py +14 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# CI Support and Reference Alignment
|
|
2
|
+
|
|
3
|
+
These workflows assume this package is its own repository root. GitHub does not
|
|
4
|
+
discover them while nested in the parent workspace. Reference workflow/action
|
|
5
|
+
files in `reference-pookiepy`, not its potentially stale README, define the
|
|
6
|
+
comparison. This note records the original differences, implemented changes
|
|
7
|
+
and intentional deviations.
|
|
8
|
+
|
|
9
|
+
## Original Differences
|
|
10
|
+
|
|
11
|
+
- Local `test.yml` covered Python 3.10-3.14 on three desktop runners, with raw
|
|
12
|
+
unittest discovery, no coverage report, ARM jobs or floating-Python probes.
|
|
13
|
+
Reference `ci.yml` also has examples, integration and latest-Python jobs.
|
|
14
|
+
- Local `pylint.yml` ran Python 3.14 Pylint directly, without a shared action or
|
|
15
|
+
local rcfile. Development requirements only listed `pylint>=4.0,<5`, omitting
|
|
16
|
+
runtime requirements and coverage; reference mandatory lint pins Pylint 3.2.6.
|
|
17
|
+
- Local jobs repeated setup/install commands and disabled uv caching; reference
|
|
18
|
+
shares setup, dependency probing and check dispatch with CI caches enabled.
|
|
19
|
+
- Weekly latest-Python checks and actionlint/zizmor audits were absent. Reference
|
|
20
|
+
additionally has weekly gRPC compatibility and installed CLI skeleton smoke.
|
|
21
|
+
- Local `build.yml` already ran tests, built and Twine-checked wheel/sdist,
|
|
22
|
+
smoke-tested both installations and uploaded artifacts. Reference has no
|
|
23
|
+
equivalent build workflow, but does have its framework CLI smoke workflow.
|
|
24
|
+
- Local publishing used GitHub Release publication, two jobs and OIDC with
|
|
25
|
+
attestations, but lacked skip-existing/hash controls and post-publish tagging.
|
|
26
|
+
Reference `pypi_publish.yml` uses master pushes, those controls and tags, in a
|
|
27
|
+
single privileged job. The local publish environment already had its PyPI URL.
|
|
28
|
+
- Checkout, setup-python and setup-uv SHA pins already matched the reference.
|
|
29
|
+
Standalone artifact upload/download actions were already SHA-pinned; the
|
|
30
|
+
reference has no artifact handoff. PyPI already used exact release `v1.14.2`.
|
|
31
|
+
- Local publication used fixed Python 3.14; reference publishing uses `3.x`.
|
|
32
|
+
Release build and tag setup now use `3.x`, without optional-probe failure policy.
|
|
33
|
+
- Dynamic, file-based PDM version metadata already matched the reference model;
|
|
34
|
+
the standalone package has no framework protobuf build hook to preserve.
|
|
35
|
+
|
|
36
|
+
## Implemented Configuration and CI Contract
|
|
37
|
+
|
|
38
|
+
- `test.yml`: push/PR mandatory Python 3.10, 3.11, 3.12, 3.13 and 3.14 on
|
|
39
|
+
`ubuntu-latest`, `windows-latest`, `macos-latest`, `ubuntu-22.04-arm` and
|
|
40
|
+
`ubuntu-24.04-arm`. This core OS/Python matrix exactly matches the reference.
|
|
41
|
+
Nonblocking `3.x` probes use the same five runners. Dependency-resolution
|
|
42
|
+
failures warn and skip probe checks; actual check failures are reported.
|
|
43
|
+
- Core checks use `coverage run -m unittest discover -s tests -p "test_*.py"`
|
|
44
|
+
followed by `coverage report -m`, exactly the reference gate, with no minimum.
|
|
45
|
+
`coverage>=7.10,<8` is development tooling, not runtime metadata. Timer unit
|
|
46
|
+
tests exercise actual spawned worker processes, not fabricated integration jobs.
|
|
47
|
+
- `pylint.yml`: Ubuntu / Python 3.14 runs `pylint --jobs=1 simpytick tests`, using
|
|
48
|
+
`pylint==4.0.10` from `requirements-dev.txt`, which includes runtime requirements.
|
|
49
|
+
No diagnostics are disabled; default fail-under is 10, with no custom threshold.
|
|
50
|
+
- The compact `.pylintrc` preserves every effective reference setting, without
|
|
51
|
+
comments. Only domain-specific entries are removed: the custom integration
|
|
52
|
+
path in `init-hook`, generated protobuf ignore patterns/modules, grpc ignored
|
|
53
|
+
classes, and `grpc`/`google` third-party classifications. The root import hook,
|
|
54
|
+
`source-roots`, `ignore=third_party,interface`, optparse/thread ignored classes,
|
|
55
|
+
empty settings and all other reference values remain. Configured `jobs=4`
|
|
56
|
+
remains; the CI command overrides it to 1, just like the reference.
|
|
57
|
+
- `weekly-latest-python.yml`: Monday 12:00 UTC (`0 12 * * 1`) and manual runs,
|
|
58
|
+
nonblocking `3.x` unit jobs on the same five runners, plus Ubuntu lint with
|
|
59
|
+
latest Pylint, not the mandatory development pin, matching reference weekly.
|
|
60
|
+
- `verify-workflows.yml`: reference syntax/expression checks with actionlint and
|
|
61
|
+
security audit with zizmor, on workflow/action/script changes and manual runs.
|
|
62
|
+
- Shared `setup-python`, `install-dependencies` and `python-check` actions use
|
|
63
|
+
adapted `install_dependencies.py`, `run_checks.py` and `report_probe.py` helpers.
|
|
64
|
+
Callers own matrices/policy; helpers omit grpc/proto/examples handling. The
|
|
65
|
+
selected interpreter is passed through `UV_PYTHON`; the local project
|
|
66
|
+
is installed for distribution-metadata tests. CI uv caches are enabled and
|
|
67
|
+
keyed by suite, Python, OS and architecture; release caches stay disabled.
|
|
68
|
+
- Action pins follow the reference. PyPI publishing intentionally uses exact
|
|
69
|
+
`pypa/gh-action-pypi-publish@v1.14.2`, with the same narrow zizmor unpinned-use
|
|
70
|
+
suppression because its published container is tag-based. No API token is stored.
|
|
71
|
+
|
|
72
|
+
## Publication and Justified Deviations
|
|
73
|
+
|
|
74
|
+
`pypi_publish.yml` matches the configured PyPI trusted publisher identity. Pushes
|
|
75
|
+
to `master`, not GitHub Releases, start a read-only build job: preflight unit
|
|
76
|
+
tests, wheel/sdist build, Twine validation, isolated installed-artifact smoke
|
|
77
|
+
checks and validated artifact upload. Only its dependent publish job receives
|
|
78
|
+
OIDC and tag-write permissions, under environment `pypi-release` with URL
|
|
79
|
+
`https://pypi.org/project/SimPyTick/`. It downloads the validated artifacts and uses
|
|
80
|
+
`verify-metadata: true`, `skip-existing: true`, `print-hash: false` and
|
|
81
|
+
`attestations: true`. Publication success precedes an annotated `v<version>` tag;
|
|
82
|
+
same-commit tags are no-ops, different-commit tags cause failure and are never moved.
|
|
83
|
+
|
|
84
|
+
- Two publication jobs deliberately retain least privilege and validated
|
|
85
|
+
artifacts, instead of copying the reference's single privileged job.
|
|
86
|
+
- Pylint is pinned to 4.0.10, not reference 3.2.6, because Python 3.14 support
|
|
87
|
+
was added in Pylint 4.0. See the [upstream release notes](https://pylint.readthedocs.io/en/latest/whatsnew/4/4.0/index.html).
|
|
88
|
+
Applicable reference configuration and failure policy remain unchanged;
|
|
89
|
+
diagnostic results can differ between Pylint major versions.
|
|
90
|
+
- `build.yml` remains an additional Ubuntu / Python 3.14 gate. Reference CLI
|
|
91
|
+
skeleton smoke covers 3.10/3.14/3.x on three desktop OSes, but tests installed
|
|
92
|
+
pookiepy skeleton generation, which this package does not provide. Package
|
|
93
|
+
wheel/sdist smoke checks are retained, not claimed as literal CLI matrix parity.
|
|
94
|
+
- Weekly gRPC, protobuf generation, examples, framework integration and CLI
|
|
95
|
+
suites/dependencies are excluded because they are absent here. No fake suites
|
|
96
|
+
or dependencies are added. Actual timer worker-process coverage stays in units.
|
|
97
|
+
- Existing `test.yml`, `pylint.yml` and `build.yml` identities remain
|
|
98
|
+
instead of merging into reference `ci.yml`, reducing branch-protection
|
|
99
|
+
migration. Check job display names may change; review required status contexts.
|
|
100
|
+
- Publication has no full-matrix CI `needs` barrier, matching the reference;
|
|
101
|
+
only its own validated build is a dependency. Configure external controls below.
|
|
102
|
+
- `simpytick/_version.py` remains the authoritative manually edited version source.
|
|
103
|
+
PDM dynamic metadata needs no backend change or version bump. Tags are outputs;
|
|
104
|
+
there is no automatic bump or semantic-version inference from commit messages.
|
|
105
|
+
- Source distributions retain their existing CI-source inclusion policy, now
|
|
106
|
+
including all of `.github/` and `.pylintrc`, so workflows do not ship without
|
|
107
|
+
their new helpers. Unlike the reference, CI sources were already included here.
|
|
108
|
+
- Tag extraction uses uv and the selected interpreter, rather than bare Python,
|
|
109
|
+
consistent with the standalone tooling; version parsing and tag policy match.
|
|
110
|
+
|
|
111
|
+
## External Settings and Validation
|
|
112
|
+
|
|
113
|
+
Configure the PyPI publisher with the actual GitHub owner/repository,
|
|
114
|
+
`pypi_publish.yml` and `pypi-release`; verify project availability and use unpublished
|
|
115
|
+
versions. Skipping existing files supports retries, not artifact replacement.
|
|
116
|
+
Protect `master` with mandatory supported-version test, lint and build checks.
|
|
117
|
+
The path-filtered workflow audit should not be universally required unless your
|
|
118
|
+
ruleset handles skipped workflows, or unrelated PRs can wait indefinitely.
|
|
119
|
+
Configure environment reviewers, allowed release branches,
|
|
120
|
+
and tag rules that allow the release bot to create, but not move, release tags.
|
|
121
|
+
Grant repository Actions the tag-write permission required by publication.
|
|
122
|
+
Floating-Python and weekly probes should not be required checks.
|
|
123
|
+
|
|
124
|
+
Some editor/actionlint versions cannot resolve the reference's `$/...` local
|
|
125
|
+
action syntax, especially in this nested workspace. Paths are relative to each
|
|
126
|
+
standalone repository root. The audit workflow retains the reference's narrow
|
|
127
|
+
actionlint compatibility filter; no security findings are filtered out.
|
|
128
|
+
|
|
129
|
+
No tests or linters were run for these configuration/documentation changes.
|
|
130
|
+
Reference-setting comparison is not evidence that package code passes Pylint.
|
|
131
|
+
Local verification parsed all nine workflow/action YAML files, Python helpers,
|
|
132
|
+
embedded shell commands, TOML and INI configuration. Reference matrices, release
|
|
133
|
+
options and permissions were compared structurally. `uv build` successfully
|
|
134
|
+
built the sdist and a wheel from it; the sdist includes CI helpers and `.pylintrc`.
|
|
135
|
+
Actual runner behavior, installed-artifact smoke checks, actionlint, zizmor and
|
|
136
|
+
PyPI/OIDC publication still require GitHub Actions validation.
|
|
137
|
+
|
|
138
|
+
## Patch Summary
|
|
139
|
+
|
|
140
|
+
| File | Change |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| `.github/workflows/test.yml` | Add ARM, coverage dispatch and nonblocking latest-Python probes. |
|
|
143
|
+
| `.github/workflows/pylint.yml` | Shared pinned-Python lint action and development requirements. |
|
|
144
|
+
| `.github/workflows/build.yml` | Shared setup; preserve preflight, build, metadata and artifact smoke gates. |
|
|
145
|
+
| `.github/workflows/pypi_publish.yml` | Master pushes, latest stable Python, reference publish options, tag after publish. |
|
|
146
|
+
| `.github/workflows/weekly-latest-python.yml` | Add reference weekly/manual latest-Python unit and lint probes. |
|
|
147
|
+
| `.github/workflows/verify-workflows.yml` | Add reference syntax and security audits. |
|
|
148
|
+
| `.github/actions/setup-python/action.yml` | Shared pinned tooling, interpreter output and cache policy. |
|
|
149
|
+
| `.github/actions/install-dependencies/action.yml` | Shared installation and nonblocking dependency probe. |
|
|
150
|
+
| `.github/actions/python-check/action.yml` | Shared setup, local package installation and check dispatch. |
|
|
151
|
+
| `.github/scripts/install_dependencies.py` | One dependency command for installation and dry-run probes. |
|
|
152
|
+
| `.github/scripts/run_checks.py` | Coverage/unit and serial Pylint execution. |
|
|
153
|
+
| `.github/scripts/report_probe.py` | Warnings and job summaries for skipped/failed probes. |
|
|
154
|
+
| `.github/scripts/tag_release.sh` | Annotated version tags; same-commit retry; refuse tag movement. |
|
|
155
|
+
| `.github/CI.md` | Differences, deviations, validation, external setup and this inventory. |
|
|
156
|
+
| `.pylintrc` | Applicable reference lint configuration without framework exclusions. |
|
|
157
|
+
| `requirements-dev.txt` | Add runtime requirements, coverage and a supported fixed Pylint pin. |
|
|
158
|
+
| `pyproject.toml` | Include complete CI sources and lint configuration in sdists. |
|
|
159
|
+
| `README.md` | Correct release lifecycle, quality checks and version-management instructions. |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Install CI dependencies
|
|
2
|
+
description: Probe optional latest-Python jobs before installing dependencies.
|
|
3
|
+
inputs:
|
|
4
|
+
python-path:
|
|
5
|
+
description: Interpreter selected by setup-python.
|
|
6
|
+
required: true
|
|
7
|
+
requirements:
|
|
8
|
+
description: Requirement files, one per line.
|
|
9
|
+
default: requirements.txt
|
|
10
|
+
packages:
|
|
11
|
+
description: Additional packages or local project paths, one per line.
|
|
12
|
+
default: ''
|
|
13
|
+
probe:
|
|
14
|
+
description: Skip checks when latest-Python dependencies cannot resolve.
|
|
15
|
+
default: 'false'
|
|
16
|
+
outputs:
|
|
17
|
+
ready:
|
|
18
|
+
description: String true when checks can run; false when the probe was skipped.
|
|
19
|
+
value: ${{ inputs.probe != 'true' || steps.wheel-check.outcome == 'success' }}
|
|
20
|
+
runs:
|
|
21
|
+
using: composite
|
|
22
|
+
steps:
|
|
23
|
+
- name: Check latest Python dependencies
|
|
24
|
+
id: wheel-check
|
|
25
|
+
if: inputs.probe == 'true'
|
|
26
|
+
continue-on-error: true
|
|
27
|
+
shell: bash
|
|
28
|
+
env:
|
|
29
|
+
UV_PYTHON: ${{ inputs.python-path }}
|
|
30
|
+
CI_REQUIREMENTS: ${{ inputs.requirements }}
|
|
31
|
+
CI_PACKAGES: ${{ inputs.packages }}
|
|
32
|
+
run: uv run --no-project python .github/scripts/install_dependencies.py --dry-run
|
|
33
|
+
- name: Explain skipped latest Python probe
|
|
34
|
+
if: inputs.probe == 'true' && steps.wheel-check.outcome != 'success'
|
|
35
|
+
shell: bash
|
|
36
|
+
env:
|
|
37
|
+
UV_PYTHON: ${{ inputs.python-path }}
|
|
38
|
+
run: uv run --no-project python .github/scripts/report_probe.py skipped
|
|
39
|
+
- name: Install dependencies
|
|
40
|
+
if: inputs.probe != 'true' || steps.wheel-check.outcome == 'success'
|
|
41
|
+
shell: bash
|
|
42
|
+
env:
|
|
43
|
+
UV_PYTHON: ${{ inputs.python-path }}
|
|
44
|
+
CI_REQUIREMENTS: ${{ inputs.requirements }}
|
|
45
|
+
CI_PACKAGES: ${{ inputs.packages }}
|
|
46
|
+
run: uv run --no-project python .github/scripts/install_dependencies.py
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Python check
|
|
2
|
+
description: Set up Python, install dependencies, and run one suite.
|
|
3
|
+
inputs:
|
|
4
|
+
python-version:
|
|
5
|
+
description: Python version; floating 3.x enables the optional dependency probe.
|
|
6
|
+
required: true
|
|
7
|
+
suite:
|
|
8
|
+
description: Check suite (unit or lint).
|
|
9
|
+
required: true
|
|
10
|
+
requirements:
|
|
11
|
+
description: Core requirement file.
|
|
12
|
+
default: requirements.txt
|
|
13
|
+
extra-requirements:
|
|
14
|
+
description: Optional additional requirement file.
|
|
15
|
+
default: ''
|
|
16
|
+
runs:
|
|
17
|
+
using: composite
|
|
18
|
+
steps:
|
|
19
|
+
- name: Set up Python and uv
|
|
20
|
+
id: setup
|
|
21
|
+
uses: $/.github/actions/setup-python
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ inputs.python-version }}
|
|
24
|
+
cache-suffix: ${{ inputs.suite }}-py${{ inputs.python-version }}-${{ runner.os }}-${{ runner.arch }}
|
|
25
|
+
- name: Prepare dependencies
|
|
26
|
+
id: dependencies
|
|
27
|
+
uses: $/.github/actions/install-dependencies
|
|
28
|
+
with:
|
|
29
|
+
python-path: ${{ steps.setup.outputs.python-path }}
|
|
30
|
+
probe: ${{ inputs.python-version == '3.x' }}
|
|
31
|
+
requirements: |
|
|
32
|
+
${{ inputs.requirements }}
|
|
33
|
+
${{ inputs.extra-requirements }}
|
|
34
|
+
packages: |
|
|
35
|
+
.
|
|
36
|
+
${{ inputs.suite == 'unit' && 'coverage' || 'pylint' }}
|
|
37
|
+
- name: Run checks
|
|
38
|
+
if: steps.dependencies.outputs.ready == 'true'
|
|
39
|
+
shell: bash
|
|
40
|
+
env:
|
|
41
|
+
UV_PYTHON: ${{ steps.setup.outputs.python-path }}
|
|
42
|
+
CHECK_SUITE: ${{ inputs.suite }}
|
|
43
|
+
run: uv run --no-project python .github/scripts/run_checks.py "$CHECK_SUITE"
|
|
44
|
+
- name: Report latest Python probe failure
|
|
45
|
+
if: failure() && inputs.python-version == '3.x'
|
|
46
|
+
shell: bash
|
|
47
|
+
env:
|
|
48
|
+
UV_PYTHON: ${{ steps.setup.outputs.python-path }}
|
|
49
|
+
run: uv run --no-project python .github/scripts/report_probe.py failed
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Set up Python and uv
|
|
2
|
+
description: Install pinned CI tooling and expose the selected Python interpreter.
|
|
3
|
+
inputs:
|
|
4
|
+
python-version:
|
|
5
|
+
description: Python version requested from actions/setup-python.
|
|
6
|
+
required: true
|
|
7
|
+
cache-suffix:
|
|
8
|
+
description: Cache namespace for this job.
|
|
9
|
+
default: ${{ github.job }}
|
|
10
|
+
enable-cache:
|
|
11
|
+
description: Whether uv should cache dependencies.
|
|
12
|
+
default: 'true'
|
|
13
|
+
outputs:
|
|
14
|
+
python-path:
|
|
15
|
+
description: Absolute path to the selected Python interpreter.
|
|
16
|
+
value: ${{ steps.python.outputs.python-path }}
|
|
17
|
+
runs:
|
|
18
|
+
using: composite
|
|
19
|
+
steps:
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
id: python
|
|
22
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ inputs.python-version }}
|
|
25
|
+
- name: Set up uv
|
|
26
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: ${{ inputs.enable-cache }}
|
|
29
|
+
cache-suffix: ${{ inputs.cache-suffix }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Install CI dependencies, or probe resolution without changing the environment."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> None:
|
|
12
|
+
"""Resolve the same requirements for probes and actual installations."""
|
|
13
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
14
|
+
parser.add_argument("--dry-run", action="store_true")
|
|
15
|
+
args = parser.parse_args()
|
|
16
|
+
command = ["uv", "pip", "install", "--python", sys.executable]
|
|
17
|
+
if args.dry_run:
|
|
18
|
+
command.append("--dry-run")
|
|
19
|
+
for requirement in os.environ.get("CI_REQUIREMENTS", "").splitlines():
|
|
20
|
+
if requirement.strip():
|
|
21
|
+
command.extend(["-r", requirement.strip()])
|
|
22
|
+
command.extend(
|
|
23
|
+
package.strip()
|
|
24
|
+
for package in os.environ.get("CI_PACKAGES", "").splitlines()
|
|
25
|
+
if package.strip()
|
|
26
|
+
)
|
|
27
|
+
subprocess.run(command, check=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Report non-blocking latest-Python probe outcomes consistently on all runners."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
"""Emit a workflow warning and append an actionable job summary."""
|
|
12
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
13
|
+
parser.add_argument("status", choices=("skipped", "failed"))
|
|
14
|
+
args = parser.parse_args()
|
|
15
|
+
title = f"Latest Python probe {args.status}"
|
|
16
|
+
details = (
|
|
17
|
+
"Required dependencies unavailable; checks skipped."
|
|
18
|
+
if args.status == "skipped"
|
|
19
|
+
else "Check job logs for Python compatibility issues or missing dependencies."
|
|
20
|
+
)
|
|
21
|
+
message = (
|
|
22
|
+
f"{os.environ.get('GITHUB_JOB', 'probe')} on "
|
|
23
|
+
f"{os.environ.get('RUNNER_OS', 'local')}: {details} "
|
|
24
|
+
"This probe is non-blocking; pinned Python checks remain mandatory."
|
|
25
|
+
)
|
|
26
|
+
print(f"::warning title={title}::{message}")
|
|
27
|
+
summary = os.environ.get("GITHUB_STEP_SUMMARY")
|
|
28
|
+
if summary:
|
|
29
|
+
with Path(summary).open("a", encoding="utf-8") as output:
|
|
30
|
+
output.write(f"### {title}\n{message}\n")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Run one CI check suite using the selected interpreter."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_module(*arguments: str) -> None:
|
|
11
|
+
"""Run a Python module and propagate failures to the CI job."""
|
|
12
|
+
subprocess.run([sys.executable, "-m", *arguments], check=True)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> None:
|
|
16
|
+
"""Dispatch a named suite without duplicating commands in workflow YAML."""
|
|
17
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
18
|
+
parser.add_argument("suite", choices=("unit", "lint"))
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
if args.suite == "unit":
|
|
21
|
+
run_module(
|
|
22
|
+
"coverage", "run", "-m", "unittest", "discover",
|
|
23
|
+
"-s", "tests", "-p", "test_*.py",
|
|
24
|
+
)
|
|
25
|
+
run_module("coverage", "report", "-m")
|
|
26
|
+
else:
|
|
27
|
+
run_module("pylint", "--jobs=1", "simpytick", "tests")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
PACKAGE_INIT="${SCRIPT_DIR}/../../simpytick/_version.py"
|
|
6
|
+
|
|
7
|
+
VERSION="$(uv run --no-project python - "$PACKAGE_INIT" <<'PY'
|
|
8
|
+
import pathlib
|
|
9
|
+
import re
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
version_file = pathlib.Path(sys.argv[1])
|
|
13
|
+
text = version_file.read_text(encoding='utf-8')
|
|
14
|
+
match = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']\s*$', text, re.MULTILINE)
|
|
15
|
+
if not match:
|
|
16
|
+
raise SystemExit(f'Could not find __version__ in {version_file}')
|
|
17
|
+
print(match.group(1))
|
|
18
|
+
PY
|
|
19
|
+
)"
|
|
20
|
+
|
|
21
|
+
TAG="v${VERSION}"
|
|
22
|
+
|
|
23
|
+
git fetch --tags
|
|
24
|
+
|
|
25
|
+
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
|
26
|
+
TAG_SHA="$(git rev-list -n 1 "refs/tags/${TAG}")"
|
|
27
|
+
HEAD_SHA="$(git rev-parse HEAD)"
|
|
28
|
+
|
|
29
|
+
if [ "$TAG_SHA" = "$HEAD_SHA" ]; then
|
|
30
|
+
echo "Tag ${TAG} already points to this commit; nothing to do."
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "Tag ${TAG} already exists on a different commit (${TAG_SHA}); refusing to move it."
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
git config user.name "github-actions[bot]"
|
|
39
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
40
|
+
git tag -a "$TAG" -m "Release $TAG"
|
|
41
|
+
git push origin "$TAG"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Build
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
defaults:
|
|
12
|
+
run:
|
|
13
|
+
shell: bash
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
20
|
+
with:
|
|
21
|
+
persist-credentials: false
|
|
22
|
+
- id: python
|
|
23
|
+
uses: $/.github/actions/setup-python
|
|
24
|
+
with:
|
|
25
|
+
python-version: '3.14'
|
|
26
|
+
enable-cache: 'false'
|
|
27
|
+
- name: Run release preflight tests
|
|
28
|
+
env:
|
|
29
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
30
|
+
run: >-
|
|
31
|
+
uv run --with-requirements requirements.txt
|
|
32
|
+
python -m unittest discover -s tests -p "test_*.py"
|
|
33
|
+
- name: Build source distribution and wheel
|
|
34
|
+
env:
|
|
35
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
36
|
+
run: uv build
|
|
37
|
+
- name: Validate distribution metadata
|
|
38
|
+
env:
|
|
39
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
40
|
+
run: uvx twine check dist/*
|
|
41
|
+
- name: Install and smoke-test each distribution
|
|
42
|
+
env:
|
|
43
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
44
|
+
run: |
|
|
45
|
+
for artifact in dist/*.whl dist/*.tar.gz; do
|
|
46
|
+
uv run --isolated --no-project --with "$artifact" python -I -c '
|
|
47
|
+
import sys
|
|
48
|
+
import threading
|
|
49
|
+
from importlib.metadata import version
|
|
50
|
+
from simpytick import __version__
|
|
51
|
+
from simpytick.timer import TimedEvent, timer
|
|
52
|
+
assert version("SimPyTick") == __version__
|
|
53
|
+
assert "pookiepy" not in sys.modules
|
|
54
|
+
timer(0, 0.01, threading.Event(), False, None)
|
|
55
|
+
with TimedEvent(s=0.01, n=3, compensation=False, backend="thread") as ticks:
|
|
56
|
+
assert list(ticks) == [0, 1, 2]
|
|
57
|
+
'
|
|
58
|
+
done
|
|
59
|
+
- name: Upload validated distributions
|
|
60
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
61
|
+
with:
|
|
62
|
+
name: python-distributions
|
|
63
|
+
path: dist/*
|
|
64
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Pylint
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
|
|
8
|
+
defaults:
|
|
9
|
+
run:
|
|
10
|
+
shell: bash
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
pylint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
- name: Run Pylint
|
|
20
|
+
uses: $/.github/actions/python-check
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.14"
|
|
23
|
+
suite: lint
|
|
24
|
+
extra-requirements: requirements-dev.txt
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
defaults:
|
|
11
|
+
run:
|
|
12
|
+
shell: bash
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
21
|
+
with:
|
|
22
|
+
persist-credentials: false
|
|
23
|
+
- id: python
|
|
24
|
+
uses: $/.github/actions/setup-python
|
|
25
|
+
with:
|
|
26
|
+
python-version: '3.x'
|
|
27
|
+
enable-cache: 'false'
|
|
28
|
+
- name: Run release preflight tests
|
|
29
|
+
env:
|
|
30
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
31
|
+
run: uv run --with-requirements requirements.txt python -m unittest discover -s tests -p "test_*.py"
|
|
32
|
+
- name: Build source distribution and wheel
|
|
33
|
+
env:
|
|
34
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
35
|
+
run: uv build
|
|
36
|
+
- name: Validate distribution metadata
|
|
37
|
+
env:
|
|
38
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
39
|
+
run: uvx twine check dist/*
|
|
40
|
+
- name: Install and smoke-test each distribution
|
|
41
|
+
shell: bash
|
|
42
|
+
env:
|
|
43
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
44
|
+
run: |
|
|
45
|
+
for artifact in dist/*.whl dist/*.tar.gz; do
|
|
46
|
+
uv run --isolated --no-project --with "$artifact" python -I -c '
|
|
47
|
+
import sys
|
|
48
|
+
import threading
|
|
49
|
+
from importlib.metadata import version
|
|
50
|
+
from simpytick import __version__
|
|
51
|
+
from simpytick.timer import TimedEvent, timer
|
|
52
|
+
assert version("SimPyTick") == __version__
|
|
53
|
+
assert "pookiepy" not in sys.modules
|
|
54
|
+
timer(0, 0.01, threading.Event(), False, None)
|
|
55
|
+
with TimedEvent(s=0.01, n=3, compensation=False, backend="thread") as ticks:
|
|
56
|
+
assert list(ticks) == [0, 1, 2]
|
|
57
|
+
'
|
|
58
|
+
done
|
|
59
|
+
- name: Upload validated distributions
|
|
60
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
61
|
+
with:
|
|
62
|
+
name: python-distributions
|
|
63
|
+
path: dist/*
|
|
64
|
+
if-no-files-found: error
|
|
65
|
+
|
|
66
|
+
publish:
|
|
67
|
+
needs: build
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
environment:
|
|
70
|
+
name: pypi-release
|
|
71
|
+
url: https://pypi.org/project/SimPyTick/
|
|
72
|
+
permissions:
|
|
73
|
+
contents: write
|
|
74
|
+
id-token: write
|
|
75
|
+
steps:
|
|
76
|
+
- name: Checkout repository
|
|
77
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
78
|
+
with:
|
|
79
|
+
persist-credentials: true # zizmor: ignore[artipacked] required for release tag push
|
|
80
|
+
- name: Set up Python and uv
|
|
81
|
+
id: python
|
|
82
|
+
uses: $/.github/actions/setup-python
|
|
83
|
+
with:
|
|
84
|
+
python-version: '3.x'
|
|
85
|
+
enable-cache: 'false'
|
|
86
|
+
- name: Download validated distributions
|
|
87
|
+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
|
88
|
+
with:
|
|
89
|
+
name: python-distributions
|
|
90
|
+
path: dist
|
|
91
|
+
- name: Publish using PyPI Trusted Publishing
|
|
92
|
+
uses: pypa/gh-action-pypi-publish@v1.14.2 # zizmor: ignore[unpinned-uses] exact release tag intentionally used
|
|
93
|
+
with:
|
|
94
|
+
repository-url: https://upload.pypi.org/legacy/
|
|
95
|
+
# repository-url: https://test.pypi.org/legacy/
|
|
96
|
+
packages-dir: dist
|
|
97
|
+
verify-metadata: true
|
|
98
|
+
skip-existing: true
|
|
99
|
+
print-hash: false
|
|
100
|
+
attestations: true
|
|
101
|
+
|
|
102
|
+
- name: Create and push release tag
|
|
103
|
+
if: success() && github.ref_name == 'master'
|
|
104
|
+
env:
|
|
105
|
+
UV_PYTHON: ${{ steps.python.outputs.python-path }}
|
|
106
|
+
run: bash .github/scripts/tag_release.sh
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
|
|
8
|
+
defaults:
|
|
9
|
+
run:
|
|
10
|
+
shell: bash
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: Python ${{ matrix.python-version }} / ${{ matrix.os }}
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
os: [ubuntu-latest, windows-latest, macos-latest, ubuntu-22.04-arm, ubuntu-24.04-arm]
|
|
20
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
- name: Run unit tests
|
|
26
|
+
uses: $/.github/actions/python-check
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
suite: unit
|
|
30
|
+
|
|
31
|
+
latest-python-probe:
|
|
32
|
+
name: Latest Python probe (${{ matrix.os }})
|
|
33
|
+
continue-on-error: true
|
|
34
|
+
runs-on: ${{ matrix.os }}
|
|
35
|
+
strategy:
|
|
36
|
+
fail-fast: false
|
|
37
|
+
matrix:
|
|
38
|
+
os: [ubuntu-latest, windows-latest, macos-latest, ubuntu-22.04-arm, ubuntu-24.04-arm]
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
41
|
+
with:
|
|
42
|
+
persist-credentials: false
|
|
43
|
+
- name: Run latest Python probe
|
|
44
|
+
uses: $/.github/actions/python-check
|
|
45
|
+
with:
|
|
46
|
+
python-version: '3.x'
|
|
47
|
+
suite: unit
|