fix(types): fix ty python env resolution + triage discord adapter

The .venv directory (created by uv run) contained Python 3.13 with no
deps installed. ty auto-discovers .venv for module resolution, so it
could not find discord.py, aiohttp, etc., producing ~1000 false
"Module has no member" errors.

Removing the stray .venv makes ty fall back to the nix env (Python 3.12
with all deps installed). Also set python-version = "3.12" in
[tool.ty.environment] with a comment explaining why.

ty diagnostics: 4,422 -> 3,385 (-1,037)
Tests: 496 passed, 0 failed

fix(nix): use python311 in dev shell (matches requires-python floor)

Production venv still uses python312 (nixos-unstable default). The dev
editable venv now uses python311 — the requires-python floor (>=3.11) —
so ty type checking and local dev catch 3.12+-only syntax that wouldnt
This commit is contained in:
ethernet 2026-07-17 15:11:31 -04:00
parent 0cb42c10a4
commit 7acd6c902c
4 changed files with 86 additions and 19 deletions

View file

@ -9,6 +9,7 @@
stdenv,
makeWrapper,
callPackage,
python311,
python312,
nodejs_22,
electron,
@ -37,15 +38,28 @@
}:
let
nodejs = nodejs_22;
mkHermesVenv =
extraDependencyGroups:
{
extraDependencyGroups,
python ? null,
}:
callPackage ./python.nix {
inherit uv2nix pyproject-nix pyproject-build-systems;
inherit
uv2nix
pyproject-nix
pyproject-build-systems
python
;
pythonSrc = hermesNpmLib.pythonSrc;
dependency-groups = [ "all" ] ++ extraDependencyGroups;
};
hermesVenv = (mkHermesVenv extraDependencyGroups).venv;
hermesVenv =
(mkHermesVenv {
inherit extraDependencyGroups;
python = python312;
}).venv;
hermesNpmLib = callPackage ./lib.nix {
inherit npm-lockfile-fix nodejs;
@ -61,8 +75,7 @@ let
bundledSkills = lib.cleanSourceWith {
src = ../skills;
filter =
path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path);
filter = path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path);
};
# Optional skills are NOT in the wheel (pythonSrc excludes them, see
@ -70,8 +83,7 @@ let
# same mechanism Homebrew packaging uses.
bundledOptionalSkills = lib.cleanSourceWith {
src = ../optional-skills;
filter =
path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path);
filter = path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path);
};
# Import bundled plugins (memory, context_engine, platforms/*). Keeping
@ -224,8 +236,13 @@ stdenv.mkDerivation (finalAttrs: {
'';
passthru =
python:
let
devPython = (mkHermesVenv (extraDependencyGroups ++ [ "dev" ])).editableVenv;
devPython =
(mkHermesVenv ({
extraDependencyGroups = extraDependencyGroups ++ [ "dev" ];
python = python311;
})).editableVenv;
in
{
inherit

View file

@ -1,6 +1,6 @@
# nix/python.nix — uv2nix virtual environment builder
{
python312,
python,
lib,
callPackage,
uv2nix,
@ -65,30 +65,30 @@ let
final: _prev:
if isAarch64Darwin then
{
numpy = mkPrebuiltOverride final python312.pkgs.numpy { };
numpy = mkPrebuiltOverride final python.pkgs.numpy { };
pyarrow = mkPrebuiltOverride final python312.pkgs.pyarrow { };
pyarrow = mkPrebuiltOverride final python.pkgs.pyarrow { };
av = mkPrebuiltOverride final python312.pkgs.av { };
av = mkPrebuiltOverride final python.pkgs.av { };
humanfriendly = mkPrebuiltOverride final python312.pkgs.humanfriendly { };
humanfriendly = mkPrebuiltOverride final python.pkgs.humanfriendly { };
coloredlogs = mkPrebuiltOverride final python312.pkgs.coloredlogs {
coloredlogs = mkPrebuiltOverride final python.pkgs.coloredlogs {
humanfriendly = [ ];
};
onnxruntime = mkPrebuiltOverride final python312.pkgs.onnxruntime {
onnxruntime = mkPrebuiltOverride final python.pkgs.onnxruntime {
coloredlogs = [ ];
numpy = [ ];
packaging = [ ];
};
ctranslate2 = mkPrebuiltOverride final python312.pkgs.ctranslate2 {
ctranslate2 = mkPrebuiltOverride final python.pkgs.ctranslate2 {
numpy = [ ];
pyyaml = [ ];
};
faster-whisper = mkPrebuiltOverride final python312.pkgs.faster-whisper {
faster-whisper = mkPrebuiltOverride final python.pkgs.faster-whisper {
av = [ ];
ctranslate2 = [ ];
huggingface-hub = [ ];
@ -102,7 +102,7 @@ let
pythonSet =
(callPackage pyproject-nix.build.packages {
python = python312;
python = python;
}).overrideScope
(
lib.composeManyExtensions [

View file

@ -261,3 +261,52 @@ duck-typed access.
all 190 are ty being unable to track either cross-module attribute init
(186) or duck-typed hasattr-guarded access (4). no logic bugs found.
### remaining 6 unresolved-attribute (post class-level annotation fix)
1. **line 2162** (`object.function` ×2): duck-typed access to OpenAI SDK
`ChatCompletionMessageToolCall` objects. `hasattr` guard makes it safe.
ty can't infer the list element type through `hasattr`. not a bug.
2. **lines 3586-3587** (`iteration_budget.used` / `.max_total`): `iteration_budget`
is `Optional[IterationBudget]` but `init_agent` always sets it to a non-None
value (`iteration_budget or IterationBudget(max_iterations)`). the annotation
says `| None` but the code guarantees non-None at runtime. not a bug — but
the annotation could be tightened to just `IterationBudget` (without Optional).
3. **lines 4677 + 4799** (`_anthropic_client.close()`): `_anthropic_client` is
`Any | None` — could be None if the anthropic path was never taken. but both
call sites are wrapped in `try/except Exception: pass`, so even if it IS None,
the AttributeError is caught. safe.
---
## plugins/platforms/discord/adapter.py — unresolved-attribute analysis (269 errors)
### root cause: ty resolving the wrong Python environment
the `.venv` directory (created by `uv run` earlier) contained Python 3.13 with
NO dependencies installed. ty auto-discovers `.venv` and uses it for module
resolution — so it couldn't find `discord.py`, `aiohttp`, etc., producing 254
`Module 'discord' has no member X` errors.
the actual dependencies are installed in the nix develop environment
(Python 3.12, all deps in site-packages). removing the empty `.venv` made ty
fall back to the system/nix python, resolving all 254 import errors instantly.
**fix:** `rm -rf .venv` (it was a stray artifact, not a real venv).
also set `python-version = "3.12"` in `[tool.ty.environment]` to match the
nix env, with a comment explaining why.
**impact:** 4,422 → 3,385 diagnostics (1,037) from this single fix.
### remaining 122 discord errors (post-fix)
after ty could resolve discord.py:
- 17 `not defined on None` — `self._client` (Optional[commands.Bot]) accessed
before ty can prove it's non-None. all are in event handlers called after
the bot is ready. not bugs, but could benefit from `assert self._client is not None`
guards for type narrowing.
- ~100 remaining are real type-checking errors from ty now being able to
see discord.py's actual types (mismatched args, wrong attribute access, etc.)
these need individual triage.

View file

@ -366,7 +366,8 @@ markers = [
addopts = "-m 'not integration'"
[tool.ty.environment]
python-version = "3.13"
# Match requires-python floor (>=3.11).
python-version = "3.11"
[tool.ty.rules]
unknown-argument = "warn"