opendatalab/MinerU · error · AttributeError

module {__name__!r} has no attribute {name!r}

Error message

module {__name__!r} has no attribute {name!r}

What it means

AttributeError raised by the module-level __getattr__ in mineru/data/io/__init__.py (PEP 562) for any attribute access that is not one of the eagerly listed names ('IOReader', 'IOWriter', 'HttpReader', 'HttpWriter') or the lazily loaded 'S3Reader'/'S3Writer'. The module defers importing the S3 classes to avoid a hard boto3 dependency, so unknown names fall through to this explicit error.

Source

Thrown at mineru/data/io/__init__.py:18

# Copyright (c) Opendatalab. All rights reserved.

from .base import IOReader, IOWriter
from .http import HttpReader, HttpWriter

__all__ = ['IOReader', 'IOWriter', 'HttpReader', 'HttpWriter', 'S3Reader', 'S3Writer']


def __getattr__(name):
    """按需加载 S3 IO 类,避免默认安装场景强制依赖 boto3。"""
    if name in {'S3Reader', 'S3Writer'}:
        from .s3 import S3Reader, S3Writer

        s3_exports = {'S3Reader': S3Reader, 'S3Writer': S3Writer}
        globals().update(s3_exports)
        return s3_exports[name]

    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View on GitHub (pinned to 4fe4bde114)

Solutions

  1. Check __all__ in mineru/data/io/__init__.py and import only listed names: IOReader, IOWriter, HttpReader, HttpWriter, S3Reader, S3Writer.
  2. Fix the typo in the import (e.g. S3Reeder -> S3Reader).
  3. If importing S3Reader/S3Writer, ensure the [s3] extra is installed since the lazy import of .s3 still needs boto3.
  4. For renamed/moved classes, grep the package for the new location instead of relying on old paths.

Example fix

# before
from mineru.data.io import S3Reeder  # AttributeError via __getattr__

# after
from mineru.data.io import S3Reader
Defensive patterns

Strategy: type-guard

Validate before calling

import mineru.data.io as mineru_io

ALLOWED = set(mineru_io.__all__)

def safe_getattr(name: str):
    if name not in ALLOWED:
        raise AttributeError(f'mineru.data.io exports only {sorted(ALLOWED)}, not {name!r}')
    return getattr(mineru_io, name)

Type guard

def is_mineru_io_export(name: str) -> bool:
    import mineru.data.io as m
    return name in m.__all__  # ['IOReader','IOWriter','HttpReader','HttpWriter','S3Reader','S3Writer']

Try / catch

try:
    from mineru.data.io import S3Reader
except ImportError as e:
    raise ImportError(
        "expected 'mineru.data.io' exports: "
        "IOReader, IOWriter, HttpReader, HttpWriter, S3Reader, S3Writer"
    ) from e

Prevention

When it happens

Trigger: `from mineru.data.io import S3Reeder` (typo), `getattr(mineru.data.io, 'FsReader')`, or `hasattr`/dir-driven probing of names removed or never exported from the package.

Common situations: Upgrades where a class was renamed or moved but old import statements remain; IDE autocomplete suggesting a stale name; generic plugin loaders that iterate candidate class names via getattr.

Related errors


AI-assisted analysis of opendatalab/MinerU@4fe4bde114 (2026-08-14). Data as JSON: /api/errors/e925ec2629a88ec5. Report an issue: GitHub.