Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python fix venv #358823

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Python fix venv #358823

wants to merge 3 commits into from

Conversation

MagicRB
Copy link
Contributor

@MagicRB MagicRB commented Nov 24, 2024

This is a continuation of #326094 by @mcdonc which is itself a continuation of #297628 by @cwp. I approached the disabling of the preamble differently than those two previous PRs, namely my solution relies on prefixing the preamble with a setting of the NIX_PYTHON_IN_ENV environment variable, which causes the preamble to disable itself and then delete the injected variable. This way it effectively deletes it, without having to do textual matching on python code. I've tested this using the following snippet:

let
  pkgs = import ./. {};
in
{
  env = (pkgs.python3.withPackages (ps: [ (ps.toPythonModule pkgs.meson) ps.zope-interface ]));
}

result/bin/meson is functional, so is result/bin/python in which import zope.interface and import mesonbuild works. meson itself also relies on Python while building, so it will catch any problems sooner. I'll next build pretalx and verify that works, though I doubt I managed to break that.

Furthermore I ran and verified the showcased commands from #297628 still work:

# build using a patched nixpkgs
$ nix-build   -I nixpkgs="$HOME/repos/nixpkgs"   -E '{pkgs ? import <nixpkgs> {}}: pkgs.python3.withPackages (ps: [ps.requests])'   -o fixed

/nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env
# we can import packages from the nix environment and the venv
$ /nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env/bin/python -m venv --system-site-packages fixed-venv
$ fixed-venv/bin/pip install six
Collecting six
  Using cached six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB)
Using cached six-1.17.0-py2.py3-none-any.whl (11 kB)
Installing collected packages: six
Successfully installed six-1.17.0

[notice] A new release of pip is available: 24.2 -> 24.3.1
[notice] To update, run: /home/main/repos/nixpkgs/fixed-venv/bin/python -m pip install --upgrade pip
$ fixed-venv/bin/python -c 'import six; import requests; print(six); print(requests)'
<module 'six' from '/home/main/repos/nixpkgs/fixed-venv/lib/python3.12/site-packages/six.py'>
<module 'requests' from '/nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env/lib/python3.12/site-packages/requests/__init__.py'>

# sys.prefix matches sys.base_prefix, so it doesn't look like a venv
$ /nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env/bin/python -c 'import sys; print(sys.base_prefix); print(sys.prefix)'
/nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env
/nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env

# this also works when python is invoked through a symlink
$ fixed/bin/python -c 'import sys; print(sys.base_prefix); print(sys.prefix)'
/home/main/repos/nixpkgs/fixed
/home/main/repos/nixpkgs/fixed

# the venv has sys.base_prefix set to the environment
$ fixed-venv/bin/python -c 'import sys; print(sys.base_prefix); print(sys.prefix)'
/nix/store/qmfiqibns0z4zcl7xwk8awx3hpdr28ny-python3-3.12.7-env
/home/main/repos/nixpkgs/fixed-venv

Finally finished the pretalx build

#!/nix/store/sgj421xaafqqn3hf4592h6x61my5d0vc-python3-3.12.7/bin/python3
import sys;import site;import functools;import os;_nix_direct = os.environ.pop("NIX_PYTHON_IN_ENV", None) != "true";sys.argv[0] = '/nix/store/3yz5c8pkgvlkym382kq51d8r1pc7p425-pretalx-2024.3.1/bin/pretalx-manage' if _nix_direct else sys.argv[0];functools.reduce(lambda k, p: site.addsitedir(p, k), ['/nix/store/3yz5c8pkgvlkym382kq51d8r1pc7p425-pretalx-2024.3.1/lib/python3.12/site-packages','/nix/store/g9pypn7jshk0as9b97lsjifr3yk81gq6-python3.12-beautifulsoup4-4.12.3/lib/python3.12/site-packages','/nix/store/0nniy78lhb1826x0vmx0jcrhv323k526-python3.12-chardet-5.2.0/lib/python3.12/site-packages','/nix/store/0jig3pyigz38vrs3zxbm99j92fqcyy88-python3.12-soupsieve-2.6/lib/python3.12/site-packages','/nix/store/nmwwwbm7ha79vlws7l3nqya22dfapcsk-python3.12-bleach-6.1.0/lib/python3.12/site-packages','/nix/store/lnmn549dzixbc65sf0y2w7dl4lf3mfkq-python3.12-html5lib-1.1/lib/python3.12/site-packages','/nix/store/z5xyw19hwgdz2p5rjhn14n02sz1w96z2-python3.12-six-1.16.0/lib/python3.12/site-packages','/nix/store/l8qzd3cbc3amabnqq8gxsyvd3bk7j55k-python3.12-webencodings-0.5.1/lib/python3.12/site-packages','/nix/store/yv9xxf8bi7iwxjhpzdxk086fi98cs0mx-python3.12-packaging-24.1/lib/python3.12/site-packages','/nix/store/36hwvzrpqqjb3fd8j8cf8d0q0apzsi0n-python3.12-setuptools-75.1.1/lib/python3.12/site-packages','/nix/store/n5aldd4jpbafmihlx9hsvsxzb84aqwjk-python3.12-celery-5.4.0/lib/python3.12/site-packages','/nix/store/am6imv5i0vij26faxb40jr3xzyml9x4a-python3.12-billiard-4.2.1/lib/python3.12/site-packages','/nix/store/2f6hnhxwyins7k9fh5i71scxkajf65l1-python3.12-click-8.1.7/lib/python3.12/site-packages','/nix/store/khdkv8qvksyzg56dablrkp60gin9242a-python3.12-click-didyoumean-0.3.1/lib/python3.12/site-packages','/nix/store/wf0hnrbvif63wxq641h1x4n5vp8l41ln-python3.12-click-plugins-1.1.1/lib/python3.12/site-packages','/nix/store/mchm2mxss2dasfr4ngk7wka0wrffqgri-python3.12-click-repl-0.3.0/lib/python3.12/site-packages','/nix/store/xgs8vzv400r5h7na2a8cd00wp1cm12wc-python3.12-prompt-toolkit-3.0.48/lib/python3.12/site-packages','/nix/store/807sy4s5c5l5jqvb3nnnzl3nq0w6kp53-python3.12-wcwidth-0.2.13/lib/python3.12/site-packages','/nix/store/kadi13ybmb9si58fqixkrgx0cmlrqc72-python3.12-kombu-5.4.2/lib/python3.12/site-packages','/nix/store/ljn3hbgyvvf6jnpv8r1f10zx89f97kzk-python3.12-amqp-5.3.0/lib/python3.12/site-packages','/nix/store/fmg8xdajni97g2xhfi50q8g0gjgki4kd-python3.12-vine-5.1.0/lib/python3.12/site-packages','/nix/store/8mmgi8p1bfl3dwfgc8j8k9wcgrk7i5rg-python3.12-tzdata-2024.2/lib/python3.12/site-packages','/nix/store/88ifl2q4ijqqm07wwczpzdli03y3mv0d-python3.12-python-dateutil-2.9.0.post0/lib/python3.12/site-packages','/nix/store/56fwsb83qdd7gvn2qz1p6k7xknzi3fjg-python3.12-css-inline-0.14.1/lib/python3.12/site-packages','/nix/store/56q8w9nfc71yhlbxzfkyjp6hfcf9nh3j-python3.12-csscompressor-0.9.5/lib/python3.12/site-packages','/nix/store/dxghmhggk8pxg2bwk9kaq1kva1c60f3m-python3.12-cssutils-2.11.1/lib/python3.12/site-packages','/nix/store/pbpvrm98hrnhrrnz1p843i6bhyj4bbxw-python3.12-more-itertools-10.5.0/lib/python3.12/site-packages','/nix/store/4cs5cm14lzjva9dp6vir9m8bp8kqsd4n-python3.12-defusedcsv-2.0.0/lib/python3.12/site-packages','/nix/store/09a3yg7q24h1004lgj6b23aq63iq02yk-python3.12-defusedxml-0.8.0rc2/lib/python3.12/site-packages','/nix/store/zzy9k9fjk5bxp94rpq6n2sk4dyacrvlr-python3.12-django-5.1.4/lib/python3.12/site-packages','/nix/store/0xiw6nnp33sk7y9w5l4ff6bf46wws892-python3.12-asgiref-3.8.1/lib/python3.12/site-packages','/nix/store/k2xvdcl7wqw5gmilscrskvigwysd5hdd-python3.12-typing-extensions-4.12.2/lib/python3.12/site-packages','/nix/store/2qn6qgkbahbpyfpl7vjhc7xha0nrmky9-python3.12-sqlparse-0.5.1/lib/python3.12/site-packages','/nix/store/9iv0b2kanmmhb107fpny6jg1kcalcbhs-python3.12-django-bootstrap4-3.0.0/lib/python3.12/site-packages','/nix/store/jkalw45pfyfissmi21gpwgjp5fa06lp6-python3.12-django-compressor-4.5.1/lib/python3.12/site-packages','/nix/store/v7cp70zhbsjk10mkiqz9jfcad8gsv3qv-python3.12-calmjs-3.4.4/lib/python3.12/site-packages','/nix/store/qks5lsc690177xwlgk5ga59r1lgkpgh0-python3.12-calmjs-parse-1.3.2/lib/python3.12/site-packages','/nix/store/rzsii0fymgiik7dk7jing39lk6kx4v9v-python3.12-ply-3.11/lib/python3.12/site-packages','/nix/store/0fmczj65nmzzbmkgi0qa4p03zg6mmp4s-python3.12-calmjs-types-1.0.1/lib/python3.12/site-packages','/nix/store/zpmikliwi833ial4qalhzbj46qw0b928-python3.12-django-appconf-1.0.6/lib/python3.12/site-packages','/nix/store/72y9cqdfrvvd1p61ggi272jcz1qj3jhh-python3.12-jinja2-3.1.4/lib/python3.12/site-packages','/nix/store/7aqf4095ccvhyy07zvhy155bgmwvf50h-python3.12-markupsafe-3.0.2/lib/python3.12/site-packages','/nix/store/ka93h814ll654b9ax7g8fm3mb9rgq48p-python3.12-rcssmin-1.1.2/lib/python3.12/site-packages','/nix/store/3wq6sy7q0mjlrzba6agy7j80wbb56mc1-python3.12-rjsmin-1.2.2/lib/python3.12/site-packages','/nix/store/z2g50pf9wi7p59d910l8g0caspdgj1an-python3.12-django-context-decorator-1.6.1/lib/python3.12/site-packages','/nix/store/5g424biq7j19plcjwf8n87n3cb5im973-python3.12-django-countries-7.6.1/lib/python3.12/site-packages','/nix/store/5v5njrcj5ycscpc2p1bw6ya13bcpj14r-python3.12-django-csp-3.8/lib/python3.12/site-packages','/nix/store/w6ggsahavr20ysnhrisy8nxb2bc6v78w-python3.12-django-filter-24.3/lib/python3.12/site-packages','/nix/store/5r7bx7lcdghdhzx9wp47f5mkz06apgpx-python3.12-django-formset-js-improved-0.5.0.3/lib/python3.12/site-packages','/nix/store/crcfbviqnx4mc0gb48a3s670jzkzxz82-python3.12-django-jquery-js-3.1.1/lib/python3.12/site-packages','/nix/store/ks0408s6n8gvgm84nw5pa281vbvwlv43-python3.12-django-formtools-2.5.1/lib/python3.12/site-packages','/nix/store/4my9wa7zs6525n51c87c6j0cxgdfgzjf-python3.12-django-hierarkey-1.2.0/lib/python3.12/site-packages','/nix/store/5cx0g08r87khn5qv1npm98mzwxdilxqi-python3.12-django-i18nfield-1.9.4/lib/python3.12/site-packages','/nix/store/mmiz6av057pcdrnxprcly7x8phxqs6av-python3.12-django-libsass-0.9/lib/python3.12/site-packages','/nix/store/sg80plxwdg1pb68kfipja6zyb28yvd0h-python3.12-libsass-0.23.0/lib/python3.12/site-packages','/nix/store/hifncj0mwbla99l9i2d0138zn8dv8fsk-python3.12-django-scopes-2.0.0/lib/python3.12/site-packages','/nix/store/aynggq0swz36gy0q0nvnm2vv85k98cgb-python3.12-djangorestframework-3.15.2/lib/python3.12/site-packages','/nix/store/cbwdx15dyf3hc01zq5vd2ga0as85l862-python3.12-pygments-2.18.0/lib/python3.12/site-packages','/nix/store/15wgkzznnpj8cxz7r740gjw81da06k8m-python3.12-markdown-3.7/lib/python3.12/site-packages','/nix/store/wfpxnh13qvxx8s9p2wmnrii3wpwgg3gh-python3.12-pillow-11.0.0/lib/python3.12/site-packages','/nix/store/gbalrvbpp20d7x0y4dwm7315kpfc5cyv-python3.12-publicsuffixlist-1.0.2.20241216/lib/python3.12/site-packages','/nix/store/ky8wr9vpmjzwzkj6q223w6z3rlci4dxb-python3.12-qrcode-8.0/lib/python3.12/site-packages','/nix/store/0q7n7q1nzgjzzq9gr4dpzd4wpj56g3k6-python3.12-reportlab-4.2.4/lib/python3.12/site-packages','/nix/store/ki99ygiww9ysrgd43ldf2hrl3vcdz379-python3.12-requests-2.32.3/lib/python3.12/site-packages','/nix/store/fqvnx9hqgbni28f6zk6fvvkmkpza60jf-python3.12-brotlicffi-1.1.0.0/lib/python3.12/site-packages','/nix/store/617bxjm79kjy1r23lwvdznfmp56b49w2-python3.12-cffi-1.17.1/lib/python3.12/site-packages','/nix/store/xlhls6i03f5k1dls45hn996xxsc5pd6m-python3.12-pycparser-2.22/lib/python3.12/site-packages','/nix/store/n3fs1vqfgqk0lpy8zh7b3ik18d29rf7p-python3.12-certifi-2024.08.30/lib/python3.12/site-packages','/nix/store/xkqhhzrfvyn8iyqa27441jzly8n2k3vq-python3.12-charset-normalizer-3.3.2/lib/python3.12/site-packages','/nix/store/8gh5fsdfznvcz1znpapqk95gfjmf992c-python3.12-idna-3.10/lib/python3.12/site-packages','/nix/store/sdrsnagvifpblm5xck3lkv6jm9p415cg-python3.12-urllib3-2.2.3/lib/python3.12/site-packages','/nix/store/fd3px25yz7x5i8rmacw5mzinjh794ilw-python3.12-rules-3.5.0/lib/python3.12/site-packages','/nix/store/7ybn94vzjylb1z8bgcbmmjr6hmxvjzpl-python3.12-urlman-2.0.1/lib/python3.12/site-packages','/nix/store/nrbfjn683x0fsyif7d0vqn7r22cf8w7p-python3.12-vobject-0.9.8/lib/python3.12/site-packages','/nix/store/v4c1s4ri7arqxmdf4ixajr5fhlpl0k5l-python3.12-pytz-2024.2/lib/python3.12/site-packages','/nix/store/v2bqf42l79qmn2s6s9khr5b2gml9v0an-python3.12-whitenoise-6.7.0/lib/python3.12/site-packages','/nix/store/y5cs49vdy44qkh4p1j0yq9502yxcq0ji-python3.12-brotli-1.1.0/lib/python3.12/site-packages','/nix/store/6wmqafwc1ich4bi2f1c23vk9rmy42qgp-python3.12-zxcvbn-4.4.28/lib/python3.12/site-packages','/nix/store/1pps8i6s7d33zd6q4y2k013jlln4cprh-python3.12-lxml-5.3.0/lib/python3.12/site-packages','/nix/store/m3r72j60scvbpp0vin7gs5mgvi8128yl-python3.12-argon2-cffi-23.1.0/lib/python3.12/site-packages','/nix/store/bi8pa87078xgdirbckqav447jgafwxax-python3.12-argon2-cffi-bindings-21.2.0/lib/python3.12/site-packages'], site._init_pathinfo()) if _nix_direct else None;
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretalx.settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

and it also launches

cwp and others added 3 commits December 20, 2024 00:06
The way we build python environments is subtly broken. A python
environment should be semantically identical to a vanilla Python
installation in, say, /usr/local. The current implementation, however,
differs in two important ways. The first is that it's impossible to use
python packages from the environment in python virtual environments. The
second is that the nix-generated environment appears to be a venv, but
it's not.

This commit changes the way python environments are built:

  * When generating wrappers for python executables, we inherit argv[0]
    from the wrapper. This causes python to initialize its configuration
    in the environment with all the correct paths.
  * We remove the sitecustomize.py file from the base python package.
    This file was used tweak the python configuration after it was
    incorrectly initialized. That's no longer necessary.

The end result is that python environments no longer appear to be venvs,
and behave more like a vanilla python installation. In addition it's
possible to create a venv using an environment and use packages from
both the environment and the venv.

(cherry picked from commit 234bb31)
When building a python environment's bin directory, we now detect
wrapped python scripts from installed packages, and generate unwrapped
copies with the environment's python executable as the interpreter.

(cherry picked from commit 9611885)
@MagicRB
Copy link
Contributor Author

MagicRB commented Dec 20, 2024

I've rebased again and I'm currently verifying that pretalx works and doesn't get any lines deleted. The build is taking ages
done and pretalx works

@MagicRB MagicRB marked this pull request as ready for review December 20, 2024 01:09
@MagicRB MagicRB requested a review from ruro December 20, 2024 13:57
@zimbatm zimbatm requested review from domenkozar and removed request for ruro December 22, 2024 11:21
@ruro
Copy link
Contributor

ruro commented Jan 7, 2025

I'm going to cherry-pick this on top of my current system nixpkgs. I'll try to daily drive this for a bit.

Meanwhile, I've been coming back to this PR once in a while and thinking about how we could make the situation less jank. I'll publish the resulting review comments shortly.

@@ -42,7 +42,15 @@ let
if [ -f "$prg" ]; then
rm -f "$out/bin/$prg"
if [ -x "$prg" ]; then
makeWrapper "$path/bin/$prg" "$out/bin/$prg" --set NIX_PYTHONPREFIX "$out" --set NIX_PYTHONEXECUTABLE ${pythonExecutable} --set NIX_PYTHONPATH ${pythonPath} ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"''} ${lib.concatStringsSep " " makeWrapperArgs}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the only place where pythonPath was used in this file, so it should probably be removed from the above let-in.

import os
_nix_direct = os.environ.pop("NIX_PYTHON_IN_ENV", None) != "true"
sys.argv[0] = '"'$(readlink -f "$f")'"' if _nix_direct else sys.argv[0]
functools.reduce(lambda k, p: site.addsitedir(p, k), ['"$([ -n "$program_PYTHONPATH" ] && (echo "'$program_PYTHONPATH'" | sed "s|:|','|g") || true)"'], site._init_pathinfo()) if _nix_direct else None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I am still kind of conflicted about all this wrapping/unwrapping nonsense.

It seems to me that the sys.argv and site.addsitedir shenanigans here are basically doing the same thing as the removed sitecustomize.py. So shouldn't we also remove this "preamble" and instead just set substitutions.executable to an instance of python that already has all the required dependencies?

Copy link
Contributor

@ruro ruro Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought about it some more, and it seems to me that the primary reason why we can't do this is that it would create a circular reference.

Ideally, instead of doing site.addsitedir at runtime, the shebang in /nix/store/aaaa-python3.12-foo-1.0.0/bin/foo should just be replaced with #!/nix/store/bbbb-python3-3.12.8-env/bin/python. Where bbbb is an instance of python that would just "pick up" the correct environment (from /nix/store/bbbb-python3-3.12.8-env/lib/python3.12/site-packages).

However, /nix/store/bbbb-python3-3.12.8-env/lib/python3.12/site-packages would also need to contain symlinks to /nix/store/aaaa-python3.12-foo-1.0.0/lib/python3.12/site-packages/libfoo (because bin/foo also needs libfoo). But this is not possible since this would create a circular dependency aaaa (bin/foo) -> bbbb (bin/python) -> bbbb (lib/*/site-packages) -> aaaa (lib/*/site-packages/libfoo).

I think that this could be solved without creating any circular references if #170577 (or something like it) were to be implemented. Alternatively, the whole environment could be included inside the aaaa derivation (something like /nix/store/aaaa-python3.12-foo-1.0.0/nix-support/env/...).

Honestly, all of this is kind of yucky 😒 (less yucky than the current situation, of course).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You lost me :) i don't know nearly enough about python to effectively and correctly implement your suggestions. I went to fix the immediate problem if venvs being slightly broken, which I managed. I can implement the magic comment approach but as for fixing up the PR you linked, its probably beyond me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, @cwp and some other people were also considering removing the wrapping/unwrapping logic, but it was never actually implemented. My comments above were mainly my way of documenting my thought process for why removing this extra wrapping is more complicated than it might seem at first glance.

So yeah, this is mostly me talking to myself, but also kind of hoping that somebody with a better understanding of the current python3Packages machinery might swing by and sanity check my thoughts.

Removing the preamble-wrap thing should probably be beyond the scope of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah no worries feel free to think out-loud, it helps and your input is very welcome. Might be good merge this, if no one has objections so that we have a partial if cursed solution already in. Dunno if anyone who knows enough about the python infra will show up soon.

if [ -f ".$prg-wrapped" ] && ( cat ".$prg-wrapped" | head -n 1 | grep -q "python" ) ; then
echo "#!${pythonExecutable}" >> "$out/bin/$prg"
echo "import os" >> "$out/bin/$prg"
echo 'os.environ["NIX_PYTHON_IN_ENV"] = "true"' >> "$out/bin/$prg"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if we assume that we really DO need to wrap/unwrap python scripts, I still don't like that we have to write to the environment here simply to disable a piece of code a few lines later in the same file.

We are using the magic NIX_PYTHON_IN_ENV variable here, but the only other place this variable is used in nixpkgs is in the "preamble" in wrap-python.nix. Why do we need to touch the environment (or search the correct line to delete like in the previous iteration of this code)?

Since we control both the wrapping and the unwrapping logic, couldn't we massively simplify all this. Keep the preamble as it was before (always set sys.argv[0] and call site.addsitedir), but add a "magic comment" to the end (say, # NIX_PYTHON_ENV_PREAMBLE). Then, in the unwrapping code, just delete the line with the magic comment (sed '/NIX_PYTHON_ENV_PREAMBLE/d').

Copy link
Contributor

@ruro ruro Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it might be better to replace the old shebang with the new one, instead of just prepending the new one. Prepending the new shebang means that the resulting derivation contains references to both the old and the new python executable, which might be confusing/suboptimal in terms of why-depends/requisites/closures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right the magic comment thing would work and is quite easy to do. I'm personally hesitant to do it that way because deleting anything carries the risk of breaking things. Could be that at some point something else in nixpkgs wraps a wrapped python executable and then this code tries to undo the preamble which proceeds to delete the other wrap because it ended up on the same line. I know it's very unlikely but it just doesn't seem like a clean approach, neither is my solution, it's awful in a different way.

As for the still remaining reference, yeah you're right I didn't think of that. With my solution it's not possible to get rid of it.

Thanks for the daily drive, I don't use any python things on my laptop sadly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be that at some point something else in nixpkgs wraps a wrapped python executable and then this code tries to undo the preamble which proceeds to delete the other wrap because it ended up on the same line.

I don't think that this is possible, because that would imply that this hypothetical wrapping code inserts the code onto the same line with already existing code (which is broken anyway) instead of inserting a new line (like this wrapping code does).

If anything, I think that my proposed solution might be slightly more robust, because it would actually verify (by checking for the presence of the magic comment) that the script that we are trying to unwrap was actually wrapped by wrap-python.nix.

Also, I noticed, that the current code doesn't clean up the .$prg-wrapped scripts after replacing $prg with the proper "unwrapped" version.

As for the still remaining reference, yeah you're right I didn't think of that. With my solution it's not possible to get rid of it.

I think, that once we've verified that the NIX_PYTHON_ENV_PREAMBLE comment is present in the file, removing the old shebang line should be 100% safe, since the shebang line must be the first line in the script for it to function as a shebang in the first place.


Please, let me know if you are planning on adding any of the changes that I've suggested in the previous review. No pressure, if you are busy or unwilling to add these changes (I understand your reasoning for implementing the current approach, even if I still think that we ought to try to remove as much of this wrapping/unwrapping cruft as possible). I'd just like to know if it's worth waiting for you to add these changes or if I should make my own PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, now that I started thinking about the .$prg-wrapped scripts, I am suddenly unsure if the current "unwrapping" implementation is 100% correct.

The "wrapping" performed in wrap-python.nix consists of two parts:

  1. $prg - is a bash script produced by wrapProgram that add stuff to $PATH, sets PYTHONNOUSERSITE and then calls .$prg-wrapped (2)
  2. .$prg-wrapped - is a patched version of the original python script that is being wrapped, but with a magic preamble that sets argv[0] back to $prg (1) and sets up the site dir appropriately

The current "unwrapping" logic replaces $prg with .$prg-wrapped, but with the argv[0] and site dir logic disabled and with a different shebang (pointing to the python interpreter with the correct environment setup).

However, this might be subtly wrong, since it only accounts for (2) in the original wrapping logic. At the very least, PYTHONNOUSERSITE is no longer being set. Additionally, PATH isn't being set correctly (although it seems that this problem is present in the current master anyway). I think that ideally, the currently constructed bin folder should be added to the PATH.

@pbsds pbsds requested a review from adisbladis January 13, 2025 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants