nate nowack commited on
Commit
670f900
·
unverified ·
1 Parent(s): 5f2c09d

switch from `pyright` to `ty` (#1545)

Browse files
.github/workflows/marvin.yml CHANGED
@@ -70,6 +70,6 @@ jobs:
70
  mode: tag
71
  trigger_phrase: "/marvin"
72
  allowed_bots: "*"
73
- allowed_tools: "WebSearch,WebFetch,Bash(uv:*),Bash(pre-commit:*),Bash(pytest:*),Bash(ruff:*),Bash(pyright:*),Bash(git:*),Bash(gh:*),mcp__github__add_issue_comment,mcp__github__create_issue,mcp__github__get_issue,mcp__github__list_issues,mcp__github__search_issues,mcp__github__update_issue,mcp__github__update_issue_comment,mcp__github__create_pull_request,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_files,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__list_pull_requests,mcp__github__update_pull_request,mcp__github__update_pull_request_branch,mcp__github__update_pull_request_comment,mcp__github__merge_pull_request"
74
  additional_permissions: |
75
  actions: read
 
70
  mode: tag
71
  trigger_phrase: "/marvin"
72
  allowed_bots: "*"
73
+ allowed_tools: "WebSearch,WebFetch,Bash(uv:*),Bash(pre-commit:*),Bash(pytest:*),Bash(ruff:*),Bash(ty:*),Bash(git:*),Bash(gh:*),mcp__github__add_issue_comment,mcp__github__create_issue,mcp__github__get_issue,mcp__github__list_issues,mcp__github__search_issues,mcp__github__update_issue,mcp__github__update_issue_comment,mcp__github__create_pull_request,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_files,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__list_pull_requests,mcp__github__update_pull_request,mcp__github__update_pull_request_branch,mcp__github__update_pull_request_comment,mcp__github__merge_pull_request"
74
  additional_permissions: |
75
  actions: read
.github/workflows/run-static.yml CHANGED
@@ -36,12 +36,9 @@ jobs:
36
  with:
37
  enable-cache: true
38
  cache-dependency-glob: "uv.lock"
39
- - name: Set up Python
40
- uses: actions/setup-python@v5
41
- with:
42
- python-version: "3.12"
43
  - name: Install dependencies
44
- run: uv sync --dev
45
  - name: Check lockfile is up to date
46
  run: |
47
  if ! uv lock --check; then
@@ -51,6 +48,6 @@ jobs:
51
  fi
52
  echo "✅ Lockfile is up to date"
53
  - name: Run pre-commit
54
- uses: pre-commit/action@v3.0.1
55
  env:
56
  SKIP: no-commit-to-branch
 
36
  with:
37
  enable-cache: true
38
  cache-dependency-glob: "uv.lock"
39
+
 
 
 
40
  - name: Install dependencies
41
+ run: uv sync
42
  - name: Check lockfile is up to date
43
  run: |
44
  if ! uv lock --check; then
 
48
  fi
49
  echo "✅ Lockfile is up to date"
50
  - name: Run pre-commit
51
+ run: uv run pre-commit run --all-files
52
  env:
53
  SKIP: no-commit-to-branch
.pre-commit-config.yaml CHANGED
@@ -22,11 +22,16 @@ repos:
22
  # Run the formatter.
23
  - id: ruff-format
24
 
25
- - repo: https://github.com/northisup/pyright-pretty
26
- rev: v0.1.0
27
  hooks:
28
- - id: pyright-pretty
 
 
 
 
29
  files: ^src/|^tests/
 
 
30
 
31
  - repo: https://github.com/pre-commit/pre-commit-hooks
32
  rev: v4.3.0
 
22
  # Run the formatter.
23
  - id: ruff-format
24
 
25
+ - repo: local
 
26
  hooks:
27
+ - id: ty
28
+ name: type check
29
+ entry: ty check
30
+ language: system
31
+ types: [python]
32
  files: ^src/|^tests/
33
+ pass_filenames: false
34
+ require_serial: true
35
 
36
  - repo: https://github.com/pre-commit/pre-commit-hooks
37
  rev: v4.3.0
AGENTS.md CHANGED
@@ -10,7 +10,7 @@ FastMCP is a comprehensive Python framework (Python ≥3.10) for building Model
10
 
11
  ```bash
12
  uv sync # Install dependencies
13
- uv run pre-commit run --all-files # Ruff + Prettier + Pyright
14
  uv run pytest # Run full test suite
15
  ```
16
 
@@ -222,7 +222,7 @@ uv sync # Installs all deps including dev tools
222
  ### Validation Commands (Run Frequently)
223
 
224
  - **Linting**: `uv run ruff check` (or with `--fix`)
225
- - **Type Checking**: `uv run pyright`
226
  - **All Checks**: `uv run pre-commit run --all-files`
227
 
228
  ### Testing
@@ -247,5 +247,5 @@ uv sync # Installs all deps including dev tools
247
 
248
  1. **Dependencies**: Always `uv sync` first
249
  2. **Pre-commit fails**: Run `uv run pre-commit run --all-files` to see failures
250
- 3. **Type errors**: Use `uv run pyright` directly, check `pyproject.toml` config
251
  4. **Test timeouts**: Default 3s - optimize or mark as integration tests
 
10
 
11
  ```bash
12
  uv sync # Install dependencies
13
+ uv run pre-commit run --all-files # Ruff + Prettier + ty
14
  uv run pytest # Run full test suite
15
  ```
16
 
 
222
  ### Validation Commands (Run Frequently)
223
 
224
  - **Linting**: `uv run ruff check` (or with `--fix`)
225
+ - **Type Checking**: `uv run ty check`
226
  - **All Checks**: `uv run pre-commit run --all-files`
227
 
228
  ### Testing
 
247
 
248
  1. **Dependencies**: Always `uv sync` first
249
  2. **Pre-commit fails**: Run `uv run pre-commit run --all-files` to see failures
250
+ 3. **Type errors**: Use `uv run ty check` directly, check `pyproject.toml` config
251
  4. **Test timeouts**: Default 3s - optimize or mark as integration tests
justfile CHANGED
@@ -6,9 +6,9 @@ build:
6
  test: build
7
  uv run --frozen pytest -xvs tests
8
 
9
- # Run pyright on all files
10
  typecheck:
11
- uv run --frozen pyright
12
 
13
  # Serve documentation locally
14
  docs:
 
6
  test: build
7
  uv run --frozen pytest -xvs tests
8
 
9
+ # Run ty type checker on all files
10
  typecheck:
11
+ uv run --frozen ty check
12
 
13
  # Serve documentation locally
14
  docs:
pyproject.toml CHANGED
@@ -50,7 +50,7 @@ dev = [
50
  "psutil",
51
  "pyinstrument>=5.0.2",
52
  "pyperclip>=1.9.0",
53
- "pyright>=1.1.389",
54
  "pytest>=8.3.3",
55
  "pytest-asyncio>=0.23.5",
56
  "pytest-cov>=6.1.1",
@@ -114,18 +114,23 @@ python_files = ["test_*.py", "*_test.py"]
114
  python_classes = ["Test*"]
115
  python_functions = ["test_*"]
116
 
117
- [tool.pyright]
118
  include = ["src", "tests"]
119
  exclude = ["**/node_modules", "**/__pycache__", ".venv", ".git", "dist"]
120
- pythonVersion = "3.10"
121
- pythonPlatform = "Darwin"
122
- typeCheckingMode = "basic"
123
- reportMissingImports = true
124
- reportMissingTypeStubs = false
125
- useLibraryCodeForTypes = true
126
- venvPath = "."
127
- venv = ".venv"
128
- strict = ["src/fastmcp/server/server.py"]
 
 
 
 
 
129
 
130
  [tool.ruff.lint]
131
  extend-select = ["I", "UP"]
 
50
  "psutil",
51
  "pyinstrument>=5.0.2",
52
  "pyperclip>=1.9.0",
53
+ "ty>=0.0.1a19",
54
  "pytest>=8.3.3",
55
  "pytest-asyncio>=0.23.5",
56
  "pytest-cov>=6.1.1",
 
114
  python_classes = ["Test*"]
115
  python_functions = ["test_*"]
116
 
117
+ [tool.ty.src]
118
  include = ["src", "tests"]
119
  exclude = ["**/node_modules", "**/__pycache__", ".venv", ".git", "dist"]
120
+
121
+ [tool.ty.environment]
122
+ python-version = "3.10"
123
+
124
+ [tool.ty.rules]
125
+ # Rules with too many errors to fix right now (40+ each)
126
+ invalid-argument-type = "ignore" # 40 errors
127
+ no-matching-overload = "ignore" # 126 errors
128
+ unknown-argument = "ignore" # 61 errors
129
+ unresolved-attribute = "ignore" # 60 errors
130
+
131
+ # Rules with moderate errors that need more investigation
132
+ call-non-callable = "ignore" # 7 errors
133
+ missing-argument = "ignore" # 23 errors
134
 
135
  [tool.ruff.lint]
136
  extend-select = ["I", "UP"]
src/fastmcp/cli/run.py CHANGED
@@ -107,7 +107,7 @@ async def import_server(file: Path, server_or_factory: str | None = None) -> Any
107
  sys.exit(1)
108
 
109
  # Handle module:object syntax
110
- if ":" in server_or_factory:
111
  module_name, object_name = server_or_factory.split(":", 1)
112
  try:
113
  server_module = importlib.import_module(module_name)
 
107
  sys.exit(1)
108
 
109
  # Handle module:object syntax
110
+ if server_or_factory and ":" in server_or_factory:
111
  module_name, object_name = server_or_factory.split(":", 1)
112
  try:
113
  server_module = importlib.import_module(module_name)
src/fastmcp/experimental/utilities/openapi/parser.py CHANGED
@@ -549,7 +549,11 @@ class OpenAPIParser(
549
  return
550
 
551
  # Add this schema and recursively find its dependencies
552
- if schema_name not in collected and schema_name in all_schemas:
 
 
 
 
553
  collected.add(schema_name)
554
  # Recursively find dependencies of this schema
555
  find_refs(all_schemas[schema_name])
 
549
  return
550
 
551
  # Add this schema and recursively find its dependencies
552
+ if (
553
+ collected is not None
554
+ and schema_name not in collected
555
+ and schema_name in all_schemas
556
+ ):
557
  collected.add(schema_name)
558
  # Recursively find dependencies of this schema
559
  find_refs(all_schemas[schema_name])
src/fastmcp/server/context.py CHANGED
@@ -44,7 +44,7 @@ from fastmcp.utilities.types import get_cached_typeadapter
44
  logger = get_logger(__name__)
45
 
46
  T = TypeVar("T")
47
- _current_context: ContextVar[Context | None] = ContextVar("context", default=None)
48
  _flush_lock = asyncio.Lock()
49
 
50
 
 
44
  logger = get_logger(__name__)
45
 
46
  T = TypeVar("T")
47
+ _current_context: ContextVar[Context | None] = ContextVar("context", default=None) # type: ignore[assignment]
48
  _flush_lock = asyncio.Lock()
49
 
50
 
src/fastmcp/server/http.py CHANGED
@@ -64,7 +64,7 @@ class StreamableHTTPASGIApp:
64
  raise
65
 
66
 
67
- _current_http_request: ContextVar[Request | None] = ContextVar(
68
  "http_request",
69
  default=None,
70
  )
 
64
  raise
65
 
66
 
67
+ _current_http_request: ContextVar[Request | None] = ContextVar( # type: ignore[assignment]
68
  "http_request",
69
  default=None,
70
  )
src/fastmcp/tools/tool_transform.py CHANGED
@@ -29,7 +29,7 @@ logger = get_logger(__name__)
29
 
30
 
31
  # Context variable to store current transformed tool
32
- _current_tool: ContextVar[TransformedTool | None] = ContextVar(
33
  "_current_tool", default=None
34
  )
35
 
 
29
 
30
 
31
  # Context variable to store current transformed tool
32
+ _current_tool: ContextVar[TransformedTool | None] = ContextVar( # type: ignore[assignment]
33
  "_current_tool", default=None
34
  )
35
 
tests/client/test_elicitation.py CHANGED
@@ -547,6 +547,8 @@ class TestPatternMatching:
547
  return "You declined"
548
  case CancelledElicitation():
549
  return "Cancelled"
 
 
550
 
551
  async def elicitation_handler(message, response_type, params, ctx):
552
  return ElicitResult(action="accept", content={"value": "Alice"})
@@ -570,6 +572,8 @@ class TestPatternMatching:
570
  return "You declined"
571
  case CancelledElicitation():
572
  return "Cancelled"
 
 
573
 
574
  async def elicitation_handler(message, response_type, params, ctx):
575
  return ElicitResult(action="decline")
@@ -593,6 +597,8 @@ class TestPatternMatching:
593
  return "You declined"
594
  case CancelledElicitation():
595
  return "Cancelled"
 
 
596
 
597
  async def elicitation_handler(message, response_type, params, ctx):
598
  return ElicitResult(action="cancel")
 
547
  return "You declined"
548
  case CancelledElicitation():
549
  return "Cancelled"
550
+ case _:
551
+ return "Unknown result"
552
 
553
  async def elicitation_handler(message, response_type, params, ctx):
554
  return ElicitResult(action="accept", content={"value": "Alice"})
 
572
  return "You declined"
573
  case CancelledElicitation():
574
  return "Cancelled"
575
+ case _:
576
+ return "Unknown result"
577
 
578
  async def elicitation_handler(message, response_type, params, ctx):
579
  return ElicitResult(action="decline")
 
597
  return "You declined"
598
  case CancelledElicitation():
599
  return "Cancelled"
600
+ case _:
601
+ return "Unknown result"
602
 
603
  async def elicitation_handler(message, response_type, params, ctx):
604
  return ElicitResult(action="cancel")
tests/integration_tests/auth/test_github_provider_integration.py CHANGED
@@ -116,7 +116,7 @@ def create_github_server_with_mock_callback(
116
  separator = "&" if "?" in str(params.redirect_uri) else "?"
117
  return f"{params.redirect_uri}{separator}{urlencode(callback_params)}"
118
 
119
- auth.authorize = mock_authorize
120
 
121
  # Mock the token verifier to accept our fake tokens
122
  original_verify_token = auth._token_validator.verify_token
@@ -137,7 +137,7 @@ def create_github_server_with_mock_callback(
137
  # Fall back to original verification for other tokens
138
  return await original_verify_token(token)
139
 
140
- auth._token_validator.verify_token = mock_verify_token
141
 
142
  # Create FastMCP server with mocked GitHub authentication
143
  server = FastMCP("GitHub OAuth Integration Test Server (Mock)", auth=auth)
 
116
  separator = "&" if "?" in str(params.redirect_uri) else "?"
117
  return f"{params.redirect_uri}{separator}{urlencode(callback_params)}"
118
 
119
+ auth.authorize = mock_authorize # type: ignore[assignment]
120
 
121
  # Mock the token verifier to accept our fake tokens
122
  original_verify_token = auth._token_validator.verify_token
 
137
  # Fall back to original verification for other tokens
138
  return await original_verify_token(token)
139
 
140
+ auth._token_validator.verify_token = mock_verify_token # type: ignore[assignment]
141
 
142
  # Create FastMCP server with mocked GitHub authentication
143
  server = FastMCP("GitHub OAuth Integration Test Server (Mock)", auth=auth)
uv.lock CHANGED
@@ -558,7 +558,6 @@ dev = [
558
  { name = "psutil" },
559
  { name = "pyinstrument" },
560
  { name = "pyperclip" },
561
- { name = "pyright" },
562
  { name = "pytest" },
563
  { name = "pytest-asyncio" },
564
  { name = "pytest-cov" },
@@ -569,6 +568,7 @@ dev = [
569
  { name = "pytest-timeout" },
570
  { name = "pytest-xdist" },
571
  { name = "ruff" },
 
572
  ]
573
 
574
  [package.metadata]
@@ -599,7 +599,6 @@ dev = [
599
  { name = "psutil" },
600
  { name = "pyinstrument", specifier = ">=5.0.2" },
601
  { name = "pyperclip", specifier = ">=1.9.0" },
602
- { name = "pyright", specifier = ">=1.1.389" },
603
  { name = "pytest", specifier = ">=8.3.3" },
604
  { name = "pytest-asyncio", specifier = ">=0.23.5" },
605
  { name = "pytest-cov", specifier = ">=6.1.1" },
@@ -610,6 +609,7 @@ dev = [
610
  { name = "pytest-timeout", specifier = ">=2.4.0" },
611
  { name = "pytest-xdist", specifier = ">=3.6.1" },
612
  { name = "ruff" },
 
613
  ]
614
 
615
  [[package]]
@@ -1422,19 +1422,6 @@ wheels = [
1422
  { url = "https://files.pythonhosted.org/packages/bd/a5/ce97a778f096aaa27cfcb7ad09f1198cf73277dcab6c68a4b8f332d91e48/pyrepl-0.11.4-py3-none-any.whl", hash = "sha256:ac30d6340267a21c39e1b1934f92bca6b8735017d14b17e40f903b2d1563541d", size = 55596, upload-time = "2025-07-17T22:56:24.537Z" },
1423
  ]
1424
 
1425
- [[package]]
1426
- name = "pyright"
1427
- version = "1.1.403"
1428
- source = { registry = "https://pypi.org/simple" }
1429
- dependencies = [
1430
- { name = "nodeenv" },
1431
- { name = "typing-extensions" },
1432
- ]
1433
- sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" }
1434
- wheels = [
1435
- { url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" },
1436
- ]
1437
-
1438
  [[package]]
1439
  name = "pytest"
1440
  version = "8.4.1"
@@ -2101,6 +2088,31 @@ wheels = [
2101
  { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
2102
  ]
2103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2104
  [[package]]
2105
  name = "typer"
2106
  version = "0.16.0"
 
558
  { name = "psutil" },
559
  { name = "pyinstrument" },
560
  { name = "pyperclip" },
 
561
  { name = "pytest" },
562
  { name = "pytest-asyncio" },
563
  { name = "pytest-cov" },
 
568
  { name = "pytest-timeout" },
569
  { name = "pytest-xdist" },
570
  { name = "ruff" },
571
+ { name = "ty" },
572
  ]
573
 
574
  [package.metadata]
 
599
  { name = "psutil" },
600
  { name = "pyinstrument", specifier = ">=5.0.2" },
601
  { name = "pyperclip", specifier = ">=1.9.0" },
 
602
  { name = "pytest", specifier = ">=8.3.3" },
603
  { name = "pytest-asyncio", specifier = ">=0.23.5" },
604
  { name = "pytest-cov", specifier = ">=6.1.1" },
 
609
  { name = "pytest-timeout", specifier = ">=2.4.0" },
610
  { name = "pytest-xdist", specifier = ">=3.6.1" },
611
  { name = "ruff" },
612
+ { name = "ty", specifier = ">=0.0.1a19" },
613
  ]
614
 
615
  [[package]]
 
1422
  { url = "https://files.pythonhosted.org/packages/bd/a5/ce97a778f096aaa27cfcb7ad09f1198cf73277dcab6c68a4b8f332d91e48/pyrepl-0.11.4-py3-none-any.whl", hash = "sha256:ac30d6340267a21c39e1b1934f92bca6b8735017d14b17e40f903b2d1563541d", size = 55596, upload-time = "2025-07-17T22:56:24.537Z" },
1423
  ]
1424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1425
  [[package]]
1426
  name = "pytest"
1427
  version = "8.4.1"
 
2088
  { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
2089
  ]
2090
 
2091
+ [[package]]
2092
+ name = "ty"
2093
+ version = "0.0.1a19"
2094
+ source = { registry = "https://pypi.org/simple" }
2095
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/04/281c1a3c9c53dae5826b9d01a3412de653e3caf1ca50ce1265da66e06d73/ty-0.0.1a19.tar.gz", hash = "sha256:894f6a13a43989c8ef891ae079b3b60a0c0eae00244abbfbbe498a3840a235ac", size = 4098412, upload-time = "2025-08-19T13:29:58.559Z" }
2096
+ wheels = [
2097
+ { url = "https://files.pythonhosted.org/packages/3e/65/a61cfcc7248b0257a3110bf98d3d910a4729c1063abdbfdcd1cad9012323/ty-0.0.1a19-py3-none-linux_armv6l.whl", hash = "sha256:e0e7762f040f4bab1b37c57cb1b43cc3bc5afb703fa5d916dfcafa2ef885190e", size = 8143744, upload-time = "2025-08-19T13:29:13.88Z" },
2098
+ { url = "https://files.pythonhosted.org/packages/02/d9/232afef97d9afa2274d23a4c49a3ad690282ca9696e1b6bbb6e4e9a1b072/ty-0.0.1a19-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cd0a67ac875f49f34d9a0b42dcabf4724194558a5dd36867209d5695c67768f7", size = 8305799, upload-time = "2025-08-19T13:29:17.322Z" },
2099
+ { url = "https://files.pythonhosted.org/packages/20/14/099d268da7a9cccc6ba38dfc124f6742a1d669bc91f2c61a3465672b4f71/ty-0.0.1a19-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ff8b1c0b85137333c39eccd96c42603af8ba7234d6e2ed0877f66a4a26750dd4", size = 7901431, upload-time = "2025-08-19T13:29:21.635Z" },
2100
+ { url = "https://files.pythonhosted.org/packages/c2/cd/3f1ca6e1d7f77cc4d08910a3fc4826313c031c0aae72286ae859e737670c/ty-0.0.1a19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fef34a29f4b97d78aa30e60adbbb12137cf52b8b2b0f1a408dd0feb0466908a", size = 8051501, upload-time = "2025-08-19T13:29:23.741Z" },
2101
+ { url = "https://files.pythonhosted.org/packages/47/72/ddbec39f48ce3f5f6a3fa1f905c8fff2873e59d2030f738814032bd783e3/ty-0.0.1a19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b0f219cb43c0c50fc1091f8ebd5548d3ef31ee57866517b9521d5174978af9fd", size = 7981234, upload-time = "2025-08-19T13:29:25.839Z" },
2102
+ { url = "https://files.pythonhosted.org/packages/f2/0f/58e76b8d4634df066c790d362e8e73b25852279cd6f817f099b42a555a66/ty-0.0.1a19-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22abb6c1f14c65c1a2fafd38e25dd3c87994b3ab88cb0b323235b51dbad082d9", size = 8916394, upload-time = "2025-08-19T13:29:27.932Z" },
2103
+ { url = "https://files.pythonhosted.org/packages/70/30/01bfd93ccde11540b503e2539e55f6a1fc6e12433a229191e248946eb753/ty-0.0.1a19-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5b49225c349a3866e38dd297cb023a92d084aec0e895ed30ca124704bff600e6", size = 9412024, upload-time = "2025-08-19T13:29:30.942Z" },
2104
+ { url = "https://files.pythonhosted.org/packages/a8/a2/2216d752f5f22c5c0995f9b13f18337301220f2a7d952c972b33e6a63583/ty-0.0.1a19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88f41728b3b07402e0861e3c34412ca963268e55f6ab1690208f25d37cb9d63c", size = 9032657, upload-time = "2025-08-19T13:29:33.933Z" },
2105
+ { url = "https://files.pythonhosted.org/packages/24/c7/e6650b0569be1b69a03869503d07420c9fb3e90c9109b09726c44366ce63/ty-0.0.1a19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33814a1197ec3e930fcfba6fb80969fe7353957087b42b88059f27a173f7510b", size = 8812775, upload-time = "2025-08-19T13:29:36.505Z" },
2106
+ { url = "https://files.pythonhosted.org/packages/35/c6/b8a20e06b97fe8203059d56d8f91cec4f9633e7ba65f413d80f16aa0be04/ty-0.0.1a19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71b7f2b674a287258f628acafeecd87691b169522945ff6192cd8a69af15857", size = 8631417, upload-time = "2025-08-19T13:29:38.837Z" },
2107
+ { url = "https://files.pythonhosted.org/packages/be/99/821ca1581dcf3d58ffb7bbe1cde7e1644dbdf53db34603a16a459a0b302c/ty-0.0.1a19-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a7f8ef9ac4c38e8651c18c7380649c5a3fa9adb1a6012c721c11f4bbdc0ce24", size = 7928900, upload-time = "2025-08-19T13:29:41.08Z" },
2108
+ { url = "https://files.pythonhosted.org/packages/08/cb/59f74a0522e57565fef99e2287b2bc803ee47ff7dac250af26960636939f/ty-0.0.1a19-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:60f40e72f0fbf4e54aa83d9a6cb1959f551f83de73af96abbb94711c1546bd60", size = 8003310, upload-time = "2025-08-19T13:29:43.165Z" },
2109
+ { url = "https://files.pythonhosted.org/packages/4c/b3/1209b9acb5af00a2755114042e48fb0f71decc20d9d77a987bf5b3d1a102/ty-0.0.1a19-py3-none-musllinux_1_2_i686.whl", hash = "sha256:64971e4d3e3f83dc79deb606cc438255146cab1ab74f783f7507f49f9346d89d", size = 8496463, upload-time = "2025-08-19T13:29:46.136Z" },
2110
+ { url = "https://files.pythonhosted.org/packages/a2/d6/a4b6ba552d347a08196d83a4d60cb23460404a053dd3596e23a922bce544/ty-0.0.1a19-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9aadbff487e2e1486e83543b4f4c2165557f17432369f419be9ba48dc47625ca", size = 8700633, upload-time = "2025-08-19T13:29:49.351Z" },
2111
+ { url = "https://files.pythonhosted.org/packages/96/c5/258f318d68b95685c8d98fb654a38882c9d01ce5d9426bed06124f690f04/ty-0.0.1a19-py3-none-win32.whl", hash = "sha256:00b75b446357ee22bcdeb837cb019dc3bc1dc5e5013ff0f46a22dfe6ce498fe2", size = 7811441, upload-time = "2025-08-19T13:29:52.077Z" },
2112
+ { url = "https://files.pythonhosted.org/packages/fb/bb/039227eee3c0c0cddc25f45031eea0f7f10440713f12d333f2f29cf8e934/ty-0.0.1a19-py3-none-win_amd64.whl", hash = "sha256:aaef76b2f44f6379c47adfe58286f0c56041cb2e374fd8462ae8368788634469", size = 8441186, upload-time = "2025-08-19T13:29:54.53Z" },
2113
+ { url = "https://files.pythonhosted.org/packages/74/5f/bceb29009670ae6f759340f9cb434121bc5ed84ad0f07bdc6179eaaa3204/ty-0.0.1a19-py3-none-win_arm64.whl", hash = "sha256:893755bb35f30653deb28865707e3b16907375c830546def2741f6ff9a764710", size = 8000810, upload-time = "2025-08-19T13:29:56.796Z" },
2114
+ ]
2115
+
2116
  [[package]]
2117
  name = "typer"
2118
  version = "0.16.0"