`lazy import a.b as c` resolves `b` as an attribute of `a` instead of importing the module `a.b`
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- Python
- Estrellas
- 77.2k
- Forks
- 35.9k
- Métricas de merge de PR
- Métricas de PR pendientes
Descripción
Bug report
Bug description:
lazy import a.b as c never imports the module a.b by name. On first use it imports a and reads the attribute b off it, as lazy from a import b would, so c becomes whatever that attribute lookup finds. This manifests in two ways.
Case A: a.b is not a module. The eager statement raises; the lazy one binds the attribute.
lazy import math.pi as lazy_x
print(lazy_x) # 3.141592653589793
import math.pi as x # ModuleNotFoundError: No module named 'math.pi'; 'math' is not a package
The bare lazy import math.pi was fixed in #155194; the as form was not.
Case B: a.b is a module, but a has an attribute b that shadows it and a.b has not been imported yet. The eager statement imports the submodule, which rebinds a.b to the module; the lazy one binds the attribute and never imports the submodule. json does not import json.tool, so the assignment below stands in for an attribute a package's __init__.py would define:
import json
json.tool = "attribute"
lazy import json.tool as lazy_c
print(type(lazy_c).__name__) # str
import json.tool as c
print(type(c).__name__) # module
Case B is related to #151208, which reports the same shadowing for import a.foo under -X lazy_imports=all followed by an attribute access a.foo, resolved in Objects/moduleobject.c. That script now passes on main; this statement still fails, because it is resolved on a different path.
Possible root cause
import a.b as c compiles to IMPORT_NAME a.b followed by IMPORT_FROM b. Lazily, IMPORT_NAME leaves a placeholder holding a.b, and _PyEval_LazyImportFrom (Python/ceval.c:3329-3384) replaces it with one holding lz_from = "a", lz_attr = "b". That is the same placeholder lazy from a import b produces.
Reification in _PyImport_LoadLazyImportTstate (Python/import.c:3902-4093) therefore runs __import__("a", fromlist=("b",)) followed by getattr(a, "b") for both statements. Nothing records that the statement was an import, so the module a.b is never imported by its own name: the "is not a package" check never runs, and an existing attribute b is never overwritten by the submodule.
This is not #157614, which is about the sys.modules fast path in _PyEval_LazyImportFrom consulting the wrong module. The present issue reproduces with the fix in #157626 applied.
CPython versions tested on:
3.15
Operating systems tested on:
Linux
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Empieza leyendo _PyEval_LazyImportFrom en Python/ceval.c y _PyImport_LoadLazyImportTstate en Python/import.c; después, reproduce los ejemplos Case A y Case B del issue en Python 3.15. Se considera terminado cuando lazy import a.b as c sigue la semántica de eager import: importa a.b por nombre, genera un error para un subpath que no sea un módulo y no usa un atributo que lo oculte.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- python
- Área
- backend
- Tipo de issue
- Error
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Estado de actividad
- Activo
- Claridad
- Bien especificado
- Aptitud para principiantes
- 68/100