Repository: pypa/pyproject-hooks (language: python)

Issue:
Build/check should fallback to setuptools unconditionally
In [this comment](https://github.com/pypa/pep517/pull/48#discussion_r287282963), @gaborbernat suggests that the project should unconditionally fallback to setuptools if PEP 517 is not indicated.

That seems like a plausible suggestion, but I'd like to separate that concern from the concern in the PR.


Discussion / hints:
I've closed most of the issues relating to the `build` and `check` layer, but I'm still open to the simple change to make them fallback to setuptools, if people want to implement that. There's already a `compat_system` function implementing such a fallback.

Fix patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 7eb57d2..8fd02a3 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -3,4 +3,4 @@ current_version = 0.13.0
 commit = True
 tag = True
 
-[bumpversion:file:pep517/__init__.py]
+[bumpversion:file:src/pyproject_hooks/__init__.py]
diff --git a/README.rst b/README.rst
index a035f33..2d798c2 100644
--- a/README.rst
+++ b/README.rst
@@ -23,7 +23,7 @@ Usage—you are responsible for ensuring build requirements are available:
 
     import os
     import tomli
-    from pep517.wrappers import Pep517HookCaller
+    from pyproject_hooks import BuildBackendHookCaller
 
     src = 'path/to/source'  # Folder containing 'pyproject.toml'
     with open(os.path.join(src, 'pyproject.toml'), 'rb') as f:
@@ -33,8 +33,8 @@ Usage—you are responsible for ensuring build requirements are available:
     # The caller is responsible for installing these and running the hooks in
     # an environment where they are available.
 
-    hooks = Pep517HookCaller(
-        src, 
+    hooks = BuildBackendHookCaller(
+        src,
         build_backend=build_sys['build-backend'],
         backend_path=build_sys.get('backend-path'),
     )
@@ -47,43 +47,3 @@ Usage—you are responsible for ensuring build requirements are available:
     destination = 'also/a/folder'
     whl_filename = hooks.build_wheel(destination, config_options)
     assert os.path.isfile(os.path.join(destination, whl_filename))
-
-Deprecated high-level
----------------------
-
-For now, ``pep517`` also contains higher-level functions which install the build
-dependencies into a temporary environment and build a wheel/sdist using them.
-This is a rough implementation, e.g. it does not do proper build isolation.
-The `PyPA build project <https://github.com/pypa/build>`_ is recommended as an
-alternative, although it's still quite young in October 2020.
-This layer of functionality in ``pep517`` is now deprecated, but won't be
-removed for some time, as there is code relying on it.
-
-High level usage, with build requirements handled:
-
-.. code-block:: python
-
-    import os
-    from pep517.envbuild import build_wheel, build_sdist
-
-    src = 'path/to/source'  # Folder containing 'pyproject.toml'
-    destination = 'also/a/folder'
-    whl_filename = build_wheel(src, destination)
-    assert os.path.isfile(os.path.join(destination, whl_filename))
-
-    targz_filename = build_sdist(src, destination)
-    assert os.path.isfile(os.path.join(destination, targz_filename))
-
-To test the build backend for a project, run in a system shell:
-
-.. code-block:: shell
-
-    python3 -m pep517.check path/to/source  # source dir containing pyproject.toml
-
-To build a backend into source and/or binary distributions, run in a shell:
-
-.. code-block:: shell
-
-    python -m pep517.build path/to/source  # source dir containing pyproject.toml
-
-All of this high-level functionality is deprecated.
diff --git a/doc/callhooks.rst b/doc/callhooks.rst
index 23ee06c..0b057bb 100644
--- a/doc/callhooks.rst
+++ b/doc/callhooks.rst
@@ -1,12 +1,12 @@
 API reference
 =============
 
-.. module:: pep517
+.. module:: pyproject_hooks
 
 Calling the build system
 ------------------------
 
-.. autoclass:: Pep517HookCaller
+.. autoclass:: BuildBackendHookCaller
 
    .. automethod:: get_requires_for_build_sdist
 
@@ -29,8 +29,8 @@ Calling the build system
 Subprocess runners
 ------------------
 
-These functions may be provided when creating :class:`Pep517HookCaller`,
-or to :meth:`Pep517HookCaller.subprocess_runner`.
+These functions may be provided when creating :class:`BuildBackendHookCaller`,
+or to :meth:`BuildBackendHookCaller.subprocess_runner`.
 
 .. autofunction:: default_subprocess_runner
 
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 337beb7..f74c2f1 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -50,7 +50,7 @@ Changelog
 - Deprecated the higher level API which handles creating an environment and
   installing build dependencies. This was not very complete, and the `PyPA build
   project <https://github.com/pypa/build>`_ is designed for this use case.
-- New ``python_executable`` parameter for :class:`.Pep517HookCaller` to run hooks
+- New ``python_executable`` parameter for :class:`.BuildBackendHookCaller` to run hooks
   with a different Python interpreter.
 - Fix for locating the script to run in the subprocess in some scenarios.
 - Fix example in README to get ``build-backend`` correctly.
diff --git a/doc/conf.py b/doc/conf.py
index 2bb968c..bdac2f7 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -18,7 +18,7 @@
 
 # -- Project information -----------------------------------------------------
 
-project = 'pep517'
+project = 'pyproject-hooks'
 copyright = '2020, Thomas Kluyver'
 author = 'Thomas Kluyver'
 
diff --git a/doc/index.rst b/doc/index.rst
index b564e9b..cae08ea 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1,5 +1,5 @@
-pep517
-======
+pyproject-hooks
+===============
 
 This package provides an API to call the hooks defined in :pep:`517`.
 
diff --git a/issue_template.md b/issue_template.md
deleted file mode 100644
index e2986ff..0000000
--- a/issue_template.md
+++ /dev/null
@@ -1,4 +0,0 @@
-<!--
-Note that the CLI tools pep517.build and pep517.check are frozen (we stopped improving them) and will eventually be deprecated.
-All improvements should go to https://github.com/pypa/build
--->
diff --git a/pep517/__init__.py b/pep517/__init__.py
deleted file mode 100644
index 38ea0f5..0000000
--- a/pep517/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""Wrappers to build Python packages using PEP 517 hooks
-"""
-
-__version__ = '0.13.0'
-
-from .wrappers import *  # noqa: F401, F403
diff --git a/pep517/build.py b/pep517/build.py
deleted file mode 100644
index b30909c..0000000
--- a/pep517/build.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""Build a project using PEP 517 hooks.
-"""
-import argparse
-import logging
-import os
-import shutil
-import tempfile
-
-from ._compat import tomllib
-from .envbuild import BuildEnvironment
-from .wrappers import Pep517HookCaller
-
-log = logging.getLogger(__name__)
-
-
-def validate_system(system):
-    """
-    Ensure build system has the requisite fields.
-    """
-    required = {'requires', 'build-backend'}
-    if not (required <= set(system)):
-        message = "Missing required fields: {missing}".format(
-            missing=required-set(system),
-        )
-        raise ValueError(message)
-
-
-def load_system(source_dir):
-    """
-    Load the build system from a source dir (pyproject.toml).
-    """
-    pyproject = os.path.join(source_dir, 'pyproject.toml')
-    with open(pyproject, 'rb') as f:
-        pyproject_data = tomllib.load(f)
-    return pyproject_data['build-system']
-
-
-def compat_system(source_dir):
-    """
-    Given a source dir, attempt to get a build system backend
-    and requirements from pyproject.toml. Fallback to
-    setuptools but only if the file was not found or a build
-    system was not indicated.
-    """
-    try:
-        system = load_system(source_dir)
-    except (FileNotFoundError, KeyError):
-        system = {}
-    system.setdefault(
-        'build-backend',
-        'setuptools.build_meta:__legacy__',
-    )
-    system.setdefault('requires', ['setuptools', 'wheel'])
-    return system
-
-
-def _do_build(hooks, env, dist, dest):
-    get_requires_name = 'get_requires_for_build_{dist}'.format(**locals())
-    get_requires = getattr(hooks, get_requires_name)
-    reqs = get_requires({})
-    log.info('Got build requires: %s', reqs)
-
-    env.pip_install(reqs)
-    log.info('Installed dynamic build dependencies')
-
-    with tempfile.TemporaryDirectory() as td:
-        log.info('Trying to build %s in %s', dist, td)
-        build_name = 'build_{dist}'.format(**locals())
-        build = getattr(hooks, build_name)
-        filename = build(td, {})
-        source = os.path.join(td, filename)
-        shutil.move(source, os.path.join(dest, os.path.basename(filename)))
-
-
-def build(source_dir, dist, dest=None, system=None):
-    system = system or load_system(source_dir)
-    dest = os.path.join(source_dir, dest or 'dist')
-    os.makedirs(dest, exist_ok=True)
-
-    validate_system(system)
-    hooks = Pep517HookCaller(
-        source_dir, system['build-backend'], system.get('backend-path')
-    )
-
-    with BuildEnvironment() as env:
-        env.pip_install(system['requires'])
-        _do_build(hooks, env, dist, dest)
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    'source_dir',
-    help="A directory containing pyproject.toml",
-)
-parser.add_argument(
-    '--binary', '-b',
-    action='store_true',
-    default=False,
-)
-parser.add_argument(
-    '--source', '-s',
-    action='store_true',
-    default=False,
-)
-parser.add_argument(
-    '--out-dir', '-o',
-    help="Destination in which to save the builds relative to source dir",
-)
-
-
-def main(args):
-    log.warning('pep517.build is deprecated. '
-                'Consider switching to https://pypi.org/project/build/')
-
-    # determine which dists to build
-    dists = list(filter(None, (
-        'sdist' if args.source or not args.binary else None,
-        'wheel' if args.binary or not args.source else None,
-    )))
-
-    for dist in dists:
-        build(args.source_dir, dist, args.out_dir)
-
-
-if __name__ == '__main__':
-    main(parser.parse_args())
diff --git a/pep517/check.py b/pep517/check.py
deleted file mode 100644
index b79f627..0000000
--- a/pep517/check.py
+++ /dev/null
@@ -1,207 +0,0 @@
-"""Check a project and backend by attempting to build using PEP 517 hooks.
-"""
-import argparse
-import logging
-import os
-import shutil
-import sys
-import tarfile
-import zipfile
-from os.path import isfile
-from os.path import join as pjoin
-from subprocess import CalledProcessError
-from tempfile import mkdtemp
-
-from ._compat import tomllib
-from .colorlog import enable_colourful_output
-from .envbuild import BuildEnvironment
-from .wrappers import Pep517HookCaller
-
-log = logging.getLogger(__name__)
-
-
-def check_build_sdist(hooks, build_sys_requires):
-    with BuildEnvironment() as env:
-        try:
-            env.pip_install(build_sys_requires)
-            log.info('Installed static build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install static build dependencies')
-            return False
-
-        try:
-            reqs = hooks.get_requires_for_build_sdist({})
-            log.info('Got build requires: %s', reqs)
-        except Exception:
-            log.error('Failure in get_requires_for_build_sdist', exc_info=True)
-            return False
-
-        try:
-            env.pip_install(reqs)
-            log.info('Installed dynamic build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install dynamic build dependencies')
-            return False
-
-        td = mkdtemp()
-        log.info('Trying to build sdist in %s', td)
-        try:
-            try:
-                filename = hooks.build_sdist(td, {})
-                log.info('build_sdist returned %r', filename)
-            except Exception:
-                log.info('Failure in build_sdist', exc_info=True)
-                return False
-
-            if not filename.endswith('.tar.gz'):
-                log.error(
-                    "Filename %s doesn't have .tar.gz extension", filename)
-                return False
-
-            path = pjoin(td, filename)
-            if isfile(path):
-                log.info("Output file %s exists", path)
-            else:
-                log.error("Output file %s does not exist", path)
-                return False
-
-            if tarfile.is_tarfile(path):
-                log.info("Output file is a tar file")
-            else:
-                log.error("Output file is not a tar file")
-                return False
-
-        finally:
-            shutil.rmtree(td)
-
-        return True
-
-
-def check_build_wheel(hooks, build_sys_requires):
-    with BuildEnvironment() as env:
-        try:
-            env.pip_install(build_sys_requires)
-            log.info('Installed static build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install static build dependencies')
-            return False
-
-        try:
-            reqs = hooks.get_requires_for_build_wheel({})
-            log.info('Got build requires: %s', reqs)
-        except Exception:
-            log.error('Failure in get_requires_for_build_sdist', exc_info=True)
-            return False
-
-        try:
-            env.pip_install(reqs)
-            log.info('Installed dynamic build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install dynamic build dependencies')
-            return False
-
-        td = mkdtemp()
-        log.info('Trying to build wheel in %s', td)
-        try:
-            try:
-                filename = hooks.build_wheel(td, {})
-                log.info('build_wheel returned %r', filename)
-            except Exception:
-                log.info('Failure in build_wheel', exc_info=True)
-                return False
-
-            if not filename.endswith('.whl'):
-                log.error("Filename %s doesn't have .whl extension", filename)
-                return False
-
-            path = pjoin(td, filename)
-            if isfile(path):
-                log.info("Output file %s exists", path)
-            else:
-                log.error("Output file %s does not exist", path)
-                return False
-
-            if zipfile.is_zipfile(path):
-                log.info("Output file is a zip file")
-            else:
-                log.error("Output file is not a zip file")
-                return False
-
-        finally:
-            shutil.rmtree(td)
-
-        return True
-
-
-def check(source_dir):
-    pyproject = pjoin(source_dir, 'pyproject.toml')
-    if isfile(pyproject):
-        log.info('Found pyproject.toml')
-    else:
-        log.error('Missing pyproject.toml')
-        return False
-
-    try:
-        with open(pyproject, 'rb') as f:
-            pyproject_data = tomllib.load(f)
-        # Ensure the mandatory data can be loaded
-        buildsys = pyproject_data['build-system']
-        requires = buildsys['requires']
-        backend = buildsys['build-backend']
-        backend_path = buildsys.get('backend-path')
-        log.info('Loaded pyproject.toml')
-    except (tomllib.TOMLDecodeError, KeyError):
-        log.error("Invalid pyproject.toml", exc_info=True)
-        return False
-
-    hooks = Pep517HookCaller(source_dir, backend, backend_path)
-
-    sdist_ok = check_build_sdist(hooks, requires)
-    wheel_ok = check_build_wheel(hooks, requires)
-
-    if not sdist_ok:
-        log.warning('Sdist checks failed; scroll up to see')
-    if not wheel_ok:
-        log.warning('Wheel checks failed')
-
-    return sdist_ok
-
-
-def main(argv=None):
-    log.warning('pep517.check is deprecated. '
-                'Consider switching to https://pypi.org/project/build/')
-
-    ap = argparse.ArgumentParser()
-    ap.add_argument(
-        'source_dir',
-        help="A directory containing pyproject.toml")
-    args = ap.parse_args(argv)
-
-    enable_colourful_output()
-
-    ok = check(args.source_dir)
-
-    if ok:
-        print(ansi('Checks passed', 'green'))
-    else:
-        print(ansi('Checks failed', 'red'))
-        sys.exit(1)
-
-
-ansi_codes = {
-    'reset': '\x1b[0m',
-    'bold': '\x1b[1m',
-    'red': '\x1b[31m',
-    'green': '\x1b[32m',
-}
-
-
-def ansi(s, attr):
-    if os.name != 'nt' and sys.stdout.isatty():
-        return ansi_codes[attr] + str(s) + ansi_codes['reset']
-    else:
-        return str(s)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/pep517/colorlog.py b/pep517/colorlog.py
deleted file mode 100644
index 66310a7..0000000
--- a/pep517/colorlog.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""Nicer log formatting with colours.
-
-Code copied from Tornado, Apache licensed.
-"""
-# Copyright 2012 Facebook
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import logging
-import sys
-
-try:
-    import curses
-except ImportError:
-    curses = None
-
-
-def _stderr_supports_color():
-    color = False
-    if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
-        try:
-            curses.setupterm()
-            if curses.tigetnum("colors") > 0:
-                color = True
-        except Exception:
-            pass
-    return color
-
-
-class LogFormatter(logging.Formatter):
-    """Log formatter with colour support
-    """
-    DEFAULT_COLORS = {
-        logging.INFO: 2,  # Green
-        logging.WARNING: 3,  # Yellow
-        logging.ERROR: 1,  # Red
-        logging.CRITICAL: 1,
-    }
-
-    def __init__(self, color=True, datefmt=None):
-        r"""
-        :arg bool color: Enables color support.
-        :arg string fmt: Log message format.
-        It will be applied to the attributes dict of log records. The
-        text between ``%(color)s`` and ``%(end_color)s`` will be colored
-        depending on the level if color support is on.
-        :arg dict colors: color mappings from logging level to terminal color
-        code
-        :arg string datefmt: Datetime format.
-        Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
-        .. versionchanged:: 3.2
-        Added ``fmt`` and ``datefmt`` arguments.
-        """
-        logging.Formatter.__init__(self, datefmt=datefmt)
-        self._colors = {}
-        if color and _stderr_supports_color():
-            # The curses module has some str/bytes confusion in
-            # python3. Until version 3.2.3, most methods return
-            # bytes, but only accept strings. In addition, we want to
-            # output these strings with the logging module, which
-            # works with unicode strings. The explicit calls to
-            # unicode() below are harmless in python2 but will do the
-            # right conversion in python 3.
-            fg_color = (curses.tigetstr("setaf") or
-                        curses.tigetstr("setf") or "")
-
-            for levelno, code in self.DEFAULT_COLORS.items():
-                self._colors[levelno] = str(
-                    curses.tparm(fg_color, code), "ascii")
-            self._normal = str(curses.tigetstr("sgr0"), "ascii")
-
-            scr = curses.initscr()
-            self.termwidth = scr.getmaxyx()[1]
-            curses.endwin()
-        else:
-            self._normal = ''
-            # Default width is usually 80, but too wide is
-            # worse than too narrow
-            self.termwidth = 70
-
-    def formatMessage(self, record):
-        mlen = len(record.message)
-        right_text = '{initial}-{name}'.format(initial=record.levelname[0],
-                                               name=record.name)
-        if mlen + len(right_text) < self.termwidth:
-            space = ' ' * (self.termwidth - (mlen + len(right_text)))
-        else:
-            space = '  '
-
-        if record.levelno in self._colors:
-            start_color = self._colors[record.levelno]
-            end_color = self._normal
-        else:
-            start_color = end_color = ''
-
-        return record.message + space + start_color + right_text + end_color
-
-
-def enable_colourful_output(level=logging.INFO):
-    handler = logging.StreamHandler()
-    handler.setFormatter(LogFormatter())
-    logging.root.addHandler(handler)
-    logging.root.setLevel(level)
diff --git a/pep517/dirtools.py b/pep517/dirtools.py
deleted file mode 100644
index 3eff4d8..0000000
--- a/pep517/dirtools.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import io
-import os
-import zipfile
-
-
-def dir_to_zipfile(root):
-    """Construct an in-memory zip file for a directory."""
-    buffer = io.BytesIO()
-    zip_file = zipfile.ZipFile(buffer, 'w')
-    for root, dirs, files in os.walk(root):
-        for path in dirs:
-            fs_path = os.path.join(root, path)
-            rel_path = os.path.relpath(fs_path, root)
-            zip_file.writestr(rel_path + '/', '')
-        for path in files:
-            fs_path = os.path.join(root, path)
-            rel_path = os.path.relpath(fs_path, root)
-            zip_file.write(fs_path, rel_path)
-    return zip_file
diff --git a/pep517/envbuild.py b/pep517/envbuild.py
deleted file mode 100644
index c0415c4..0000000
--- a/pep517/envbuild.py
+++ /dev/null
@@ -1,170 +0,0 @@
-"""Build wheels/sdists by installing build deps to a temporary environment.
-"""
-
-import logging
-import os
-import shutil
-import sys
-from subprocess import check_call
-from sysconfig import get_paths
-from tempfile import mkdtemp
-
-from ._compat import tomllib
-from .wrappers import LoggerWrapper, Pep517HookCaller
-
-log = logging.getLogger(__name__)
-
-
-def _load_pyproject(source_dir):
-    with open(
-            os.path.join(source_dir, 'pyproject.toml'),
-            'rb',
-            ) as f:
-        pyproject_data = tomllib.load(f)
-    buildsys = pyproject_data['build-system']
-    return (
-        buildsys['requires'],
-        buildsys['build-backend'],
-        buildsys.get('backend-path'),
-    )
-
-
-class BuildEnvironment:
-    """Context manager to install build deps in a simple temporary environment
-
-    Based on code I wrote for pip, which is MIT licensed.
-    """
-    # Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file)
-    #
-    # Permission is hereby granted, free of charge, to any person obtaining
-    # a copy of this software and associated documentation files (the
-    # "Software"), to deal in the Software without restriction, including
-    # without limitation the rights to use, copy, modify, merge, publish,
-    # distribute, sublicense, and/or sell copies of the Software, and to
-    # permit persons to whom the Software is furnished to do so, subject to
-    # the following conditions:
-    #
-    # The above copyright notice and this permission notice shall be
-    # included in all copies or substantial portions of the Software.
-    #
-    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-    # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-    # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-    # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-    # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-    # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-    # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-    path = None
-
-    def __init__(self, cleanup=True):
-        self._cleanup = cleanup
-
-    def __enter__(self):
-        self.path = mkdtemp(prefix='pep517-build-env-')
-        log.info('Temporary build environment: %s', self.path)
-
-        self.save_path = os.environ.get('PATH', None)
-        self.save_pythonpath = os.environ.get('PYTHONPATH', None)
-
-        install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
-        install_dirs = get_paths(install_scheme, vars={
-            'base': self.path,
-            'platbase': self.path,
-        })
-
-        scripts = install_dirs['scripts']
-        if self.save_path:
-            os.environ['PATH'] = scripts + os.pathsep + self.save_path
-        else:
-            os.environ['PATH'] = scripts + os.pathsep + os.defpath
-
-        if install_dirs['purelib'] == install_dirs['platlib']:
-            lib_dirs = install_dirs['purelib']
-        else:
-            lib_dirs = install_dirs['purelib'] + os.pathsep + \
-                install_dirs['platlib']
-        if self.save_pythonpath:
-            os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \
-                self.save_pythonpath
-        else:
-            os.environ['PYTHONPATH'] = lib_dirs
-
-        return self
-
-    def pip_install(self, reqs):
-        """Install dependencies into this env by calling pip in a subprocess"""
-        if not reqs:
-            return
-        log.info('Calling pip to install %s', reqs)
-        cmd = [
-            sys.executable, '-m', 'pip', 'install', '--ignore-installed',
-            '--prefix', self.path] + list(reqs)
-        check_call(
-            cmd,
-            stdout=LoggerWrapper(log, logging.INFO),
-            stderr=LoggerWrapper(log, logging.ERROR),
-        )
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        needs_cleanup = (
-            self._cleanup and
-            self.path is not None and
-            os.path.isdir(self.path)
-        )
-        if needs_cleanup:
-            shutil.rmtree(self.path)
-
-        if self.save_path is None:
-            os.environ.pop('PATH', None)
-        else:
-            os.environ['PATH'] = self.save_path
-
-        if self.save_pythonpath is None:
-            os.environ.pop('PYTHONPATH', None)
-        else:
-            os.environ['PYTHONPATH'] = self.save_pythonpath
-
-
-def build_wheel(source_dir, wheel_dir, config_settings=None):
-    """Build a wheel from a source directory using PEP 517 hooks.
-
-    :param str source_dir: Source directory containing pyproject.toml
-    :param str wheel_dir: Target directory to create wheel in
-    :param dict config_settings: Options to pass to build backend
-
-    This is a blocking function which will run pip in a subprocess to install
-    build requirements.
-    """
-    if config_settings is None:
-        config_settings = {}
-    requires, backend, backend_path = _load_pyproject(source_dir)
-    hooks = Pep517HookCaller(source_dir, backend, backend_path)
-
-    with BuildEnvironment() as env:
-        env.pip_install(requires)
-        reqs = hooks.get_requires_for_build_wheel(config_settings)
-        env.pip_install(reqs)
-        return hooks.build_wheel(wheel_dir, config_settings)
-
-
-def build_sdist(source_dir, sdist_dir, config_settings=None):
-    """Build an sdist from a source directory using PEP 517 hooks.
-
-    :param str source_dir: Source directory containing pyproject.toml
-    :param str sdist_dir: Target directory to place sdist in
-    :param dict config_settings: Options to pass to build backend
-
-    This is a blocking function which will run pip in a subprocess to install
-    build requirements.
-    """
-    if config_settings is None:
-        config_settings = {}
-    requires, backend, backend_path = _load_pyproject(source_dir)
-    hooks = Pep517HookCaller(source_dir, backend, backend_path)
-
-    with BuildEnvironment() as env:
-        env.pip_install(requires)
-        reqs = hooks.get_requires_for_build_sdist(config_settings)
-        env.pip_install(reqs)
-        return hooks.build_sdist(sdist_dir, config_settings)
diff --git a/pep517/in_process/__init__.py b/pep517/in_process/__init__.py
deleted file mode 100644
index 281a356..0000000
--- a/pep517/in_process/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""This is a subpackage because the directory is on sys.path for _in_process.py
-
-The subpackage should stay as empty as possible to avoid shadowing modules that
-the backend might import.
-"""
-from contextlib import contextmanager
-from os.path import abspath, dirname
-from os.path import join as pjoin
-
-try:
-    import importlib.resources as resources
-    try:
-        resources.files
-    except AttributeError:
-        # Python 3.8 compatibility
-        def _in_proc_script_path():
-            return resources.path(__package__, '_in_process.py')
-    else:
-        def _in_proc_script_path():
-            return resources.as_file(
-                resources.files(__package__).joinpath('_in_process.py'))
-except ImportError:
-    # Python 3.6 compatibility
-    @contextmanager
-    def _in_proc_script_path():
-        yield pjoin(dirname(abspath(__file__)), '_in_process.py')
diff --git a/pep517/meta.py b/pep517/meta.py
deleted file mode 100644
index 4afc3c0..0000000
--- a/pep517/meta.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""Build metadata for a project using PEP 517 hooks.
-"""
-import argparse
-import functools
-import logging
-import os
-import shutil
-import tempfile
-
-try:
-    import importlib.metadata as imp_meta
-except ImportError:
-    import importlib_metadata as imp_meta
-
-try:
-    from zipfile import Path
-except ImportError:
-    from zipp import Path
-
-from .build import compat_system, load_system, validate_system
-from .dirtools import dir_to_zipfile
-from .envbuild import BuildEnvironment
-from .wrappers import Pep517HookCaller, quiet_subprocess_runner
-
-log = logging.getLogger(__name__)
-
-
-def _prep_meta(hooks, env, dest):
-    reqs = hooks.get_requires_for_build_wheel({})
-    log.info('Got build requires: %s', reqs)
-
-    env.pip_install(reqs)
-    log.info('Installed dynamic build dependencies')
-
-    with tempfile.TemporaryDirectory() as td:
-        log.info('Trying to build metadata in %s', td)
-        filename = hooks.prepare_metadata_for_build_wheel(td, {})
-        source = os.path.join(td, filename)
-        shutil.move(source, os.path.join(dest, os.path.basename(filename)))
-
-
-def build(source_dir='.', dest=None, system=None):
-    system = system or load_system(source_dir)
-    dest = os.path.join(source_dir, dest or 'dist')
-    os.makedirs(dest, exist_ok=True)
-    validate_system(system)
-    hooks = Pep517HookCaller(
-        source_dir, system['build-backend'], system.get('backend-path')
-    )
-
-    with hooks.subprocess_runner(quiet_subprocess_runner):
-        with BuildEnvironment() as env:
-            env.pip_install(system['requires'])
-            _prep_meta(hooks, env, dest)
-
-
-def build_as_zip(builder=build):
-    with tempfile.TemporaryDirectory() as out_dir:
-        builder(dest=out_dir)
-        return dir_to_zipfile(out_dir)
-
-
-def load(root):
-    """
-    Given a source directory (root) of a package,
-    return an importlib.metadata.Distribution object
-    with metadata build from that package.
-    """
-    root = os.path.expanduser(root)
-    system = compat_system(root)
-    builder = functools.partial(build, source_dir=root, system=system)
-    path = Path(build_as_zip(builder))
-    return imp_meta.PathDistribution(path)
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    'source_dir',
-    help="A directory containing pyproject.toml",
-)
-parser.add_argument(
-    '--out-dir', '-o',
-    help="Destination in which to save the builds relative to source dir",
-)
-
-
-def main():
-    args = parser.parse_args()
-    build(args.source_dir, args.out_dir)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/pyproject.toml b/pyproject.toml
index f24e689..dff4ee1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,19 +1,20 @@
 [build-system]
-requires = ["flit_core >=2,<4"]
+requires = ["flit_core >=3.2,<4"]
 build-backend = "flit_core.buildapi"
 
 [tool.flit.metadata]
-module = "pep517"
+module = "pyproject_hooks"
+dist-name = "pyproject-hooks"
 author = "Thomas Kluyver"
 author-email = "thomas@kluyver.me.uk"
-home-page = "https://github.com/pypa/pep517"
+home-page = "https://github.com/pypa/pyproject-hooks"
 description-file = "README.rst"
 requires = [
     "tomli >=1.1.0;python_version<'3.11'",
     "importlib_metadata;python_version<'3.8'",
     "zipp;python_version<'3.8'",
 ]
-requires-python = ">=3.6"
+requires-python = ">=3.7"
 classifiers = [
     "License :: OSI Approved :: MIT License",
     "Programming Language :: Python :: 3",
diff --git a/src/pyproject_hooks/__init__.py b/src/pyproject_hooks/__init__.py
new file mode 100644
index 0000000..b6684aa
--- /dev/null
+++ b/src/pyproject_hooks/__init__.py
@@ -0,0 +1,23 @@
+"""Wrappers to build Python packages using PEP 517 hooks
+"""
+
+from ._impl import (
+    BackendInvalid,
+    BackendUnavailable,
+    BuildBackendHookCaller,
+    HookMissing,
+    UnsupportedOperation,
+    default_subprocess_runner,
+    quiet_subprocess_runner,
+)
+
+__version__ = '0.13.0'
+__all__ = [
+    'BackendUnavailable',
+    'BackendInvalid',
+    'HookMissing',
+    'UnsupportedOperation',
+    'default_subprocess_runner',
+    'quiet_subprocess_runner',
+    'BuildBackendHookCaller',
+]
diff --git a/pep517/_compat.py b/src/pyproject_hooks/_compat.py
similarity index 100%
rename from pep517/_compat.py
rename to src/pyproject_hooks/_compat.py
diff --git a/pep517/wrappers.py b/src/pyproject_hooks/_impl.py
similarity index 97%
rename from pep517/wrappers.py
rename to src/pyproject_hooks/_impl.py
index 987a62a..6720e5b 100644
--- a/pep517/wrappers.py
+++ b/src/pyproject_hooks/_impl.py
@@ -8,17 +8,7 @@
 from os.path import join as pjoin
 from subprocess import STDOUT, check_call, check_output
 
-from .in_process import _in_proc_script_path
-
-__all__ = [
-    'BackendUnavailable',
-    'BackendInvalid',
-    'HookMissing',
-    'UnsupportedOperation',
-    'default_subprocess_runner',
-    'quiet_subprocess_runner',
-    'Pep517HookCaller',
-]
+from ._in_process import _in_proc_script_path
 
 
 def write_json(obj, path, **kwargs):
@@ -100,7 +90,7 @@ def norm_and_check(source_tree, requested):
     return abs_requested
 
 
-class Pep517HookCaller:
+class BuildBackendHookCaller:
     """A wrapper around a source directory to be built with a PEP 517 backend.
 
     :param source_dir: The path to the source directory, containing
diff --git a/src/pyproject_hooks/_in_process/__init__.py b/src/pyproject_hooks/_in_process/__init__.py
new file mode 100644
index 0000000..917fa06
--- /dev/null
+++ b/src/pyproject_hooks/_in_process/__init__.py
@@ -0,0 +1,18 @@
+"""This is a subpackage because the directory is on sys.path for _in_process.py
+
+The subpackage should stay as empty as possible to avoid shadowing modules that
+the backend might import.
+"""
+
+import importlib.resources as resources
+
+try:
+    resources.files
+except AttributeError:
+    # Python 3.8 compatibility
+    def _in_proc_script_path():
+        return resources.path(__package__, '_in_process.py')
+else:
+    def _in_proc_script_path():
+        return resources.as_file(
+            resources.files(__package__).joinpath('_in_process.py'))
diff --git a/pep517/in_process/_in_process.py b/src/pyproject_hooks/_in_process/_in_process.py
similarity index 100%
rename from pep517/in_process/_in_process.py
rename to src/pyproject_hooks/_in_process/_in_process.py
diff --git a/tox.ini b/tox.ini
index 00b1010..40b9ecf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
-envlist = py36, py37, py38, py39, py310, py311, pypy3, isort
-skipsdist = true
+envlist = py37, py38, py39, py310, py311, pypy3, isort
+isolated_build = true
 
 [testenv]
 passenv =
@@ -14,7 +14,7 @@ commands = python -m isort --check --diff {toxinidir}
 
 [testenv:flake8]
 deps = flake8
-commands = flake8 pep517
+commands = flake8 src
 
 [testenv:release]
 skip_install = True


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index aea19d3..51c989e 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -30,7 +30,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
         os: [ubuntu-latest, macos-latest, windows-latest]
 
     steps:
diff --git a/tests/test_build.py b/tests/test_build.py
deleted file mode 100644
index 2a7c2bc..0000000
--- a/tests/test_build.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import pytest
-
-from pep517 import build
-
-
-def system(*args):
-    return dict.fromkeys(args)
-
-
-class TestValidateSystem:
-    def test_missing(self):
-        with pytest.raises(ValueError):
-            build.validate_system(system())
-        with pytest.raises(ValueError):
-            build.validate_system(system('requires'))
-        with pytest.raises(ValueError):
-            build.validate_system(system('build-backend'))
-        with pytest.raises(ValueError):
-            build.validate_system(system('other'))
-
-    def test_missing_and_extra(self):
-        with pytest.raises(ValueError):
-            build.validate_system(system('build-backend', 'other'))
-
-    def test_satisfied(self):
-        build.validate_system(system('build-backend', 'requires'))
-        build.validate_system(system('build-backend', 'requires', 'other'))
diff --git a/tests/test_call_hooks.py b/tests/test_call_hooks.py
index 9f73d50..c3d690b 100644
--- a/tests/test_call_hooks.py
+++ b/tests/test_call_hooks.py
@@ -11,9 +11,9 @@
 from testpath import assert_isfile, modified_env
 from testpath.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
 
-from pep517.wrappers import (
+from pyproject_hooks import (
     BackendUnavailable,
-    Pep517HookCaller,
+    BuildBackendHookCaller,
     UnsupportedOperation,
     default_subprocess_runner,
 )
@@ -26,7 +26,7 @@ def get_hooks(pkg, **kwargs):
     source_dir = pjoin(SAMPLES_DIR, pkg)
     with open(pjoin(source_dir, 'pyproject.toml'), 'rb') as f:
         data = tomli.load(f)
-    return Pep517HookCaller(
+    return BuildBackendHookCaller(
         source_dir, data['build-system']['build-backend'], **kwargs
     )
 
diff --git a/tests/test_envbuild.py b/tests/test_envbuild.py
deleted file mode 100644
index 3a85c29..0000000
--- a/tests/test_envbuild.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import tarfile
-import zipfile
-from os.path import abspath, dirname
-from os.path import join as pjoin
-from unittest.mock import call, patch
-
-from testpath import assert_isfile, modified_env
-from testpath.tempdir import TemporaryDirectory
-
-from pep517.envbuild import BuildEnvironment, build_sdist, build_wheel
-
-SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples')
-BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs')
-
-
-@patch.object(BuildEnvironment, 'pip_install')
-def test_build_wheel(mock_pip_install):
-    with modified_env({'PYTHONPATH': BUILDSYS_PKGS}), \
-            TemporaryDirectory() as outdir:
-        filename = build_wheel(pjoin(SAMPLES_DIR, 'pkg1'), outdir)
-        assert_isfile(pjoin(outdir, filename))
-        assert zipfile.is_zipfile(pjoin(outdir, filename))
-
-    assert mock_pip_install.call_count == 2
-    assert mock_pip_install.call_args_list[0] == call(['eg_buildsys'])
-    assert mock_pip_install.call_args_list[1] == call(['wheelwright'])
-
-
-@patch.object(BuildEnvironment, 'pip_install')
-def test_build_sdist(mock_pip_install):
-    with modified_env({'PYTHONPATH': BUILDSYS_PKGS}), \
-            TemporaryDirectory() as outdir:
-        filename = build_sdist(pjoin(SAMPLES_DIR, 'pkg1'), outdir)
-        assert_isfile(pjoin(outdir, filename))
-        assert tarfile.is_tarfile(pjoin(outdir, filename))
-
-    assert mock_pip_install.call_count == 2
-    assert mock_pip_install.call_args_list[0] == call(['eg_buildsys'])
-    assert mock_pip_install.call_args_list[1] == call(['frog'])
diff --git a/tests/test_hook_fallbacks.py b/tests/test_hook_fallbacks.py
index cdfd581..40a2fc8 100644
--- a/tests/test_hook_fallbacks.py
+++ b/tests/test_hook_fallbacks.py
@@ -6,7 +6,7 @@
 from testpath import assert_isfile, modified_env
 from testpath.tempdir import TemporaryDirectory
 
-from pep517.wrappers import HookMissing, Pep517HookCaller
+from pyproject_hooks import BuildBackendHookCaller, HookMissing
 
 SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples')
 BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs')
@@ -16,7 +16,7 @@ def get_hooks(pkg):
     source_dir = pjoin(SAMPLES_DIR, pkg)
     with open(pjoin(source_dir, 'pyproject.toml'), 'rb') as f:
         data = tomli.load(f)
-    return Pep517HookCaller(source_dir, data['build-system']['build-backend'])
+    return BuildBackendHookCaller(source_dir, data['build-system']['build-backend'])
 
 
 def test_get_requires_for_build_wheel():
diff --git a/tests/test_inplace_hooks.py b/tests/test_inplace_hooks.py
index 40c9f9d..5e0ee57 100644
--- a/tests/test_inplace_hooks.py
+++ b/tests/test_inplace_hooks.py
@@ -5,7 +5,7 @@
 import tomli
 from testpath import modified_env
 
-from pep517.wrappers import BackendInvalid, Pep517HookCaller
+from pyproject_hooks import BackendInvalid, BuildBackendHookCaller
 
 SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples')
 BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs')
@@ -20,7 +20,7 @@ def get_hooks(pkg, backend=None, path=None):
         backend = data['build-system']['build-backend']
     if path is None:
         path = data['build-system']['backend-path']
-    return Pep517HookCaller(source_dir, backend, path)
+    return BuildBackendHookCaller(source_dir, backend, path)
 
 
 @pytest.mark.parametrize('backend_path', [
@@ -28,7 +28,7 @@ def get_hooks(pkg, backend=None, path=None):
     ['../pkg1', 'subdir/..'],
 ])
 def test_backend_path_within_tree(backend_path):
-    Pep517HookCaller(SOURCE_DIR, 'dummy', backend_path)
+    BuildBackendHookCaller(SOURCE_DIR, 'dummy', backend_path)
 
 
 @pytest.mark.parametrize('backend_path', [
@@ -40,7 +40,7 @@ def test_backend_path_within_tree(backend_path):
 def test_backend_out_of_tree(backend_path):
     # TODO: Do we want to insist on ValueError, or invent another exception?
     with pytest.raises(Exception):
-        Pep517HookCaller(SOURCE_DIR, 'dummy', backend_path)
+        BuildBackendHookCaller(SOURCE_DIR, 'dummy', backend_path)
 
 
 def test_intree_backend():
diff --git a/tests/test_meta.py b/tests/test_meta.py
deleted file mode 100644
index ddd219e..0000000
--- a/tests/test_meta.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import re
-
-from pep517 import meta
-
-
-def test_meta_for_this_package():
-    dist = meta.load('.')
-    assert re.match(r'[\d.]+', dist.version)
-    assert dist.metadata['Name'] == 'pep517'
-
-
-def test_classic_package(tmpdir):
-    (tmpdir / 'setup.py').write_text(
-        'from distutils.core import setup; setup(name="foo", version="1.0")',
-        encoding='utf-8',
-    )
-    dist = meta.load(str(tmpdir))
-    assert dist.version == '1.0'
-    assert dist.metadata['Name'] == 'foo'
-
-
-def test_meta_output(capfd):
-    """load shouldn't emit any output"""
-    meta.load('.')
-    captured = capfd.readouterr()
-    assert captured.out == captured.err == ''