python namespaces vs packages: making a package the default namespace

爷,独闯天下 提交于 2021-02-05 11:25:23

问题


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?


回答1:


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.




回答2:


Martin Pieters has the right idea if I were using packages, but a namespace package is a setuptools thing.

So that didn't work. after more research, I learned that there's no way to do what I'm trying to do. So if I really want to do it I must convert everything to a regular package hierarchy instead of namespace package, then use martin's solution.

I've decided to modify the legacy code instead to import it the new way.



来源:https://stackoverflow.com/questions/53486486/python-namespaces-vs-packages-making-a-package-the-default-namespace

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!