使用本地目录__pypackages__中的包运行Python
项目描述
pythonloc:从本地目录导入包的Python替代品
pythonloc是python
和pip
的替代品,它可以自动识别一个__pypackages__
目录,并优先导入安装在此位置的包。如果你熟悉node,__pypackages__
的工作方式与node_modules
类似。
因此,你不用运行python
,而是运行pythonloc
,它将首先搜索__pypackages__
路径中的包。同样,你不用运行pip
,而是运行piploc
,它将从这个__pypackages__
中安装/卸载。
这是使用虚拟环境的一种替代方案。
这是PEP 582的Python实现,"Python本地包目录"。pythonloc的目标是在讨论将此功能添加到CPython本身的同时,提供一个可访问的工具。如果你愿意,你可以使用这些更改而不是pythonloc
来构建自己的CPython。
请注意,PEP 582尚未被接受。它可能最终会被接受。pythonloc
是实验性的,其API可能在将来发生变化。
示例
脚本
# myapp.py
import requests
print(requests)
> piploc install requests
Installing collected packages: urllib3, certifi, chardet, idna, requests
Successfully installed certifi-2018.11.29 chardet-3.0.4 idna-2.8 requests-2.21.0 urllib3-1.24.1
> pipfreezeloc
requests==2.21.0
> pythonloc myapp.py # works!
<module 'requests' from '/tmp/demo/__pypackages__/3.6/lib/requests/__init__.py'>
客户评价
在Python bytes播客的第117期节目中展出:节目 #117
"Chad一直在打包/pip空间中工作并编写一些令人兴奋的python工具和文章。"
—— Jeff Triplett,Python软件基金会董事
"我对__pypackages__
如何帮助简化并优化Python依赖项工作流程感到非常热情。为人们提供早期原型实现,做得很好!”
—— Florimond Manca,Bocadillo Project的创始人,Bocadillo Project
系统要求
- Python 2.7+
- pip
安装:盒子里的内容
使用以下方式安装后
pip install --user pythonloc
或
python3 -m pip install --user pythonloc
您将拥有四个可用的CLI工具:pythonloc、piploc、pipx和pipfreezeloc。
pythonloc
简称“python local”,它是python的一个直接替代品,但有一个重要的不同之处:将本地目录__pypackages__/
添加到sys.path
的前面。<version>
是Python版本,例如3.7
。所有参数都传递给python
。
因此,您将运行
python ...
而不是
pythonloc ...
如果采用PEP 582,那么python
本身将具有这种行为。
piploc
简称“pip local”,它使用与pythonloc
相同的sys.path
调用pip。如果安装包,目标安装目录将修改为__pypackages__
而不是全局的site-packages
。
如果__pypackages__
目录不存在,它将被创建。
所有参数都传递给pip
。
因此,您将运行
pip ...
而不是
piploc ...
如果采用PEP 582,我认为pip
应该默认在适当的__pypackages__
目录下工作。如果需要,可以添加一个标志将其安装到site-packages。
pipx
注意:pipx仅包含在Python 3.6+的pythonloc中。
安装到__pypackages__
的具有所谓“入口点”的包会引发问题。这些入口点或“二进制文件”在您的$PATH中不再可用,就像您在虚拟环境中或系统上安装它们时一样。这些二进制文件非常流行且有用。二进制文件的示例包括black
、pytest
、tox
、flake8
、mypy
、poetry
和pipenv
(以及实际上pythonloc
本身)。
pipx
是Python的二进制安装程序和运行器,当运行时,它会在适当的__pypackages__
位置搜索二进制文件并运行它。如果您熟悉JavaScript的npx
,它与之类似。
因此,您将运行
BINARY [BINARY ARGS]
而不是
pipx run BINARY [BINARY ARGS]
如果没有找到,pipx将从临时目录安装和运行它。如果您需要二进制文件在__pypackages__
目录中,您可以运行
pipx run --pypackages BINARY [BINARY ARGS]
如果二进制文件未找到,将显示错误。
注意:在将新包安装到现有的__pypackages__
目录时,不会在例如.../3.6/lib/bin
中创建入口点,如果那里已经存在某些内容。为此,您需要运行piploc install -U PACKAGE
。当您这样做时,目录的全部内容将被替换。修复此问题需要修改pip
本身。
如果采用PEP 582,那么pipx
将是一个运行二进制文件的好伴侣工具。
pipfreezeloc
运行pip freeze
会引发问题,因为它显示所有已安装的Python包:在site-packages
中以及在__pypackages__
中。您可能只想输出安装到__pypackages__
的包,这正是pipfreezeloc
所做的工作。
它是pip freeze
的等价物,但只输出__pypackages__
中的包。这是因为没有内置的方式使用标准pip来完成此操作。例如,命令pip freeze --target __pypackages__
不存在。
pipfreezeloc不处理任何参数。
因此,您将运行
pip freeze > requirements.txt
而不是
pipfreezeloc > requirements.txt
如果采用PEP 582,则应创建一个更健壮的方案来冻结__pypackages__
的状态。
从requirements.txt/Lockfiles安装
这和pip中的操作一样。您只需要一个requirements.txt
文件来安装。
从requirements.txt
安装
piploc install -r requirements.txt
pythonloc <app>
从poetry.lock
安装
pip无法读取poetry.lock文件,因此您需要生成一个requirements.txt文件。
Poetry 1.x提供export
命令
poetry self:update --preview # install 1.x version of Poetry
poetry export -f requirements.txt
piploc install -r requirements.txt
pythonloc <app>
对于Poetry 0.x,您可以采用以下方法
poetry run pip freeze > requirements.txt
piploc install -r requirements.txt
pythonloc <app>
从Pipfile.lock
安装
pip还不能读取Pipfile
,只有pipenv可以。因此,您需要使用pipenv生成requirements.txt。
pipenv lock --requirements
pipenv lock --requirements --dev
piploc install -r requirements.txt
pythonloc <app>
从长远来看,工具将能够直接安装到__pypackages__
,或者piploc将能够读取各种lockfile格式。
命令行界面
您可以使用pythonloc运行任何Python命令,它将在底层运行Python
> pythonloc --help
> pythonloc --version
另一个示例展示了导入是如何工作的
> ls
> pythonloc -c "import requests; print(requests)"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'requests'
> piploc install requests # installs to __pypackages__/3.6/lib/requests
Installing collected packages: urllib3, certifi, chardet, idna, requests
Successfully installed certifi-2018.11.29 chardet-3.0.4 idna-2.8 requests-2.21.0 urllib3-1.24.1
> pythonloc -c "import requests; print(requests)" # requests is now found
<module 'requests' from '/tmp/demo/__pypackages__/3.6/lib/requests/__init__.py'>
> piploc uninstall requests # uninstalls from __pypackages__/3.6/lib/requests
Successfully uninstalled requests-2.21.0
入口点/二进制文件
> piploc install cowsay
Collecting cowsay
Using cached https://files.pythonhosted.org/packages/e7/e7/e93f311adf63ac8936beb962223771b1ab61227ae3d9ec86e8d9f8f9da1c/cowsay-2.0-py2.py3-none-any.whl
Installing collected packages: cowsay
Successfully installed cowsay-2.0
> pipx run cowsay moooo from local __pypackages__!
________________________________
< moooo from local __pypackages__! >
================================
\
\
^__^
(oo)\_______
(__)\ )\/ ||----w |
|| ||
缺点?
虽然这个PEP非常令人兴奋,但它也有一些没有解决的问题。
- 操作系统依赖的包:在
__pypackages__
中的目录结构在Python版本上是命名空间化的,因此Python 3.6的包不会与3.7的包混合,这是非常好的。但有时不同操作系统上的包安装方式不同,例如Windows可能不会与mac匹配等。 - site-packages:此PEP首先查找
__pypackages__
,但会回退到在site-packages
中查找。这并不完全封闭,可能会导致一些混淆,不清楚使用了哪些包。我更愿意默认搜索路径仅为__pypackages__
,而不使用其他任何内容。 - 感知的缺点——膨胀:许多人在各种论坛中提出了这一点,将其与
node_modules
进行比较,但我认为这不适用。首先,相同或更多内容被安装到虚拟环境中,所以这只是在本地目录中移动。没有额外的膨胀。事实上,这更加明显,可以删除,因为它不在虚拟环境目录中隐藏。但更重要的是,我认为它膨胀或将被滥用的假设源于JavaScript的生态系统。JavaScript有一个臭名昭著的有限标准库,开发人员需要更频繁地寻找第三方包。此外,JavaScript社区严重依赖于许多插件和转译。Python不是。我没有找到膨胀论据令人信服。 - 一些pip安装的怪癖。例如,使用
pip install
和--target
时,如果传递了-U
标志,将会清除lib/bin
目录中的内容,如果没有传递该标志,则不会放置任何内容。
常见问题解答
这与虚拟环境有何不同?
- 虚拟环境可能包含或不包含系统包,而
pythonloc
将首先在.
中查找包,然后是__pypackages__
,然后是在用户或site-packages等其他位置。 pythonloc
不需要激活或关闭pythonloc
只查找一个名为__pypackages__
的本地目录。另一方面,虚拟环境激活会修改您的PATH
,以便无论您处于哪个目录,都可以访问虚拟环境中的包。
它是如何工作的?
这非常简单,代码少于100行。它使用Python和pip中已经构建的功能。
它所做的只是在调用Python和pip时提供一定程度的间接性。当运行Python时,它修改PYTHONPATH
环境变量以包含__pypackages__
。
如果您查看python --help
的输出,您会看到以下内容
PYTHONPATH
是一个以“:”分隔的目录列表,它作为默认模块搜索路径的前缀。结果是sys.path。
pythonloc
是PYTHONPATH=.:__pypackages__/<version>/lib:$PYTHONPATH python PYTHONARGS
的别名
要将包安装到__pypackages__
目录,它使用pip并运行
PYTHONPATH=.:__pypackages__/<version>/lib:$PYTHONPATH python -m pip PIPARGS
其中PIPARGS
是您传递给它的任何参数,例如piploc install requests
。
如果您正在安装软件包,它将插入参数 --target __pypackages__
。
实际上 __pypackages__
中放置了什么内容?
安装的软件包会被放置在那里。这包括它们的源代码,例如下面的 requests
目录。软件包的元数据存储在 *.dist-info
目录中。
如果您想修改或调试已安装软件包的源代码,这非常简单。只需在 __pypackages__
中打开相应的文件并编辑即可!
> piploc install requests
Installing collected packages: urllib3, certifi, chardet, idna, requests
Successfully installed certifi-2018.11.29 chardet-3.0.4 idna-2.8 requests-2.21.0 urllib3-1.24.1
> ls # note __pypackages__ was created
__pypackages__
> ls __pypackages__/3.6/lib
bin idna-2.8.dist-info
certifi requests
certifi-2018.11.29.dist-info requests-2.21.0.dist-info
chardet urllib3
chardet-3.0.4.dist-info urllib3-1.24.1.dist-info
idna
我如何从 __pypackages__
中卸载软件包?
piploc
将自动将 __pypackages__
添加到 $PYTHONPATH
,因此
piploc uninstall PACKAGE
将生效。
如果您遇到错误
未在 ..., 环境外部卸载软件包 PACKAGE
然后运行 deactivate
以确保您没有使用虚拟环境,然后再次尝试。
我能否让 python
来执行此操作而不是调用 pythonloc
?
要实现此行为的一种简单方法是,在您的本地目录中创建一个符号链接
ln -s `which pythonloc` python
ln -s `which piploc` pip
然后使用以下命令运行它们
./python
./pip
否则,您将不得不自己构建带有 GitHub 上 参考实现 的 CPython。
为什么不使用 PEP 582 的参考实现?
构建和分发自定义 CPython 构建的开销比安装 pip 软件包要大。
如果您感兴趣,则鼓励您查看它,它非常酷!
如果它被接受并添加到 CPython 中,那么 pythonloc
可能就不再需要了。
项目详情
下载文件
下载您平台上的文件。如果您不确定要选择哪个,请了解更多关于 安装软件包 的信息。