Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Shipped rdkit-stubs .pyi files are not valid Python, which stops mypy before it checks any user code

Đang mở
#9,540 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
45/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
cpp, python

Hướng nghiên cứu

Start with generate_stubs_internal in Scripts/gen_rdkit_stubs/ and inspect rdkit-stubs/CMakeLists.txt to understand how generated files are validated and how failures affect the stubs target. Compare the generated .pyi files across platforms and review Code/RDBoost/Wrap.h for the compiler-dependent names. Done means the chosen fix is tested through the relevant generation path and every shipped .pyi parses with ast.parse.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Describe the bug

Three of the .pyi files shipped in rdkit-stubs are not syntactically valid Python. Because mypy aborts on a syntax error in a followed import, a single one of these stops the type check of the entire user project before a single line of user code is analysed:

rdkit-stubs/Chem/rdMolDescriptors.pyi:10: error: Invalid syntax. Perhaps you forgot a comma?  [syntax]
Found 1 error in 1 file (errors prevented further checking)

mypy exits 2, so in CI this is indistinguishable from a broken configuration.

Two of the three are Windows-only; one ships on every platform.

To Reproduce

Any project that imports rdkit.Chem.rdMolDescriptors and runs mypy will hit it on Windows. The defects themselves can be shown without mypy:

import ast, pathlib, rdkit

root = pathlib.Path(rdkit.__file__).resolve().parent.parent / "rdkit-stubs"
for f in sorted(root.rglob("*.pyi")):
    try:
        ast.parse(f.read_text(encoding="utf-8", errors="replace"))
    except SyntaxError as e:
        print(f"{f.relative_to(root)}:{e.lineno}: {e.msg}")

rdkit==2026.3.5, cp313, win_amd64:

Chem\rdMolDescriptors.pyi:10: invalid syntax. Perhaps you forgot a comma?
Chem\rdmolfiles.pyi:332: parameter without a default follows parameter with a default
Chem\rdRGroupDecomposition.pyi:60: illegal target for annotation

rdkit==2026.3.5, cp312, manylinux_2_28_x86_64 and cp312, macosx_11_0_arm64 (283 stubs each):

Chem/rdRGroupDecomposition.pyi:60: illegal target for annotation

The three lines

  1. Chem/rdMolDescriptors.pyi:10 — Windows only

    # win_amd64
    atomTypes: typing.ClassVar[rdkit.rdBase._vectunsigned int]  # value = <rdkit.rdBase._vectunsigned int object at 0x0000023BAACBDEC0>
    # manylinux
    atomTypes: typing.ClassVar[rdkit.rdBase._vectj]  # value = <rdkit.rdBase._vectj object>
    
  2. Chem/rdmolfiles.pyi:332 — Windows only

    # win_amd64  (props_list/structstd/classstd repeated, and a non-default after a default)
    def GetText(mol: Mol, confId: int = -1, props_list: ..., structstd: ..., classstd: ... = ..., structstd: ..., classstd: ...) -> str:
    # manylinux
    def GetText(mol: Mol, confId: int = -1, props_list: _vectNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE = ...) -> str:
    
  3. Chem/rdRGroupDecomposition.pyi:60 — all platforms

    None: typing.ClassVar[RGroupCoreAlignment]  # value = rdkit.Chem.rdRGroupDecomposition.RGroupCoreAlignment.None
    

    RGroupCoreAlignment has a member named None, which is a keyword and cannot be an annotation target.

Cause

(1) and (2) are not really stub bugs. Code/RDBoost/Wrap.h builds the Python
class name for the container converters out of typeid(T).name():

template <typename T>
void RegisterVectorConverter(bool noproxy = false) {
  std::string name = "_vect";
  name += typeid(T).name();
  RegisterVectorConverter<T>(name.c_str(), noproxy);
}

RegisterListConverter does the same a few lines below. The return value of
typeid(T).name() is implementation-defined:

typeid(unsigned int).name() resulting class name valid identifier
gcc/clang j (mangled) _vectj yes
MSVC unsigned int (demangled) _vectunsigned int no

The Itanium mangled name happens to contain only [A-Za-z0-9_], so on Linux
and macOS these names are accidentally valid Python identifiers. MSVC returns a
human-readable spelling, so the name carries spaces and angle brackets. On a
win_amd64 build seven of these classes have names that are not identifiers:

_vectunsigned int
_vectclass std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
_vectclass std::vector<double,class std::allocator<double> >
_vectclass std::vector<int,class std::allocator<int> >
_vectclass std::vector<unsigned int,class std::allocator<unsigned int> >
_listclass std::vector<int,class std::allocator<int> >
_listclass std::vector<unsigned int,class std::allocator<unsigned int> >

Only _vectdouble and _vectint come out usable, and the generated stub takes
such a name as-is.

That the generator was written against gcc naming is visible in
ProcessDocLines.CPP_PYTHONIC_RETURN_TYPES, whose keys are all Itanium mangled
names:

CPP_PYTHONIC_RETURN_TYPES = {
    "_vectd": "typing.Sequence[double]",
    "_vecti": "typing.Sequence[int]",
    "_vectj": "typing.Sequence[int]",
    "_vectNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE": "typing.Sequence[str]",
    "_vectSt6vectorIiSaIiEE": "typing.Sequence[typing.Sequence[int]]",
    ...
}

_vectj and the NSt7__cxx11 libstdc++ ABI tag only ever appear on a gcc or
clang build. On MSVC the corresponding names are _vectunsigned int and
_vectclass std::basic_string<...>, so no key matches and the raw C++ spelling
falls through into the stub.

(3) is independent of the compiler: RGroupCoreAlignment has a member named
None, which is a legal C++ identifier and a Python keyword, so it is only
reachable as getattr(RGroupCoreAlignment, "None") and cannot be written as an
annotation target.

Underlying all three: nothing parses the generated stubs, so an unparseable one
is packaged and shipped rather than failing the build.

Expected behavior

Every .pyi in rdkit-stubs parses with ast.parse.

Possible directions

Two separate layers, and which one you want is your call:

Root — Wrap.h deriving a Python identifier from typeid(T).name(). Making
those names identifier-safe would fix the class of problem rather than its
symptoms, but it changes names that are visible on rdBase, so it did not seem
like something to propose as a patch from outside.

Mitigation — have stub generation refuse to emit something it cannot parse.
The cheapest form is a check at the end of generate_stubs_internal, which
turns a silently shipped defect into a visible failure of the stubs target:

import ast
...
try:
    ast.parse(generated)
except SyntaxError as e:
    raise RuntimeError(f"{pyi}:{e.lineno}: {e.msg}")

On its own that does not break the build — rdkit-stubs/CMakeLists.txt reports a
non-zero return with message() rather than message(FATAL_ERROR ...), so the
stubs target would print the failure and carry on. The effect would be that
win_amd64 stops shipping stubs at all, which is arguably still better than
shipping ones that stop mypy dead, but it is a trade rather than a fix.

A prototype that repairs the two shapes seen here and raises on anything else is
on a branch:

https://github.com/HiroYokoyama/rdkit/tree/fix/validate-generated-stubs-min

It drops an annotation line that does not parse, rewrites a def whose
parameter list does not parse to *args/**kwargs, and fails otherwise. Run
against the published 2026.3.5 wheels it takes win_amd64 from 3 unparseable
stubs to 0 and manylinux_2_28_x86_64 from 1 to 0, touching only the offending
files (3 of 278 and 1 of 283).

It is a prototype, it is unverified, and it has no tests. Specifically:

  • No unit test. Scripts/gen_rdkit_stubs/ has none today, so where one belongs
    is a question for you rather than something to guess at.
  • Not exercised through a real build. I cannot build the RDKit here, so the
    function was run against the .pyi files from the released wheels instead of
    through cmake --build . --target stubs. Whether it sits correctly inside
    generate_stubs_internal is read from the source, not observed.
  • Only the two defect shapes seen here are handled. Anything else raises, which
    is deliberate, but it does mean an unfamiliar shape would stop a build rather
    than degrade.

Opened as an issue and not a pull request for that reason. It is 57 added lines
in one file with nothing removed and no existing function touched, so it is
small — it is just not something I can honestly say I have verified end to end.
Happy to turn it into a proper PR with tests if this is a direction you want to
take, or to drop it entirely if you would rather fix the naming in Wrap.h.

The existing patch mechanism does not cover (1) and (2) on its own: a diff
written against the win_amd64 stub does not apply to the manylinux one, and
apply_patch only logs when git apply fails, so those builds would carry a
CRITICAL line on every run.

Configuration

  • RDKit version: 2026.3.5 (also reproduced against the manylinux_2_28_x86_64 wheel of the same version)
  • OS: Windows 11 (Linux comparison from the published wheel)
  • Python version: 3.13 (Windows), 3.12 (Linux wheel inspected)
  • Are you using conda? No
  • How installed: pip install rdkit==2026.3.5 from PyPI

Additional context

Two earlier reports of the same family were closed by the stale bot without a fix, and both still reproduce in 2026.3.5:

  • #8339 — "mypy error: parameter without a default follows parameter with a default" — this is defect (2) above.

  • #8673 — "Incorrect type stub for EmbedParameters attributes results in mypy errors" — still present. EmbedParameters properties are declared as bare def randomSeed(*args, **kwargs) with no annotations, so

    from rdkit.Chem import rdDistGeom
    params = rdDistGeom.ETKDGv2()
    params.randomSeed = 42
    

    gives error: Incompatible types in assignment (expression has type "int", variable has type "EmbedParameters"). Note this only becomes visible once the stubs above parse — otherwise mypy stops before reaching it.

The practical impact is larger than the three lines suggest: because mypy stops at the first unparseable stub, a downstream project on Windows cannot type-check at all until it patches its own site-packages.


The analysis in this report — comparing the win_amd64, manylinux and macOS
wheels, tracing the naming back to Wrap.h, and the prototype branch — was done
with the assistance of Claude Opus 5.

Ngôn ngữ chính
HTML
Star
3.6k
Fork
1.1k
Merge trung bình
3 ngày 8 giờ
Pull request đã merge (30 ngày)
44

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của rdkit/rdkit

Tất cả issue của rdkit/rdkit

Issue tương tự

Thêm issue về Build System

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.