Commit 4725b1cb authored by Xavier Thompson's avatar Xavier Thompson

[fixup] Fix .egg-link resolution for PEP 660 case

Fixup "Use pip install --editable in easy_install.develop"

Support namespace packages where `spec.submodule_earch_locations` is
a `_NamespacePath` object instead of a simple `list` and add support
for cases where the layout of the source project does not follow the
same structure as the package tree - meaning some custom magic might
be involved in making editable imports work as intended.
parent a5f13df6
...@@ -23,6 +23,7 @@ import distutils.errors ...@@ -23,6 +23,7 @@ import distutils.errors
import distutils.sysconfig import distutils.sysconfig
import errno import errno
import glob import glob
import json
import logging import logging
import os import os
import pkg_resources import pkg_resources
...@@ -1229,15 +1230,15 @@ def develop(setup, dest, build_ext=None, executable=None, verbosity=-10): ...@@ -1229,15 +1230,15 @@ def develop(setup, dest, build_ext=None, executable=None, verbosity=-10):
with open(os.path.join(entry_path, 'top_level.txt')) as f: with open(os.path.join(entry_path, 'top_level.txt')) as f:
top_level = f.read().splitlines() top_level = f.read().splitlines()
try: try:
package_paths = subprocess.check_output([ package_paths = json.loads(subprocess.check_output([
sys.executable, '-S', '-c', sys.executable, '-S', '-c',
"import importlib.util, site; " "import importlib.util, json, site; "
"site.addsitedir(%(site)r); " "site.addsitedir(%(site)r); "
"packages = %(packages)r; " "packages = %(packages)r; "
"specs = [importlib.util.find_spec(p) for p in packages]; " "specs = [importlib.util.find_spec(p) for p in packages]; "
"paths = sum((s.submodule_search_locations" "paths = {p: list(s.submodule_search_locations)"
" for s in specs), []); " " for p, s in zip(packages, specs)}; "
"print('\\n'.join(paths))" % { "print(json.dumps(paths))" % {
'site': tmp3, 'site': tmp3,
'packages': tuple(p for p in top_level if p), 'packages': tuple(p for p in top_level if p),
} }
...@@ -1245,20 +1246,47 @@ def develop(setup, dest, build_ext=None, executable=None, verbosity=-10): ...@@ -1245,20 +1246,47 @@ def develop(setup, dest, build_ext=None, executable=None, verbosity=-10):
env=dict(os.environ, PYTHONPATH=''), env=dict(os.environ, PYTHONPATH=''),
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
universal_newlines=True, universal_newlines=True,
).splitlines() ))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
logger.error(e.output) logger.error(e.output)
raise raise
# Resolve import path; warn if there are several and # Resolve import path.
# arbitrarily take the first one. directory = os.path.normpath(directory)
import_paths = [os.path.dirname(p) for p in package_paths] import_paths = []
import_path = import_paths[0] for package, paths in package_paths.items():
if len(set(import_paths)) > 1: for path in paths:
# Filter out magic hook values from setuptools PEP 660.
if (path.startswith('__editable__')
and path.endswith('.__path_hook__')): continue
path = os.path.normpath(path)
# Do not parent-path out of the source repository.
if path == directory:
import_paths.append(path)
continue
parent, folder = os.path.split(path)
# Only parent-path if the folder has the name of the package.
import_paths.append(parent if folder == package else path)
# Warn if there are several and arbitrarily take the first one.
if import_paths:
import_path = import_paths[0]
unique_import_paths = set(import_paths)
if len(unique_import_paths) > 1:
logger.warning(
"Found multiple package import paths"
" in develop project %s\n(\n %s\n)\n"
"Selected %s" % (
directory,
'\n '.join(unique_import_paths),
import_path
))
# Warn if no import path is found; fallback to source directory.
else:
logger.warning( logger.warning(
"Found multiple package import paths in develop project %s" "Found no package import path in develop project %s "
"\n(\n%s\n)\n" "for packages %r\n"
"Selected %s" "Falling back to project's root directory"
% (directory, '\n'.join(set(import_paths)), import_path)) % (directory, tuple(packages)),)
import_path = directory
# Move the .dist-info folder to the import path like setuptools. # Move the .dist-info folder to the import path like setuptools.
move(entry_path, os.path.join(import_path, project_name + ext)) move(entry_path, os.path.join(import_path, project_name + ext))
# Create a temporary .egg-link. # Create a temporary .egg-link.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment