1

I have a project that looks like this :

.
├── A
│   ├── setup.py
│   ├── __init__.py
│   ├── a.py
│   └── data.py
└── B
    ├── b.py
    └── data.py

I don't have control over A but I would like to use it within B/b.py. I can't rewrite any code within folder A (except setup.py).

I tried this:

# File A/setup.py
from setuptools import setup, find_packages
setup(name='packageA', version='1.0', packages=find_packages())

With the following installation/tests:

$ cd B
$ pip install -e ../A
$ python -c "import a; print(a)"
<module 'a' from 'A/a.py'>
$ python -c "import data; print(data)"
<module 'data' from 'B/data.py'> 
# How to get the same for <module 'data' from 'A/data.py'>?

The name collision bothers me here, I would like to be able to import both A/data.py and B/data.py in b.py. I wanted to know if there is a way around this?

I would like to be able to write something like:

$ python -c "from A import data; print(data)"
<module 'data' from 'A/data.py'>

I tried to have setup.py one level above (in the root directory), but if I do that then I have problems within A:

$ cd B
$ pip install -e ..
$ python -c "from A import data; print(data)"
Traceback (most recent call last):
  File "A/data.py", line 1, in <module>
    from a import some_function
ImportError: cannot import name 'some_function' from 'a' (unknown location)

If I could rewrite A/data.py I could just do this (but I can't):

from A.a import some_function

Is it possible to modify setup.py to encapsulate A under a (fake) module name? Any solution is welcome.

cglacet
  • 8,873
  • 4
  • 45
  • 60
  • Make a new package in `B`, move the module files inside it, adapt the imports. – hoefling Mar 24 '20 at 12:49
  • I could, but if `A` changes I would have to redo this all over again, right? – cglacet Mar 24 '20 at 13:04
  • No, why? The issue here is: `A` defines a top-level module `data`, `B` defines a top-level module `data`, there's a name clash (similar to the problem "I have two files named `data.py` in the same directory"). In order to resolve it, you can either: rename the module (one of the `data.py` files is renamed) or change it's import path (one of the `data.py` files wanders one level deeper in a separate directory under `A` or `B`). – hoefling Mar 24 '20 at 13:18
  • Ah ok, I think get it, instead of having `A/data.py` and `B/data.py` you suggest to have `A/data.py` and `B/somename/data.py`? – cglacet Mar 24 '20 at 13:24
  • 1
    Exactly, also create an empty `B/somename/__init__.py` to mark `somename` a package. Your imports should now be: `import data` imports from `A/data.py`, `import somename.data` imports from `B/somename/data.py`. – hoefling Mar 24 '20 at 13:38
  • 1
    This is why top-level modules are (almost) always bad. If you plan to redistribute the code, creating a top-level package with a meaningful name that will contain the modules is always a good idea. – hoefling Mar 24 '20 at 13:39
  • That makes sense indeed. I'll try to work with that idea. – cglacet Mar 24 '20 at 13:53

1 Answers1

0

Multiple possible solutions:

  1. Install A as a package. cd A && python3 setup.py install. Presumably this is an installable package. Now you can import A as you would normally expect.
  2. Add A to PYTHONPATH. It's useful when you are programming in B but need information extracted from A. So you can modify the content in A e.g. debug messages and still have your program work. PYTHONPATH=/path/to/A python -c "import a; print(a)"
  3. You can create start the program from a parent folder import them like normal modules. Make sure you are using relative imports.

$ cd B
$ pip install -e ../A
$ python -c "import a; print(a)"
<module 'a' from 'A/a.py'>
$ python -c "import data; print(data)"
<module 'data' from 'B/data.py'> 
# How to get the same for <module 'data' from 'A/data.py'>?

The name collision bothers me here, I would like to be able to import both A/data.py and B/data.py in b.py. I wanted to know if there is a way around this?

I would like to be able to write something like:

$ python -c "from A import data; print(data)"
<module 'data' from 'A/data.py'>

You do from A import data. Then you have "A/data.py". In case of name collisions you could also do from a import data as newname.
This assumes you have A installed as a python module. (Be it over setup.py install, pip install or adding the library to PYTHONPATH.)

Tin Nguyen
  • 5,250
  • 1
  • 12
  • 32