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.
Files changed (38) hide show
  1. simpytick-0.0.1/.github/CI.md +159 -0
  2. simpytick-0.0.1/.github/actions/install-dependencies/action.yml +46 -0
  3. simpytick-0.0.1/.github/actions/python-check/action.yml +49 -0
  4. simpytick-0.0.1/.github/actions/setup-python/action.yml +29 -0
  5. simpytick-0.0.1/.github/scripts/install_dependencies.py +31 -0
  6. simpytick-0.0.1/.github/scripts/report_probe.py +34 -0
  7. simpytick-0.0.1/.github/scripts/run_checks.py +31 -0
  8. simpytick-0.0.1/.github/scripts/tag_release.sh +41 -0
  9. simpytick-0.0.1/.github/workflows/build.yml +64 -0
  10. simpytick-0.0.1/.github/workflows/pylint.yml +24 -0
  11. simpytick-0.0.1/.github/workflows/pypi_publish.yml +106 -0
  12. simpytick-0.0.1/.github/workflows/test.yml +47 -0
  13. simpytick-0.0.1/.github/workflows/verify-workflows.yml +41 -0
  14. simpytick-0.0.1/.github/workflows/weekly-latest-python.yml +52 -0
  15. simpytick-0.0.1/.pylintrc +106 -0
  16. simpytick-0.0.1/LICENSE +28 -0
  17. simpytick-0.0.1/PKG-INFO +274 -0
  18. simpytick-0.0.1/README.md +260 -0
  19. simpytick-0.0.1/pyproject.toml +44 -0
  20. simpytick-0.0.1/requirements-dev.txt +3 -0
  21. simpytick-0.0.1/requirements.txt +2 -0
  22. simpytick-0.0.1/simpytick/__init__.py +11 -0
  23. simpytick-0.0.1/simpytick/_version.py +3 -0
  24. simpytick-0.0.1/simpytick/_worker.py +13 -0
  25. simpytick-0.0.1/simpytick/logger.py +183 -0
  26. simpytick-0.0.1/simpytick/sleep.py +38 -0
  27. simpytick-0.0.1/simpytick/sleep_2.py +76 -0
  28. simpytick-0.0.1/simpytick/timer.py +317 -0
  29. simpytick-0.0.1/tests/__init__.py +0 -0
  30. simpytick-0.0.1/tests/test_logger.py +228 -0
  31. simpytick-0.0.1/tests/test_requirements.py +18 -0
  32. simpytick-0.0.1/tests/test_sleep.py +61 -0
  33. simpytick-0.0.1/tests/test_sleep_2.py +77 -0
  34. simpytick-0.0.1/tests/test_timer.py +80 -0
  35. simpytick-0.0.1/tests/test_timer_lifecycle.py +174 -0
  36. simpytick-0.0.1/tests/test_timer_process.py +42 -0
  37. simpytick-0.0.1/tests/test_timer_worker.py +158 -0
  38. 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