7

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

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

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

4

2 回答 2

10

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

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

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

第一步:实施行动

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

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

重要的:

__init__.py不要忘记移动单元测试,如果需要保留旧代码中的代码,也要移动's。__init__.py否则,如果它们还没有到位,请确保 commit 's 在那里

  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中创建的方式相同。但是,大多数人会回避修改. 事实上,我很犹豫把这个建议留在这里,如果它不在 Python 标准库中,我也不会。sys.modules

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

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

第二步:实现引用

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

(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 

等等。

重要的:

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

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

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

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

不过,在您可以证明没有其他用户在生产中引用旧包位置之前,请不要删除它。如果你不能证明它,不要删除它。

于 2015-06-14T18:13:11.550 回答
0

查克移动可能对你有用。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

于 2016-01-25T11:34:00.660 回答