From 5b3ae26f0c202a44d2e0d7c3b3dc2b54dd71f07b Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Mon, 15 Aug 2022 14:37:11 -0400 Subject: [PATCH] initial reproduction --- Dockerfile | 7 + README.md | 50 ++++ _child.html | 312 +++++++++++++++++++++++++ pyproject.toml | 3 + setup.py | 20 ++ shadow_import_issue/__init__.py | 7 + shadow_import_issue/c_exts/__init__.py | 0 shadow_import_issue/c_exts/_child.pyx | 1 + shadow_import_issue/c_exts/py_mod.py | 1 + shadow_import_issue/c_exts/shadow.py | 1 + shadow_import_issue/shadow.py | 1 + shadow_import_issue/sub/__init__.py | 0 shadow_import_issue/sub/child.py | 1 + shadow_import_issue/sub/shadow.py | 1 + test.py | 1 + 15 files changed, 406 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 _child.html create mode 100644 pyproject.toml create mode 100644 setup.py create mode 100644 shadow_import_issue/__init__.py create mode 100644 shadow_import_issue/c_exts/__init__.py create mode 100644 shadow_import_issue/c_exts/_child.pyx create mode 100644 shadow_import_issue/c_exts/py_mod.py create mode 100644 shadow_import_issue/c_exts/shadow.py create mode 100644 shadow_import_issue/shadow.py create mode 100644 shadow_import_issue/sub/__init__.py create mode 100644 shadow_import_issue/sub/child.py create mode 100644 shadow_import_issue/sub/shadow.py create mode 100644 test.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8635400 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.10 +WORKDIR /src/ +RUN pip install -U pip +COPY . . +RUN pip install -e . + +CMD python test.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..f4c44d4 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Shadow Import Issue + +This repo includes a reproduction of an import issue when using `setuptools>=64.0.0` and Cython when installed in editable mode. + +This issue occurs when importing a module from a parent module which has the same name as a sibling module. + +For example: + +``` +├── shadow_import_issue +│   ├── __init__.py +│   ├── c_exts +│   │   ├── __init__.py +│   │   ├── _child.pyx +│   │   └── shadow.py +│   ├── shadow.py +│   └── sub +│   ├── __init__.py +│   ├── child.py +└── └── shadow.py +``` + +When trying to import `shadow_import_issue.shadow` from `shadow_import_issue.c_exts._child` it will import `shadow_import_issue.c_exts.shadow` instead. + +This issue is not present when importing `shadow_import_issue.shadow` from `shadow_import_issue.sub.child` (pure-python version). + +## Running + +``` shell +git clone https://github.com/brettlangdon/shadow-import-issue.git +cd ./shadow-import-issue/ +docker build -t shadow-import-issue . +docker run --rm shadow-import-issue +``` + +Expected output is to fail with: + +``` +pure-python VAR +Traceback (most recent call last): + File "/src/test.py", line 1, in + import shadow_import_issue + File "/src/shadow_import_issue/__init__.py", line 5, in + from .c_exts import _child # isort:skip + File "shadow_import_issue/c_exts/_child.pyx", line 1, in init shadow_import_issue.c_exts._child + from shadow_import_issue.shadow import VAR +ImportError: cannot import name VAR +``` + +The module and variable will import fine with Python, but will fail the import from the Cython module. diff --git a/_child.html b/_child.html new file mode 100644 index 0000000..e91e3c6 --- /dev/null +++ b/_child.html @@ -0,0 +1,312 @@ + + + + + + Cython: _child.pyx + + + +

Generated by Cython 0.29.32

+

+ Yellow lines hint at Python interaction.
+ Click on a line that starts with a "+" to see the C code that Cython generated for it. +

+

Raw output: _child.c

+
+1: from setuptools_repro.shadow import VAR
+
  __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  __Pyx_INCREF(__pyx_n_s_VAR);
+  __Pyx_GIVEREF(__pyx_n_s_VAR);
+  PyList_SET_ITEM(__pyx_t_1, 0, __pyx_n_s_VAR);
+  __pyx_t_2 = __Pyx_Import(__pyx_n_s_setuptools_repro_shadow, __pyx_t_1, -1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+  __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_VAR); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_VAR, __pyx_t_1) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..61f52b3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools >= 64.0.0", "cython"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..745d2f2 --- /dev/null +++ b/setup.py @@ -0,0 +1,20 @@ +import Cython.Distutils +from Cython.Build import cythonize +from setuptools import find_packages, setup + +setup( + name="shadow_import_issue", + version="1.0", + packages=find_packages(), + ext_modules=cythonize( + [ + Cython.Distutils.Extension( + "shadow_import_issue.c_exts._child", + sources=["shadow_import_issue/c_exts/_child.pyx"], + language="c", + ), + ], + force=True, + annotate=True, + ), +) diff --git a/shadow_import_issue/__init__.py b/shadow_import_issue/__init__.py new file mode 100644 index 0000000..cd8b01d --- /dev/null +++ b/shadow_import_issue/__init__.py @@ -0,0 +1,7 @@ +from .sub import child # isort:skip + +print("pure-python", child.VAR) + +from .c_exts import _child # isort:skip + +print("cython", _child.VAR) diff --git a/shadow_import_issue/c_exts/__init__.py b/shadow_import_issue/c_exts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shadow_import_issue/c_exts/_child.pyx b/shadow_import_issue/c_exts/_child.pyx new file mode 100644 index 0000000..2ee6b05 --- /dev/null +++ b/shadow_import_issue/c_exts/_child.pyx @@ -0,0 +1 @@ +from shadow_import_issue.shadow import VAR diff --git a/shadow_import_issue/c_exts/py_mod.py b/shadow_import_issue/c_exts/py_mod.py new file mode 100644 index 0000000..7f96e8c --- /dev/null +++ b/shadow_import_issue/c_exts/py_mod.py @@ -0,0 +1 @@ +# empty on purpose diff --git a/shadow_import_issue/c_exts/shadow.py b/shadow_import_issue/c_exts/shadow.py new file mode 100644 index 0000000..7f96e8c --- /dev/null +++ b/shadow_import_issue/c_exts/shadow.py @@ -0,0 +1 @@ +# empty on purpose diff --git a/shadow_import_issue/shadow.py b/shadow_import_issue/shadow.py new file mode 100644 index 0000000..e581d9d --- /dev/null +++ b/shadow_import_issue/shadow.py @@ -0,0 +1 @@ +VAR = "VAR" diff --git a/shadow_import_issue/sub/__init__.py b/shadow_import_issue/sub/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shadow_import_issue/sub/child.py b/shadow_import_issue/sub/child.py new file mode 100644 index 0000000..2ee6b05 --- /dev/null +++ b/shadow_import_issue/sub/child.py @@ -0,0 +1 @@ +from shadow_import_issue.shadow import VAR diff --git a/shadow_import_issue/sub/shadow.py b/shadow_import_issue/sub/shadow.py new file mode 100644 index 0000000..7f96e8c --- /dev/null +++ b/shadow_import_issue/sub/shadow.py @@ -0,0 +1 @@ +# empty on purpose diff --git a/test.py b/test.py new file mode 100644 index 0000000..c717564 --- /dev/null +++ b/test.py @@ -0,0 +1 @@ +import shadow_import_issue