I have a project with an overarching namespace, with packages inside it. Here's the folder structure:
pypackage
├── pypackage <-- Source code for use in this project.
| |
│ ├── bin <-- Module: Cli entry point into pyproject.
| | ├── __init__.py
| | └── pypackage.py
| |
| └── core <-- Module: Core functionality.
| ├── __init__.py
| └── pypackage.py
|
├── tests
├── README.md
└── setup.py
Pretty simple. If I want to import it I use:
from pypackage.core import pypackage
and it works great because my setup.py looks like this:
from setuptools import setup, find_packages
...
NAME = 'pypackage'
setup(
name=NAME,
namespace_packages=[NAME],
packages=[f'{NAME}.{p}' for p in find_packages(where=NAME)],
entry_points={
"console_scripts": [
f'{NAME} = {NAME}.bin.{NAME}:cli',
]
},
...
)
However, I have legacy code that imports this pypackage
when it used to just be a stand alone python file. like this:
import pypackage
So how do I make it so I can keep the same structure with namespaces and subpackages but still import it the old way? How do I turn this:
from pypackage.core import pypackage
into this:
import pypackage
In other words, how do I alias the pypackage.core.pypackage
module to be pypackage
for when I'm importing pypackage
into an external project?
You would add the 'old' names inside your new package by importing into the top-level package.
Names imported as globals in pypackage/__init__.py
are attributes on the pypackage
package. Make use of that to give access to 'legacy' locations:
# add all public names from pypackage.core.pypackage to the top level for
# legacy package use
from .core.pypackage import *
Now any code that uses import pypackage
can use pypackage.foo
and pypackage.bar
if in reality these objects were defined in pypackage.core.pypackage
instead.
Now, because pypackage
is a setuptools namespace package you have a different problem; namespace packages are there for multiple separate distributions to install into so that top-level package must either be empty or only contain a minimum __init__.py
file (namespace packages created with empty directories require Python 3.3).
If you are the only publisher of distributions that use this namespace, you can cheat a little here and use a single __init__.py
file in your core
package that could use pkg-util-style __init__.py
file with the additional import I used above, but then you must not use any __init__.py
files in other distribution packages or require that they all use the exact same __init__.py
content. Coordination is key here.
Or you would have to use a different approach. Leave pypackage
as a legacy wrapper module, and rename the new package format to use a new, different top-level name that can live next to the old module. At this point you can then just include the legacy package in your project, directly, as an extra top-level module.