我有一个共享的Python代码库,我负责其他人依赖的代码。我需要将模块从一个子包移动到另一个目录/包以重新组织它。如何以最安全的方式做到这一点?
如果我只是移动代码,我不得不担心使用它的其他人可能没有重定向他们的导入。如果移动了代码,并且代码的用户未更改其导入,则当导入失败时,其代码将意外失败。
如何确保无缝过渡?我是否只是复制代码并保留旧代码,直到导入更改?有什么需要注意的吗?如果我将import *
与__all__
一起使用怎么办?在什么情况下,我必须无限期地支持从旧位置导入?
我最近被要求在工作中将代码从一个子包移动到另一个子包,而我使用的方法对其他开发人员来说似乎并不明显,所以我在这里为其他人记录它。
我不建议在旧地方留下副本。如果您有同一脚本的两个副本,则一个副本可能会更改而没有另一个副本。相反,我建议使用以下多步骤过程。
第一步涉及两个部分,如果控制代码的两个位置,则可以同时实现这两个部分。
第一步:实施移动
-
首先,将文件从旧位置移动到版本控制下的新位置。我使用简化的 CVS 界面,所以它是一个版本控制副本。在大多数其他版本控制系统(如 mercurial、Subversion 和 git)中,你应该使用
mv
来移动文件,例如使用 git:git mv /location/old/script.py /location/new/script.py
重要:
不要忘记移动单元测试,如果旧代码中有代码需要保留,也不要忘记移动__init__.py
。否则,请确保提交__init__.py
如果它们尚未到位或
接下来,代替旧代码,从新位置导入所有名称,
所以在
/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 y
、import x.y.z as w
等