ftready 1.0.0__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 (39) hide show
  1. ftready-1.0.0/.github/copilot-instructions.md +62 -0
  2. ftready-1.0.0/.github/workflows/ci.yml +68 -0
  3. ftready-1.0.0/.github/workflows/ft_check.yml +91 -0
  4. ftready-1.0.0/.github/workflows/publish.yml +133 -0
  5. ftready-1.0.0/.github/workflows/release.yml +82 -0
  6. ftready-1.0.0/.gitignore +35 -0
  7. ftready-1.0.0/.markdownlint.json +12 -0
  8. ftready-1.0.0/.pre-commit-config.yaml +87 -0
  9. ftready-1.0.0/.pre-commit-hooks.yaml +23 -0
  10. ftready-1.0.0/.python-version +1 -0
  11. ftready-1.0.0/AGENTS.md +140 -0
  12. ftready-1.0.0/CHANGELOG.md +6 -0
  13. ftready-1.0.0/LICENSE +21 -0
  14. ftready-1.0.0/PKG-INFO +290 -0
  15. ftready-1.0.0/README.md +265 -0
  16. ftready-1.0.0/_typos.toml +4 -0
  17. ftready-1.0.0/pyproject.toml +182 -0
  18. ftready-1.0.0/src/ftready/__init__.py +27 -0
  19. ftready-1.0.0/src/ftready/__main__.py +7 -0
  20. ftready-1.0.0/src/ftready/checker.py +108 -0
  21. ftready-1.0.0/src/ftready/cli.py +280 -0
  22. ftready-1.0.0/src/ftready/constants.py +43 -0
  23. ftready-1.0.0/src/ftready/diff.py +248 -0
  24. ftready-1.0.0/src/ftready/models.py +74 -0
  25. ftready-1.0.0/src/ftready/parser.py +148 -0
  26. ftready-1.0.0/src/ftready/py.typed +1 -0
  27. ftready-1.0.0/src/ftready/report.py +220 -0
  28. ftready-1.0.0/src/ftready/scraper.py +311 -0
  29. ftready-1.0.0/tests/__init__.py +0 -0
  30. ftready-1.0.0/tests/conftest.py +102 -0
  31. ftready-1.0.0/tests/test_checker.py +144 -0
  32. ftready-1.0.0/tests/test_cli.py +177 -0
  33. ftready-1.0.0/tests/test_diff.py +269 -0
  34. ftready-1.0.0/tests/test_models.py +35 -0
  35. ftready-1.0.0/tests/test_parser.py +251 -0
  36. ftready-1.0.0/tests/test_report.py +196 -0
  37. ftready-1.0.0/tests/test_scraper.py +275 -0
  38. ftready-1.0.0/tests/test_smoke.py +36 -0
  39. ftready-1.0.0/uv.lock +1016 -0
@@ -0,0 +1,62 @@
1
+ # Copilot Instructions
2
+
3
+ These instructions guide GitHub Copilot in understanding code style, conventions, and best practices for this repository. For executable commands, build/test workflows, and agent-specific boundaries, see the [AGENTS.md](../AGENTS.md) file in the repository root.
4
+
5
+ ---
6
+
7
+ ## Code Standards
8
+
9
+ ### Documentation Requirements
10
+
11
+ * All documentation and code must be written in **English**.
12
+ * Use **Sphinx-style reStructuredText (RST)** docstrings for all public classes and functions:
13
+
14
+ ```python
15
+ """Check project dependencies for free-threaded Python compatibility.
16
+
17
+ :param pyproject_path: Path to the pyproject.toml file
18
+ :param include_dev: When True, also include dev dependencies
19
+ :return: List of compatibility results
20
+ """
21
+ ```
22
+
23
+ * Exclude `:type`, `:raises`, and `:rtype` fields — these are inferred from type annotations.
24
+ * For small, self-explanatory functions, use one-line docstrings.
25
+
26
+ ### Python Code Style
27
+
28
+ * Follow **Ruff** linting rules as configured in `pyproject.toml`.
29
+ * `rich-click` is the sole required runtime dependency (provides Click CLI + Rich output).
30
+ * Apply core design principles: **SOC**, **DRY**, **KISS**, **YAGNI**.
31
+ * Optimize for **clarity and maintainability** over cleverness or micro-optimization.
32
+
33
+ ---
34
+
35
+ ## Import Management
36
+
37
+ * Use `__init__.py` to define the public API of `ftready`.
38
+ * Use explicit imports — no wildcard imports (`from .module import *`).
39
+ * Group imports: standard library → third-party → local application.
40
+
41
+ ---
42
+
43
+ ## Testing Best Practices
44
+
45
+ ### Mock Guidelines
46
+
47
+ * Mock where the object is **used**, not where it's defined.
48
+ * Use `autospec=True` to prevent invalid mock usage.
49
+ * Add call assertions to verify mock interactions.
50
+ * Use **pytest-mock's `mocker` fixture** (not `@patch` decorators).
51
+
52
+ ```python
53
+ def test_example(mocker):
54
+ mock_func = mocker.patch("ftready.scraper._http_get", autospec=True)
55
+ mock_func.assert_called_once_with(expected_url)
56
+ ```
57
+
58
+ ### Network Isolation
59
+
60
+ * All tests must run **without network access** by default.
61
+ * Mock HTTP calls in unit tests — never hit ft-checker.com or PyPI in CI.
62
+ * Tests that require network access must be marked with `@pytest.mark.network`.
@@ -0,0 +1,68 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ lint:
16
+ name: Lint & type check
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - name: Checkout repository
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v6
25
+ with:
26
+ enable-cache: true
27
+
28
+ - name: Set up Python
29
+ run: uv python install
30
+
31
+ - name: Install dependencies
32
+ run: uv sync --all-extras
33
+
34
+ - name: Lint
35
+ run: uv run ruff check .
36
+
37
+ - name: Type check
38
+ run: uv run pyright
39
+
40
+ test:
41
+ name: Test (Python ${{ matrix.python-version }})
42
+ runs-on: ubuntu-latest
43
+ strategy:
44
+ fail-fast: false
45
+ matrix:
46
+ python-version: ["3.11", "3.12", "3.13"]
47
+
48
+ concurrency:
49
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.python-version }}
50
+ cancel-in-progress: true
51
+
52
+ steps:
53
+ - name: Checkout repository
54
+ uses: actions/checkout@v4
55
+
56
+ - name: Install uv
57
+ uses: astral-sh/setup-uv@v6
58
+ with:
59
+ enable-cache: true
60
+
61
+ - name: Set up Python ${{ matrix.python-version }}
62
+ run: uv python install ${{ matrix.python-version }}
63
+
64
+ - name: Install dependencies
65
+ run: uv sync --all-extras --python ${{ matrix.python-version }}
66
+
67
+ - name: Run tests with coverage
68
+ run: uv run pytest --cov-branch --cov=ftready
@@ -0,0 +1,91 @@
1
+ name: Free-threaded Compatibility Check
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 6 * * 1" # Monday 06:00 UTC
6
+ workflow_dispatch:
7
+ inputs:
8
+ include_dev:
9
+ description: Include dev-only dependencies
10
+ type: boolean
11
+ default: false
12
+ no_cache:
13
+ description: Force a fresh scrape (ignore cache)
14
+ type: boolean
15
+ default: false
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ ft-check:
22
+ name: Check free-threaded Python compatibility
23
+ runs-on: ubuntu-latest
24
+
25
+ concurrency:
26
+ group: ${{ github.workflow }}-${{ github.ref }}
27
+ cancel-in-progress: true
28
+
29
+ steps:
30
+ - name: Checkout repository
31
+ uses: actions/checkout@v4
32
+
33
+ - name: Get current date (cache key helper)
34
+ id: date
35
+ run: echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
36
+
37
+ - name: Restore ft-checker cache
38
+ id: ft-cache
39
+ if: ${{ !inputs.no_cache }}
40
+ uses: actions/cache@v4
41
+ with:
42
+ path: .ft_cache.json
43
+ key: ft-checker-${{ runner.os }}-${{ hashFiles('pyproject.toml') }}-${{ steps.date.outputs.date }}
44
+ restore-keys: |
45
+ ft-checker-${{ runner.os }}-${{ hashFiles('pyproject.toml') }}-
46
+
47
+ - name: Install uv
48
+ uses: astral-sh/setup-uv@v6
49
+ with:
50
+ enable-cache: true
51
+
52
+ - name: Set up Python
53
+ run: uv python install 3.12
54
+
55
+ - name: Install ftready
56
+ run: uv sync --all-extras
57
+
58
+ - name: Run ftready
59
+ id: run-check
60
+ run: |
61
+ ARGS="--output ft_report.txt --verbose --plain"
62
+ if [ "${{ inputs.include_dev }}" = "true" ]; then ARGS="$ARGS --include-dev"; fi
63
+ if [ "${{ inputs.no_cache }}" = "true" ]; then ARGS="$ARGS --no-cache"; fi
64
+ uv run ftready $ARGS
65
+ echo "exit_code=$?" >> "$GITHUB_OUTPUT"
66
+ continue-on-error: true
67
+
68
+ - name: Write job summary
69
+ if: always()
70
+ run: |
71
+ echo "## Free-threaded Python Compatibility Report" >> "$GITHUB_STEP_SUMMARY"
72
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
73
+ cat ft_report.txt >> "$GITHUB_STEP_SUMMARY"
74
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
75
+
76
+ - name: Upload report artifact
77
+ if: always()
78
+ uses: actions/upload-artifact@v4
79
+ with:
80
+ name: ft-check-report-${{ github.run_id }}
81
+ path: ft_report.txt
82
+ retention-days: 30
83
+
84
+ - name: Evaluate result
85
+ if: steps.run-check.outputs.exit_code != '0'
86
+ run: |
87
+ echo "::warning::One or more dependencies are marked as FAILED for free-threaded Python."
88
+ echo "See the uploaded artifact ft_report.txt for details."
89
+ exit 1
90
+ echo "See the uploaded artifact ft_report.txt for details."
91
+ exit 1
@@ -0,0 +1,133 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ # Least-privilege defaults; jobs escalate individually
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ name: Build distributions
15
+ runs-on: ubuntu-latest
16
+ outputs:
17
+ hashes: ${{ steps.hash.outputs.hashes }}
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@v4
21
+ with:
22
+ persist-credentials: false
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v6
26
+ with:
27
+ enable-cache: true
28
+
29
+ - name: Set up Python
30
+ run: uv python install
31
+
32
+ - name: Build package
33
+ run: uv build --no-sources
34
+
35
+ - name: Generate subject hashes for SLSA provenance
36
+ id: hash
37
+ run: |
38
+ cd dist
39
+ echo "hashes=$(sha256sum * | base64 -w0)" >> "$GITHUB_OUTPUT"
40
+
41
+ - name: Upload distributions
42
+ uses: actions/upload-artifact@v4
43
+ with:
44
+ name: dist
45
+ path: dist/
46
+
47
+ test-install:
48
+ name: Verify installation
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ steps:
52
+ - name: Install uv
53
+ uses: astral-sh/setup-uv@v6
54
+
55
+ - name: Download distributions
56
+ uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+
61
+ - name: Test install from wheel
62
+ run: |
63
+ uv venv --python 3.12
64
+ uv pip install dist/*.whl
65
+ uv run python -c "import ftready; print('ftready imported successfully')"
66
+
67
+ # --------------------------------------------------------------- #
68
+ # SLSA Build Level 3 provenance via slsa-framework #
69
+ # --------------------------------------------------------------- #
70
+ provenance:
71
+ name: Generate SLSA provenance
72
+ needs: build
73
+ permissions:
74
+ actions: read
75
+ contents: write
76
+ id-token: write # OIDC for SLSA
77
+ uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
78
+ with:
79
+ base64-subjects: ${{ needs.build.outputs.hashes }}
80
+ upload-assets: true
81
+ compile-generator: true
82
+
83
+ publish-testpypi:
84
+ name: Publish to TestPyPI
85
+ needs: [build, test-install, provenance]
86
+ runs-on: ubuntu-latest
87
+ environment: test-pypi
88
+ permissions:
89
+ id-token: write
90
+ attestations: write # Sigstore attestations
91
+ steps:
92
+ - name: Download distributions
93
+ uses: actions/download-artifact@v4
94
+ with:
95
+ name: dist
96
+ path: dist/
97
+
98
+ - name: Generate Sigstore attestations
99
+ uses: actions/attest-build-provenance@v2
100
+ with:
101
+ subject-path: dist/*
102
+
103
+ - name: Publish to TestPyPI
104
+ uses: pypa/gh-action-pypi-publish@release/v1
105
+ with:
106
+ repository-url: https://test.pypi.org/legacy/
107
+ skip-existing: true
108
+ attestations: true
109
+
110
+ publish-pypi:
111
+ name: Publish to PyPI
112
+ needs: [build, test-install, provenance, publish-testpypi]
113
+ runs-on: ubuntu-latest
114
+ environment: pypi
115
+ permissions:
116
+ id-token: write
117
+ attestations: write # Sigstore attestations
118
+ steps:
119
+ - name: Download distributions
120
+ uses: actions/download-artifact@v4
121
+ with:
122
+ name: dist
123
+ path: dist/
124
+
125
+ - name: Generate Sigstore attestations
126
+ uses: actions/attest-build-provenance@v2
127
+ with:
128
+ subject-path: dist/*
129
+
130
+ - name: Publish to PyPI
131
+ uses: pypa/gh-action-pypi-publish@release/v1
132
+ with:
133
+ attestations: true
@@ -0,0 +1,82 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches:
7
+ - main
8
+ paths-ignore:
9
+ - "CHANGELOG.md"
10
+ - "docs/**"
11
+
12
+ permissions:
13
+ contents: write
14
+ id-token: write
15
+
16
+ jobs:
17
+ release:
18
+ name: Semantic release
19
+ runs-on: ubuntu-latest
20
+ concurrency:
21
+ group: ${{ github.workflow }}-release-${{ github.ref_name }}
22
+ cancel-in-progress: false
23
+
24
+ steps:
25
+ - name: Checkout repository on release branch
26
+ uses: actions/checkout@v4
27
+ with:
28
+ ref: ${{ github.ref_name }}
29
+ fetch-depth: 0
30
+ token: ${{ secrets.GITHUB_TOKEN }}
31
+
32
+ - name: Force release branch to workflow SHA
33
+ run: git reset --hard ${{ github.sha }}
34
+
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v6
37
+ with:
38
+ enable-cache: true
39
+
40
+ - name: Set up Python
41
+ run: uv python install
42
+
43
+ - name: Install dependencies
44
+ run: uv sync --group dev
45
+
46
+ - name: Python Semantic Release
47
+ id: release
48
+ env:
49
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50
+ run: |
51
+ BEFORE_SHA=$(git rev-parse HEAD)
52
+
53
+ uv run semantic-release -v version --no-push --no-vcs-release
54
+
55
+ AFTER_SHA=$(git rev-parse HEAD)
56
+
57
+ if [ "$BEFORE_SHA" = "$AFTER_SHA" ]; then
58
+ echo "released=false" >> "$GITHUB_OUTPUT"
59
+ echo "No release needed."
60
+ else
61
+ git push --follow-tags
62
+ VERSION=$(git tag --points-at HEAD | sed 's/^v//' | head -1)
63
+ TAG=$(git tag --points-at HEAD | head -1)
64
+ echo "released=true" >> "$GITHUB_OUTPUT"
65
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
66
+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
67
+ fi
68
+
69
+ # Create a draft release for review before publishing.
70
+ # Publishing the draft triggers the publish workflow.
71
+ - name: Create draft GitHub Release
72
+ if: steps.release.outputs.released == 'true'
73
+ env:
74
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75
+ run: |
76
+ TAG="${{ steps.release.outputs.tag }}"
77
+ uv run semantic-release changelog --post-to-release-tag "$TAG"
78
+ gh release create "$TAG" \
79
+ --draft \
80
+ --title "$TAG" \
81
+ --notes-file CHANGELOG.md \
82
+ --verify-tag
@@ -0,0 +1,35 @@
1
+ # ftready
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+
10
+ # Cache and reports
11
+ .ft_cache.json
12
+ .ft_cache.tmp
13
+ ft_report.txt
14
+
15
+ # Coverage
16
+ .coverage
17
+ .coverage.*
18
+ coverage.xml
19
+ htmlcov/
20
+ tests/reports/
21
+
22
+ # Virtual environments
23
+ .venv/
24
+ venv/
25
+ env/
26
+
27
+ # IDE
28
+ .idea/
29
+ .vscode/
30
+ *.swp
31
+ *.swo
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
@@ -0,0 +1,12 @@
1
+ {
2
+ "MD013": false,
3
+ "MD033": {
4
+ "allowed_elements": [
5
+ "h1",
6
+ "p",
7
+ "strong",
8
+ "a",
9
+ "img"
10
+ ]
11
+ }
12
+ }
@@ -0,0 +1,87 @@
1
+ default_stages: [pre-commit]
2
+ fail_fast: true
3
+
4
+ default_language_version:
5
+ python: python3
6
+
7
+ repos:
8
+ # Conventional commit validation
9
+ - repo: https://github.com/compilerla/conventional-pre-commit
10
+ rev: v4.4.0
11
+ hooks:
12
+ - id: conventional-pre-commit
13
+ stages: [commit-msg]
14
+ args:
15
+ - feat
16
+ - fix
17
+ - docs
18
+ - style
19
+ - refactor
20
+ - perf
21
+ - test
22
+ - build
23
+ - ci
24
+ - chore
25
+ - revert
26
+
27
+ # Sanity check for pre-commit hooks and excludes
28
+ - repo: meta
29
+ hooks:
30
+ - id: check-hooks-apply
31
+ - id: check-useless-excludes
32
+
33
+ # Update pre-commit hooks
34
+ - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update
35
+ rev: v0.9.0
36
+ hooks:
37
+ - id: pre-commit-update
38
+ args: [--verbose, --warnings]
39
+
40
+ # Find spelling mistakes in source code
41
+ - repo: https://github.com/crate-ci/typos
42
+ rev: v1.45.0
43
+ hooks:
44
+ - id: typos
45
+
46
+ # Linting and Formatting
47
+ - repo: https://github.com/astral-sh/ruff-pre-commit
48
+ rev: v0.15.9
49
+ hooks:
50
+ - id: ruff-check
51
+ args: [--fix, --unsafe-fixes]
52
+ - id: ruff-format
53
+ - id: ruff-check
54
+
55
+ # Type checking
56
+ - repo: local
57
+ hooks:
58
+ - id: pyright
59
+ name: pyright
60
+ description: Static type checker for Python
61
+ entry: uv run pyright
62
+ language: system
63
+ pass_filenames: false
64
+ files: \.py$
65
+
66
+ - repo: https://github.com/pre-commit/mirrors-prettier
67
+ rev: v3.1.0
68
+ hooks:
69
+ - id: prettier
70
+ files: \.(yml|yaml|toml)
71
+ args: ["--print-width=120"]
72
+
73
+ - repo: https://github.com/tox-dev/pyproject-fmt
74
+ rev: v2.21.0
75
+ hooks:
76
+ - id: pyproject-fmt
77
+
78
+ # Miscellaneous
79
+ - repo: https://github.com/pre-commit/pre-commit-hooks
80
+ rev: v6.0.0
81
+ hooks:
82
+ - id: end-of-file-fixer
83
+ - id: trailing-whitespace
84
+ - id: check-toml
85
+ - id: check-yaml
86
+ args: [--unsafe]
87
+ - id: check-case-conflict
@@ -0,0 +1,23 @@
1
+ - id: ftready
2
+ name: Free-threaded Python compatibility check (pyproject.toml)
3
+ description: >
4
+ Check pyproject.toml dependencies for free-threaded Python (3.13t/3.14t)
5
+ compatibility using ft-checker.com data and PyPI wheel tags.
6
+ entry: ftready
7
+ language: python
8
+ types: [toml]
9
+ files: ^pyproject\.toml$
10
+ pass_filenames: false
11
+ args: [--fail-on=failed]
12
+
13
+ - id: ftready-requirements
14
+ name: Free-threaded Python compatibility check (requirements.txt)
15
+ description: >
16
+ Check requirements.txt (or any requirements-style file) for free-threaded
17
+ Python (3.13t/3.14t) compatibility.
18
+ entry: ftready
19
+ language: python
20
+ types_or: [text]
21
+ files: ^requirements.*\.txt$
22
+ pass_filenames: false
23
+ args: [--requirements, requirements.txt, --fail-on=failed]
@@ -0,0 +1 @@
1
+ 3.12