Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 66 additions & 6 deletions pep517/in_process/_in_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,19 @@ def _dist_info_files(whl_zip):


def _get_wheel_metadata_from_wheel(
backend, metadata_directory, config_settings):
backend, metadata_directory, config_settings, editable=False):
"""Build a wheel and extract the metadata from it.

Fallback for when the build backend does not
define the 'get_wheel_metadata' hook.
define the 'prepare_metadata' hook.
"""
from zipfile import ZipFile
whl_basename = backend.build_wheel(metadata_directory, config_settings)
if editable:
whl_basename = backend.build_editable(
metadata_directory, config_settings)
else:
whl_basename = backend.build_wheel(
metadata_directory, config_settings)
with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'):
pass # Touch marker file

Expand All @@ -167,7 +172,7 @@ def _get_wheel_metadata_from_wheel(
return dist_info[0].split('/')[0]


def _find_already_built_wheel(metadata_directory):
def _find_already_built_wheel(metadata_directory, editable=False):
"""Check for a wheel already built during the get_wheel_metadata hook.
"""
if not metadata_directory:
Expand All @@ -181,8 +186,9 @@ def _find_already_built_wheel(metadata_directory):
print('Found wheel built marker, but no .whl files')
return None
if len(whl_files) > 1:
hook_name = 'build_editable' if editable else 'build_wheel'
print('Found multiple .whl files; unspecified behaviour. '
'Will call build_wheel.')
'Will call {}.'.format(hook_name))
return None

# Exactly one .whl file
Expand All @@ -206,7 +212,7 @@ def build_wheel(wheel_directory, config_settings, metadata_directory=None):


def get_requires_for_build_sdist(config_settings):
"""Invoke the optional get_requires_for_build_wheel hook
"""Invoke the optional get_requires_for_build_sdist hook

Returns [] if the hook is not defined.
"""
Expand All @@ -219,6 +225,57 @@ def get_requires_for_build_sdist(config_settings):
return hook(config_settings)


def prepare_metadata_for_build_editable(
metadata_directory, config_settings, _allow_fallback):
"""Invoke optional prepare_metadata_for_build_editable

Implements a fallback by building a wheel for editable if the hook
isn't defined, unless _allow_fallback is False in which case
HookMissing is raised.
"""
backend = _build_backend()
try:
hook = backend.prepare_metadata_for_build_editable
except AttributeError:
if not _allow_fallback:
raise HookMissing()
return _get_wheel_metadata_from_wheel(backend, metadata_directory,
config_settings, editable=True)
else:
return hook(metadata_directory, config_settings)


def build_editable(wheel_directory, config_settings, metadata_directory=None):
"""Invoke the optional build_editable hook.
"""
prebuilt_whl = _find_already_built_wheel(metadata_directory, editable=True)
if prebuilt_whl:
shutil.copy2(prebuilt_whl, wheel_directory)
return os.path.basename(prebuilt_whl)

backend = _build_backend()
try:
hook = backend.build_wheel_for_editable
except AttributeError:
raise HookMissing()
else:
return hook(wheel_directory, config_settings, metadata_directory)


def get_requires_for_build_editable(config_settings):
"""Invoke the optional get_requires_for_build_editable hook

Returns [] if the hook is not defined.
"""
backend = _build_backend()
try:
hook = backend.get_requires_for_build_editable
except AttributeError:
return []
else:
return hook(config_settings)


class _DummyException(Exception):
"""Nothing should ever raise this exception"""

Expand All @@ -244,6 +301,9 @@ def build_sdist(sdist_directory, config_settings):
'build_wheel',
'get_requires_for_build_sdist',
'build_sdist',
'get_requires_for_build_editable',
'prepare_metadata_for_build_editable',
'build_editable',
}


Expand Down
53 changes: 53 additions & 0 deletions pep517/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,59 @@ def build_sdist(self, sdist_directory, config_settings=None):
'config_settings': config_settings,
})

def get_requires_for_build_editable(self, config_settings=None):
"""Identify packages required for building a editable

Returns a list of dependency specifications, e.g.::

["wheel >= 0.25", "setuptools"]

This does not include requirements specified in pyproject.toml.
It returns the result of calling the equivalently named hook in a
subprocess.
"""
return self._call_hook('get_requires_for_build_editable', {
'config_settings': config_settings
})

def prepare_metadata_for_build_editable(
self, metadata_directory, config_settings=None,
_allow_fallback=True):
"""Prepare a ``*.dist-info`` folder with metadata for this project.

Returns the name of the newly created folder.

If the build backend defines a hook with this name, it will be called
in a subprocess. If not, the backend will be asked to build a wheel,
and the dist-info extracted from that (unless _allow_fallback is
False).
"""
return self._call_hook('prepare_metadata_for_build_editable', {
'metadata_directory': abspath(metadata_directory),
'config_settings': config_settings,
'_allow_fallback': _allow_fallback,
})

def build_editable(
self, wheel_directory, config_settings=None,
metadata_directory=None):
"""Build a wheel from this project.

Returns the name of the newly created file.

In general, this will call the 'build_editable' hook in the backend.
However, if that was previously called by
'prepare_metadata_for_build_editable', and the same metadata_directory is
used, the previously built wheel will be copied to wheel_directory.
"""
if metadata_directory is not None:
metadata_directory = abspath(metadata_directory)
return self._call_hook('build_editable', {
'wheel_directory': abspath(wheel_directory),
'config_settings': config_settings,
'metadata_directory': metadata_directory,
})

def _call_hook(self, hook_name, kwargs):
# On Python 2, pytoml returns Unicode values (which is correct) but the
# environment passed to check_call needs to contain string values. We
Expand Down