editables: fix path ordering, remove relics...

...of the former feature which linked sources in .dream2nix/editables.
This was sadly uncleanly removed before
This commit is contained in:
phaer 2024-07-29 18:22:38 +02:00
parent ece62d699f
commit 601b7dfd2b
4 changed files with 38 additions and 42 deletions

View File

@ -317,8 +317,10 @@ see where they are imported from:
```shell-session ```shell-session
$ nix develop $ nix develop
Some python dependencies of my-tool are installed in editable mode evaluating derivation 'git+file://[path_to_your_repo]#devShells.x86_64-linux.default'
To disable editable mode for a package, remove the corresponding entry from the 'editables' field in the dream2nix configuration file. Some python dependencies of /Users/phaer/src/dream2nix/examples/packages/languages/python-local-development are installed in editable mode
my-tool
installed at: .
$ ipython $ ipython
[...] [...]
In [1]: import my_tool In [1]: import my_tool

View File

@ -38,9 +38,9 @@ in {
paths.lockFile = "lock.${config.deps.stdenv.system}.json"; paths.lockFile = "lock.${config.deps.stdenv.system}.json";
pip = { pip = {
# Setting editables.$pkg to an absolute path will link this path as an editable # Setting editables.$pkg to a relative or absolute path, as a string, will
# install to .dream2nix/editables in devShells. The root package is always installed # link this path as an editable install to .dream2nix/editables in
# as editable. # devShells. The root package is always installed as editable.
# editables.charset-normalizer = "/home/my-user/src/charset-normalizer"; # editables.charset-normalizer = "/home/my-user/src/charset-normalizer";
requirementsList = requirementsList =

View File

@ -41,18 +41,11 @@ def make_editable(
python_environment, python_environment,
bin_dir, bin_dir,
site_dir, site_dir,
editables_dir,
site_packages,
name, name,
path, path,
root_dir,
): ):
normalized_name = name.replace("-", "_") normalized_name = name.replace("-", "_")
editable_dir = editables_dir / name
full_path = path if path.is_absolute() else root_dir / path full_path = path if path.is_absolute() else root_dir / path
if editable_dir.exists():
relative_editable_dir = editable_dir.relative_to(root_dir)
return
if not full_path.exists(): if not full_path.exists():
print( print(
f"Error: The python dependency {name} of {root_name} is configured to be installed in editable mode, but the provided source location {full_path} does not exist.\n" f"Error: The python dependency {name} of {root_name} is configured to be installed in editable mode, but the provided source location {full_path} does not exist.\n"
@ -60,31 +53,30 @@ def make_editable(
file=sys.stderr, file=sys.stderr,
) )
exit(1) exit(1)
# link_editable_source(editable_dir, site_dir, normalized_name, full_path)
make_pth(site_dir, full_path, normalized_name)
editable_dist_info = make_dist_info(
site_dir, full_path, site_packages, normalized_name
)
make_entrypoints(python_environment, bin_dir, editable_dist_info)
def make_pth(site_dir, editable_dir, normalized_name):
# Check if source uses a "src"-layout or a flat-layout and # Check if source uses a "src"-layout or a flat-layout and
# write the .pth file # write the .pth file
if (editable_dir / "src").exists(): if (full_path / "src").exists():
pth = editable_dir / "src" editable_dir = full_path / "src"
else: else:
# TODO this approach is risky as it puts everything inside # TODO this approach is risky as it puts everything inside
# upstreams repo on $PYTHONPATH. Maybe we should try to # upstreams repo on $PYTHONPATH. Maybe we should try to
# get packages from toplevel.txt first and if found, # get packages from toplevel.txt first and if found,
# create a dir with only them linked? # create a dir with only them linked?
pth = editable_dir editable_dir = full_path
make_pth(site_dir, editable_dir, normalized_name)
editable_dist_info = make_dist_info(site_dir, full_path)
make_entrypoints(python_environment, bin_dir, editable_dist_info)
return editable_dir
def make_pth(site_dir, editable_dir, normalized_name):
with open((site_dir / normalized_name).with_suffix(".pth"), "w") as f: with open((site_dir / normalized_name).with_suffix(".pth"), "w") as f:
f.write(f"{pth}\n") f.write(f"{editable_dir}\n")
# create a packages .dist-info by calling its build backend # create a packages .dist-info by calling its build backend
def make_dist_info(site_dir, editable_dir, site_packages, normalized_name): def make_dist_info(site_dir, editable_dir):
os.chdir(editable_dir) os.chdir(editable_dir)
pyproject_file = editable_dir / "pyproject.toml" pyproject_file = editable_dir / "pyproject.toml"
if pyproject_file.exists(): if pyproject_file.exists():
@ -182,7 +174,7 @@ def export_environment_vars(python_environment, bin_dir, site_dir, site_packages
) )
def pretty_print_editables(editables, root_dir, root_name): def pretty_print_editables(editables, root_name):
if os.environ.get("D2N_QUIET"): if os.environ.get("D2N_QUIET"):
return return
C = Colors C = Colors
@ -213,12 +205,11 @@ if __name__ == "__main__":
python_environment = Path(args["pyEnv"]) python_environment = Path(args["pyEnv"])
root_name = args["rootName"] root_name = args["rootName"]
site_packages = args["sitePackages"] site_packages = args["sitePackages"]
editables = args["editables"] editables = {k: Path(v) for k, v in args["editables"].items()}
# directories to use # directories to use
root_dir = Path(run([find_root])) root_dir = Path(run([find_root]))
dream2nix_python_dir = root_dir / ".dream2nix" / "python" dream2nix_python_dir = root_dir / ".dream2nix" / "python"
editables_dir = dream2nix_python_dir / "editables"
bin_dir = dream2nix_python_dir / "bin" bin_dir = dream2nix_python_dir / "bin"
site_dir = dream2nix_python_dir / "site" site_dir = dream2nix_python_dir / "site"
@ -228,25 +219,22 @@ if __name__ == "__main__":
shutil.rmtree(dream2nix_python_dir) shutil.rmtree(dream2nix_python_dir)
else: else:
export_environment_vars(python_environment, bin_dir, site_dir, site_packages) export_environment_vars(python_environment, bin_dir, site_dir, site_packages)
pretty_print_editables(editables, root_dir, root_name) pretty_print_editables(editables, root_dir)
exit(0) exit(0)
bin_dir.mkdir(parents=True, exist_ok=True) bin_dir.mkdir(parents=True, exist_ok=True)
editables_dir.mkdir(parents=True, exist_ok=True)
site_dir.mkdir(parents=True, exist_ok=True) site_dir.mkdir(parents=True, exist_ok=True)
editable_dirs = []
for name, path in editables.items(): for name, path in editables.items():
make_editable( editable_dir = make_editable(
python_environment, python_environment,
bin_dir, bin_dir,
site_dir, site_dir,
editables_dir,
site_packages,
name, name,
Path(path), path,
root_dir,
) )
editable_dirs.append(str(editable_dir.absolute()))
with open(site_dir / "sitecustomize.py", "w") as f: with open(site_dir / "sitecustomize.py", "w") as f:
f.write( f.write(
f"""import sys f"""import sys
@ -264,7 +252,7 @@ site.addsitedir("{site_dir}")
# in our pyEnv, those would shadow the editables. So we move # in our pyEnv, those would shadow the editables. So we move
# the editables to the front of sys.path. # the editables to the front of sys.path.
for index, path in enumerate(sys.path): for index, path in enumerate(sys.path):
if path.startswith("{editables_dir}"): if path in {editable_dirs}:
sys.path.insert(0, sys.path.pop(index)) sys.path.insert(0, sys.path.pop(index))
""" """
) )
@ -273,4 +261,4 @@ for index, path in enumerate(sys.path):
json.dump(args, f, indent=2) json.dump(args, f, indent=2)
export_environment_vars(python_environment, bin_dir, site_dir, site_packages) export_environment_vars(python_environment, bin_dir, site_dir, site_packages)
pretty_print_editables(editables, root_dir, root_name) pretty_print_editables(editables, root_dir)

View File

@ -8,15 +8,21 @@ in {
options = { options = {
editables = lib.mkOption { editables = lib.mkOption {
description = '' description = ''
An attribute set mapping package names to absolute paths of source directories An attribute set mapping package names to a path, absolute or relative,
which should be installed in editable mode in [editablesShellHook](#pipeditablesshellhook). of source directories which should be installed in editable mode in
[editablesShellHook](#pipeditablesshellhook).
i.e. i.e.
``` ```
pip.editables.charset-normalizer = "/home/user/src/charset-normalizer". pip.editables.charset-normalizer = "/home/user/src/charset-normalizer".
``` ```
The top-level package is added automatically. The top-level package is included automatically.
This must be a string, as otherwise content would be copied to the nix store
and loaded from there, voiding the benefits of editable installs.
For the same reason, it is advised to use source filtering if you
use a path inside the current repo to avoid unecessary rebuilds.
''; '';
type = t.attrsOf t.str; type = t.attrsOf t.str;
}; };