如何将 Python 脚本从一个子包移动到另一个目录/包,保持向后兼容性



我有一个共享的Python代码库,我负责其他人依赖的代码。我需要将模块从一个子包移动到另一个目录/包以重新组织它。如何以最安全的方式做到这一点?

如果我只是移动代码,我不得不担心使用它的其他人可能没有重定向他们的导入。如果移动了代码,并且代码的用户未更改其导入,则当导入失败时,其代码将意外失败。

如何确保无缝过渡?我是否只是复制代码并保留旧代码,直到导入更改?有什么需要注意的吗?如果我将import *__all__一起使用怎么办?在什么情况下,我必须无限期地支持从旧位置导入?

我最近被要求在工作中将代码从一个子包移动到另一个子包,而我使用的方法对其他开发人员来说似乎并不明显,所以我在这里为其他人记录它。

我不建议在旧地方留下副本。如果您有同一脚本的两个副本,则一个副本可能会更改而没有另一个副本。相反,我建议使用以下多步骤过程。

第一步涉及两个部分,如果控制代码的两个位置,则可以同时实现这两个部分。

第一步:实施移动

  1. 首先,将文件从旧位置移动到版本控制下的新位置。我使用简化的 CVS 界面,所以它是一个版本控制副本。在大多数其他版本控制系统(如 mercurial、Subversion 和 git)中,你应该使用 mv 来移动文件,例如使用 git:

     git mv /location/old/script.py /location/new/script.py
    

重要:

不要忘记移动

单元测试,如果旧代码中有代码需要保留,也不要忘记移动__init__.py。否则,请确保提交__init__.py 如果它们尚未到位或

  1. 接下来,代替旧代码,从新位置导入所有名称,

    所以在/location/old/script.py

     from location.new.script import *
    

    并发表评论,解释为什么需要这样做,并将更改提交到版本控制。如果您移动了__init__.py,只需确保提交一个新的空__init__.py

这里有一个重要的警告import *受到__all__的影响。如果声明了__all__,则有两种方法可以提供缺少的名称。您可以显式导入它们:

from location.new.script import *
# names not in the new.script's __all__:
from location.new.script import foo, bar, baz 

或者您可以删除该文件,改为在__init__.py中导入模块,并将路径添加到 sys.modules,如下所示:

from location.new import script
import sys
sys.modules['location.old.script'] = script

此代码将初始化包并将模块添加到sys.modules,以便导入程序在那里查找它。这与在 Python 源代码中创建os.path的方式相同。然而,大多数人会回避修改sys.modules。事实上,我犹豫是否要把这个建议留在这里,如果它不在 Python 标准库中,我也不会。

这两部分可以一起推入生产,并且已经无缝实施。如果您无法控制代码的用户,则可能需要无限期地保留此代码以实现向后兼容性。

可选:然后我会删除头部的旧脚本(只是头部,暂时不要推送它!),以便其他开发人员可以看到即将发生的更改并及时解决更改。

步骤 2:实现重新引用

如果可以对依赖于代码的所有代码执行正则表达式搜索,我建议在代码中搜索以下正则表达式:

(import|from).*location.old.*script

如果你在Unix(或有Cygwin),你可以做一个正则表达式搜索它:

grep -rEe "(import|from).*location.old.*script" .

或者大多数 IDE 都有正则表达式搜索。

如果您确实可以控制使用它的代码,或者您对使用它的其他代码有看法,那么将导入从旧更改为新是相当简单的,例如:

import location.old.script

import location.new.script

和从

from location.old import script

from location.new import script 

等等。

重要:

所有这些更改都需要实施并发布到生产环境。如果任何生产安装仍未完成这些操作,则删除旧位置,它们将失败。

第三步:删除生产中的旧脚本

这是危险的一步。如果您错过了任何用户/导入程序,他们的代码将失败,直到他们将导入修复程序投入生产。您可以选择无限期推迟此步骤,但我的偏好是,如果我能证明所有更改都已投入生产,则最好及时完成此步骤。

如果您在进行更改后立即将其删除,以便其他人可以看到开发中的更改,则可能不必担心。

不过,在证明

没有其他用户引用生产中的旧包位置之前,不要删除此内容。如果您无法证明,请不要删除它。

> ChuckMove可能适合你。 ChuckMove 是一个工具,可让您递归重写整个源代码树中的导入以引用模块的新位置。

chuckmove --old sound.utils --new media.sound.utils src

。这将下降到 src,并重写导入 sound.utils 的语句以导入 media.sound.utils。它支持整个范围的Python导入格式。即 from x import yimport x.y.z as w

最新更新