X Tutup
Skip to content

Reapply "ast.NodeVisitor for import tracking"#7241

Merged
youknowone merged 5 commits intoRustPython:mainfrom
ShaharNaveh:deps-nodevisitor
Feb 28, 2026
Merged

Reapply "ast.NodeVisitor for import tracking"#7241
youknowone merged 5 commits intoRustPython:mainfrom
ShaharNaveh:deps-nodevisitor

Conversation

@ShaharNaveh
Copy link
Contributor

@ShaharNaveh ShaharNaveh commented Feb 27, 2026

This reverts commit a47572c.

Lets see what breaks

Summary by CodeRabbit

  • Bug Fixes

    • Corrected test name parsing to handle tests that begin with underscores.
  • Refactor

    • Replaced fragile text-based import parsing with a structural parser for more reliable import detection.
    • Improved detection of test dependencies, including dynamic runtime patterns.
    • Simplified TODO counting and dependency extraction for clearer processing.
  • Tests

    • Added a new IO test snippet covering readinto/isatty behavior on open and closed streams.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0d938d and e9703d5.

📒 Files selected for processing (1)
  • extra_tests/snippets/stdlib_io.py

📝 Walkthrough

Walkthrough

Refactors import parsing in scripts/update_lib/deps.py from regex to an AST-driven ImportVisitor, adds dynamic test-dependency detection via visit_Call, introduces a TODO_MARKER constant and simplifies todo counting, and fixes library-name extraction in scripts/update_lib/cmd_todo.py to handle test names beginning with _test.

Changes

Cohort / File(s) Summary
Test Name Extraction Fix
scripts/update_lib/cmd_todo.py
Adjusts compute_test_todo_list to strip an additional leading _test after removing test_ when deriving a library name for test identifiers not present in DEPENDENCIES.
AST-based Import Parsing
scripts/update_lib/deps.py
Replaces regex parsing with a public ImportVisitor class using AST traversal; adds test_imports/lib_imports properties and public parse_test_imports/parse_lib_imports functions; implements import collection in visit_Import, visit_ImportFrom, and detects dynamic test deps in visit_Call.
TODO Marker & Counting
scripts/update_lib/deps.py
Introduces TODO_MARKER constant and replaces explicit loops with an expression-based count_todo_in_path over Python files; migrates RustPython TODO counting to use the new marker.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • moreal
  • ShaharNaveh

Poem

🐰 I nibble code and chase each tree,

AST leaves tumble soft for me,
I strip the sneaky _test away,
I count the TODOs in one small sway,
Hoppity—dependencies at play!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and specifically references the main change: reapplying AST-based import tracking via NodeVisitor, which is confirmed by the raw summary showing replacement of regex-based import parsing with AST-based ImportVisitor in deps.py.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@youknowone youknowone changed the title Reapply "ast.NodeVisitor for import tracking (#7229)" (#7230) Reapply "ast.NodeVisitor for import tracking" Feb 27, 2026
@ShaharNaveh
Copy link
Contributor Author

New output:

  • [no deps] struct (20 dependents) | 2026-01-21
    • test_struct (7 TODO)
  • [no deps] stat (14 dependents) | 2026-02-14
    • test_stat
  • [no deps] keyword (7 dependents) | 2024-06-21
    • test_keyword
  • [no deps] reprlib (7 dependents) | 2026-01-21
    • test_reprlib (2 TODO)
  • [no deps] __future__ (6 dependents) | 2024-04-23
    • test_future_stmt (8 TODO)
  • [no deps] annotationlib (6 dependents) | 2026-02-24
    • test_annotationlib
  • [no deps] token (5 dependents) | 2026-01-22
  • [no deps] bisect (5 dependents) | 2023-12-24
    • test_bisect
  • [no deps] tty (2 dependents) | 2026-01-05
    • test_tty (2 TODO)
  • [no deps] stringprep (1 dependents) | 2019-12-21
    • test_stringprep
  • [no deps] __hello__ | 2023-02-25
  • [no deps] __phello__ | 2023-02-25
  • [no deps] colorsys | 2025-04-21
    • test_colorsys
  • [no deps] this | 2019-06-22
  • [0/4 deps] re (59 dependents) | 2026-01-20
    • test_re (14 TODO)
  • [0/2 deps] types (40 dependents) | 2026-02-18
    • test_types (10 TODO)
  • [0/9 deps] collections (37 dependents) | 2026-02-14 Δ61
    • test_collections (3 TODO)
    • test_deque (3 TODO)
    • test_defaultdict (1 TODO)
    • test_ordered_dict (8 TODO)
  • [0/9 deps] argparse (29 dependents) | 2026-02-07
    • test_argparse
  • [0/4 deps] socket (17 dependents) | 2026-02-01
    • test_socket (16 TODO)
  • [0/2 deps] enum (16 dependents) | 2026-02-15
    • test_enum (4 TODO)
  • [0/4 deps] weakref (16 dependents) | 2026-01-30
    • test_weakref (20 TODO)
    • test_weakset
  • [0/2 deps] abc (15 dependents) | 2025-06-29
    • test_abc (1 TODO)
  • [0/3 deps] copy (14 dependents) | 2026-02-10
    • test_copy
  • [0/1 deps] operator (14 dependents) | 2026-01-23
    • test_operator
  • [0/2 deps] getopt (10 dependents) | 2026-02-08
    • test_getopt
  • [0/3 deps] base64 (10 dependents) | 2026-01-19
    • test_base64
  • [0/3 deps] _colorize (9 dependents) | 2026-01-17
    • test__colorize
  • [0/9 deps] pickle (9 dependents) | 2026-02-05 Δ8
    • test_pickle (21 TODO)
    • test_picklebuffer (12 TODO)
    • test_pickletools (8 TODO)
  • [0/4 deps] posixpath (9 dependents) | 2026-02-13
    • test_posixpath
  • [0/1 deps] textwrap (8 dependents) | 2026-01-30
    • test_textwrap
  • [0/4 deps] fnmatch (8 dependents) | 2026-02-13
    • test_fnmatch
  • [0/2 deps] codecs (8 dependents) | 2026-01-31
    • test_codecs (12 TODO)
    • test_codeccallbacks (9 TODO)
    • test_codecencodings_cn (4 TODO)
    • test_codecencodings_hk (1 TODO)
    • test_codecencodings_iso2022 (5 TODO)
    • test_codecencodings_jp (7 TODO)
    • test_codecencodings_kr (3 TODO)
    • test_codecencodings_tw (1 TODO)
    • test_codecmaps_cn (3 TODO)
    • test_codecmaps_hk (1 TODO)
    • test_codecmaps_jp (6 TODO)
    • test_codecmaps_kr (3 TODO)
    • test_codecmaps_tw (3 TODO)
    • test_charmapcodec
    • test_multibytecodec (untracked)
  • [0/12 deps] pathlib (8 dependents) | 2026-02-13
    • test_pathlib
  • [0/6 deps] locale (7 dependents) | 2025-08-20 Δ11
    • test_locale
    • test__locale
  • [0/8 deps] random (7 dependents) | 2024-11-11 Δ149
    • test_random
  • [0/2 deps] copyreg (6 dependents) | 2026-01-24
    • test_copyreg
  • [0/1 deps] signal (6 dependents) | 2024-04-26
    • test_signal (1 TODO)
  • [0/9 deps] dataclasses (6 dependents) | 2026-02-08
    • test_dataclasses (10 TODO)
  • [0/2 deps] bz2 (6 dependents) | 2026-01-19
    • test_bz2 (1 TODO)
  • [0/1 deps] heapq (5 dependents) | 2026-01-18
    • test_heapq
  • [0/4 deps] json (5 dependents) | 2026-01-18 Δ14
    • test_json (14 TODO)
  • [0/8 deps] glob (4 dependents) | 2026-02-13
    • test_glob
  • [0/6 deps] socketserver (4 dependents) | 2026-01-03
    • test_socketserver (1 TODO)
  • [0/3 deps] code (4 dependents) | 2026-02-01
    • test_code (11 TODO)
    • test_code_module (3 TODO)
  • [0/2 deps] hmac (4 dependents) | 2026-02-15
    • test_hmac (14 TODO)
  • [0/2 deps] lzma (4 dependents) | 2026-01-19
    • test_lzma (49 TODO)
  • [0/6 deps] gzip (4 dependents) | 2026-01-19
    • test_gzip
  • [0/2 deps] codeop (3 dependents) | 2026-02-07
    • test_codeop (4 TODO)
  • [0/1 deps] numbers (3 dependents) | 2025-09-11
    • test_abstract_numbers
  • [0/1 deps] contextvars (3 dependents) | 2026-01-18
  • [0/3 deps] ntpath (3 dependents) | 2026-01-25
    • test_ntpath
  • [0/4 deps] rlcompleter (3 dependents) | 2025-07-17
    • test_rlcompleter (1 TODO)
  • [0/6 deps] compression (3 dependents) | 2026-01-19
  • [0/17 deps] zipfile (3 dependents) | 2026-02-24
    • test_zipfile (15 TODO)
    • test_zipfile64
  • [0/2 deps] genericpath (2 dependents) | 2025-08-04
    • test_genericpath
  • [0/1 deps] _ios_support (2 dependents) | 2025-03-10
  • [0/2 deps] html (2 dependents) | 2026-02-15
    • test_html
    • test_htmlparser
  • [0/3 deps] optparse (2 dependents) | 2026-01-30
    • test_optparse
  • [0/10 deps] bdb (2 dependents) | 2025-12-30 Δ271
    • test_bdb (9 TODO)
  • [0/1 deps] cmd (2 dependents) | 2026-01-24
    • test_cmd
  • [0/2 deps] quopri (2 dependents) | 2026-01-24
    • test_quopri
  • [0/2 deps] site (2 dependents) | 2026-01-17 Δ29
    • test_site (4 TODO)
  • [0/7 deps] plistlib (2 dependents) | 2026-01-04 Δ2
    • test_plistlib (6 TODO)
  • [0/4 deps] mimetypes (2 dependents) | 2026-01-21
    • test_mimetypes
  • [0/6 deps] pstats (2 dependents)
    • test_pstats (untracked)
  • [0/4 deps] py_compile (2 dependents) | 2026-01-30
    • test_py_compile (3 TODO)
  • [0/7 deps] encodings (2 dependents) | 2026-02-06
  • [0/2 deps] ipaddress (1 dependents) | 2026-01-18
    • test_ipaddress
  • [0/3 deps] csv (1 dependents) | 2026-02-18
    • test_csv (27 TODO)
  • [0/2 deps] netrc (1 dependents) | 2025-08-20
    • test_netrc
  • [0/4 deps] filecmp (1 dependents) | 2026-01-03
    • test_filecmp
  • [0/5 deps] fractions (1 dependents) | 2026-01-30
    • test_fractions (2 TODO)
  • [0/1 deps] opcode (1 dependents) | 2026-02-27 Δ382
    • test__opcode (2 TODO)
    • test_opcodes
  • [0/2 deps] pyclbr (1 dependents) | 2025-07-25
    • test_pyclbr (2 TODO)
  • [0/7 deps] dbm (1 dependents) | 2026-01-01
    • test_dbm (2 TODO)
    • test_dbm_dumb
    • test_dbm_gnu (untracked)
    • test_dbm_ndbm (untracked)
    • test_dbm_sqlite3
  • [0/4 deps] getpass (1 dependents) | 2026-02-08
    • test_getpass
  • [0/5 deps] sqlite3 (1 dependents) | 2026-01-19
    • test_sqlite3 (82 TODO)
  • [0/13 deps] tarfile (1 dependents) | 2025-05-07 Δ527
    • test_tarfile (3 TODO)
  • [0/2 deps] zipimport (1 dependents) | 2026-01-24
    • test_zipimport (2 TODO)
    • test_zipimport_support (untracked)
  • [0/1 deps] graphlib | 2026-01-18
    • test_graphlib
  • [0/1 deps] _apple_support | 2025-03-10
  • [0/7 deps] fileinput | 2025-04-30
    • test_fileinput
  • [0/2 deps] nturl2path | 2026-01-25
    • test_nturl2path
  • [0/7 deps] zipapp | 2025-08-06 Δ14
    • test_zipapp
  • [0/2 deps] _android_support | 2025-10-22 Δ7
  • [0/3 deps] symtable | 2026-01-30
    • test_symtable (17 TODO)
  • [0/2 deps] pty | 2026-01-05 Δ51
    • test_pty (4 TODO)
  • [0/4 deps] modulefinder
    • test_modulefinder (untracked)
  • [0/5 deps] timeit | 2026-02-01
    • test_timeit
  • [0/7 deps] tomllib | 2026-02-08
    • test_tomllib
  • [0/1 deps] curses
    • test_curses (untracked)
  • [0/13 deps] xmlrpc | 2026-02-14
    • test_xmlrpc (5 TODO)
    • test_docxmlrpc
  • [1/8 deps] os (85 dependents) | 2026-02-03
    • test_os (2 TODO)
    • test_popen
  • [1/8 deps] warnings (58 dependents) | 2026-01-18
    • test_warnings (12 TODO)
  • [1/7 deps] io (56 dependents) | 2026-02-13
    • test_io (21 TODO)
    • test_bufio
    • test_fileio (1 TODO)
    • test_memoryio (28 TODO)
  • [1/10 deps] functools (37 dependents) | 2026-01-20
    • test_functools (9 TODO)
  • [1/6 deps] contextlib (25 dependents) | 2025-08-01
    • test_contextlib (2 TODO)
    • test_contextlib_async (2 TODO)
  • [1/8 deps] threading (21 dependents) | 2026-01-18
    • test_threading (18 TODO)
    • test_threadedtempfile
    • test_threading_local (3 TODO)
  • [1/11 deps] traceback (20 dependents) | 2026-02-06
    • test_traceback (34 TODO)
  • [1/9 deps] subprocess (15 dependents) | 2026-02-04 Δ2
    • test_subprocess (4 TODO)
  • [1/2 deps] linecache (12 dependents) | 2026-02-09
    • test_linecache
  • [1/7 deps] tokenize (11 dependents) | 2022-08-09 Δ357
    • test_tokenize (2 TODO)
  • [1/8 deps] datetime (11 dependents) | 2026-02-24
    • test_datetime
    • test_strptime (untracked)
  • [1/8 deps] tempfile (10 dependents) | 2026-01-04 Δ26
    • test_tempfile (1 TODO)
  • [1/11 deps] typing (10 dependents) | 2026-02-11
    • test_typing (4 TODO)
    • test_type_aliases
    • test_type_annotations (1 TODO)
    • test_type_params (6 TODO)
    • test_genericalias
  • [1/7 deps] ssl (8 dependents) | 2025-10-28 Δ4
    • test_ssl (21 TODO)
  • [1/7 deps] ast (7 dependents) | 2026-02-02
    • test_ast (54 TODO)
    • test_unparse
    • test_type_comments (15 TODO)
  • [1/6 deps] calendar (7 dependents) | 2026-01-17
    • test_calendar
  • [1/2 deps] selectors (6 dependents) | 2025-07-20
    • test_selectors
  • [1/1 deps] hashlib (6 dependents) | 2026-02-14
    • test_hashlib (7 TODO)
  • [1/2 deps] string (5 dependents) | 2026-01-17
    • test_string
    • test_userstring
  • [1/4 deps] runpy (5 dependents) | 2025-09-11
    • test_runpy (2 TODO)
  • [1/4 deps] difflib (4 dependents) | 2026-02-08
    • test_difflib
  • [1/5 deps] pprint (4 dependents) | 2026-02-06
    • test_pprint
  • [1/9 deps] webbrowser (4 dependents) | 2025-04-21 Δ49
    • test_webbrowser
  • [1/4 deps] queue (4 dependents) | 2026-02-04
    • test_queue
  • [1/2 deps] shlex (4 dependents) | 2026-02-02
    • test_shlex (4 TODO)
  • [1/7 deps] pkgutil (4 dependents) | 2026-01-04 Δ57
    • test_pkgutil (1 TODO)
  • [1/7 deps] gettext (3 dependents) | 2026-01-30
    • test_gettext (7 TODO)
  • [1/6 deps] configparser (2 dependents) | 2026-02-07
    • test_configparser
  • [1/3 deps] secrets (1 dependents) | 2025-07-17
    • test_secrets
  • [1/4 deps] ftplib (1 dependents) | 2026-02-02
    • test_ftplib (4 TODO)
  • [1/3 deps] tabnanny (1 dependents) | 2026-02-02
    • test_tabnanny (5 TODO)
  • [1/4 deps] tracemalloc (1 dependents)
    • test_tracemalloc (untracked)
  • [1/5 deps] profile (1 dependents)
    • test_profile (untracked)
    • test_cprofile (untracked)
  • [1/8 deps] smtplib (1 dependents) | 2025-12-19 Δ11
    • test_smtplib
    • test_smtpnet
  • [1/2 deps] antigravity | 2023-02-28
  • [1/6 deps] pickletools | 2026-02-06
  • [1/3 deps] shelve | 2026-01-01
    • test_shelve
  • [1/4 deps] poplib
    • test_poplib (untracked)
  • [1/3 deps] sched | 2025-04-21
    • test_sched
  • [1/4 deps] wave | 2026-02-13
    • test_wave
  • [1/9 deps] compileall | 2026-02-25
    • test_compileall (2 TODO)
  • [1/9 deps] mailbox | 2026-02-14
    • test_mailbox
  • [1/10 deps] xml | 2025-08-21 Δ35
    • test_xml_etree (55 TODO)
    • test_xml_etree_c
    • test_minidom (untracked)
    • test_pulldom (4 TODO)
    • test_pyexpat (28 TODO)
    • test_sax (untracked)
    • test_xml_dom_minicompat
    • test_xml_dom_xmlbuilder
  • [2/18 deps] inspect (19 dependents) | 2026-02-07
    • test_inspect (46 TODO)
  • [2/10 deps] shutil (13 dependents) | 2026-02-15
    • test_shutil
  • [2/5 deps] dis (5 dependents) | 2026-01-26
    • test_dis (42 TODO)
  • [2/6 deps] decimal (3 dependents) | 2026-01-30
    • test_decimal (1 TODO)
  • [2/17 deps] email (3 dependents) | 2026-01-17 Δ238
    • test_email (12 TODO)
  • [2/9 deps] tkinter (2 dependents) | 2025-04-06 Δ279
    • test_tkinter (untracked)
    • test_ttk (untracked)
    • test_ttk_textonly (untracked)
    • test_tcl (untracked)
    • test_idle (untracked)
  • [2/8 deps] statistics (1 dependents) | 2026-02-11
    • test_statistics (1 TODO)
  • [2/6 deps] cProfile
  • [2/10 deps] wsgiref | 2026-01-03 Δ7
    • test_wsgiref (1 TODO)
  • [3/12 deps] sysconfig (6 dependents) | 2025-10-22 Δ265
    • test_sysconfig (8 TODO)
    • test__osx_support
  • [3/9 deps] ctypes (6 dependents) | 2026-02-18
    • test_ctypes (11 TODO)
    • test_stable_abi_ctypes
  • [3/12 deps] platform (5 dependents) | 2026-01-04 Δ124
    • test_platform
  • [3/14 deps] doctest (1 dependents) | 2026-02-11
    • test_doctest (9 TODO)
  • [3/10 deps] uuid (1 dependents) | 2026-02-02
    • test_uuid
  • [3/12 deps] trace (1 dependents) | 2025-07-25 Δ15
    • test_trace (14 TODO)
  • [3/9 deps] ensurepip | 2026-01-17
    • test_ensurepip
  • [3/8 deps] venv | 2026-01-17 Δ29
    • test_venv (4 TODO)
  • [3/13 deps] zoneinfo | 2025-09-07 Δ24
    • test_zoneinfo (3 TODO)
  • [3/12 deps] imaplib | 2026-02-01
    • test_imaplib (1 TODO)
  • [3/12 deps] turtle
    • test_turtle (untracked)
  • [3/24 deps] http | 2026-02-27
    • test_httplib (2 TODO)
    • test_http_cookiejar
    • test_http_cookies
    • test_httpservers (1 TODO)
  • [4/21 deps] logging (7 dependents) | 2025-07-20 Δ77
    • test_logging (5 TODO)
  • [4/23 deps] unittest (2 dependents) | 2026-01-18 Δ102
    • test_unittest (15 TODO)
  • [4/20 deps] urllib (1 dependents) | 2026-02-13
    • test_urllib
    • test_urllib2
    • test_urllib2_localnet
    • test_urllib2net
    • test_urllibnet
    • test_urlparse
    • test_urllib_response
    • test_robotparser
  • [4/11 deps] concurrent | 2026-01-09 Δ1012
    • test_concurrent_futures (18 TODO)
    • test_interpreters (untracked)
    • test__interpreters (untracked)
    • test__interpchannels (untracked)
    • test_crossinterp (untracked)
  • [5/23 deps] importlib (7 dependents) | 2026-02-05
    • test_importlib (16 TODO)
  • [5/35 deps] pdb (1 dependents) | 2026-02-11 Δ2601
    • test_pdb (untracked)
  • [6/20 deps] pydoc (4 dependents) | 2026-02-13
    • test_pydoc (36 TODO)
  • [6/29 deps] multiprocessing (2 dependents) | 2026-02-04 Δ314
    • test_multiprocessing_fork (35 TODO)
    • test_multiprocessing_forkserver (10 TODO)
    • test_multiprocessing_spawn (13 TODO)
    • test_multiprocessing_main_handling
  • [7/32 deps] asyncio (2 dependents) | 2026-02-02 Δ26
    • test_asyncio (38 TODO)
  • [8/35 deps] _pyrepl | 2025-04-11 Δ2534
  • [11/43 deps] idlelib

Standalone Tests

  • test_atexit
    • _test_atexit
  • test_eintr
    • _test_eintr (6 TODO)
  • _test_embed_structseq (untracked)
  • _test_gc_fast_cycles (untracked)
  • _test_monitoring_shutdown (untracked)
  • _test_multiprocessing (15 TODO)
  • _test_venv_multiprocessing
  • test___all__
  • test_android
  • test_apple
  • test_array (55 TODO)
  • test_asdl_parser (untracked)
  • test_asyncgen (4 TODO)
  • test_audit
  • test_augassign
  • test_exceptions (25 TODO)
    • test_baseexception
    • test_except_star (1 TODO)
    • test_exception_group (3 TODO)
    • test_exception_hierarchy (2 TODO)
    • test_exception_variations
  • test_bigaddrspace
  • test_bigmem (4 TODO)
  • test_binascii (1 TODO)
  • test_binop
  • test_bool
  • test_buffer (11 TODO)
  • test_build_details (untracked)
  • test_builtin (25 TODO)
  • test_bytes (17 TODO)
  • test_c_locale_coercion
  • test_call (1 TODO)
  • test_capi (untracked)
  • test_cext (untracked)
  • test_class (15 TODO)
    • test_genericclass
    • test_subclassinit
  • test_clinic (untracked)
  • test_cmath
  • test_cmd_line (24 TODO)
  • test_cmd_line_script (15 TODO)
  • test_compare
  • test_compile (31 TODO)
    • test_compiler_assemble
    • test_compiler_codegen
    • test_peepholer (31 TODO)
  • test_complex (2 TODO)
  • test_contains (1 TODO)
  • test_context (6 TODO)
  • test_coroutines (19 TODO)
  • test_cppext (untracked)
  • test_decorators (1 TODO)
  • test_descr (47 TODO)
    • test_descrtut (3 TODO)
  • test_devpoll
  • test_dict (6 TODO)
    • test_dictcomps (1 TODO)
    • test_dictviews (2 TODO)
    • test_userdict
  • test_dtrace (8 TODO)
  • test_dynamic (1 TODO)
  • test_dynamicclassattribute
  • test_embed (untracked)
  • test_enumerate
  • test_eof (6 TODO)
  • test_epoll
  • test_errno
  • test_extcall (8 TODO)
  • test_external_inspection (untracked)
  • test_faulthandler (4 TODO)
  • test_fcntl
    • test_ioctl
  • test_file
    • test_largefile
  • test_file_eintr (1 TODO)
  • test_fileutils (untracked)
  • test_finalization (untracked)
  • test_float (6 TODO)
    • test_strtod (6 TODO)
  • test_flufl (4 TODO)
  • test_fork1 (1 TODO)
  • test_format (5 TODO)
  • test_frame (untracked)
  • test_free_threading (untracked)
  • test_frozen
  • test_str (16 TODO)
    • test_fstring (19 TODO)
    • test_string_literals (5 TODO)
  • test_funcattrs (6 TODO)
  • test_gc
  • test_gdb (untracked)
  • test_generated_cases (untracked)
  • test_generators (12 TODO)
    • test_genexps (untracked)
    • test_generator_stop (untracked)
    • test_yield_from (6 TODO)
  • test_getpath (untracked)
  • test_global (3 TODO)
  • test_grammar (18 TODO)
  • test_grp
  • test_hash (4 TODO)
  • test_import (3 TODO)
  • test_index
  • test_int (7 TODO)
    • test_long (4 TODO)
    • test_int_literal
  • test_isinstance
  • test_iter (1 TODO)
  • test_iterlen
  • test_itertools (6 TODO)
  • test_keywordonlyarg
  • test_kqueue
  • test_launcher
  • test_list (5 TODO)
    • test_listcomps (1 TODO)
    • test_userlist (1 TODO)
  • test_lltrace (untracked)
  • test_longexp
  • test_marshal (21 TODO)
  • test_math
    • test_math_property
  • test_memoryview (9 TODO)
  • test_metaclass (10 TODO)
  • test_mmap (2 TODO)
  • test_module (4 TODO)
  • test_monitoring (13 TODO)
  • test_msvcrt
  • test_named_expressions (12 TODO)
  • test_numeric_tower
  • test_opcache
  • test_openpty
  • test_optimizer (untracked)
  • test_osx_env
  • test_patma (20 TODO)
  • test_peg_generator (untracked)
  • test_pep646_syntax (12 TODO)
  • test_perf_profiler (untracked)
  • test_perfmaps (untracked)
  • test_pkg
  • test_select (3 TODO)
    • test_poll (1 TODO)
  • test_positional_only_arg (4 TODO)
  • test_posix (4 TODO)
  • test_pow
  • test_print (6 TODO)
  • test_property
  • test_pwd
  • test_pyrepl (untracked)
    • test_repl
  • test_raise
  • test_range (1 TODO)
  • test_readline (untracked)
  • test_regrtest (10 TODO)
  • test_remote_pdb (untracked)
  • test_resource (2 TODO)
  • test_richcmp
  • test_scope (1 TODO)
  • test_support (3 TODO)
    • test_script_helper
  • test_set (5 TODO)
  • test_setcomps
  • test_slice (1 TODO)
  • test_sort (2 TODO)
  • test_source_encoding (untracked)
  • test_startfile (untracked)
  • test_time
    • test_strftime
  • test_structseq
  • test_sundry
  • test_super (4 TODO)
  • test_syntax (26 TODO)
  • test_sys (8 TODO)
    • test_syslog (2 TODO)
    • test_sys_setprofile (14 TODO)
    • test_sys_settrace (52 TODO)
  • test_termios (15 TODO)
  • test_thread
    • test_thread_local_bytecode (untracked)
    • test_threadsignals
  • test_timeout
  • test_tools
  • test_tstring (4 TODO)
  • test_tuple (1 TODO)
  • test_type_cache (untracked)
  • test_typechecks
  • test_unicodedata (9 TODO)
    • test_unicode_file
    • test_unicode_file_functions
    • test_unicode_identifiers (1 TODO)
    • test_ucn (3 TODO)
  • test_unary
  • test_univnewlines
  • test_unpack (1 TODO)
    • test_unpack_ex (11 TODO)
  • test_utf8_mode (6 TODO)
  • test_utf8source (2 TODO)
  • test_wait3
  • test_wait4
  • test_winapi
  • test_winconsoleio
  • test_winreg
  • test_winsound
  • test_with (1 TODO)
  • test_wmi
  • test_xpickle (untracked)
  • test_xxlimited (untracked)
  • test_xxtestfuzz (untracked)
  • test_zlib (2 TODO)
  • test_zstd

Untracked Files

  • site-packages/README.txt

Original Files

  • PSF-LICENSE
  • README.md
  • _dummy_os.py
  • _dummy_thread.py
  • _pycodecs.py
  • dummy_threading.py

@ShaharNaveh ShaharNaveh marked this pull request as ready for review February 27, 2026 12:39
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
scripts/update_lib/deps.py (4)

53-60: Remove commented-out code.

Line 56 contains a commented-out expression # module.split(".", 1)[0] that appears to be a debug artifact or deferred decision.

♻️ Clean up
     `@property`
     def lib_imports(self) -> frozenset[str]:
         return frozenset(
-            # module.split(".", 1)[0]
             module
             for module in self.__imports
             if not module.startswith("test.")
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 53 - 60, Remove the leftover
commented-out expression from the lib_imports property: delete the line
containing "# module.split(\".\", 1)[0]" inside the lib_imports getter so the
generator comprehension only yields module from self.__imports when not starting
with "test."; ensure no other logic is altered in the lib_imports property or
references to __imports.

110-112: Potential IndexError if support.findfile() is called without arguments.

Line 110 accesses node.args[0] without checking if args is non-empty. While unlikely in practice (a call to findfile() without arguments would be a bug in the test code), defensive coding would prevent a crash in the import parser.

🛡️ Proposed defensive check
         if (value.id != "support") or (func.attr != "findfile"):
             return
 
+        if not node.args:
+            return
+
         arg = node.args[0]
         if not isinstance(arg, ast.Constant):
             return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 110 - 112, The code accesses
node.args[0] without ensuring node.args is non-empty which can raise IndexError
when parsing a call like support.findfile() with no arguments; update the logic
around the node handling (where node.args is read) to first check that node.args
is truthy/has length > 0 before accessing node.args[0], and early-return (as
done for the ast.Constant check) if there are no args so support.findfile()
calls without args are safely ignored.

122-139: Return type annotation mismatch.

parse_test_imports and parse_lib_imports are annotated to return set[str], but they return visitor.test_imports and visitor.lib_imports which are frozenset[str]. This works at runtime (frozenset is compatible), but the type hints are inaccurate.

♻️ Fix type annotations
-def parse_test_imports(content: str) -> set[str]:
+def parse_test_imports(content: str) -> frozenset[str]:
     """Parse test file content and extract test package dependencies."""
     if not (tree := safe_parse_ast(content)):
-        return set()
+        return frozenset()
 
     visitor = ImportVisitor()
     visitor.visit(tree)
     return visitor.test_imports


-def parse_lib_imports(content: str) -> set[str]:
+def parse_lib_imports(content: str) -> frozenset[str]:
     """Parse library file and extract all imported module names."""
     if not (tree := safe_parse_ast(content)):
-        return set()
+        return frozenset()
 
     visitor = ImportVisitor()
     visitor.visit(tree)
     return visitor.lib_imports
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 122 - 139, The functions
parse_test_imports and parse_lib_imports are annotated to return set[str] but
actually return frozenset values (visitor.test_imports and visitor.lib_imports);
either change the return type annotations to frozenset[str] or explicitly
convert the returned frozenset to a set() before returning. Update the
signatures of parse_test_imports and parse_lib_imports to return frozenset[str]
(or wrap visitor.test_imports/visitor.lib_imports with set(...) if you prefer a
mutable set) so the type annotations match the actual return values from
ImportVisitor.

67-74: Unnecessary try/except block: ast.ImportFrom.module never raises AttributeError.

The module attribute always exists on ast.ImportFrom nodes—it's None for relative imports like from . import x, not missing. The except AttributeError branch is unreachable. The subsequent if module is None check at line 73 correctly handles this case.

♻️ Simplified version
     def visit_ImportFrom(self, node):
-        try:
-            module = node.module
-        except AttributeError:
-            # Ignore `from . import my_internal_module`
-            return
-
-        if module is None:  # Ignore `from . import my_internal_module`
+        module = node.module
+        if module is None:  # Relative import: `from . import my_internal_module`
             return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 67 - 74, Remove the unreachable
try/except around accessing node.module: replace the try block that sets "module
= node.module" and the except AttributeError branch with a direct assignment
(module = node.module) and keep the existing None check (if module is None:
return); remove the except AttributeError branch and any comment about ignoring
AttributeError so the code simply reads module = node.module followed by the
None-check.
scripts/update_lib/cmd_todo.py (1)

337-338: Potential edge case: _test prefix extraction leaves leading underscore.

For test names like _test_atexit not found in DEPENDENCIES:

  • "_test_atexit".removeprefix("test_")"_test_atexit" (unchanged)
  • .removeprefix("_test")"_atexit" (still has leading underscore)

This likely works in practice because _test_* files (e.g., _test_atexit.py) are typically listed in DEPENDENCIES and handled at lines 331-334. If a _test_* file isn't in DEPENDENCIES, the extracted lib_name would retain a leading underscore.

💡 If full extraction is needed
-            lib_name = test_name.removeprefix("test_").removeprefix("_test")
+            lib_name = test_name.removeprefix("test_").removeprefix("_test").lstrip("_")

Or handle the _test_ prefix explicitly:

-            lib_name = test_name.removeprefix("test_").removeprefix("_test")
+            if test_name.startswith("_test_"):
+                lib_name = test_name.removeprefix("_test_")
+            else:
+                lib_name = test_name.removeprefix("test_")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/cmd_todo.py` around lines 337 - 338, When extracting
lib_name from test_name in the block using
test_name.removeprefix("test_").removeprefix("_test"), handle the edge case
where a leading underscore remains (e.g., "_test_atexit"); after the two
removeprefix calls normalize the result by stripping a leading underscore (e.g.,
call lstrip or conditional trim) so lib_name has no leading underscore before
checking DEPENDENCIES and assigning test_order; update the logic around the
lib_name assignment (the variable named lib_name and its use with
DEPENDENCIES/test_order) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/update_lib/deps.py`:
- Line 119: The call to self.add_import(f"test.{target}") from
ImportVisitor.visit_Call fails because ImportVisitor lacks a public add_import
method and only has the private self.__imports set; add a public method named
add_import(self, name) on the ImportVisitor class that appends/inserts the given
name into self.__imports (or otherwise exposes a safe API to modify the set) and
update any tests/uses to call that method; alternatively, if you prefer not to
add a new method, change the visit_Call usage to directly use the private set
(e.g., self._ImportVisitor__imports.add(f"test.{target}")), but the preferred
fix is to implement add_import on ImportVisitor so visit_Call can call it
safely.

---

Nitpick comments:
In `@scripts/update_lib/cmd_todo.py`:
- Around line 337-338: When extracting lib_name from test_name in the block
using test_name.removeprefix("test_").removeprefix("_test"), handle the edge
case where a leading underscore remains (e.g., "_test_atexit"); after the two
removeprefix calls normalize the result by stripping a leading underscore (e.g.,
call lstrip or conditional trim) so lib_name has no leading underscore before
checking DEPENDENCIES and assigning test_order; update the logic around the
lib_name assignment (the variable named lib_name and its use with
DEPENDENCIES/test_order) accordingly.

In `@scripts/update_lib/deps.py`:
- Around line 53-60: Remove the leftover commented-out expression from the
lib_imports property: delete the line containing "# module.split(\".\", 1)[0]"
inside the lib_imports getter so the generator comprehension only yields module
from self.__imports when not starting with "test."; ensure no other logic is
altered in the lib_imports property or references to __imports.
- Around line 110-112: The code accesses node.args[0] without ensuring node.args
is non-empty which can raise IndexError when parsing a call like
support.findfile() with no arguments; update the logic around the node handling
(where node.args is read) to first check that node.args is truthy/has length > 0
before accessing node.args[0], and early-return (as done for the ast.Constant
check) if there are no args so support.findfile() calls without args are safely
ignored.
- Around line 122-139: The functions parse_test_imports and parse_lib_imports
are annotated to return set[str] but actually return frozenset values
(visitor.test_imports and visitor.lib_imports); either change the return type
annotations to frozenset[str] or explicitly convert the returned frozenset to a
set() before returning. Update the signatures of parse_test_imports and
parse_lib_imports to return frozenset[str] (or wrap
visitor.test_imports/visitor.lib_imports with set(...) if you prefer a mutable
set) so the type annotations match the actual return values from ImportVisitor.
- Around line 67-74: Remove the unreachable try/except around accessing
node.module: replace the try block that sets "module = node.module" and the
except AttributeError branch with a direct assignment (module = node.module) and
keep the existing None check (if module is None: return); remove the except
AttributeError branch and any comment about ignoring AttributeError so the code
simply reads module = node.module followed by the None-check.

ℹ️ Review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09cf492 and fb9f477.

📒 Files selected for processing (2)
  • scripts/update_lib/cmd_todo.py
  • scripts/update_lib/deps.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/update_lib/deps.py (1)

66-74: Unnecessary try/except AttributeError block.

The module attribute always exists on ast.ImportFrom nodes—it's None for relative imports like from . import x, not missing. The try/except AttributeError block (lines 67-71) will never catch anything; the if module is None check on line 73 already handles the relative import case correctly.

♻️ Simplify by removing dead code
     def visit_ImportFrom(self, node):
-        try:
-            module = node.module
-        except AttributeError:
-            # Ignore `from . import my_internal_module`
-            return
-
-        if module is None:  # Ignore `from . import my_internal_module`
+        if node.module is None:  # Ignore `from . import my_internal_module`
             return
+        module = node.module

         for alias in node.names:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 66 - 74, Remove the dead try/except
block in the visit_ImportFrom method: the AST ImportFrom node always has a
module attribute (it may be None for relative imports), so delete the try/except
AttributeError and keep the existing if module is None early-return logic in
visit_ImportFrom to ignore relative imports; update the method body to directly
read node.module and proceed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/update_lib/deps.py`:
- Around line 122-139: The functions parse_test_imports and parse_lib_imports
declare -> set[str] but actually return visitor.test_imports and
visitor.lib_imports (which are frozenset[str]); update the return type
annotations to -> frozenset[str] for both functions to match the actual return
values (or alternatively convert the frozensets to set(...) if you prefer a
mutable collection), and ensure the references to visitor.test_imports and
visitor.lib_imports remain unchanged.
- Around line 110-112: The code accesses node.args[0] without ensuring an
argument exists, which can raise IndexError for calls like support.findfile();
add a defensive bounds check before accessing node.args[0] (e.g. if not
node.args: return or if len(node.args) == 0: return) so the existing
isinstance(arg, ast.Constant) check remains safe; update the block that
currently reads "arg = node.args[0]" to first verify node.args is non-empty.

---

Nitpick comments:
In `@scripts/update_lib/deps.py`:
- Around line 66-74: Remove the dead try/except block in the visit_ImportFrom
method: the AST ImportFrom node always has a module attribute (it may be None
for relative imports), so delete the try/except AttributeError and keep the
existing if module is None early-return logic in visit_ImportFrom to ignore
relative imports; update the method body to directly read node.module and
proceed.

ℹ️ Review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb9f477 and b0d938d.

📒 Files selected for processing (1)
  • scripts/update_lib/deps.py

Comment on lines +110 to +112
arg = node.args[0]
if not isinstance(arg, ast.Constant):
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing bounds check for node.args could raise IndexError.

If a malformed call like support.findfile() (no arguments) exists, accessing node.args[0] will raise an IndexError, potentially crashing the dependency scan for that file.

🛡️ Add defensive check
         if (value.id != "support") or (func.attr != "findfile"):
             return

+        if not node.args:
+            return
+
         arg = node.args[0]
         if not isinstance(arg, ast.Constant):
             return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 110 - 112, The code accesses
node.args[0] without ensuring an argument exists, which can raise IndexError for
calls like support.findfile(); add a defensive bounds check before accessing
node.args[0] (e.g. if not node.args: return or if len(node.args) == 0: return)
so the existing isinstance(arg, ast.Constant) check remains safe; update the
block that currently reads "arg = node.args[0]" to first verify node.args is
non-empty.

Comment on lines +122 to +139
def parse_test_imports(content: str) -> set[str]:
"""Parse test file content and extract test package dependencies."""
if not (tree := safe_parse_ast(content)):
return set()

visitor = ImportVisitor()
visitor.visit(tree)
return visitor.test_imports


def parse_lib_imports(content: str) -> set[str]:
"""Parse library file and extract all imported module names."""
if not (tree := safe_parse_ast(content)):
return set()

visitor = ImportVisitor()
visitor.visit(tree)
return visitor.lib_imports
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Return type annotations don't match actual return types.

Both functions declare -> set[str] but return frozenset[str] from the visitor properties. This mismatch could cause type checker warnings and is misleading to callers.

🔧 Fix type annotations
-def parse_test_imports(content: str) -> set[str]:
+def parse_test_imports(content: str) -> frozenset[str]:
     """Parse test file content and extract test package dependencies."""
     if not (tree := safe_parse_ast(content)):
-        return set()
+        return frozenset()

     visitor = ImportVisitor()
     visitor.visit(tree)
     return visitor.test_imports


-def parse_lib_imports(content: str) -> set[str]:
+def parse_lib_imports(content: str) -> frozenset[str]:
     """Parse library file and extract all imported module names."""
     if not (tree := safe_parse_ast(content)):
-        return set()
+        return frozenset()

     visitor = ImportVisitor()
     visitor.visit(tree)
     return visitor.lib_imports
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update_lib/deps.py` around lines 122 - 139, The functions
parse_test_imports and parse_lib_imports declare -> set[str] but actually return
visitor.test_imports and visitor.lib_imports (which are frozenset[str]); update
the return type annotations to -> frozenset[str] for both functions to match the
actual return values (or alternatively convert the frozensets to set(...) if you
prefer a mutable collection), and ensure the references to visitor.test_imports
and visitor.lib_imports remain unchanged.

@github-actions
Copy link
Contributor

Code has been automatically formatted

The code in this PR has been formatted using:

  • ruff format
    Please pull the latest changes before pushing again:
git pull origin deps-nodevisitor

@youknowone youknowone merged commit e0eb6d5 into RustPython:main Feb 28, 2026
23 of 24 checks passed
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.

2 participants

X Tutup