重命名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 不导入moduleAmoduleB 所以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 subpackageAimport package.subpackageAfrom package.subpackageA import SomeClass 等)。 您也许可以创建一个subpackageA/__init__.py 文件来执行from package import subpackageA 然后将__all__ 设置为dir(subpackageA) 并执行一些类似的__getattr__ 重整以重定向到导入的包?目前我的机器上没有 3.7+ 的安装来测试。

以上是关于重命名python子包,将旧名称标记为已弃用的主要内容,如果未能解决你的问题,请参考以下文章

delphi 如何将属性标记为已弃用?

将 PLSQL 过程/函数标记为已弃用

如何在 RAML 中将 REST 服务标记为已弃用

是否可以在打字稿中将某些内容标记为已弃用?

如何在 Delphi 中将单个枚举值标记为已弃用

代码分析/FxCop CA1726:UsePreferredTerms 显示已取消为已弃用