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
import distutils.sysconfig
import errno
import glob
import json
import logging
import os
import pkg_resources
......@@ -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:
top_level = f.read().splitlines()
try:
package_paths = subprocess.check_output([
package_paths = json.loads(subprocess.check_output([
sys.executable, '-S', '-c',
"import importlib.util, site; "
"import importlib.util, json, site; "
"site.addsitedir(%(site)r); "
"packages = %(packages)r; "
"specs = [importlib.util.find_spec(p) for p in packages]; "
"paths = sum((s.submodule_search_locations"
" for s in specs), []); "
"print('\\n'.join(paths))" % {
"paths = {p: list(s.submodule_search_locations)"
" for p, s in zip(packages, specs)}; "
"print(json.dumps(paths))" % {
'site': tmp3,
'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):
env=dict(os.environ, PYTHONPATH=''),
stderr=subprocess.STDOUT,
universal_newlines=True,
).splitlines()
))
except subprocess.CalledProcessError as e:
logger.error(e.output)
raise
# Resolve import path; warn if there are several and
# arbitrarily take the first one.
import_paths = [os.path.dirname(p) for p in package_paths]
import_path = import_paths[0]
if len(set(import_paths)) > 1:
# Resolve import path.
directory = os.path.normpath(directory)
import_paths = []
for package, paths in package_paths.items():
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(
"Found multiple package import paths in develop project %s"
"\n(\n%s\n)\n"
"Selected %s"
% (directory, '\n'.join(set(import_paths)), import_path))
"Found no package import path in develop project %s "
"for packages %r\n"
"Falling back to project's root directory"
% (directory, tuple(packages)),)
import_path = directory
# Move the .dist-info folder to the import path like setuptools.
move(entry_path, os.path.join(import_path, project_name + ext))
# 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