mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-10 00:14:16 +00:00
## Problem
Python 3.14 provides native lazy annotations, but the codebase still
relied on legacy future annotation imports. Those imports also made
type-only import cycles easier to hide instead of fixing ownership
boundaries.
## Changes
| Before | After |
| --- | --- |
| Python files used `from __future__ import annotations`. | Python files
rely on Python 3.14 native lazy annotations. |
| Some runtime modules used `TYPE_CHECKING` or local imports for
required dependencies. | Runtime modules use top-level owner-module
imports with explicit boundaries. |
| Local and GitHub guardrails only rejected type ignore suppressions. |
Local and GitHub guardrails reject type ignore suppressions and legacy
future annotation imports. |
| Agent docs only documented the no-type-ignore rule. | Agent docs
document the Python 3.14 annotation and import-boundary rules. |
<!-- greptile_comment -->
<details open><summary><h3>Greptile Summary</h3></summary>
This PR moves the codebase to Python 3.14 native lazy annotations. The
main changes are:
- Removed legacy `from __future__ import annotations` imports across
Python modules.
- Promoted selected runtime dependencies from `TYPE_CHECKING` or local
imports to explicit owner-module imports.
- Added local, GitHub, and contract-test guardrails to reject legacy
future annotation imports.
- Updated agent docs with the annotation and import-boundary rules.
- Bumped the package patch version for production-file changes.
</details>
<h3>Confidence Score: 5/5</h3>
Safe to merge with low risk.
The changes are mostly mechanical annotation cleanup with matching CI
and contract-test guardrails. Reviewed import-boundary updates did not
show a confirmed runtime cycle or dependency break.
No files require special attention.
<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>
**What T-Rex did**
- Performed an end-to-end validation of the guardrail contract suite: an
environment check confirmed uv availability, a guardrail pytest run used
CPython 3.14.0 with 5 passing contract tests, 3 focused CI-script tests
passed, and the direct CI suppressions guardrail command (including the
legacy future-annotations grep) also passed.
<a
href="https://app.greptile.com/trex/runs/13303335/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>
<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>
<details open><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| api/runtime.py | Moves messaging, CLI manager, session, limiter, and
tree dependencies from local/type-checking imports to explicit top-level
owner-module imports. |
| messaging/platforms/telegram.py | Removes future annotations and
promotes Telegram SDK type imports into the existing availability guard.
|
| messaging/platforms/telegram_inbound.py | Removes future annotations
and imports Telegram SDK types at module scope for inbound
normalization. |
| tests/contracts/test_import_boundaries.py | Adds an AST contract that
rejects legacy future annotation imports across Python files. |
| scripts/ci.sh | Extends the local suppression check to reject legacy
future annotation imports alongside type-ignore suppressions. |
| scripts/ci.ps1 | Mirrors the local PowerShell CI suppression check for
legacy future annotations. |
| .github/workflows/tests.yml | Renames and broadens the GitHub
guardrail job to reject both type suppressions and legacy future
annotations. |
| pyproject.toml | Bumps the patch version for production-file changes.
|
</details>
<details open><summary><h3>Sequence Diagram</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Dev as Developer/CI
participant Guard as Suppression guard
participant AST as Import-boundary contract test
participant Py as Python modules
Dev->>Guard: Run local/GitHub suppression check
Guard->>Py: "Scan *.py for type ignores and future annotations"
Guard-->>Dev: Fail if legacy annotation import remains
Dev->>AST: Run pytest contract tests
AST->>Py: Parse imports with ast
AST-->>Dev: Assert no future annotations/import-boundary violations
Py-->>Dev: Use Python 3.14 native lazy annotations
```
</a>
<a href="#gh-dark-mode-only">
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Dev as Developer/CI
participant Guard as Suppression guard
participant AST as Import-boundary contract test
participant Py as Python modules
Dev->>Guard: Run local/GitHub suppression check
Guard->>Py: "Scan *.py for type ignores and future annotations"
Guard-->>Dev: Fail if legacy annotation import remains
Dev->>AST: Run pytest contract tests
AST->>Py: Parse imports with ast
AST-->>Dev: Assert no future annotations/import-boundary violations
Py-->>Dev: Use Python 3.14 native lazy annotations
```
</a>
</details>
<sub>Reviews (2): Last reviewed commit: ["Remove legacy future
annotations
import"](6e6cda69da)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41875785)</sub>
<!-- /greptile_comment -->
212 lines
4.6 KiB
Bash
Executable file
212 lines
4.6 KiB
Bash
Executable file
#!/bin/sh
|
|
set -eu
|
|
|
|
CHECK_ORDER="suppressions ruff-format ruff-check ty pytest"
|
|
|
|
dry_run=0
|
|
only_checks=""
|
|
skip_checks=""
|
|
|
|
show_usage() {
|
|
cat <<'USAGE'
|
|
Usage: ci.sh [options]
|
|
|
|
Runs the local sequence for the same check IDs enforced by GitHub CI.
|
|
Requires uv on PATH when running ruff, ty, or pytest checks.
|
|
Local ruff checks repair formatting and autofixable lint before later checks.
|
|
|
|
Checks (in order):
|
|
suppressions Ban type ignores and legacy future annotations
|
|
ruff-format uv run ruff format
|
|
ruff-check uv run ruff check --fix
|
|
ty uv run ty check
|
|
pytest uv run pytest -v --tb=short
|
|
|
|
Options:
|
|
--only ID Run only the given check (repeatable)
|
|
--skip ID Skip the given check (repeatable)
|
|
--dry-run Print commands without running them.
|
|
--help Show this help text.
|
|
USAGE
|
|
}
|
|
|
|
fail() {
|
|
printf 'error: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
step() {
|
|
printf '\n==> %s\n' "$1"
|
|
}
|
|
|
|
quote_arg() {
|
|
case "$1" in
|
|
*[!A-Za-z0-9_./:@%+=,-]*|"")
|
|
escaped=$(printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
printf '"%s"' "$escaped"
|
|
;;
|
|
*)
|
|
printf '%s' "$1"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
print_command() {
|
|
printf '+'
|
|
for arg in "$@"; do
|
|
printf ' '
|
|
quote_arg "$arg"
|
|
done
|
|
printf '\n'
|
|
}
|
|
|
|
run() {
|
|
print_command "$@"
|
|
if [ "$dry_run" -eq 0 ]; then
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
valid_check_id() {
|
|
case "$1" in
|
|
suppressions | ruff-format | ruff-check | ty | pytest) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
validate_check_id() {
|
|
if ! valid_check_id "$1"; then
|
|
fail "unknown check id: $1 (expected one of: $CHECK_ORDER)"
|
|
fi
|
|
}
|
|
|
|
contains_check_id() {
|
|
list=$1
|
|
id=$2
|
|
case " $list " in
|
|
*" $id "*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
should_run_check() {
|
|
check_id=$1
|
|
|
|
if [ -n "$only_checks" ] && ! contains_check_id "$only_checks" "$check_id"; then
|
|
return 1
|
|
fi
|
|
|
|
if contains_check_id "$skip_checks" "$check_id"; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
assert_uv_available() {
|
|
if ! command -v uv >/dev/null 2>&1; then
|
|
fail "uv is required but was not found on PATH. Install uv first (see README or scripts/install.sh)."
|
|
fi
|
|
}
|
|
|
|
selected_checks_need_uv() {
|
|
if [ "$dry_run" -ne 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
for check_id in $CHECK_ORDER; do
|
|
if should_run_check "$check_id" && [ "$check_id" != "suppressions" ]; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
run_suppressions() {
|
|
step "Ban suppressions and legacy annotations"
|
|
pattern='# type: ignore|# ty: ignore|from __future__ import annotations'
|
|
print_command grep -rE "$pattern" --include='*.py' . \
|
|
--exclude-dir=.venv --exclude-dir=.git
|
|
if [ "$dry_run" -eq 0 ]; then
|
|
if grep -rE "$pattern" --include='*.py' . \
|
|
--exclude-dir=.venv --exclude-dir=.git; then
|
|
fail "type: ignore / ty: ignore comments and legacy future annotations are not allowed. Fix the underlying type/import issue instead."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
run_ruff_format() {
|
|
step "ruff format"
|
|
run uv run ruff format
|
|
}
|
|
|
|
run_ruff_check() {
|
|
step "ruff check --fix"
|
|
run uv run ruff check --fix
|
|
}
|
|
|
|
run_ty() {
|
|
step "ty check"
|
|
run uv run ty check
|
|
}
|
|
|
|
run_pytest() {
|
|
step "pytest"
|
|
run uv run pytest -v --tb=short
|
|
}
|
|
|
|
run_check() {
|
|
case "$1" in
|
|
suppressions) run_suppressions ;;
|
|
ruff-format) run_ruff_format ;;
|
|
ruff-check) run_ruff_check ;;
|
|
ty) run_ty ;;
|
|
pytest) run_pytest ;;
|
|
*) fail "unknown check id: $1" ;;
|
|
esac
|
|
}
|
|
|
|
parse_args() {
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--only)
|
|
shift
|
|
[ "$#" -gt 0 ] || fail "--only requires a check id."
|
|
validate_check_id "$1"
|
|
only_checks="${only_checks} $1"
|
|
;;
|
|
--skip)
|
|
shift
|
|
[ "$#" -gt 0 ] || fail "--skip requires a check id."
|
|
validate_check_id "$1"
|
|
skip_checks="${skip_checks} $1"
|
|
;;
|
|
--dry-run)
|
|
dry_run=1
|
|
;;
|
|
--help|-h)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
show_usage >&2
|
|
fail "unknown option: $1"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
}
|
|
|
|
parse_args "$@"
|
|
if selected_checks_need_uv; then
|
|
assert_uv_available
|
|
fi
|
|
|
|
for check_id in $CHECK_ORDER; do
|
|
if should_run_check "$check_id"; then
|
|
run_check "$check_id"
|
|
fi
|
|
done
|
|
|
|
printf '\nAll selected CI checks passed.\n'
|