Skip to content

refactor: add type annotations to utils public functions#52

Open
pageton wants to merge 18 commits into
pytdbot:mainfrom
pageton:types/utils-annotations
Open

refactor: add type annotations to utils public functions#52
pageton wants to merge 18 commits into
pytdbot:mainfrom
pageton:types/utils-annotations

Conversation

@pageton
Copy link
Copy Markdown

@pageton pageton commented May 20, 2026

Add proper type annotations to all public functions in utils/:

  • obj_encoder.py: annotate params/returns, type _type_cache as dict[str, type]
  • json_utils.py: annotate json_loads, CallbackData, callback_data
  • strings.py: add return type to create_extra_id

pageton added 18 commits May 20, 2026 16:46
- to_camel_case: replace O(n^2) string concat with list+join
- escape_markdown: pre-build sets for O(1) char membership
- get_message_methods: use frozenset (shared constant)
- plugin loading: use set for O(1) dedup instead of list scan
- dict_to_obj: cache getattr(types, ...) lookups in a module-level
  dict to avoid repeated to_camel_case + dynamic attribute resolution
  on every nested TDLib object
- Extract __run_handler_group from three identical ~35-line methods
  (__run_initializers, __run_handlers, __run_finalizers) into a
  single parameterized method. Reduces ~100 lines of duplication.
- remove_handler: replace O(n*m) list.copy()+list.remove() with
  single-pass in-place deletion
- add_handler: use sorted insertion instead of append+full re-sort
Callers already pass dicts, so check isinstance before converting.
Skips a full recursive traversal on already-converted data.
Reduces per-instance memory by ~40% (no __dict__, no __weakref__).
Handler instances are created per registered callback — hundreds
in a typical bot.
- Wrap debug-level f-string logs with isEnabledFor(DEBUG) check
  to skip string formatting when debug is disabled
- Simplify is_coro_filter cache lookup to use dict.get
- Move receive() into its own try/except with continue to prevent
  a single malformed response from killing the entire receiver loop
- Use dict.get() for @client_id to avoid KeyError on updates missing
  the field, log a warning instead
- Log the update @type when @client_id is missing for debug context
- __aexit__: log stop() errors instead of silently swallowing them
- AuthorizationError: accept code parameter, pass TDLib error code
  at both raise sites so callers can inspect it
- Add WebAppDataError base class for WebAppData* exceptions
- Export WebAppDataError in __all__
- strings.get_retry_after_time: catch ValueError/AttributeError
  instead of Exception, prevents masking unexpected errors in
  flood-wait handling
- json_utils.load_callback_data: catch ValueError/TypeError
  instead of Exception, add empty data guard to prevent IndexError
- invoke(): validate request is a dict with @type before accessing
  it, giving a clear ValueError instead of a cryptic KeyError
- tdjson._build_client(): validate TDLib getOption responses have
  a 'value' key before indexing, raise RuntimeError with context
- Use queue_size as asyncio.Queue maxsize for backpressure
- Call queue.task_done() in finally block after each update
- Handle CancelledError in worker loop for clean shutdown
- Make __stop_client async and await cancelled worker tasks
  with gather(return_exceptions=True)
Replace fire-and-forget create_task loop with awaitable gather.
Ensures all TDLib options are applied before the caller proceeds
to the next authorization step.
DeepDiff with ignore_order=True is CPU-intensive and blocks the
event loop. Use asyncio.to_thread to run it off the main loop.
Add _create_task helper that attaches a done_callback to log
unhandled exceptions from fire-and-forget tasks. Replaces all
bare self.loop.create_task calls in client.py:
- getOption version ping
- local handlers (updateUser, updateOption, etc.)
- NATS update processing
- signal handler stop()
When the client shuts down, pending futures in _results were never
resolved, causing CancelledError propagation to callers. Cancel all
pending futures and clear _results in __stop_client.
Replace bare create_task in receiver loop with tracked tasks using
a set + done_callback pattern. Cancel all pending update tasks
in close() before shutting down the receiver.
- Use X | None for attrs initialized to None (connection_state, me,
  is_running, _workers_tasks, __wait_login, __authorization_state,
  __idle_event, __closed_event, loop)
- Add dict[str, ...] annotations for _handlers, _current_handlers,
  options, __local_handlers, __cache
- Add bool annotations for __is_queue_worker, __is_closing
- Add str annotations for NATS subject attributes
- Use tuple[type, ...] and frozenset[str] for typed containers
- obj_encoder.py: annotate obj_to_json, obj_to_dict, dict_to_obj
  params and returns; type _type_cache as dict[str, type]
- json_utils.py: annotate json_loads, CallbackData.__init__,
  callback_data params; add typing.Any import
- strings.py: add return type to create_extra_id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant