Skip to content

Commit b5d5c20

Browse files
authored
Merge pull request #83 from pypa/separate-highlevel
Pull out high-level interfaces as an extended example
2 parents 3f1949a + e70e03e commit b5d5c20

30 files changed

+299
-157
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
cache: pip
3232

3333
install:
34-
- pip install tox tox-venv
34+
- pip install -U virtualenv
35+
- pip install tox
3536

3637
script: tox

README.rst

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,21 @@ provides:
88
the current process.
99
- Fallbacks for the optional hooks, so that frontends can call the hooks without
1010
checking which are defined.
11-
- Higher-level functions which install the build dependencies into a
12-
temporary environment and build a wheel/sdist using them.
11+
- Functions to load the build system table from ``pyproject.toml``, with
12+
optional fallback to setuptools.
1313

1414
Run the tests with ``pytest`` or `tox <https://pypi.org/project/tox>`_.
1515

16-
High level usage, with build requirements handled:
16+
Usage:
1717

1818
.. code-block:: python
1919
2020
import os
21-
from pep517.envbuild import build_wheel, build_sdist
21+
from pep517 import Pep517HookCaller
22+
from pep517.pyproject import load_system
2223
2324
src = 'path/to/source' # Folder containing 'pyproject.toml'
24-
destination = 'also/a/folder'
25-
whl_filename = build_wheel(src, destination)
26-
assert os.path.isfile(os.path.join(destination, whl_filename))
27-
28-
targz_filename = build_sdist(src, destination)
29-
assert os.path.isfile(os.path.join(destination, targz_filename))
30-
31-
Lower level usage—you are responsible for ensuring build requirements are
32-
available:
33-
34-
.. code-block:: python
35-
36-
import os
37-
import toml
38-
from pep517.wrappers import Pep517HookCaller
39-
40-
src = 'path/to/source' # Folder containing 'pyproject.toml'
41-
with open(os.path.join(src, 'pyproject.toml')) as f:
42-
build_sys = toml.load(f)['build-system']
25+
build_sys = load_system(src)
4326
4427
print(build_sys['requires']) # List of static requirements
4528
@@ -57,18 +40,9 @@ available:
5740
whl_filename = hooks.build_wheel(destination, config_options)
5841
assert os.path.isfile(os.path.join(destination, whl_filename))
5942
60-
To test the build backend for a project, run in a system shell:
61-
62-
.. code-block:: shell
63-
64-
python3 -m pep517.check path/to/source # source dir containing pyproject.toml
65-
66-
To build a backend into source and/or binary distributions, run in a shell:
67-
68-
.. code-block:: shell
69-
70-
python -m pep517.build path/to/source # source dir containing pyproject.toml
43+
The caller is responsible for installing build dependencies.
44+
The static requirements should be installed before trying to call any hooks.
7145

72-
This 'build' module should be considered experimental while the PyPA `decides
73-
on the best place for this functionality
74-
<https://github.com/pypa/packaging-problems/issues/219>`_.
46+
The ``buildtool_demo`` package in this repository gives a more complete
47+
example of how to use the hooks. This is an example, and doesn't get installed
48+
with the ``pep517`` package.

examples/README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Examples of using the ``pep517`` library.
2+
3+
* ``buildtool`` is about the simplest possible PEP 517 frontend, which can use
4+
PEP 517 backends to build packages. It installs build dependencies into a
5+
temporary but non-isolated environment using pip.

examples/buildtool/__init__.py

Whitespace-only changes.

pep517/build.py renamed to examples/buildtool/build.py

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,16 @@
33
import argparse
44
import logging
55
import os
6-
import toml
76
import shutil
87

98
from .envbuild import BuildEnvironment
10-
from .wrappers import Pep517HookCaller
9+
from pep517 import Pep517HookCaller
10+
from pep517.pyproject import load_system, validate_system
1111
from .dirtools import tempdir, mkdir_p
12-
from .compat import FileNotFoundError
1312

1413
log = logging.getLogger(__name__)
1514

1615

17-
def validate_system(system):
18-
"""
19-
Ensure build system has the requisite fields.
20-
"""
21-
required = {'requires', 'build-backend'}
22-
if not (required <= set(system)):
23-
message = "Missing required fields: {missing}".format(
24-
missing=required-set(system),
25-
)
26-
raise ValueError(message)
27-
28-
29-
def load_system(source_dir):
30-
"""
31-
Load the build system from a source dir (pyproject.toml).
32-
"""
33-
pyproject = os.path.join(source_dir, 'pyproject.toml')
34-
with open(pyproject) as f:
35-
pyproject_data = toml.load(f)
36-
return pyproject_data['build-system']
37-
38-
39-
def compat_system(source_dir):
40-
"""
41-
Given a source dir, attempt to get a build system backend
42-
and requirements from pyproject.toml. Fallback to
43-
setuptools but only if the file was not found or a build
44-
system was not indicated.
45-
"""
46-
try:
47-
system = load_system(source_dir)
48-
except (FileNotFoundError, KeyError):
49-
system = {}
50-
system.setdefault(
51-
'build-backend',
52-
'setuptools.build_meta:__legacy__',
53-
)
54-
system.setdefault('requires', ['setuptools', 'wheel'])
55-
return system
56-
57-
5816
def _do_build(hooks, env, dist, dest):
5917
get_requires_name = 'get_requires_for_build_{dist}'.format(**locals())
6018
get_requires = getattr(hooks, get_requires_name)

pep517/check.py renamed to examples/buildtool/check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from .colorlog import enable_colourful_output
1616
from .envbuild import BuildEnvironment
17-
from .wrappers import Pep517HookCaller
17+
from pep517 import Pep517HookCaller
1818

1919
log = logging.getLogger(__name__)
2020

File renamed without changes.
File renamed without changes.

pep517/envbuild.py renamed to examples/buildtool/envbuild.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
import sys
1010
from sysconfig import get_paths
1111
from tempfile import mkdtemp
12+
import threading
1213

13-
from .wrappers import Pep517HookCaller, LoggerWrapper
14+
from pep517 import Pep517HookCaller
1415

1516
log = logging.getLogger(__name__)
1617

@@ -26,6 +27,40 @@ def _load_pyproject(source_dir):
2627
)
2728

2829

30+
class LoggerWrapper(threading.Thread):
31+
"""
32+
Read messages from a pipe and redirect them
33+
to a logger (see python's logging module).
34+
"""
35+
36+
def __init__(self, logger, level):
37+
threading.Thread.__init__(self)
38+
self.daemon = True
39+
40+
self.logger = logger
41+
self.level = level
42+
43+
# create the pipe and reader
44+
self.fd_read, self.fd_write = os.pipe()
45+
self.reader = os.fdopen(self.fd_read)
46+
47+
self.start()
48+
49+
def fileno(self):
50+
return self.fd_write
51+
52+
@staticmethod
53+
def remove_newline(msg):
54+
return msg[:-1] if msg.endswith(os.linesep) else msg
55+
56+
def run(self):
57+
for line in self.reader:
58+
self._write(self.remove_newline(line))
59+
60+
def _write(self, message):
61+
self.logger.log(self.level, message)
62+
63+
2964
class BuildEnvironment(object):
3065
"""Context manager to install build deps in a simple temporary environment
3166

pep517/meta.py renamed to examples/buildtool/meta.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
from zipp import Path
1818

1919
from .envbuild import BuildEnvironment
20-
from .wrappers import Pep517HookCaller, quiet_subprocess_runner
20+
from pep517 import Pep517HookCaller, quiet_subprocess_runner
21+
from pep517.pyproject import validate_system, load_system, compat_system
2122
from .dirtools import tempdir, mkdir_p, dir_to_zipfile
22-
from .build import validate_system, load_system, compat_system
2323

2424
log = logging.getLogger(__name__)
2525

examples/buildtool/tests/__init__.py

Whitespace-only changes.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""This is a very stupid backend for testing purposes.
2+
3+
Don't use this for any real code.
4+
"""
5+
6+
from glob import glob
7+
from os.path import join as pjoin
8+
import shutil
9+
import tarfile
10+
from zipfile import ZipFile
11+
12+
13+
def get_requires_for_build_wheel(config_settings):
14+
return ['wheelwright']
15+
16+
17+
def prepare_metadata_for_build_wheel(metadata_directory, config_settings):
18+
for distinfo in glob('*.dist-info'):
19+
shutil.copytree(distinfo, pjoin(metadata_directory, distinfo))
20+
21+
22+
def prepare_build_wheel_files(build_directory, config_settings):
23+
shutil.copy('pyproject.toml', build_directory)
24+
for pyfile in glob('*.py'):
25+
shutil.copy(pyfile, build_directory)
26+
for distinfo in glob('*.dist-info'):
27+
shutil.copytree(distinfo, pjoin(build_directory, distinfo))
28+
29+
30+
def build_wheel(wheel_directory, config_settings, metadata_directory=None):
31+
whl_file = 'pkg1-0.5-py2.py3-none-any.whl'
32+
with ZipFile(pjoin(wheel_directory, whl_file), 'w') as zf:
33+
for pyfile in glob('*.py'):
34+
zf.write(pyfile)
35+
for metadata in glob('*.dist-info/*'):
36+
zf.write(metadata)
37+
return whl_file
38+
39+
40+
def get_requires_for_build_sdist(config_settings):
41+
return ['frog']
42+
43+
44+
class UnsupportedOperation(Exception):
45+
pass
46+
47+
48+
def build_sdist(sdist_directory, config_settings):
49+
if config_settings.get('test_unsupported', False):
50+
raise UnsupportedOperation
51+
52+
target = 'pkg1-0.5.tar.gz'
53+
with tarfile.open(pjoin(sdist_directory, target), 'w:gz',
54+
format=tarfile.PAX_FORMAT) as tf:
55+
def _add(relpath):
56+
tf.add(relpath, arcname='pkg1-0.5/' + relpath)
57+
58+
_add('pyproject.toml')
59+
for pyfile in glob('*.py'):
60+
_add(pyfile)
61+
62+
return target
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Test backend defining only the mandatory hooks.
2+
3+
Don't use this for any real code.
4+
"""
5+
from glob import glob
6+
from os.path import join as pjoin
7+
import tarfile
8+
from zipfile import ZipFile
9+
10+
11+
def build_wheel(wheel_directory, config_settings, metadata_directory=None):
12+
whl_file = 'pkg2-0.5-py2.py3-none-any.whl'
13+
with ZipFile(pjoin(wheel_directory, whl_file), 'w') as zf:
14+
for pyfile in glob('*.py'):
15+
zf.write(pyfile)
16+
for metadata in glob('*.dist-info/*'):
17+
zf.write(metadata)
18+
return whl_file
19+
20+
21+
def build_sdist(sdist_directory, config_settings):
22+
target = 'pkg2-0.5.tar.gz'
23+
with tarfile.open(pjoin(sdist_directory, target), 'w:gz',
24+
format=tarfile.PAX_FORMAT) as tf:
25+
def _add(relpath):
26+
tf.add(relpath, arcname='pkg2-0.5/' + relpath)
27+
28+
_add('pyproject.toml')
29+
for pyfile in glob('*.py'):
30+
_add(pyfile)
31+
for distinfo in glob('*.dist-info'):
32+
_add(distinfo)
33+
34+
return target
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017 Thomas Kluyver
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Metadata-Version: 1.2
2+
Name: pkg1
3+
Version: 0.5
4+
Summary: Sample package for tests
5+
Home-page: https://github.com/takluyver/pep517
6+
License: UNKNOWN
7+
Author: Thomas Kluyver
8+
Author-email: thomas@kluyver.me.uk
9+
Classifier: License :: OSI Approved :: MIT License
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pkg1.py,sha256=ZawKBtrxtdGEheOCWvwzGZsO8Q1OSzEzecGNsRz-ekc,52
2+
pkg1-0.5.dist-info/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081
3+
pkg1-0.5.dist-info/WHEEL,sha256=jxKvNaDKHDacpaLi69-vnLKkBSynwBzmMS82pipt1T0,100
4+
pkg1-0.5.dist-info/METADATA,sha256=GDliGDwDPM11hoO79KhjyJuFgcm-TOj30gewsPNjkHw,251
5+
pkg1-0.5.dist-info/RECORD,,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Wheel-Version: 1.0
2+
Generator: buildsys 0.1
3+
Root-Is-Purelib: true
4+
Tag: py2-none-any
5+
Tag: py3-none-any
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Sample package for tests"""
2+
3+
__version__ = '0.5'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["eg_buildsys"]
3+
build-backend = "buildsys"

tests/test_envbuild.py renamed to examples/buildtool/tests/test_envbuild.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from mock import patch, call # Python 2 fallback
99
import zipfile
1010

11-
from pep517.envbuild import build_sdist, build_wheel, BuildEnvironment
11+
from ..envbuild import build_sdist, build_wheel, BuildEnvironment
1212

1313
SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples')
1414
BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs')

tests/test_meta.py renamed to examples/buildtool/tests/test_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from pep517 import meta
7+
from .. import meta
88

99

1010
pep517_needs_python_3 = pytest.mark.xfail(

pep517/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
"""
33

44
__version__ = '0.8.2'
5+
6+
from .wrappers import * # noqa: F401,F403

0 commit comments

Comments
 (0)