Skip to content

Commit ec090b5

Browse files
Add the ty type checker (#1116)
* Add the `ty` type checker Now used in prek instead of pyright. Pyright is still enabled in CI. * Use consistent return type across all file manager `.read_file` methods * Fix bug introduced when refactoring in function associated with Bitbucket pipeline anchors * Revert change in pydantic model dumping utility and disable false positive ty diagnostic * Remove debug assert statements
1 parent f29f1c1 commit ec090b5

20 files changed

Lines changed: 135 additions & 57 deletions

File tree

.github/workflows/ci.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ jobs:
5151
CI_UV_UPGRADE_ARG: ${{ matrix.dependencies == 'max' && '--upgrade' || '' }}
5252
UV_RESOLUTION: ${{ matrix.dependencies == 'min' && 'lowest-direct' || 'highest' }}
5353

54-
- name: Run prek
55-
if: ${{ matrix.pre_commit == null || matrix.pre_commit == 'true' }}
54+
- name: Run checks
55+
if: ${{ matrix.checks == null || matrix.checks == 'true' }}
5656
run: |
5757
uv run --frozen prek run --all-files
58+
uv run --frozen pyright
5859
5960
- name: Run pytest
6061
uses: pavelzw/pytest-action@510c5e90c360a185039bea56ce8b3e7e51a16507 # v2.2.0
@@ -105,16 +106,16 @@ jobs:
105106
- dependencies: "min"
106107
python_version: "3.10"
107108
uv_version: "0.6.8"
108-
pre_commit: "false"
109+
checks: "false"
109110
- dependencies: "max"
110111
python_version: "3.14"
111-
pre_commit: "false"
112+
checks: "false"
112113
- codecov: "true"
113114
python_version: "3.14"
114115

115-
pre_commit: "false"
116+
checks: "false"
116117
- codspeed: "true"
117118
python_version: "3.13"
118119

119-
pre_commit: "false"
120+
checks: "false"
120121
pytest: "false"

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ repos:
8484
pass_filenames: false
8585
- repo: local
8686
hooks:
87-
- id: pyright
88-
name: pyright
89-
entry: uv run --frozen --offline pyright
87+
- id: ty
88+
name: ty
89+
entry: uv run --frozen --offline ty check
9090
language: system
9191
types_or: [python, pyi]
9292
always_run: true

pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ dev = [
6262
"pyinstrument>=5.1.1",
6363
"pyright[nodejs]>=1.1.399",
6464
"ruff>=0.14.3",
65+
"ty>=0.0.1a25",
6566
]
6667
test = [
6768
"click>=8.1.8",
@@ -179,6 +180,17 @@ exclude_also = [
179180
]
180181
omit = [ "*/pytest-of-*/*", "*/_temp/*" ]
181182

183+
[tool.ty]
184+
environment.python-platform = "all"
185+
rules.type-assertion-failure = "ignore"
186+
187+
[[tool.ty.overrides]]
188+
include = [ "src/usethis/_integrations/file/**" ]
189+
rules.invalid-argument-type = "ignore"
190+
rules.invalid-return-type = "ignore"
191+
rules.invalid-assignment = "ignore"
192+
rules.possibly-missing-attribute = "ignore"
193+
182194
[tool.uv]
183195
required-version = ">=0.6.8" # Sync with README
184196
default-groups = [ "test", "dev", "doc" ]

requirements.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,25 @@ tomlkit==0.13.3 \
970970
--hash=sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1 \
971971
--hash=sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0
972972
# via usethis
973+
ty==0.0.1a25 \
974+
--hash=sha256:0a90d897a7c1a5ae9b41a4c7b0a42262a06361476ad88d783dbedd7913edadbc \
975+
--hash=sha256:168fc8aee396d617451acc44cd28baffa47359777342836060c27aa6f37e2445 \
976+
--hash=sha256:1711dd587eccf04fd50c494dc39babe38f4cb345bc3901bf1d8149cac570e979 \
977+
--hash=sha256:192edac94675a468bac7f6e04687a77a64698e4e1fe01f6a048bf9b6dde5b703 \
978+
--hash=sha256:4a247061bd32bae3865a236d7f8b6c9916c80995db30ae1600999010f90623a9 \
979+
--hash=sha256:5550b24b9dd0e0f8b4b2c1f0fcc608a55d0421dd67b6c364bc7bf25762334511 \
980+
--hash=sha256:5f4c9b0cf7995e2e3de9bab4d066063dea92019f2f62673b7574e3612643dd35 \
981+
--hash=sha256:93c7e7ab2859af0f866d34d27f4ae70dd4fb95b847387f082de1197f9f34e068 \
982+
--hash=sha256:949523621f336e01bc7d687b7bd08fe838edadbdb6563c2c057ed1d264e820cf \
983+
--hash=sha256:94f78f621458c05e59e890061021198197f29a7b51a33eda82bbb036e7ed73d7 \
984+
--hash=sha256:a2fad3d8e92bb4d57a8872a6f56b1aef54539d36f23ebb01abe88ac4338efafb \
985+
--hash=sha256:a9f3bbf523b49935bbd76e230408d858dce0d614f44f5807bbbd0954f64e0f01 \
986+
--hash=sha256:d35b2c1f94a014a22875d2745aa0432761d2a9a8eb7212630d5caf547daeef6d \
987+
--hash=sha256:d9656fca8062a2c6709c30d76d662c96d2e7dbfee8f70e55ec6b6afd67b5d447 \
988+
--hash=sha256:dde2962d448ed87c48736e9a4bb13715a4cced705525e732b1c0dac1d4c66e3d \
989+
--hash=sha256:eab6e33ebe202a71a50c3d5a5580e3bc1a85cda3ffcdc48cec3f1c693b7a873b \
990+
--hash=sha256:f13ea9815f4a54a0a303ca7bf411b0650e3c2a24fc6c7889ffba2c94f5e97a6a \
991+
--hash=sha256:f6b9a31da43424cdab483703a54a561b93aabba84630788505329fc5294a9c62
973992
typer==0.15.2 \
974993
--hash=sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc \
975994
--hash=sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5

src/usethis/_integrations/ci/bitbucket/steps.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def _add_step_in_default_via_doc(
137137
if script_item.name not in defined_script_item_by_name:
138138
script_item_name = script_item.name
139139
try:
140-
script_item = _SCRIPT_ITEM_LOOKUP[script_item.name]
140+
resolved_script_item = _SCRIPT_ITEM_LOOKUP[script_item.name]
141141
except KeyError:
142142
msg = f"Unrecognized script item anchor: '{script_item.name}'."
143143
raise NotImplementedError(msg) from None
@@ -156,14 +156,14 @@ def _add_step_in_default_via_doc(
156156
script_item_name=script_item_name,
157157
doc=doc,
158158
)
159-
existing_script_items.insert(insertion_index, script_item)
159+
existing_script_items.insert(insertion_index, resolved_script_item)
160160
existing_script_items = CommentedSeq(existing_script_items)
161161
else:
162162
# Otherwise, if the anchor is already defined, we need to use the
163163
# reference
164-
script_item = defined_script_item_by_name[script_item.name]
164+
resolved_script_item = defined_script_item_by_name[script_item.name]
165165

166-
step.script.root[idx] = script_item
166+
step.script.root[idx] = resolved_script_item
167167

168168
# If the step is unrecognized, it will go at the end.
169169
prerequisites: set[str] = set()
@@ -435,7 +435,8 @@ def _get_steps_in_pipeline(pipeline: Pipeline) -> list[Step]:
435435

436436

437437
@singledispatch
438-
def get_steps_in_pipeline_item(item) -> list[Step]: ...
438+
def get_steps_in_pipeline_item(item) -> list[Step]:
439+
raise NotImplementedError
439440

440441

441442
@get_steps_in_pipeline_item.register(StepItem)

src/usethis/_integrations/ci/github/tags.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import requests
4+
from requests.exceptions import RequestException
45

56
from usethis._integrations.ci.github.errors import (
67
GitHubTagError,
@@ -29,7 +30,7 @@ def get_github_latest_tag(owner: str, repo: str) -> str:
2930
try:
3031
response = requests.get(api_url, timeout=1)
3132
response.raise_for_status() # Raise an error for HTTP issues
32-
except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as err:
33+
except RequestException as err:
3334
msg = f"Failed to fetch tags from GitHub API:\n{err}"
3435
raise GitHubTagError(msg) from None
3536

src/usethis/_integrations/file/ini/io_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ def __enter__(self) -> Self:
4848
except UnexpectedFileOpenError as err:
4949
raise UnexpectedINIOpenError(err) from None
5050

51-
def read_file(self) -> None:
51+
def read_file(self) -> INIDocument:
5252
try:
53-
super().read_file()
53+
return super().read_file()
5454
except FileNotFoundError as err:
5555
raise ININotFoundError(err) from None
5656
except UnexpectedFileIOError as err:

src/usethis/_integrations/file/pyproject_toml/io_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from collections.abc import Sequence
2626
from typing import Any
2727

28+
from tomlkit import TOMLDocument
2829
from typing_extensions import Self
2930

3031
from usethis._io import Key
@@ -43,9 +44,9 @@ def __enter__(self) -> Self:
4344
except UnexpectedTOMLOpenError as err:
4445
raise UnexpectedPyprojectTOMLOpenError(err) from None
4546

46-
def read_file(self) -> None:
47+
def read_file(self) -> TOMLDocument:
4748
try:
48-
super().read_file()
49+
return super().read_file()
4950
except TOMLNotFoundError as err:
5051
raise PyprojectTOMLNotFoundError(err) from None
5152
except UnexpectedTOMLIOError as err:

src/usethis/_integrations/file/setup_cfg/io_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from collections.abc import Sequence
2626
from typing import Any
2727

28+
from configupdater import ConfigUpdater as INIDocument
2829
from typing_extensions import Self
2930

3031
from usethis._io import Key
@@ -43,9 +44,9 @@ def __enter__(self) -> Self:
4344
except UnexpectedINIOpenError as err:
4445
raise UnexpectedSetupCFGOpenError(err) from None
4546

46-
def read_file(self) -> None:
47+
def read_file(self) -> INIDocument:
4748
try:
48-
super().read_file()
49+
return super().read_file()
4950
except ININotFoundError as err:
5051
raise SetupCFGNotFoundError(err) from None
5152
except UnexpectedINIIOError as err:

src/usethis/_integrations/file/toml/io_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ def __enter__(self) -> Self:
5353
except UnexpectedFileOpenError as err:
5454
raise UnexpectedTOMLOpenError(err) from None
5555

56-
def read_file(self) -> None:
56+
def read_file(self) -> TOMLDocument:
5757
try:
58-
super().read_file()
58+
return super().read_file()
5959
except FileNotFoundError as err:
6060
raise TOMLNotFoundError(err) from None
6161
except UnexpectedFileIOError as err:

0 commit comments

Comments
 (0)