如何将 Python 脚本从一个子包移动到另一个 directory/package,保持向后兼容性
How to move a Python script from one subpackage to another directory/package, maintaining backwards compatibility
我有一个共享的 Python 代码库,我负责其他人依赖的代码。我需要将模块从一个子包移动到另一个 directory/package 以重新组织它。我该如何以最安全的方式做到这一点?
如果我只是移动代码,我不得不担心使用它的其他人可能没有重定向他们的导入。如果它被移动并且代码的用户不更改他们的导入,他们的代码将在导入失败时意外失败。
如何确保无缝过渡?我是否只复制代码并将旧代码留在原处,直到导入被更改?有什么注意事项需要注意吗?如果我将 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 source 中创建 os.path
的方式相同。但是,大多数人会回避修改 sys.modules
。事实上,我犹豫是否要在这里留下这个建议,如果它不在 Python 标准库中,我也不会。
这两个部分可以一起投入生产,并且移动已经无缝实施。如果您无法控制代码的用户,为了向后兼容,这可能需要无限期保留。
可选:然后我会删除 head 的旧脚本(就在 head,不要推送它!)以便其他开发人员可以看到即将发生的变化并及时解决变化。
第二步:实施重新引用
如果您可以对依赖于您的代码的所有代码进行正则表达式搜索,我建议在代码中搜索以下正则表达式:
(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
以此类推
重要提示:
所有这些更改都需要实施并发布到生产环境中。如果在没有完成这些操作的情况下仍然存在任何生产安装,如果您删除旧位置,它们将失败。
第三步:删除生产中的旧脚本
这是危险的一步。如果您遗漏了任何 users/importers,他们的代码将失败,直到他们将导入修复程序投入生产。您可以选择无限期推迟这一步,但我更愿意及时完成它,前提是我能证明所有更改都已投入生产。
如果您在进行更改后立即将其删除,以便其他人可以看到开发中的更改,您可能就不用担心了。
不过,在您可以证明没有其他用户在生产环境中引用旧包位置之前,请不要删除它。不能证明就别删
chuckmove 可能适合您。 chuckmove 是一种工具,可让您递归地重写整个源代码树中的导入以引用模块的新位置。
chuckmove --old sound.utils --new media.sound.utils src
...这会下降到 src,并将导入 sound.utils 的语句重写为导入媒体。sound.utils。它支持整个范围的 Python 导入格式。 IE。 from x import y
、import x.y.z as w
等
我有一个共享的 Python 代码库,我负责其他人依赖的代码。我需要将模块从一个子包移动到另一个 directory/package 以重新组织它。我该如何以最安全的方式做到这一点?
如果我只是移动代码,我不得不担心使用它的其他人可能没有重定向他们的导入。如果它被移动并且代码的用户不更改他们的导入,他们的代码将在导入失败时意外失败。
如何确保无缝过渡?我是否只复制代码并将旧代码留在原处,直到导入被更改?有什么注意事项需要注意吗?如果我将 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 source 中创建 os.path
的方式相同。但是,大多数人会回避修改 sys.modules
。事实上,我犹豫是否要在这里留下这个建议,如果它不在 Python 标准库中,我也不会。
这两个部分可以一起投入生产,并且移动已经无缝实施。如果您无法控制代码的用户,为了向后兼容,这可能需要无限期保留。
可选:然后我会删除 head 的旧脚本(就在 head,不要推送它!)以便其他开发人员可以看到即将发生的变化并及时解决变化。
第二步:实施重新引用
如果您可以对依赖于您的代码的所有代码进行正则表达式搜索,我建议在代码中搜索以下正则表达式:
(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
以此类推
重要提示:
所有这些更改都需要实施并发布到生产环境中。如果在没有完成这些操作的情况下仍然存在任何生产安装,如果您删除旧位置,它们将失败。
第三步:删除生产中的旧脚本
这是危险的一步。如果您遗漏了任何 users/importers,他们的代码将失败,直到他们将导入修复程序投入生产。您可以选择无限期推迟这一步,但我更愿意及时完成它,前提是我能证明所有更改都已投入生产。
如果您在进行更改后立即将其删除,以便其他人可以看到开发中的更改,您可能就不用担心了。
不过,在您可以证明没有其他用户在生产环境中引用旧包位置之前,请不要删除它。不能证明就别删
chuckmove 可能适合您。 chuckmove 是一种工具,可让您递归地重写整个源代码树中的导入以引用模块的新位置。
chuckmove --old sound.utils --new media.sound.utils src
...这会下降到 src,并将导入 sound.utils 的语句重写为导入媒体。sound.utils。它支持整个范围的 Python 导入格式。 IE。 from x import y
、import x.y.z as w
等