Пространства имен python против пакетов: сделать пакет пространством имен по умолчанию

У меня есть проект с всеобъемлющим пространством имен, с пакетами внутри. Вот структура папок:

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          

Довольно просто Если я хочу импортировать это, я использую:

from pypackage.core import pypackage

и это прекрасно работает, потому что мой setup.py выглядит так:

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',
        ]
    },
    ...
)

Тем не менее, у меня есть устаревший код, который импортирует этот pypackage когда-то это был просто отдельный файл Python. как это:

import pypackage

Итак, как мне сделать так, чтобы я мог сохранять ту же структуру с пространствами имен и подпакетами, но все же импортировать ее старым способом? Как мне включить это:

from pypackage.core import pypackage

в это:

import pypackage

Другими словами, как мне псевдоним pypackage.core.pypackage модуль должен быть pypackage когда я импортирую pypackage во внешний проект?

2 ответа

Решение

Вы бы добавили "старые" имена в ваш новый пакет, импортировав его в пакет верхнего уровня.

Имена, импортированные как глобальные pypackage/__init__.py атрибуты на pypackage пакет. Воспользуйтесь этим, чтобы получить доступ к "устаревшим" местам:

# add all public names from pypackage.core.pypackage to the top level for
# legacy package use
from .core.pypackage import *

Теперь любой код, который использует import pypackage можешь использовать pypackage.foo а также pypackage.bar если в действительности эти объекты были определены в pypackage.core.pypackage вместо.

Теперь, потому что pypackage это пакет пространства имен setuptools, у вас другая проблема; Пакеты пространства имен предназначены для установки нескольких отдельных дистрибутивов, поэтому пакет верхнего уровня должен быть либо пустым, либо содержать только минимум __init__.py файл (пакеты пространства имен, созданные с пустыми каталогами, требуют Python 3.3).

Если вы являетесь единственным издателем дистрибутивов, которые используют это пространство имен, вы можете немного обмануть здесь и использовать один __init__.py файл в вашем core пакет, который может использовать pkg-util-style __init__.py файл с дополнительным импортом, который я использовал выше, но тогда вы не должны использовать какие-либо __init__.py файлы в других дистрибутивах или требуют, чтобы все они использовали одно и то же __init__.py содержание. Координация является ключевой здесь.

Или вам придется использовать другой подход. Покидать pypackage как устаревший модуль-обертка, и переименуйте новый формат пакета, чтобы использовать новое, другое имя верхнего уровня, которое может находиться рядом со старым модулем. На этом этапе вы можете просто включить устаревший пакет в свой проект напрямую, как дополнительный модуль верхнего уровня.

Мартин Питерс имеет правильную идею, если бы я использовал пакеты, но пакет пространства имен - это вещь setuptools.

Так что это не сработало. после дополнительных исследований я узнал, что нет способа сделать то, что я пытаюсь сделать. Поэтому, если я действительно хочу это сделать, я должен преобразовать все в обычную иерархию пакетов вместо пакета пространств имен, а затем использовать решение Мартина.

Я решил изменить устаревший код, чтобы импортировать его по-новому.

Другие вопросы по тегам