You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**pythonloc** is a drop in replacement for `python` and `pip` that automatically recognizes a `__pypackages__` directory and prefers importing packages installed in this location over user or global site-packages. If you are familiar with node, `__pypackages__` works similarly to `node_modules`.
11
11
@@ -15,6 +15,8 @@ This is an alternative to using Virtual Environments.
15
15
16
16
This is a Python implementation of [PEP 582](https://www.python.org/dev/peps/pep-0582/), "Python local packages directory". The goal of pythonloc is to make an accessible tool while discussion takes place around adding this functionality to CPython itself. If you prefer, you can [build your own CPython](https://github.com/kushaldas/cpython/tree/pypackages) with these changes instead of using `pythonloc`.
17
17
18
+
**Please note that PEP 582 has not been accepted. It may or not be accepted in the long term. `pythonloc` is experimental and its API may change in the future.**
19
+
18
20
## Testimonials
19
21
20
22
*Featured on [episode #117](https://pythonbytes.fm/episodes/show/117/is-this-the-end-of-python-virtual-environments) of the Python bytes podcast.*
@@ -41,7 +43,7 @@ or
41
43
```
42
44
python3 -m pip install --user pythonloc
43
45
```
44
-
you will have three CLI tools available to you: **pythonloc**, **piploc**, and **pipfreezeloc**.
46
+
you will have four CLI tools available to you: **pythonloc**, **piploc**, **pipx**, and **pipfreezeloc**.
45
47
46
48
### pythonloc
47
49
Short for "python local", it is a drop-in replacement for python with one important difference: the local directory `__pypackages__/<version>/lib` is added to the front of `sys.path`. `<version>` is the Python version, something like `3.7`. All arguments are forwarded to `python`.
@@ -57,6 +59,8 @@ you would run
57
59
pythonloc ...
58
60
```
59
61
62
+
If PEP 582 is adopted, `python` itself will have this behavior.
63
+
60
64
### piploc
61
65
Short for "pip local", it invokes pip with the same `sys.path` as `pythonloc`. If installing a package, the target installation directory is modified to be `__pypackages__` instead of the global `site-packages`.
62
66
@@ -75,6 +79,31 @@ you would run
75
79
piploc ...
76
80
```
77
81
82
+
If PEP 582 is adopted, I think `pip` should default to working in the appropriate `__pypackages__` directory. A flag can be added to install to site-packages, if desired.
83
+
84
+
### pipx
85
+
Installing packages that have so called "entry points" to `__pypackages__` presents a problem. The entry points, or "binaries", are no longer available on your $PATH as they would be if you installed in a virtual environment or to your system. These binaries are massively popular and useful. Examples of binaries are `black`, `pytest`, `tox`, `flake8`, `mypy`, `poetry`, and `pipenv` (and indeed `pythonloc` itself).
86
+
87
+
`pipx` is a binary installer and runner for Python that, when run, searches for a binary in the appropriate `__pypackages__` location and runs it. If you are familiar with JavaScript's [`npx`](https://www.npmjs.com/package/npx), it's similar to that.
88
+
89
+
So instead of running
90
+
```
91
+
BINARY [BINARY ARGS]
92
+
```
93
+
you would run
94
+
```
95
+
pipx run BINARY [BINARY ARGS]
96
+
```
97
+
If not found, pipx will install and run it from a temporary directory. If you require the binary to be found in the `__pypackages__` directory, you can run
98
+
```
99
+
pipx run --pypackages BINARY [BINARY ARGS]
100
+
```
101
+
If the binary is not found, and error will be presented.
102
+
103
+
Something to note here: When installing a new package to an existing `__pypackages__` directory, the entry points will not be created in `.../3.6/lib/bin`, for example, if something is already there. To do that, you need to run `piploc install -U PACKAGE`. When you do that, the entire contents of the directory will be replaced. Fixing this would require a modification to `pip` itself.
104
+
105
+
If PEP 582 is adopted, `pipx` will be a good companion tool to run binaries.
106
+
78
107
### pipfreezeloc
79
108
Running `pip freeze` presents a problem because it shows all installed python packages: those in `site-packages` as well as in `__pypackages__`. You likely only want to output the packages installed to `__pypackages__` and that is exactly what `pipfreezeloc` does.
80
109
@@ -93,6 +122,8 @@ you would run
93
122
pipfreezeloc > requirements.txt
94
123
```
95
124
125
+
If PEP 582 is adopted, a more robust solution to freezing the state of `__pypackages__` should be created.
126
+
96
127
## Installing from requirements.txt/Lockfiles
97
128
This works just like it does in pip. You just need a `requirements.txt` file to install from.
Using cached https://files.pythonhosted.org/packages/e7/e7/e93f311adf63ac8936beb962223771b1ab61227ae3d9ec86e8d9f8f9da1c/cowsay-2.0-py2.py3-none-any.whl
214
+
Installing collected packages: cowsay
215
+
Successfully installed cowsay-2.0
216
+
217
+
> pipx run cowsay moooo from local __pypackages__!
218
+
________________________________
219
+
< moooo from local __pypackages__! >
220
+
================================
221
+
\
222
+
\
223
+
^__^
224
+
(oo)\_______
225
+
(__)\ )\/ ||----w |
226
+
|| ||
227
+
228
+
229
+
```
230
+
180
231
## Downsides?
181
232
182
233
While this PEP is pretty exciting, there are a some things it doesn't solve.
183
234
184
-
* entrypoints: when you install a package, any entry points a package may have in the `bin` folder (like `black` or `tox`) are not accessible based on this PEP. [pipx](https://github.com/pipxproject/pipx) (also my project), is the perfect tool to search in `__pypackages__/3.6/lib/bin` for the entry point you want to run. So you would run `pipx run tox` and it would locate `__pypackages__/3.6/lib/bin`. (It doesn't currently do this.) This is very similar to [npx](https://www.npmjs.com/package/npx), which will search in `node_modules/bin`.
185
235
* OS-dependent packages: The directory structure in `__pypackages__` is namespaced on python version, so packages for Python 3.6 will not mix with 3.7, which is great. But sometimes packages install differently for different OS's, so Windows may not match mac, etc.
186
-
* site-packages: This PEP first looks to `__pypackages__` but will fall back to looking in `site-packages`. This is not entirely hermetic and could lead to some confusion around which packages are being used. I would prefer the search path be **only**`__pypackages__` and nothing else.
187
-
* perceived downside -- bloat: Many have brought this up in various forums, comparing it to `node_modules`, but I don't think it applies here. For one, the same if not more "bloat" is installed into a virtual environment, so this just moves it into a local directory. No additional bloat. In fact, it is more obvious and can be deleted because it's not hidden away in a virtual env directory. But more importantly, I think the assumption that it is bloated or will be abused stems from JavaScript's ecosystem. JavaScript has a notoriously limited standard library, and developers need to reach for third party packages more often. In addition, the JavaScript development heavily relies on many plugins and transpilation, something Python does not. I do not find the bloat argument convincing.
236
+
* site-packages: This PEP first looks to `__pypackages__` but will fall back to looking in `site-packages`. This is not entirely hermetic and could lead to some confusion around which packages are being used. I would prefer the default search path be **only**`__pypackages__` and nothing else.
237
+
* perceived downside -- bloat: Many have brought this up in various forums, comparing it to `node_modules`, but I don't think it applies here. For one, the same if not more content is installed into a virtual environment, so this just moves it into a local directory. No additional bloat. In fact, it is more obvious and can be deleted because it's not hidden away in a virtual env directory. But more importantly, I think the assumption that it is bloated or will be abused stems from JavaScript's ecosystem. JavaScript has a notoriously limited standard library, and developers need to reach for third party packages more often. In addition, the JavaScript community heavily relies on many plugins and transpilation. Python does not. I do not find the bloat argument convincing.
238
+
* Some pip installation idiosyncracies. For example, `pip install` with `--target` will wipe out content in the `lib/bin` directory when the `-U` flag is passed, but not put anything there when it's not passed.
0 commit comments