Type the tree so mypy can enforce it - #206
Merged
Merged
Conversation
The mypy hook ran in an isolated venv with only types-requests and types-geoip2, so everything else became Any — including packages that already ship types. It also pinned 1.19.1 while requirements.dev.txt installed a different mypy. Run mypy from the project environment, as pylint already does, over the whole tree: a whole-program checker on one file at a time is order-dependent. types-geoip2 goes away; geoip2 ships py.typed. Results now depend on the developer's installed deps, as pylint's already do. CI installs both requirements files before pre-commit, so the merge gate still sees everything. mypy's version floats with requirements.dev.txt, so a release can break lint with no change on our side. Co-authored-by: Cursor <cursoragent@cursor.com>
GeoIPProtocol declared no members, so city() and the Reader constructor were unchecked, and `type[GeoIPProtocol] | None` made construction a type error. Declare __init__(filename) and city() — plus the City/Location shapes they return, the contract the docstring already claimed — and drop | None at the only call site. Co-authored-by: Cursor <cursoragent@cursor.com>
main() stored the argparse Namespace in args, then reused that name for the remainder of an input line. The same loop rebound msg from the raw line to the dict send_message expects. action_args for the split remainder, raw for the unparsed line, msg only for the dict. Co-authored-by: Cursor <cursoragent@cursor.com>
_connect() keeps the last OSError from getaddrinfo results, then raises it. An empty list leaves that value None and the raise is TypeError. POSIX is not specified to return empty, but mypy flags it. Raise OSError so the failure stays the type the connect loop already produces. Co-authored-by: Cursor <cursoragent@cursor.com>
time.monotonic() returns float; initializing the same kind of value with 0 made mypy infer int. 0.0 is the same sentinel and still falsy for the "never failed" check. Co-authored-by: Cursor <cursoragent@cursor.com>
stats, rsyslog_client, kafka_producer, websocket, runner, and the systest rsyslogd Popen start as None. mypy infers Any | None, and Any does not narrow under `if not x`. Annotate T | None and re-check `is None` after init. The second check is also runtime: _init_* can still return None once running is False; returning False then matches the existing send-failed path instead of AttributeError. Skip the main-loop stats metric if handle_new_config has not run; _wait_until_running says so if start() was skipped. comms_channel_round rebound `task` after asyncio.wait; pending_task is a fresh name so consumer_task can stay None. Co-authored-by: Cursor <cursoragent@cursor.com>
Empty and mixed literals inferred types that rejected later operations (converters as object, the Cloud Logging body as Collection[str], empty collections with no element type). Websocket connect() cannot take those mixed kwargs as a splat; pass the known arguments by name and splat only the extras. The Cloud Logging body is dict[str, Any]: a JSON payload for a third-party API, not a structure we own. Co-authored-by: Cursor <cursoragent@cursor.com>
_generate_request_builder typed log entries as dict[str, str]; callers pass nested jsonPayload, and sometimes timestamp and severity. Empty in_queue, pending, senders, and the senders-less reader config need an element type so later appends and JournalReader construction typecheck. Co-authored-by: Cursor <cursoragent@cursor.com>
mypy skips those bodies by default, which is one reason
reader.unregister_from_poll(self.poller) survived in configure_readers
(the other is that readers was an untyped {}). check_untyped_defs
checks them anyway.
Aiven-Open#202
Co-authored-by: Cursor <cursoragent@cursor.com>
disallow_untyped_defs and disallow_incomplete_defs would fail 293 times on this tree. A global True plus per-module overrides keeps new files checked and existing modules exempt until they are annotated. Co-authored-by: Cursor <cursoragent@cursor.com>
_float_hex_to_int returns None when |f| >= 1. encode() already rejects |lat| >= 90 and wraps lon into [-180, 180), so that None is unreachable from encode; the assertions exist only for mypy. Co-authored-by: Cursor <cursoragent@cursor.com>
requests is imported inside get_requests_session on purpose (it and its dependencies are heavy), so Session is imported under TYPE_CHECKING. default_json_serialization now returns None explicitly — same value, required once the return type is written. Logplex-Msg-Count goes out as a decimal string; requests types headers as str | bytes | None. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Empty hostname now becomes NILVALUE, matching the other optional fields. PID is stringified before formatting because journal fields may hand it as int. The unit-test socket stub only implements sendall/close, so assigning it to socket | None is ignored there. Co-authored-by: Cursor <cursoragent@cursor.com>
send_messages returns bool: True accepted, False retry. The base implementation returns False instead of implicit None, which the run loop already treated as failure. Tests install stub classes that are not LogSender subclasses, so the registry is type[Any]. rsyslog_server is required, same as file_output and logplex_token; config.get made it Optional once config was dict[str, Any]. Co-authored-by: Cursor <cursoragent@cursor.com>
Logplex now returns True after a successful post. It previously fell off the end with None, so the run loop treated a completed send as a retry. inspect.getmembers does not prove the Kafka error classes are exception types, so that helper returns tuple[Any, ...]. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
load_cert_chain and the SOCKS connect call do not accept None. When SSL is on, ca/keyfile/certfile must be set; a proxied URI must include host and port. Those were TypeError/AttributeError; they are ValueError now. Co-authored-by: Cursor <cursoragent@cursor.com>
…gLevel convert_realtime takes Any because the journal hands timestamps as bytes or int. Tests used an integer cursor stand-in, now "10". Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
stats may still be None when a message is truncated; skip the metric rather than call increase on None. Co-authored-by: Cursor <cursoragent@cursor.com>
gauge/increase are skipped when stats is None rather than called on None. ip_to_geohash returns "" when geoip or lat/lon is missing. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Tests that wrote config through pytest's legacy tmpdir now use tmp_path. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Tests that wrote config through tmpdir now use tmp_path. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Tests that wrote config through pytest's legacy tmpdir now use tmp_path. Co-authored-by: Cursor <cursoragent@cursor.com>
journalpump_initialized returns a bool; a live pump with senders was already truthy via running-and-senders. Every module now requires complete annotations. Co-authored-by: Cursor <cursoragent@cursor.com>
google-auth's from_service_account_info has no annotations, so the service-account path needs an ignore. google.auth.default returns (credentials, project); the ADC branch passed that tuple to the logging client. Co-authored-by: Cursor <cursoragent@cursor.com>
The re2/stdlib re switch is not a redefinition under mypy 2.3.1, so the no-redef ignores on those imports did nothing. Co-authored-by: Cursor <cursoragent@cursor.com>
Popen env values are strings; LOGS_DIRECTORY and RUNTIME_DIRECTORY were Paths. Co-authored-by: Cursor <cursoragent@cursor.com>
An unpinned bump can fail the lint job with no change on our side. These are the versions CI and this tree already run. Co-authored-by: Cursor <cursoragent@cursor.com>
pre-commit appended staged .py paths to `make pylint`. Make treated those as extra goals and only succeeded because the files already existed. The recipe always lints PYLINT_DIRS. Co-authored-by: Cursor <cursoragent@cursor.com>
A blanket ignore_missing_imports hides a dependency that starts shipping types. systemd, kafka, botocore, googleapiclient, snappy, re2, zstandard, httplib2, and setuptools are the ones still without stubs or a py.typed marker. Co-authored-by: Cursor <cursoragent@cursor.com>
Left unpinned; we do not pin setuptools itself. Co-authored-by: Cursor <cursoragent@cursor.com>
Left unpinned; we do not pin httplib2. Co-authored-by: Cursor <cursoragent@cursor.com>
Left unpinned; we do not pin botocore. Co-authored-by: Cursor <cursoragent@cursor.com>
The stubs describe snappy.compress; callers used the internal snappy.snappy module. Left unpinned; we do not pin python-snappy. Co-authored-by: Cursor <cursoragent@cursor.com>
entries().write expects a WriteLogEntriesRequest TypedDict. Left unpinned; we do not pin google-api-python-client. Co-authored-by: Cursor <cursoragent@cursor.com>
Pinned to 2.3.x to match kafka-python<2.4.0. The producer stub has no _COMPRESSORS; getattr keeps the zstd probe. FutureRecordMetadata.get is untyped on the stub. Co-authored-by: Cursor <cursoragent@cursor.com>
pytest tmp_path is already a Path; converting to str at every JournalPump call site existed only to satisfy ServiceDaemon's string assert. Convert argv and the JSON state-file setting at those boundaries and keep Path from there on. Co-authored-by: Cursor <cursoragent@cursor.com>
mbertheau
force-pushed
the
mbertheau-typing
branch
2 times, most recently
from
August 22, 2026 09:27
e9febb3 to
fd4fcc2
Compare
mbertheau
marked this pull request as ready for review
August 22, 2026 09:42
willyborankin
approved these changes
Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Based on #203.
Annotate the package, tests, and scripts, then turn on
disallow_untyped_defs,disallow_untyped_calls,warn_unused_ignores, anddisallow_any_generics. Stub packages cover typed third-party libraries;re2,systemd, andzstandardstay ignored because they ship no types.Made with Cursor