重命名python子包,将旧名称标记为已弃用
Posted
技术标签:
【中文标题】重命名python子包,将旧名称标记为已弃用【英文标题】:Rename python subpackage, mark old name as deprecated 【发布时间】:2019-11-27 08:06:02 【问题描述】:我有一个 python 包,它具有以下目录结构
package/
├── __init__.py
└── subpackage_A/
├── __init__.py
├── moduleA.py
└── moduleB.py
现在我想将subpackage_A
的名称更改为subpackage_B
,同时保持旧名称可用,否则很多脚本会中断。所以这应该是可能的:
from package.subpackage_B import moduleA
from package.subpackage_B.moduleB import ClassB
# This should, if possible, display a deprecation warning
from package.subpackage_A import moduleA
我尝试将新包简单地导入名为 subpackage_A
的模块中,但这不起作用:
# File: package/subpackage_A.py
# This does not work:
from package.subpackage_B import * # Import everything from the new module
# This is also not working:
sys.modules[__name__] = __import__('package.subpackage_A')
如果脚本尝试从旧位置导入moduleA
,则第一个版本会导致ModuleNotFoundError
:
from package.subpackage_A import moduleA
Traceback (most recent call last):
...
ModuleNotFoundError: No module named 'package.subpackage_A.moduleA'
如何在不破坏向后兼容性的情况下重命名模块?该解决方案应适用于 python 3.5 及更高版本。
【问题讨论】:
import *
解决方案遇到什么错误?
尝试moduleA
形成旧位置的脚本会得到ModuleNotFoundError
: ModuleNotFoundError: No module named 'package.subpackage_A.moduleA'
。我在上面的问题中添加了错误消息。
【参考方案1】:
问题是subpackage_B/__init__.py
不导入moduleA
和moduleB
所以import *
什么都不导入。
要么使用显式导入:
from .subpackage_B import moduleA, moduleB
或修改 subpackage_B/__init__.py
以显式执行导入:
from . import moduleA, moduleB
关于在使用 subpackage_A 时发出警告:您可以简单地触发它。里面subpackage_A
:
import warnings
warnings.warn('The name subpackage_A is deprecated. Please use subpackage_B')
from .subpackage_B import *
【讨论】:
在对不同的导入语句进行了一些尝试之后,这似乎在我的(有限的)测试用例中有效。【参考方案2】:如果您使用的是 Python 3.7+,则可以使用在 PEP562 中定义的模块级别 __getattr__
s。
您将您的 subpackageA 重命名为 subpackageB,然后在您的 package/__init__.py
中有如下内容
from . import subpackageB
from warnings import warn
def __getattr__(name):
if name == 'subpackageA':
warn('subpackageA has been renamed to subpackageB')
return subpackageB
raise AttributeError('No module named ' + name)
然后在使用你的包时
>>> from package import subpackageB
>>> from package import subpackageA
/path/to/package/__init__.py:5: UserWarning: subpackageA has been renamed subpackageB
warn('subB has been renamed subA')
>>> subpackageA == subpackageB
true
>>>
【讨论】:
这似乎是一个不错的解决方案! python 3.5/3.6 有类似的可能吗? 你可以用import hooks(和PEP 302)做一些事情,但这会很不愉快。如果有机会,我会尝试做一些工作。 是的,我查看了导入钩子,但对我来说,文档在哪里抽象以了解从哪里开始...... 这在使用from package import subpackageA
时有效,但在使用import package.subpackageA
时似乎失败(默认为正常导入错误)。在这种情况下有什么好的方法让它工作吗?我尝试了不同级别包 init 的一些变体,但似乎无法使其正常工作。一个理想的解决方案是您可以添加到单个包 init 中,它可以处理所有变化(例如,from package import subpackageA
、import package.subpackageA
、from package.subpackageA import SomeClass
等)。
您也许可以创建一个subpackageA/__init__.py
文件来执行from package import subpackageA
然后将__all__
设置为dir(subpackageA)
并执行一些类似的__getattr__
重整以重定向到导入的包?目前我的机器上没有 3.7+ 的安装来测试。以上是关于重命名python子包,将旧名称标记为已弃用的主要内容,如果未能解决你的问题,请参考以下文章