Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-pyi-relative-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@teslemetry/tesla-protocol": patch
---

Fix cross-file imports left unrewritten in generated `.pyi` stubs (`car_server_pb2.pyi`, `universal_message_pb2.pyi`, `vcsec_pb2.pyi`, `vehicle_pb2.pyi`, and three `energy_device` stubs). protoc's pyi generator aliases same-package imports differently than its `.py` generator (`_foo_pb2` vs `foo__pb2`), so protoletariat's import rewriter - which matches whole import statements including the alias - left the pyi side as a bare top-level `import foo_pb2 as _foo_pb2` instead of the package-relative form the `.py` sibling already got. Static type checkers (e.g. pyright) can't resolve those imports, breaking type information for any field typed through them. `scripts/generate.sh` now runs a small `scripts/fix_pyi_imports.py` pass after `protol` to patch the remaining bare pyi imports.
10 changes: 5 additions & 5 deletions packages/python/tesla_protocol/command/car_server_pb2.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import datetime
import common_pb2 as _common_pb2
from . import common_pb2 as _common_pb2
from google.protobuf import timestamp_pb2 as _timestamp_pb2
import keys_pb2 as _keys_pb2
import signatures_pb2 as _signatures_pb2
import vcsec_pb2 as _vcsec_pb2
import vehicle_pb2 as _vehicle_pb2
from . import keys_pb2 as _keys_pb2
from . import signatures_pb2 as _signatures_pb2
from . import vcsec_pb2 as _vcsec_pb2
from . import vehicle_pb2 as _vehicle_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import signatures_pb2 as _signatures_pb2
from . import signatures_pb2 as _signatures_pb2
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
Expand Down
4 changes: 2 additions & 2 deletions packages/python/tesla_protocol/command/vcsec_pb2.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import keys_pb2 as _keys_pb2
import errors_pb2 as _errors_pb2
from . import keys_pb2 as _keys_pb2
from . import errors_pb2 as _errors_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
Expand Down
6 changes: 3 additions & 3 deletions packages/python/tesla_protocol/command/vehicle_pb2.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import datetime
from google.protobuf import timestamp_pb2 as _timestamp_pb2
import vcsec_pb2 as _vcsec_pb2
import common_pb2 as _common_pb2
import managed_charging_pb2 as _managed_charging_pb2
from . import vcsec_pb2 as _vcsec_pb2
from . import common_pb2 as _common_pb2
from . import managed_charging_pb2 as _managed_charging_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
from google.protobuf import timestamp_pb2 as _timestamp_pb2
import authorization_types_pb2 as _authorization_types_pb2
from . import authorization_types_pb2 as _authorization_types_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import networking_pb2 as _networking_pb2
import device_pb2 as _device_pb2
import update_pb2 as _update_pb2
import error_pb2 as _error_pb2
from . import networking_pb2 as _networking_pb2
from . import device_pb2 as _device_pb2
from . import update_pb2 as _update_pb2
from . import error_pb2 as _error_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import authorization_types_pb2 as _authorization_types_pb2
import authorization_api_pb2 as _authorization_api_pb2
import common_api_pb2 as _common_api_pb2
import teg_api_pb2 as _teg_api_pb2
from . import authorization_types_pb2 as _authorization_types_pb2
from . import authorization_api_pb2 as _authorization_api_pb2
from . import common_api_pb2 as _common_api_pb2
from . import teg_api_pb2 as _teg_api_pb2
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
Expand Down
46 changes: 46 additions & 0 deletions scripts/fix_pyi_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""Rewrite bare cross-file imports left in protoc-generated .pyi stubs.

protoc's built-in pyi generator emits `import foo_pb2 as _foo_pb2` for a
same-package dependency, using a different alias convention than the regular
.py output (`from . import foo_pb2 as foo__pb2`). protoletariat's import
rewriter matches whole import statements including the alias, so it silently
leaves the pyi import unrewritten - the .py sibling ends up package-relative
while the .pyi stays a bare top-level import that doesn't resolve, breaking
static type checkers (e.g. pyright) for every downstream consumer.

Usage: fix_pyi_imports.py <generated-package-dir>
"""

from __future__ import annotations

import re
import sys
from pathlib import Path

_BARE_IMPORT = re.compile(r"^import (\w+_pb2) as (\w+)$", re.MULTILINE)


def fix_file(path: Path, sibling_names: set[str]) -> None:
text = path.read_text()

def repl(match: re.Match[str]) -> str:
module, alias = match.group(1), match.group(2)
if module not in sibling_names:
return match.group(0)
return f"from . import {module} as {alias}"

new_text = _BARE_IMPORT.sub(repl, text)
if new_text != text:
path.write_text(new_text)


def main(directory: str) -> None:
root = Path(directory)
sibling_names = {p.stem for p in root.glob("*_pb2.py")}
for pyi_file in root.glob("*_pb2.pyi"):
fix_file(pyi_file, sibling_names)


if __name__ == "__main__":
main(sys.argv[1])
6 changes: 6 additions & 0 deletions scripts/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ for group in $GROUPS_LIST; do
--exclude-imports-glob 'google/rpc/*' \
protoc --protoc-path="$PROTOC" \
--proto-path="proto/$group" proto/"$group"/*.proto

# protol's rewriter matches whole import statements including the alias,
# and protoc's pyi generator aliases same-package imports differently than
# its .py generator, so protol leaves those .pyi imports bare. Patch them
# up separately so the checked-in stubs are import-resolvable too.
"$VENV/bin/python" scripts/fix_pyi_imports.py "$PY_PKG/$group"
done

echo "generate: OK"
Loading