From 7333a0c868e0527b4c763332b7916049f85c48de Mon Sep 17 00:00:00 2001 From: Michael Panchenko <35432522+MischaPanch@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:26:07 +0200 Subject: [PATCH] Replace mypy by ty (#1619) --- .serena/memories/conventions.md | 8 +-- .../memories/serena_repository_structure.md | 2 +- .serena/memories/suggested_commands.md | 2 +- .serena/memories/task_completion.md | 2 +- .serena/memories/tech_stack.md | 2 +- pyproject.toml | 70 ++++++++++++------- src/serena/agent.py | 2 +- src/serena/dashboard.py | 4 +- src/serena/gui_log_viewer.py | 5 +- .../jetbrains/jetbrains_plugin_client.py | 6 +- src/serena/mcp.py | 6 +- src/serena/project_server.py | 4 +- src/serena/symbol.py | 2 +- src/serena/task_executor.py | 2 +- src/serena/tools/cmd_tools.py | 2 +- src/serena/tools/query_project_tools.py | 2 +- src/serena/tools/symbol_tools.py | 6 +- src/serena/tools/tools_base.py | 4 +- src/serena/util/exception.py | 2 +- src/serena/util/logging.py | 2 +- src/serena/util/shell.py | 2 +- .../language_servers/al_language_server.py | 2 +- .../clangd_language_server.py | 8 +-- src/solidlsp/language_servers/clojure_lsp.py | 2 +- src/solidlsp/language_servers/common.py | 4 +- .../csharp_language_server.py | 2 +- .../language_servers/eclipse_jdtls.py | 14 ++-- .../elixir_tools/elixir_tools.py | 1 + .../fortran_language_server.py | 4 +- .../language_servers/godot_language_server.py | 2 +- src/solidlsp/language_servers/julia_server.py | 4 +- src/solidlsp/language_servers/marksman.py | 2 +- .../powershell_language_server.py | 4 +- .../language_servers/pyright_server.py | 7 +- src/solidlsp/language_servers/regal_server.py | 14 ++-- src/solidlsp/language_servers/solargraph.py | 8 +-- .../solidity_language_server.py | 14 ++-- .../svelte_language_server.py | 2 +- .../typescript_language_server.py | 2 +- .../language_servers/vue_language_server.py | 6 +- src/solidlsp/ls.py | 54 +++++++------- src/solidlsp/ls_config.py | 4 +- src/solidlsp/ls_process.py | 34 +++++---- src/solidlsp/ls_utils.py | 2 +- src/solidlsp/util/subprocess_util.py | 2 +- src/solidlsp/util/zip.py | 2 +- test/serena/test_hooks.py | 8 +-- test/serena/test_mcp.py | 2 +- test/serena/test_serena_agent.py | 4 +- test/serena/test_symbol.py | 8 +-- .../angular/test_angular_error_cases.py | 2 +- .../scala/test_scala_language_server.py | 1 - uv.lock | 69 +++++++----------- 53 files changed, 221 insertions(+), 210 deletions(-) diff --git a/.serena/memories/conventions.md b/.serena/memories/conventions.md index 5c18dc52..f224f005 100644 --- a/.serena/memories/conventions.md +++ b/.serena/memories/conventions.md @@ -13,11 +13,9 @@ - `ruff format` runs on `src scripts test`; same set for `ruff check`. - mccabe complexity cap: 20. -## Typing (mypy strict) -- `disallow_untyped_defs = true`, `disallow_incomplete_defs = true`, `strict_equality`, `strict_optional`, `warn_unreachable`, `no_implicit_optional`. -- Excluded paths: `^build/`, `^docs/`, `^test/resources/`. -- Test files relax `disallow_untyped_defs` (still type-checked otherwise). - +## Typing (ty) +- Type checker is **ty** (Astral), configured under `[tool.ty]` in `pyproject.toml`. + ## Tests - Language-server tests are pytest-marker-gated (one marker per language; see `pyproject.toml` `[tool.pytest.ini_options].markers`). Default `poe test` runs unmarked tests + whatever `PYTEST_MARKERS` selects. - Snapshot tests use **syrupy** with custom `--snapshot-patch-pycharm-diff` plugin (auto-added via `addopts`). diff --git a/.serena/memories/serena_repository_structure.md b/.serena/memories/serena_repository_structure.md index 69f173de..5fc70a97 100644 --- a/.serena/memories/serena_repository_structure.md +++ b/.serena/memories/serena_repository_structure.md @@ -104,7 +104,7 @@ The project uses modern Python tooling: - **uv** for fast dependency resolution and virtual environments - **pytest** for testing with language-specific markers (`@pytest.mark.csharp`) - **ruff** for linting and formatting -- **mypy** for type checking +- **ty** (Astral) for type checking ## Build and Development diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md index 5ae6638f..e4e44816 100644 --- a/.serena/memories/suggested_commands.md +++ b/.serena/memories/suggested_commands.md @@ -6,7 +6,7 @@ Run via `uv run poe ` (or `poe ` inside the activated venv). Poe exe - `poe test` — pytest on `test/` (per-language tests are marker-gated; pass `-m ` to enable). - `poe lint` — ruff format-check + ruff check (no fixes). - `poe format` — ruff `--fix` then `ruff format` (mutates files). -- `poe type-check` — mypy on `src/serena`, `src/solidlsp`, and `test/` (test pass disables `no-untyped-def`). +- `poe type-check` — ty on `src/serena`, `src/solidlsp`, and `test/` (test pass relaxes pytest/mock-noisy rules via `[[tool.ty.overrides]]`). - Single test file: `uv run pytest test/path/to/test_x.py -vv`. Language-gated: add `-m python` etc. ## Docs diff --git a/.serena/memories/task_completion.md b/.serena/memories/task_completion.md index 5ca33012..8d6948a9 100644 --- a/.serena/memories/task_completion.md +++ b/.serena/memories/task_completion.md @@ -3,7 +3,7 @@ After any code change in `src/` or `test/`, run: 1. `uv run poe format` — applies ruff fixes + formatting (mutates files). -2. `uv run poe type-check` — mypy strict on `src/serena`, `src/solidlsp`, `test/`. +2. `uv run poe type-check` — ty on `src/serena`, `src/solidlsp`, `test/`. 3. `uv run poe test` — pytest on affected files or affected languages, using `-m` markers. If prompt templates changed: `uv run python scripts/gen_prompt_factory.py` (regenerates `src/serena/generated/generated_prompt_factory.py`; use `uv run poe format` and commit the result). diff --git a/.serena/memories/tech_stack.md b/.serena/memories/tech_stack.md index f868cca6..ca8a3a6e 100644 --- a/.serena/memories/tech_stack.md +++ b/.serena/memories/tech_stack.md @@ -5,6 +5,6 @@ - Build backend: `hatchling`. Packages: `src/serena`, `src/interprompt`, `src/solidlsp`. - Task runner: **poethepoet** (`poe `). Poe executor is `simple` (does NOT shell out via uv) — avoids env recreation while MCP server is running. - Key runtime deps: `mcp`, `flask` (dashboard), `pydantic`, `pygls` + `lsprotocol` (LSP), `anthropic`, `jinja2`, `ruamel.yaml`, `pywebview`/`pystray` (GUI), `pythonnet` (Windows only). -- Dev deps: `ruff` (lint+format), `mypy` (strict), `pytest` (+ `pytest-xdist`, `pytest-timeout`, `syrupy` snapshots), `sphinx`/`jupyter-book` for docs. +- Dev deps: `ruff` (lint+format), `ty` (Astral type checker; replaced mypy), `pytest` (+ `pytest-xdist`, `pytest-timeout`, `syrupy` snapshots), `sphinx`/`jupyter-book` for docs. - Optional extras: `agno` (Agno agent integration), `google` (gemini). - LSP client core lives under `src/solidlsp/`; one subdir per supported language server under `language_servers/`. diff --git a/pyproject.toml b/pyproject.toml index 9a1aafbe..232a4dea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ text = "MIT" dev = [ "jinja2==3.1.6", # In version 1.0.4 we get a NoneType error related to some config conversion (yml_analytics is None and should be a list) - "mypy==1.17.0", + "ty==0.0.24", "poethepoet==0.36.0", "pytest==8.4.1", "pytest-xdist==3.8.0", @@ -109,29 +109,47 @@ packages = ["src/serena", "src/interprompt", "src/solidlsp"] [tool.doc8] max-line-length = 1000 -[tool.mypy] -allow_redefinition = true -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_untyped_defs = true -ignore_missing_imports = true -no_implicit_optional = true -pretty = true -show_error_codes = true -show_error_context = true -show_traceback = true -strict_equality = true -strict_optional = true -warn_no_return = true -warn_redundant_casts = true -warn_unreachable = true -warn_unused_configs = true -warn_unused_ignores = false -exclude = "^build/|^docs/|^test/resources/" +[tool.ty.environment] +python-version = "3.11" +# Analyze for all platforms rather than defaulting to the OS ty happens to run on. This keeps the +# check deterministic across the CI matrix (Linux/Windows/macOS) and lets platform-conditional stdlib +# members (e.g. subprocess.CREATE_NO_WINDOW, ctypes.windll, pwd) resolve without per-OS type-ignores. +python-platform = "all" -[[tool.mypy.overrides]] -module = "test.*" -disallow_untyped_defs = false # untyped defs are OK in tests +[tool.ty.rules] +# Mirror mypy's ignore_missing_imports=true: optional extras (e.g. agno, google-genai) and +# platform-specific modules (e.g. AppKit on macOS) are not installed in the default dev environment, +# so we do not want unresolvable imports to fail the type check. +unresolved-import = "ignore" +possibly-missing-submodule = "ignore" + +[tool.ty.src] +# NOTE: test/resources is intentionally NOT excluded here. This config is also discovered by the ty +# language server (used by the PYTHON_TY diagnostics provider) when it analyzes the sample fixture +# repos under test/resources; excluding them here would suppress the diagnostics those tests rely on. +# test/resources is instead excluded from the `ty check test` CLI task via its --exclude flag. +exclude = ["build/", "docs/"] + +[[tool.ty.overrides]] +# Test code is heavily dynamic (pytest fixtures, MagicMock, intentionally loose Optionals). ty models +# pytest's fail/skip helpers and MagicMock far more strictly than mypy did (mypy inferred `Any` for +# these), which produces hundreds of diagnostics that are not real bugs. We therefore relax these +# specific type rules for tests only, while still catching genuinely broken test code (undefined +# names, syntax errors, unused type-ignore comments, etc.). The sample fixture repos under +# test/resources are excluded so the ty language server still reports real diagnostics on them. +include = ["test/**"] +exclude = ["test/resources/"] + +[tool.ty.overrides.rules] +invalid-argument-type = "ignore" +invalid-key = "ignore" +invalid-return-type = "ignore" +no-matching-overload = "ignore" +not-subscriptable = "ignore" +parameter-already-assigned = "ignore" +too-many-positional-arguments = "ignore" +unresolved-attribute = "ignore" +unsupported-operator = "ignore" [tool.poe.env] PYDEVD_DISABLE_FILE_VALIDATION = "1" @@ -152,9 +170,9 @@ _ruff_fix = "ruff check --fix src scripts test" _ruff_format = "ruff format src scripts test" lint = ["_ruff_format_check", "_ruff_check"] format = ["_ruff_fix", "_ruff_format"] -_mypy_core = "mypy src/serena src/solidlsp" -_mypy_test = "mypy --disable-error-code no-untyped-def test" -type-check = ["_mypy_core", "_mypy_test"] +_ty_core = "ty check src/serena src/solidlsp" +_ty_test = "ty check test --exclude test/resources" +type-check = ["_ty_core", "_ty_test"] # docs _autogen_docs = "python docs/autogen_docs.py" _sphinx_build = "sphinx-build -b html docs docs/_build -W --keep-going" diff --git a/src/serena/agent.py b/src/serena/agent.py index b5b727aa..d5b69269 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -1363,7 +1363,7 @@ class SerenaAgent: self.issue_task(lambda: self.get_active_project_or_raise().remove_language(language), name=f"RemoveLanguage:{language.value}") def get_tool(self, tool_class: type[TTool]) -> TTool: - return self._all_tools[tool_class] # type: ignore + return self._all_tools[tool_class] def print_tool_overview(self) -> None: ToolRegistry().print_tool_overview(self._active_tools.tools) diff --git a/src/serena/dashboard.py b/src/serena/dashboard.py index f424ca04..b5a85495 100644 --- a/src/serena/dashboard.py +++ b/src/serena/dashboard.py @@ -789,7 +789,9 @@ class SerenaDashboardAPI: # patch flask.cli.show_server to avoid printing the server info from flask import cli - cli.show_server_banner = lambda *args, **kwargs: None + # ty cannot model reassigning a third-party module's function attribute (it rejects any + # replacement, even one with an identical signature), so the monkeypatch is suppressed here + cli.show_server_banner = lambda *args, **kwargs: None # ty: ignore[invalid-assignment] self._app.run(host=self._host, port=port, debug=False, use_reloader=False, threaded=True) return port diff --git a/src/serena/gui_log_viewer.py b/src/serena/gui_log_viewer.py index 57d3ac08..ccf42cd4 100644 --- a/src/serena/gui_log_viewer.py +++ b/src/serena/gui_log_viewer.py @@ -1,4 +1,3 @@ -# mypy: ignore-errors import logging import queue import sys @@ -109,7 +108,7 @@ class GuiLogViewer: if self.menubar is not None: dashboard_menu = tk.Menu(self.menubar, tearoff=0) - dashboard_menu.add_command(label="Copy URL", command=copy_url) # type: ignore + dashboard_menu.add_command(label="Copy URL", command=copy_url) self.menubar.add_cascade(label="Dashboard", menu=dashboard_menu) def add_log(self, message): @@ -303,7 +302,7 @@ class GuiLogViewer: if self.mode == "dashboard": self.menubar = tk.Menu(self.root) server_menu = tk.Menu(self.menubar, tearoff=0) - server_menu.add_command(label="Shutdown", command=self._shutdown_server) # type: ignore + server_menu.add_command(label="Shutdown", command=self._shutdown_server) self.menubar.add_cascade(label="Server", menu=server_menu) self.root.config(menu=self.menubar) diff --git a/src/serena/jetbrains/jetbrains_plugin_client.py b/src/serena/jetbrains/jetbrains_plugin_client.py index 3cbd915f..b03dacfe 100644 --- a/src/serena/jetbrains/jetbrains_plugin_client.py +++ b/src/serena/jetbrains/jetbrains_plugin_client.py @@ -225,7 +225,7 @@ class JetBrainsPluginClient(ToStringMixin): return ["_port", "project_root", "_plugin_version"] @classmethod - def from_project(cls, project: Project, log_warning: bool = True) -> Self: + def from_project(cls, project: Project, log_warning: bool = True) -> "JetBrainsPluginClient": resolved_path = Path(project.project_root).resolve() if cls._last_port is not None: @@ -355,7 +355,7 @@ class JetBrainsPluginClient(ToStringMixin): """ to_snake_case = lambda s: "".join(["_" + c.lower() if c.isupper() else c for c in s]) - def convert(x): # type: ignore + def convert(x): if isinstance(x, dict): return {to_snake_case(k): convert(v) for k, v in x.items()} elif isinstance(x, list): @@ -721,5 +721,5 @@ class JetBrainsPluginClient(ToStringMixin): def __enter__(self) -> Self: return self - def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore + def __exit__(self, exc_type, exc_val, exc_tb): self.close() diff --git a/src/serena/mcp.py b/src/serena/mcp.py index 033533c1..321c9b35 100644 --- a/src/serena/mcp.py +++ b/src/serena/mcp.py @@ -34,7 +34,7 @@ from serena.util.logging import MemoryLogHandler log = logging.getLogger(__name__) -def configure_logging(*args, **kwargs) -> None: # type: ignore +def configure_logging(*args, **kwargs) -> None: # We only do something here if logging has not yet been configured. # Normally, logging is configured in the MCP server startup script. if not logging.is_enabled(): @@ -95,7 +95,7 @@ class SerenaFastMCPTool(FastMCPTool): param_desc = f"{param_doc.description.strip().strip('.') + '.'}" properties["description"] = param_desc[0].upper() + param_desc[1:] - def execute_fn(**kwargs) -> str: # type: ignore + def execute_fn(**kwargs) -> str: try: return tool.apply_ex(log_call=True, catch_exceptions=False, **kwargs) except ToolCallError as e: @@ -182,7 +182,7 @@ class SerenaMCPFactory: """ s = deepcopy(schema) - def walk(node): # type: ignore + def walk(node): if not isinstance(node, dict): # lists get handled by parent calls return node diff --git a/src/serena/project_server.py b/src/serena/project_server.py index bd6c7a79..ceec43a9 100644 --- a/src/serena/project_server.py +++ b/src/serena/project_server.py @@ -107,7 +107,9 @@ class ProjectServer: from flask import cli # suppress the default Flask startup banner - cli.show_server_banner = lambda *args, **kwargs: None + # ty cannot model reassigning a third-party module's function attribute (it rejects any + # replacement, even one with an identical signature), so the monkeypatch is suppressed here + cli.show_server_banner = lambda *args, **kwargs: None # ty: ignore[invalid-assignment] self._app.run(host=host, port=port, debug=False, use_reloader=False, threaded=True) return port diff --git a/src/serena/symbol.py b/src/serena/symbol.py index 8bae7d14..8eb00595 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -1321,7 +1321,7 @@ class SymbolDictGrouper(Generic[TSymbolDict], ABC): """ # avoid side effects by working on a deep-copy symbols_copy = copy.deepcopy(symbols) - return self._group_by(symbols_copy, self._group_keys, self._group_children_keys, is_children=False) # type: ignore + return self._group_by(symbols_copy, self._group_keys, self._group_children_keys, is_children=False) class LanguageServerSymbolDictGrouper(SymbolDictGrouper[LanguageServerSymbol.OutputDict]): diff --git a/src/serena/task_executor.py b/src/serena/task_executor.py index 1c72f068..6e560145 100644 --- a/src/serena/task_executor.py +++ b/src/serena/task_executor.py @@ -200,7 +200,7 @@ class TaskExecutor: self._task_executor_task_index += 1 else: task_prefix_name = "BackgroundTask" - task_name = f"{task_prefix_name}:{name or task.__name__}" + task_name = f"{task_prefix_name}:{name or getattr(task, '__name__', 'task')}" if logged: log.info(f"Scheduling {task_name}") task_obj = self.Task(function=task, name=task_name, logged=logged, timeout=timeout) diff --git a/src/serena/tools/cmd_tools.py b/src/serena/tools/cmd_tools.py index e548903e..52afa95f 100644 --- a/src/serena/tools/cmd_tools.py +++ b/src/serena/tools/cmd_tools.py @@ -48,5 +48,5 @@ class ExecuteShellCommandTool(Tool, ToolMarkerCanEdit): ) result = execute_shell_command(command, cwd=_cwd, capture_stderr=capture_stderr) - result = result.json() + result = result.model_dump_json() return self._limit_length(result, max_answer_chars) diff --git a/src/serena/tools/query_project_tools.py b/src/serena/tools/query_project_tools.py index 7d845a22..2340b538 100644 --- a/src/serena/tools/query_project_tools.py +++ b/src/serena/tools/query_project_tools.py @@ -62,7 +62,7 @@ class QueryProjectTool(Tool, ToolMarkerOptional, ToolMarkerDoesNotRequireActiveP assert registered_project is not None, f"Project {project_name} is not registered and cannot be queried." project = registered_project.get_project_instance(self.agent.serena_config) with tool.agent.active_project_context(project): - return tool.apply(**json.loads(tool_params_json)) # type: ignore + return tool.apply(**json.loads(tool_params_json)) def _is_project_server_required(self, tool: Tool) -> bool: match self.agent.get_language_backend(): diff --git a/src/serena/tools/symbol_tools.py b/src/serena/tools/symbol_tools.py index 7c11fce4..d88b7e9a 100644 --- a/src/serena/tools/symbol_tools.py +++ b/src/serena/tools/symbol_tools.py @@ -228,7 +228,7 @@ class FindSymbolTool(Tool, ToolMarkerSymbolicRead): # In python 3.15 we could specify extra_items=True in the TypedDict definition, # https://peps.python.org/pep-0728/ # If we ever upgrade to 3.15, we can remove the type: ignore[typeddict-unknown-key] - s_dict["info"] = symbol_info # type: ignore[typeddict-unknown-key] + s_dict["info"] = symbol_info grouped_symbol_dicts = self.symbol_dict_grouper.group(symbol_dicts) result = self._to_json(grouped_symbol_dicts) @@ -306,12 +306,12 @@ class FindReferencingSymbolsTool(Tool, ToolMarkerSymbolicRead): } ) - result = self.symbol_dict_grouper.group(reference_dicts) # type: ignore + result = self.symbol_dict_grouper.group(reference_dicts) # shortened result closures, from least to most aggressive shortening def make_refs_without_context() -> str: """References with name_path and reference line, without surrounding code lines""" - grouped = self.symbol_dict_grouper.group(copy.deepcopy(ref_summaries)) # type: ignore + grouped = self.symbol_dict_grouper.group(copy.deepcopy(ref_summaries)) return f"References without surrounding lines:\n{self._to_json(grouped)}" def make_per_file_counts() -> str: diff --git a/src/serena/tools/tools_base.py b/src/serena/tools/tools_base.py index 304333f3..0f09296f 100644 --- a/src/serena/tools/tools_base.py +++ b/src/serena/tools/tools_base.py @@ -327,7 +327,7 @@ class Tool(Component): """ return {} - def apply_ex(self, log_call: bool = True, catch_exceptions: bool = True, mcp_ctx: Context | None = None, **kwargs) -> str: # type: ignore + def apply_ex(self, log_call: bool = True, catch_exceptions: bool = True, mcp_ctx: Context | None = None, **kwargs) -> str: """ Applies the tool with logging and exception handling, using the given keyword arguments. This method either returns a string result or raises a ToolCallError in case of an error during tool application @@ -468,7 +468,7 @@ class EditingToolWithDiagnostics(Tool, ToolMarkerCanEdit): def __enter__(self) -> Self: return self - def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore + def __exit__(self, exc_type, exc_val, exc_tb): pass def format_result( diff --git a/src/serena/util/exception.py b/src/serena/util/exception.py index f9cbb562..8ab2f007 100644 --- a/src/serena/util/exception.py +++ b/src/serena/util/exception.py @@ -19,7 +19,7 @@ def is_headless_environment() -> bool: return False # Check for DISPLAY variable (required for X11) - if not os.environ.get("DISPLAY"): # type: ignore + if not os.environ.get("DISPLAY"): return True # Check for SSH session diff --git a/src/serena/util/logging.py b/src/serena/util/logging.py index 0d4f0970..279f955d 100644 --- a/src/serena/util/logging.py +++ b/src/serena/util/logging.py @@ -145,7 +145,7 @@ class SuspendedLoggersContext: root_logger.handlers.clear() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore + def __exit__(self, exc_type, exc_val, exc_tb) -> None: root_logger = lg.getLogger() root_logger.handlers = self.saved_root_handlers if self.saved_root_level is not None: diff --git a/src/serena/util/shell.py b/src/serena/util/shell.py index a8fad3b1..8de5a67c 100644 --- a/src/serena/util/shell.py +++ b/src/serena/util/shell.py @@ -47,7 +47,7 @@ def subprocess_check_output( ) -> str: output = subprocess.check_output( args, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, timeout=timeout, env=os.environ.copy(), cwd=cwd, **subprocess_kwargs() - ).decode(encoding) # type: ignore + ).decode(encoding) if strip: output = output.strip() return output diff --git a/src/solidlsp/language_servers/al_language_server.py b/src/solidlsp/language_servers/al_language_server.py index 438b1d3d..f954dbc7 100644 --- a/src/solidlsp/language_servers/al_language_server.py +++ b/src/solidlsp/language_servers/al_language_server.py @@ -1038,7 +1038,7 @@ class ALLanguageServer(SolidLanguageServer): if original_name != normalized_name and self._AL_OBJECT_NAME_PATTERN.match(original_name): sel_range = symbol.get("selectionRange") if sel_range: - start = sel_range.get("start") # type: ignore + start = sel_range.get("start") if start and "line" in start and "character" in start: line = start["line"] char = start["character"] diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index d9830ff4..b5c5d40a 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -82,7 +82,7 @@ class ClangdLanguageServer(SolidLanguageServer): @override def _document_symbols_cache_fingerprint(self) -> Hashable: cache_format_version = 1 - cpp_settings: dict[str, Any] = self._custom_settings or {} + cpp_settings = self._custom_settings or {} return ( cache_format_version, cpp_settings.get("clangd_version"), @@ -179,7 +179,7 @@ class ClangdLanguageServer(SolidLanguageServer): return None # Get the target directory from ls_specific_settings, default to .serena - cpp_settings: dict[str, Any] = self._custom_settings or {} + cpp_settings = self._custom_settings or {} compile_commands_rel_dir = cpp_settings.get("compile_commands_dir", ".serena") compile_commands_dir = os.path.join(self.repository_root_path, compile_commands_rel_dir) os.makedirs(compile_commands_dir, exist_ok=True) @@ -411,9 +411,9 @@ class ClangdLanguageServer(SolidLanguageServer): text_document_sync = capabilities["textDocumentSync"] if isinstance(text_document_sync, int): - assert text_document_sync == 2 # type: ignore + assert text_document_sync == 2 else: - assert text_document_sync["change"] == 2 # type: ignore + assert text_document_sync["change"] == 2 assert "completionProvider" in capabilities completion_provider = capabilities["completionProvider"] diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index 0a1213c4..1835ce99 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -318,7 +318,7 @@ class ClojureLSP(SolidLanguageServer): if source_paths is not None: initialization_options["source-paths"] = source_paths - result = { # type: ignore + result = { "processId": os.getpid(), "rootPath": repository_absolute_path, "rootUri": root_uri, diff --git a/src/solidlsp/language_servers/common.py b/src/solidlsp/language_servers/common.py index 3cf9dcbf..ddc3fddd 100644 --- a/src/solidlsp/language_servers/common.py +++ b/src/solidlsp/language_servers/common.py @@ -106,7 +106,7 @@ class RuntimeDependencyCollection: if not PlatformUtils.get_platform_id().is_windows(): import pwd - kwargs["user"] = pwd.getpwuid(os.getuid()).pw_name # type: ignore + kwargs["user"] = pwd.getpwuid(os.getuid()).pw_name command = subprocess_util.convert_shell_cmd(command) log.info("Running command %s in '%s'", f"'{command}'" if isinstance(command, str) else command, cwd) @@ -120,7 +120,7 @@ class RuntimeDependencyCollection: stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs, - ) # type: ignore + ) if completed_process.returncode != 0: log.warning("Command '%s' failed with return code %d", command, completed_process.returncode) log.warning("Command output:\n%s", completed_process.stdout) diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 66049a7e..296acad0 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -277,7 +277,7 @@ class CSharpLanguageServer(SolidLanguageServer): if original_name != normalized_name: sel_range = symbol.get("selectionRange") if sel_range: - start = sel_range.get("start") # type: ignore + start = sel_range.get("start") if start and "line" in start and "character" in start: line = start["line"] char = start["character"] diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 2479f13e..4614d9fe 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -1299,26 +1299,26 @@ class EclipseJDTLS(SolidLanguageServer): ], } - initialize_params["initializationOptions"]["workspaceFolders"] = [repo_uri] # type: ignore + initialize_params["initializationOptions"]["workspaceFolders"] = [repo_uri] # IntelliCode bundle: only attached in default vscode-java VSIX mode. # In upstream-jdtls mode (jdtls_path set) we don't ship IntelliCode — agentic Serena workflows # don't use completion ranking, so the bundle would be inert dead weight. if self.runtime_dependency_paths.intellicode_jar_path is not None: - initialize_params["initializationOptions"]["bundles"] = [self.runtime_dependency_paths.intellicode_jar_path] # type: ignore + initialize_params["initializationOptions"]["bundles"] = [self.runtime_dependency_paths.intellicode_jar_path] else: - initialize_params["initializationOptions"]["bundles"] = [] # type: ignore + initialize_params["initializationOptions"]["bundles"] = [] - initialize_params["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] = [ # type: ignore + initialize_params["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] = [ {"name": "JavaSE-21", "path": self.runtime_dependency_paths.jre_home_path, "default": True} ] - for runtime in initialize_params["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"]: # type: ignore + for runtime in initialize_params["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"]: assert "name" in runtime assert "path" in runtime assert os.path.exists(runtime["path"]), f"Runtime required for eclipse_jdtls at path {runtime['path']} does not exist" - gradle_settings = initialize_params["initializationOptions"]["settings"]["java"]["import"]["gradle"] # type: ignore + gradle_settings = initialize_params["initializationOptions"]["settings"]["java"]["import"]["gradle"] # In upstream-jdtls mode we don't ship a Gradle distribution — Buildship will use the project's # ./gradlew wrapper or a system-installed Gradle via its standard discovery rules. if self.runtime_dependency_paths.gradle_path is not None: @@ -1483,7 +1483,7 @@ class EclipseJDTLS(SolidLanguageServer): symbol["name"] = symbol["name"][: symbol["name"].index("(")] children = symbol.get("children") if children: - for child in children: # type: ignore + for child in children: fix_name(child) for root_symbol in result: diff --git a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py index 11e754a1..6005df89 100644 --- a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py +++ b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py @@ -212,6 +212,7 @@ class ElixirTools(SolidLanguageServer): ) self.server_ready = threading.Event() self.request_id = 0 + self._building_project = False # Set generous timeout for Expert which can be slow to initialize and respond self.set_request_timeout(180.0) diff --git a/src/solidlsp/language_servers/fortran_language_server.py b/src/solidlsp/language_servers/fortran_language_server.py index 45d7071b..39498971 100644 --- a/src/solidlsp/language_servers/fortran_language_server.py +++ b/src/solidlsp/language_servers/fortran_language_server.py @@ -299,8 +299,8 @@ class FortranLanguageServer(SolidLanguageServer): with self.open_file(relative_file_path): self.server.notify.did_save_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: uri, } } diff --git a/src/solidlsp/language_servers/godot_language_server.py b/src/solidlsp/language_servers/godot_language_server.py index 3186a123..453f0f92 100644 --- a/src/solidlsp/language_servers/godot_language_server.py +++ b/src/solidlsp/language_servers/godot_language_server.py @@ -80,7 +80,7 @@ class GodotLanguageServer(SolidLanguageServer): process_launch_info: ProcessLaunchInfo, logging_fn: Callable[[str, str, StringDict | str], None] | None, ) -> LanguageServerInterface: - settings: dict = self._custom_settings or {} + settings = self._custom_settings or {} port = settings.get("port", DEFAULT_GODOT_LS_PORT) request_timeout = settings.get("request_timeout", DEFAULT_GODOT_REQUEST_TIMEOUT) self._configured_request_timeout = settings.get("request_timeout") diff --git a/src/solidlsp/language_servers/julia_server.py b/src/solidlsp/language_servers/julia_server.py index 71f978c9..7b7579b6 100644 --- a/src/solidlsp/language_servers/julia_server.py +++ b/src/solidlsp/language_servers/julia_server.py @@ -141,7 +141,7 @@ class JuliaLanguageServer(SolidLanguageServer): Returns the initialize params for the Julia Language Server. """ root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params: InitializeParams = { # type: ignore + initialize_params: InitializeParams = { "processId": os.getpid(), "rootPath": repository_absolute_path, "rootUri": root_uri, @@ -171,7 +171,7 @@ class JuliaLanguageServer(SolidLanguageServer): } ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Start the LanguageServer.jl server process.""" diff --git a/src/solidlsp/language_servers/marksman.py b/src/solidlsp/language_servers/marksman.py index 13c32147..72d7268a 100644 --- a/src/solidlsp/language_servers/marksman.py +++ b/src/solidlsp/language_servers/marksman.py @@ -201,7 +201,7 @@ class Marksman(SolidLanguageServer): Returns the initialize params for the Marksman Language Server. """ root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params: InitializeParams = { # type: ignore + initialize_params: InitializeParams = { "processId": os.getpid(), "locale": "en", "rootPath": repository_absolute_path, diff --git a/src/solidlsp/language_servers/powershell_language_server.py b/src/solidlsp/language_servers/powershell_language_server.py index 70a06d1a..1879ac02 100644 --- a/src/solidlsp/language_servers/powershell_language_server.py +++ b/src/solidlsp/language_servers/powershell_language_server.py @@ -412,8 +412,8 @@ class PowerShellLanguageServer(SolidLanguageServer): with self.open_file(relative_file_path): self.server.notify.did_save_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: uri, } } diff --git a/src/solidlsp/language_servers/pyright_server.py b/src/solidlsp/language_servers/pyright_server.py index d4aa1265..f369706c 100644 --- a/src/solidlsp/language_servers/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_server.py @@ -65,7 +65,7 @@ class PyrightServer(SolidLanguageServer): Returns the initialize params for the Pyright Language Server. """ # Create basic initialization parameters - initialize_params = { # type: ignore + initialize_params = { "processId": os.getpid(), "rootPath": repository_absolute_path, "rootUri": pathlib.Path(repository_absolute_path).as_uri(), @@ -168,9 +168,10 @@ class PyrightServer(SolidLanguageServer): message_text = "" percentage: object | None = None if isinstance(params, dict): - raw_message = params.get("message") + params_dict = cast("dict[str, object]", params) + raw_message = params_dict.get("message") message_text = "" if raw_message is None else str(raw_message) - percentage = params.get("percentage") + percentage = params_dict.get("percentage") elif params is not None: message_text = str(params) diff --git a/src/solidlsp/language_servers/regal_server.py b/src/solidlsp/language_servers/regal_server.py index 72eb19db..25870a04 100644 --- a/src/solidlsp/language_servers/regal_server.py +++ b/src/solidlsp/language_servers/regal_server.py @@ -3,6 +3,7 @@ import logging import os import shutil +from typing import cast from overrides import override @@ -62,7 +63,7 @@ class RegalLanguageServer(SolidLanguageServer): :return: LSP initialization parameters """ root_uri = PathUtils.path_to_uri(repository_absolute_path) - return { + initialize_params = { "processId": os.getpid(), "locale": "en", "rootPath": repository_absolute_path, @@ -76,9 +77,9 @@ class RegalLanguageServer(SolidLanguageServer): "documentSymbol": { "dynamicRegistration": True, "hierarchicalDocumentSymbolSupport": True, - "symbolKind": {"valueSet": list(range(1, 27))}, # type: ignore[arg-type] + "symbolKind": {"valueSet": list(range(1, 27))}, }, - "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, # type: ignore[list-item] + "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, "codeAction": {"dynamicRegistration": True}, "formatting": {"dynamicRegistration": True}, }, @@ -95,17 +96,18 @@ class RegalLanguageServer(SolidLanguageServer): } ], } + return cast(InitializeParams, initialize_params) def _start_server(self) -> None: """Start Regal language server process and wait for initialization.""" - def register_capability_handler(params) -> None: # type: ignore[no-untyped-def] + def register_capability_handler(params) -> None: return - def window_log_message(msg) -> None: # type: ignore[no-untyped-def] + def window_log_message(msg) -> None: log.info(f"LSP: window/logMessage: {msg}") - def do_nothing(params) -> None: # type: ignore[no-untyped-def] + def do_nothing(params) -> None: return self.server.on_request("client/registerCapability", register_capability_handler) diff --git a/src/solidlsp/language_servers/solargraph.py b/src/solidlsp/language_servers/solargraph.py index f42f0854..9379bc6d 100644 --- a/src/solidlsp/language_servers/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -133,7 +133,7 @@ class Solargraph(SolidLanguageServer): if bundle_cmd.startswith("bin/"): bundle_full_path = os.path.join(repository_root_path, bundle_cmd) else: - bundle_full_path = find_executable_with_extensions(bundle_cmd) # type: ignore[assignment] + bundle_full_path = find_executable_with_extensions(bundle_cmd) if bundle_full_path and os.path.exists(bundle_full_path): bundle_path = bundle_full_path if bundle_cmd.startswith("bin/") else bundle_cmd break @@ -275,12 +275,12 @@ class Solargraph(SolidLanguageServer): root_uri = pathlib.Path(repository_absolute_path).as_uri() exclude_patterns = Solargraph._get_ruby_exclude_patterns(repository_absolute_path) - initialize_params: InitializeParams = { # type: ignore + initialize_params: InitializeParams = { "processId": os.getpid(), "rootPath": repository_absolute_path, "rootUri": root_uri, "initializationOptions": { - "exclude": exclude_patterns, # type: ignore[dict-item] + "exclude": exclude_patterns, }, "capabilities": { "workspace": { @@ -301,7 +301,7 @@ class Solargraph(SolidLanguageServer): } ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """ diff --git a/src/solidlsp/language_servers/solidity_language_server.py b/src/solidlsp/language_servers/solidity_language_server.py index d0c34053..7ee35953 100644 --- a/src/solidlsp/language_servers/solidity_language_server.py +++ b/src/solidlsp/language_servers/solidity_language_server.py @@ -313,8 +313,8 @@ class SolidityLanguageServer(SolidLanguageServer): } self.server.notify.did_open_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: uri, LSPConstants.LANGUAGE_ID: self.language_id, LSPConstants.VERSION: 0, @@ -324,12 +324,12 @@ class SolidityLanguageServer(SolidLanguageServer): ) try: self.server.notify.did_change_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: uri, LSPConstants.VERSION: 1, }, - LSPConstants.CONTENT_CHANGES: [ # type: ignore + LSPConstants.CONTENT_CHANGES: [ { LSPConstants.RANGE: { "start": {"line": 0, "character": 0}, @@ -348,8 +348,8 @@ class SolidityLanguageServer(SolidLanguageServer): ) finally: self.server.notify.did_close_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: uri, } } diff --git a/src/solidlsp/language_servers/svelte_language_server.py b/src/solidlsp/language_servers/svelte_language_server.py index a0cff5b1..c04730d8 100644 --- a/src/solidlsp/language_servers/svelte_language_server.py +++ b/src/solidlsp/language_servers/svelte_language_server.py @@ -438,7 +438,7 @@ class SvelteLanguageServer(SolidLanguageServer): return _orig_notify_send("$/onDidChangeTsOrJsFile", {"uri": uri, "changes": changes}) - self.server.notify.send_notification = send_notification_wrapped # type: ignore[method-assign] # type: ignore[method-assign] # type: ignore[method-assign] + self.server.notify.send_notification = send_notification_wrapped def _get_initialize_params(self) -> InitializeParams: """ diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 731ad835..892843f5 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -36,7 +36,7 @@ if os.name != "nt": # Unix-like systems import pwd else: # Dummy pwd module for Windows - class pwd: # type: ignore + class pwd: @staticmethod def getpwuid(uid: Any) -> Any: return type("obj", (), {"pw_name": os.environ.get("USERNAME", "unknown")})() diff --git a/src/solidlsp/language_servers/vue_language_server.py b/src/solidlsp/language_servers/vue_language_server.py index 84d1ce8c..f5112c0f 100644 --- a/src/solidlsp/language_servers/vue_language_server.py +++ b/src/solidlsp/language_servers/vue_language_server.py @@ -344,7 +344,7 @@ class VueLanguageServer(SolidLanguageServer): continue new_item: dict = {} - new_item.update(item) # type: ignore[arg-type] + new_item.update(item) new_item["absolutePath"] = str(abs_path) new_item["relativePath"] = str(rel_path) result.append(ls_types.Location(**new_item)) # type: ignore @@ -390,7 +390,7 @@ class VueLanguageServer(SolidLanguageServer): log.debug(f"Skipping invalid location item: {item}") continue - abs_path = PathUtils.uri_to_path(item["uri"]) # type: ignore[arg-type] + abs_path = PathUtils.uri_to_path(item["uri"]) if not Path(abs_path).is_relative_to(self.repository_root_path): log.warning(f"Found file reference outside repository: {abs_path}, skipping") continue @@ -401,7 +401,7 @@ class VueLanguageServer(SolidLanguageServer): continue new_item: dict = {} - new_item.update(item) # type: ignore[arg-type] + new_item.update(item) new_item["absolutePath"] = str(abs_path) new_item["relativePath"] = str(rel_path) ret.append(Location(**new_item)) # type: ignore diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index c311c0db..558a9148 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -108,8 +108,8 @@ class LSPFileBuffer: return self._is_open_in_ls = True self.language_server.server.notify.did_open_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: self.uri, LSPConstants.LANGUAGE_ID: self.language_id, LSPConstants.VERSION: 0, @@ -121,8 +121,8 @@ class LSPFileBuffer: def close(self) -> None: if self._is_open_in_ls: self.language_server.server.notify.did_close_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: self.uri, } } @@ -223,10 +223,10 @@ class SymbolBodyFactory: return existing_body assert "location" in symbol - start_line = symbol["location"]["range"]["start"]["line"] # type: ignore - end_line = symbol["location"]["range"]["end"]["line"] # type: ignore - start_col = symbol["location"]["range"]["start"]["character"] # type: ignore - end_col = symbol["location"]["range"]["end"]["character"] # type: ignore + start_line = symbol["location"]["range"]["start"]["line"] + end_line = symbol["location"]["range"]["end"]["line"] + start_col = symbol["location"]["range"]["start"]["character"] + end_col = symbol["location"]["range"]["end"]["character"] return SymbolBody(self._lines, start_line, start_col, end_line, end_col) @@ -508,7 +508,7 @@ class SolidLanguageServer(ABC): # For now, we assume that all language server implementations have the same signature of the constructor # (which, unfortunately, differs from the signature of the base class). # If this assumption is ever violated, we need branching logic here. - ls = ls_class(config, repository_root_path, solidlsp_settings) # type: ignore + ls = ls_class(config, repository_root_path, solidlsp_settings) ls.set_request_timeout(timeout) return ls @@ -590,7 +590,7 @@ class SolidLanguageServer(ABC): log.debug(f"LSP: {source} -> {target}: {msg!s}") else: - logging_fn = None # type: ignore + logging_fn = None # create the LanguageServerHandler, which provides the functionality to start the language server and communicate with it, # preparing the launch command beforehand @@ -1024,8 +1024,8 @@ class SolidLanguageServer(ABC): if self._supports_pull_diagnostics(): try: response = self.server.send.text_document_diagnostic( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.URI: uri, } } @@ -1380,8 +1380,8 @@ class SolidLanguageServer(ABC): new_contents, new_l, new_c = TextUtils.insert_text_at_position(file_buffer.contents, line, column, text_to_be_inserted) file_buffer.contents = new_contents self.server.notify.did_change_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.VERSION: file_buffer.version, LSPConstants.URI: file_buffer.uri, }, @@ -1423,8 +1423,8 @@ class SolidLanguageServer(ABC): ) file_buffer.contents = new_contents self.server.notify.did_change_text_document( - { - LSPConstants.TEXT_DOCUMENT: { # type: ignore + { # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict + LSPConstants.TEXT_DOCUMENT: { LSPConstants.VERSION: file_buffer.version, LSPConstants.URI: file_buffer.uri, }, @@ -1766,15 +1766,15 @@ class SolidLanguageServer(ABC): response = self.server.send.completion(completion_params) if isinstance(response, list): response = {"items": response, "isIncomplete": False} - if response is None or not response["isIncomplete"]: # type: ignore + if response is None or not response["isIncomplete"]: break # TODO: Understand how to appropriately handle `isIncomplete` - if response is None or (response["isIncomplete"] and not allow_incomplete): # type: ignore + if response is None or (response["isIncomplete"] and not allow_incomplete): return [] if "items" in response: - response = response["items"] # type: ignore + response = response["items"] response = cast(list[LSPTypes.CompletionItem], response) @@ -1792,8 +1792,8 @@ class SolidLanguageServer(ABC): if "label" in item: completion_item["completionText"] = item["label"] - completion_item["kind"] = item["kind"] # type: ignore - elif "insertText" in item: # type: ignore + completion_item["kind"] = item["kind"] + elif "insertText" in item: completion_item["completionText"] = item["insertText"] completion_item["kind"] = item["kind"] elif "textEdit" in item and "newText" in item["textEdit"]: @@ -1977,9 +1977,9 @@ class SolidLanguageServer(ABC): item["location"] = tree_location location = item["location"] if "absolutePath" not in location: - location["absolutePath"] = absolute_path # type: ignore + location["absolutePath"] = absolute_path if "relativePath" not in location: - location["relativePath"] = relative_file_path # type: ignore + location["relativePath"] = relative_file_path item["body"] = self.create_symbol_body(item, factory=body_factory) @@ -2021,7 +2021,7 @@ class SolidLanguageServer(ABC): if "children" in usymbol: usymbol["children"] = convert_symbols_with_common_parent(usymbol["children"], usymbol) # type: ignore else: - usymbol["children"] = [] # type: ignore + usymbol["children"] = [] unified_symbols.append(usymbol) return unified_symbols @@ -2080,7 +2080,7 @@ class SolidLanguageServer(ABC): return [] # Create package symbol for directory - package_symbol = ls_types.UnifiedSymbolInformation( # type: ignore + package_symbol = ls_types.UnifiedSymbolInformation( name=os.path.basename(abs_dir_path), kind=ls_types.SymbolKind.Package, location=ls_types.Location( @@ -2128,7 +2128,7 @@ class SolidLanguageServer(ABC): # Create file symbol, link with children file_range = self._get_range_from_file_content(file_data.contents) - file_symbol = ls_types.UnifiedSymbolInformation( # type: ignore + file_symbol = ls_types.UnifiedSymbolInformation( name=os.path.splitext(contained_dir_or_file_name)[0], kind=ls_types.SymbolKind.File, range=file_range, @@ -2980,7 +2980,7 @@ class SolidLanguageServer(ABC): migrated_cache[new_cache_key] = (file_hash, root_symbols) num_symbols_migrated += len(all_symbols) log.info("Migrated %d document symbols from legacy cache", num_symbols_migrated) - self._raw_document_symbols_cache = migrated_cache # type: ignore + self._raw_document_symbols_cache = migrated_cache self._raw_document_symbols_cache_is_modified = True self._save_raw_document_symbols_cache() legacy_cache_file.unlink() diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index b58f056c..6be2fc80 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -571,7 +571,7 @@ class Language(str, Enum): return CSharpLanguageServer case self.CSHARP_OMNISHARP: - from solidlsp.language_servers.omnisharp import OmniSharp # type: ignore[attr-defined] + from solidlsp.language_servers.omnisharp import OmniSharp return OmniSharp case self.TYPESCRIPT: @@ -679,7 +679,7 @@ class Language(str, Enum): return ZigLanguageServer case self.NIX: - from solidlsp.language_servers.nixd_ls import NixLanguageServer # type: ignore + from solidlsp.language_servers.nixd_ls import NixLanguageServer return NixLanguageServer case self.LUA: diff --git a/src/solidlsp/ls_process.py b/src/solidlsp/ls_process.py index 820947af..9fc84a8e 100644 --- a/src/solidlsp/ls_process.py +++ b/src/solidlsp/ls_process.py @@ -10,7 +10,7 @@ from abc import ABC, abstractmethod from collections.abc import Callable from dataclasses import dataclass from queue import Empty, Queue -from typing import IO, Any, AnyStr +from typing import IO, Any, AnyStr, cast from sensai.util.string import ToStringMixin @@ -462,24 +462,30 @@ class StdioLanguageServer(LanguageServerInterface): log.info("Starting language server process via command: %s", self.process_launch_info.cmd) kwargs = subprocess_util.subprocess_kwargs() kwargs["start_new_session"] = self.start_independent_lsp_process - self.process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE, - env=child_proc_env, - cwd=self.process_launch_info.cwd, - shell=True, - **kwargs, + # the language server is launched with binary (bytes) pipes; the cast is needed because the + # presence of platform-specific **kwargs prevents ty from selecting the bytes Popen overload + process = cast( + "subprocess.Popen[bytes]", + subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + env=child_proc_env, + cwd=self.process_launch_info.cwd, + shell=True, + **kwargs, + ), ) + self.process = process # Check if process terminated immediately - if self.process.returncode is not None: + if process.returncode is not None: log.error("Language server has already terminated/could not be started") # Process has already terminated - stderr_data = self.process.stderr.read() if self.process.stderr else b"" + stderr_data = process.stderr.read() if process.stderr else b"" error_message = stderr_data.decode("utf-8", errors="replace") - raise RuntimeError(f"Process terminated immediately with code {self.process.returncode}. Error: {error_message}") + raise RuntimeError(f"Process terminated immediately with code {process.returncode}. Error: {error_message}") # start threads to read stdout and stderr of the process threading.Thread( @@ -522,7 +528,7 @@ class StdioLanguageServer(LanguageServerInterface): except Exception: pass - def _read_bytes_from_process(self, process, stream, num_bytes) -> bytes: # type: ignore + def _read_bytes_from_process(self, process, stream, num_bytes) -> bytes: """Read exactly num_bytes from process stdout""" data = b"" while len(data) < num_bytes: diff --git a/src/solidlsp/ls_utils.py b/src/solidlsp/ls_utils.py index eb0456ac..0f47a3e7 100644 --- a/src/solidlsp/ls_utils.py +++ b/src/solidlsp/ls_utils.py @@ -527,7 +527,7 @@ class PlatformUtils: _anonymous_ = ("u",) sys_info = SYSTEM_INFO() - ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sys_info)) # type: ignore + ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sys_info)) arch_map = { 9: "AMD64", diff --git a/src/solidlsp/util/subprocess_util.py b/src/solidlsp/util/subprocess_util.py index 579b856e..bf994e45 100644 --- a/src/solidlsp/util/subprocess_util.py +++ b/src/solidlsp/util/subprocess_util.py @@ -15,7 +15,7 @@ def subprocess_kwargs() -> dict: """ kwargs = {} if platform.system() == "Windows": - kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW # type: ignore + kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW return kwargs diff --git a/src/solidlsp/util/zip.py b/src/solidlsp/util/zip.py index bf014923..d93d9421 100644 --- a/src/solidlsp/util/zip.py +++ b/src/solidlsp/util/zip.py @@ -115,7 +115,7 @@ class SafeZipExtractor: """ if sys.platform.startswith("win"): return Path(rf"\\?\{os.path.abspath(path)}") - return path # type: ignore + return path # Example usage: diff --git a/test/serena/test_hooks.py b/test/serena/test_hooks.py index e762c130..d7287678 100644 --- a/test/serena/test_hooks.py +++ b/test/serena/test_hooks.py @@ -464,22 +464,22 @@ class TestToolUseCounter: counter = ToolUseCounter(n_recent_grep_uses=2, n_recent_read_file_uses=1) hook_stub = type("HookStub", (), {"session_persistence_dir": str(tmp_path)})() - counter.save(hook_stub) # type: ignore[arg-type] - loaded = ToolUseCounter.load(hook_stub) # type: ignore[arg-type] + counter.save(hook_stub) + loaded = ToolUseCounter.load(hook_stub) assert loaded.n_recent_grep_uses == 2 assert loaded.n_recent_read_file_uses == 1 def test_load_returns_fresh_counter_on_missing_file(self, tmp_path: Path): hook_stub = type("HookStub", (), {"session_persistence_dir": str(tmp_path / "nonexistent")})() - loaded = ToolUseCounter.load(hook_stub) # type: ignore[arg-type] + loaded = ToolUseCounter.load(hook_stub) assert loaded == ToolUseCounter() def test_load_returns_fresh_counter_on_corrupt_file(self, tmp_path: Path): hook_stub = type("HookStub", (), {"session_persistence_dir": str(tmp_path)})() path = tmp_path / ToolUseCounter._FILE_NAME path.write_bytes(b"not a pickle") - loaded = ToolUseCounter.load(hook_stub) # type: ignore[arg-type] + loaded = ToolUseCounter.load(hook_stub) assert loaded == ToolUseCounter() def test_is_hook_active_respects_min_interval(self): diff --git a/test/serena/test_mcp.py b/test/serena/test_mcp.py index 58508ba6..731a1cc6 100644 --- a/test/serena/test_mcp.py +++ b/test/serena/test_mcp.py @@ -25,7 +25,7 @@ class BaseMockTool(Tool): """A mock Tool class for testing.""" def __init__(self): - super().__init__(MockAgent()) # type: ignore + super().__init__(MockAgent()) class BasicTool(BaseMockTool): diff --git a/test/serena/test_serena_agent.py b/test/serena/test_serena_agent.py index 7625e50a..d93722ba 100644 --- a/test/serena/test_serena_agent.py +++ b/test/serena/test_serena_agent.py @@ -46,7 +46,7 @@ class BaseCase: id: str def to_pytest_param(self, *marks: MarkDecorator | Mark) -> ParameterSet: - return pytest.param(self.language, self, marks=[*get_pytest_markers(self.language), *marks], id=self.id) # type: ignore + return pytest.param(self.language, self, marks=[*get_pytest_markers(self.language), *marks], id=self.id) @dataclass @@ -1290,7 +1290,7 @@ class TestPromptProvision: @classmethod def _call_tool(cls, agent: SerenaAgent, tool_class: type[Tool], session_id: str = "global", **kwargs) -> str: - result = agent.get_tool(tool_class).apply_ex(mcp_ctx=cls.MockContext(session_id), **kwargs) # type: ignore + result = agent.get_tool(tool_class).apply_ex(mcp_ctx=cls.MockContext(session_id), **kwargs) return result @staticmethod diff --git a/test/serena/test_symbol.py b/test/serena/test_symbol.py index 08188f49..db315270 100644 --- a/test/serena/test_symbol.py +++ b/test/serena/test_symbol.py @@ -251,10 +251,10 @@ class TestSymbolDictTypes: :param key_type: the corresponding key type (Literal[...]) that the dict should have for keys """ dict_type_keys = dict_type.__annotations__.keys() - assert len(dict_type_keys) == len( - key_type.__args__ # type: ignore - ), f"Expected {len(key_type.__args__)} keys in {dict_type}, but got {len(dict_type_keys)}" # type: ignore - for expected_key in key_type.__args__: # type: ignore + assert len(dict_type_keys) == len(key_type.__args__), ( + f"Expected {len(key_type.__args__)} keys in {dict_type}, but got {len(dict_type_keys)}" + ) + for expected_key in key_type.__args__: assert expected_key in dict_type_keys, f"Expected key '{expected_key}' not found in {dict_type}" def test_ls_symbol_dict_type(self): diff --git a/test/solidlsp/angular/test_angular_error_cases.py b/test/solidlsp/angular/test_angular_error_cases.py index 52b51592..f65e7d09 100644 --- a/test/solidlsp/angular/test_angular_error_cases.py +++ b/test/solidlsp/angular/test_angular_error_cases.py @@ -265,7 +265,7 @@ class TestAngularCoreProbe: from solidlsp.language_servers.angular_language_server import AngularLanguageServer stub = type("Stub", (), {"repository_root_path": repo_path})() - return AngularLanguageServer._find_angular_core_install(stub) # type: ignore[arg-type] + return AngularLanguageServer._find_angular_core_install(stub) def test_finds_core_at_project_root(self, tmp_path) -> None: core_pkg = tmp_path / "node_modules" / "@angular" / "core" / "package.json" diff --git a/test/solidlsp/scala/test_scala_language_server.py b/test/solidlsp/scala/test_scala_language_server.py index 930010fb..aa94eb32 100644 --- a/test/solidlsp/scala/test_scala_language_server.py +++ b/test/solidlsp/scala/test_scala_language_server.py @@ -1,4 +1,3 @@ -# type: ignore import os import pytest diff --git a/uv.lock b/uv.lock index 27c9ae97..3595e84a 100644 --- a/uv.lock +++ b/uv.lock @@ -1485,47 +1485,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/f2/66bd65ca0139675a0d7b18f0bada6e12b51a984e41a76dbe44761bf1b3ee/mslex-1.3.0-py3-none-any.whl", hash = "sha256:c7074b347201b3466fc077c5692fbce9b5f62a63a51f537a53fbbd02eff2eea4", size = 7820, upload-time = "2024-10-16T13:16:17.566Z" }, ] -[[package]] -name = "mypy" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, - { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, - { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, - { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, - { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, - { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, - { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, - { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - [[package]] name = "myst-nb" version = "1.4.0" @@ -2890,7 +2849,6 @@ agno = [ dev = [ { name = "jinja2" }, { name = "jupyter-book" }, - { name = "mypy" }, { name = "poethepoet" }, { name = "pyasn1" }, { name = "pyinstrument" }, @@ -2906,6 +2864,7 @@ dev = [ { name = "syrupy" }, { name = "toml-sort" }, { name = "tornado" }, + { name = "ty" }, { name = "types-pyyaml" }, { name = "types-requests" }, { name = "wheel" }, @@ -2931,7 +2890,6 @@ requires-dist = [ { name = "jupyter-book", marker = "extra == 'dev'", specifier = "==1.0.4.post1" }, { name = "lsprotocol", specifier = "==2025.0.0" }, { name = "mcp", specifier = "==1.27.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = "==1.17.0" }, { name = "oslex", specifier = "==2.0.0" }, { name = "overrides", specifier = "==7.7.0" }, { name = "pathspec", specifier = "==0.12.1" }, @@ -2967,6 +2925,7 @@ requires-dist = [ { name = "toml-sort", marker = "extra == 'dev'", specifier = "==0.24.2" }, { name = "tornado", marker = "extra == 'dev'", specifier = "==6.5.7" }, { name = "tqdm", specifier = "==4.67.1" }, + { name = "ty", marker = "extra == 'dev'", specifier = "==0.0.24" }, { name = "types-pyyaml", specifier = "==6.0.12.20250516" }, { name = "types-pyyaml", marker = "extra == 'dev'", specifier = "==6.0.12.20250516" }, { name = "types-requests", marker = "extra == 'dev'", specifier = "==2.32.4.20250809" }, @@ -3593,6 +3552,30 @@ wheels = [ { 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" }, ] +[[package]] +name = "ty" +version = "0.0.24" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/96/652a425030f95dc2c9548d9019e52502e17079e1daeefbc4036f1c0905b4/ty-0.0.24.tar.gz", hash = "sha256:9fe42f6b98207bdaef51f71487d6d087f2cb02555ee3939884d779b2b3cc8bfc", size = 5354286, upload-time = "2026-03-19T16:55:57.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/e5/34457ee11708e734ba81ad65723af83030e484f961e281d57d1eecf08951/ty-0.0.24-py3-none-linux_armv6l.whl", hash = "sha256:1ab4f1f61334d533a3fdf5d9772b51b1300ac5da4f3cdb0be9657a3ccb2ce3e7", size = 10394877, upload-time = "2026-03-19T16:55:54.246Z" }, + { url = "https://files.pythonhosted.org/packages/44/81/bc9a1b1a87f43db15ab64ad781a4f999734ec3b470ad042624fa875b20e6/ty-0.0.24-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:facbf2c4aaa6985229e08f8f9bf152215eb078212f22b5c2411f35386688ab42", size = 10211109, upload-time = "2026-03-19T16:55:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/e4/63/cfc805adeaa61d63ba3ea71127efa7d97c40ba36d97ee7bd957341d05107/ty-0.0.24-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b6d2a3b6d4470c483552a31e9b368c86f154dcc964bccb5406159dc9cd362246", size = 9694769, upload-time = "2026-03-19T16:55:34.309Z" }, + { url = "https://files.pythonhosted.org/packages/33/09/edc220726b6ec44a58900401f6b27140997ef15026b791e26b69a6e69eb5/ty-0.0.24-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c94c25d0500939fd5f8f16ce41cbed5b20528702c1d649bf80300253813f0a2", size = 10176287, upload-time = "2026-03-19T16:55:37.17Z" }, + { url = "https://files.pythonhosted.org/packages/f8/bf/cbe2227be711e65017655d8ee4d050f4c92b113fb4dc4c3bd6a19d3a86d8/ty-0.0.24-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:89cbe7bc7df0fab02dbd8cda79b737df83f1ef7fb573b08c0ee043dc68cffb08", size = 10214832, upload-time = "2026-03-19T16:56:08.518Z" }, + { url = "https://files.pythonhosted.org/packages/af/1d/d15803ee47e9143d10e10bd81ccc14761d08758082bda402950685f0ddfe/ty-0.0.24-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2c5d269bcc9b764850c99f457b5018a79b3ef40ecfbc03344e65effd6cf743", size = 10709892, upload-time = "2026-03-19T16:56:05.727Z" }, + { url = "https://files.pythonhosted.org/packages/36/12/6db0d86c477147f67b9052de209421d76c3e855197b000c25fcbbe86b3a2/ty-0.0.24-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba44512db5b97c3bbd59d93e11296e8548d0c9a3bdd1280de36d7ff22d351896", size = 11280872, upload-time = "2026-03-19T16:56:02.899Z" }, + { url = "https://files.pythonhosted.org/packages/1b/fc/155fe83a97c06d33ccc9e0f428258b32df2e08a428300c715d34757f0111/ty-0.0.24-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a52b7f589c3205512a9c50ba5b2b1e8c0698b72e51b8b9285c90420c06f1cae8", size = 11060520, upload-time = "2026-03-19T16:55:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f1/32c05a1c4c3c2a95c5b7361dee03a9bf1231d4ad096b161c838b45bce5a0/ty-0.0.24-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7981df5c709c054da4ac5d7c93f8feb8f45e69e829e4461df4d5f0988fe67d04", size = 10791455, upload-time = "2026-03-19T16:55:25.728Z" }, + { url = "https://files.pythonhosted.org/packages/17/2c/53c1ea6bedfa4d4ab64d4de262d8f5e405ecbffefd364459c628c0310d33/ty-0.0.24-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2860151ad95a00d0f0280b8fef79900d08dcd63276b57e6e5774f2c055979c5", size = 10156708, upload-time = "2026-03-19T16:55:45.563Z" }, + { url = "https://files.pythonhosted.org/packages/45/39/7d2919cf194707169474d80720a5f3d793e983416f25e7ffcf80504c9df2/ty-0.0.24-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5674a1146d927ab77ff198a88e0c4505134ced342a0e7d1beb4a076a728b7496", size = 10236263, upload-time = "2026-03-19T16:55:31.474Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7f/48eac722f2fd12a5b7aae0effdcb75c46053f94b783d989e3ef0d7380082/ty-0.0.24-py3-none-musllinux_1_2_i686.whl", hash = "sha256:438ecbf1608a9b16dd84502f3f1b23ef2ef32bbd0ab3e0ca5a82f0e0d1cd41ea", size = 10402559, upload-time = "2026-03-19T16:55:39.602Z" }, + { url = "https://files.pythonhosted.org/packages/75/e0/8cf868b9749ce1e5166462759545964e95b02353243594062b927d8bff2a/ty-0.0.24-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ddeed3098dd92a83964e7aa7b41e509ba3530eb539fc4cd8322ff64a09daf1f5", size = 10893684, upload-time = "2026-03-19T16:55:51.439Z" }, + { url = "https://files.pythonhosted.org/packages/17/9f/f54bf3be01d2c2ed731d10a5afa3324dc66f987a6ae0a4a6cbfa2323d080/ty-0.0.24-py3-none-win32.whl", hash = "sha256:83013fb3a4764a8f8bcc6ca11ff8bdfd8c5f719fc249241cb2b8916e80778eb1", size = 9781542, upload-time = "2026-03-19T16:56:11.588Z" }, + { url = "https://files.pythonhosted.org/packages/fb/49/c004c5cc258b10b3a145666e9a9c28ae7678bc958c8926e8078d5d769081/ty-0.0.24-py3-none-win_amd64.whl", hash = "sha256:748a60eb6912d1cf27aaab105ffadb6f4d2e458a3fcadfbd3cf26db0d8062eeb", size = 10764801, upload-time = "2026-03-19T16:55:42.752Z" }, + { url = "https://files.pythonhosted.org/packages/e2/59/006a074e185bfccf5e4c026015245ab4fcd2362b13a8d24cf37a277909a9/ty-0.0.24-py3-none-win_arm64.whl", hash = "sha256:280a3d31e86d0721947238f17030c33f0911cae851d108ea9f4e3ab12a5ed01f", size = 10194093, upload-time = "2026-03-19T16:55:48.303Z" }, +] + [[package]] name = "typer" version = "0.24.1"