Skip to content

Commit

Permalink
PEP-517 source distribution support (#954)
Browse files Browse the repository at this point in the history
create a ``.package`` virtual environment to perform build operations inside
Resolves #573 and #820.
  • Loading branch information
gaborbernat authored Sep 11, 2018
1 parent 9335906 commit ccdca4a
Show file tree
Hide file tree
Showing 29 changed files with 656 additions and 163 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repos:
rev: v1.0.1
hooks:
- id: seed-isort-config
args: [--application-directories, src]
args: [--application-directories, "src:."]
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.4
hooks:
Expand Down
3 changes: 3 additions & 0 deletions .vsts-ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.rr)

variables:
"System.PreferGit": true

trigger:
branches:
include:
Expand Down
2 changes: 2 additions & 0 deletions changelog/573.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`PEP-517 <https://www.python.org/dev/peps/pep-0517/>`_ source distribution support (create a
``.package`` virtual environment to perform build operations inside) by :user:`gaborbernat`
1 change: 1 addition & 0 deletions changelog/820.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`flit <https://flit.readthedocs.io>`_ support via implementing ``PEP-517`` by :user:`gaborbernat`
21 changes: 21 additions & 0 deletions doc/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ and will first lookup global tox settings in this section:

.. confval:: requires=LIST

.. versionadded:: 3.2.0

Specify python packages that need to exist alongside the tox installation for the tox build
to be able to start. Use this to specify plugin requirements and build dependencies.

Expand All @@ -88,6 +90,25 @@ and will first lookup global tox settings in this section:
requires = setuptools >= 30.0.0
py
.. confval:: isolated_build=True|False(default)

.. versionadded:: 3.3.0

Activate isolated build environment. tox will use a virtual environment to build
a source distribution from the source tree. For build tools and arguments use
the ``pyproject.toml`` file as specified in
`PEP-517 <https://www.python.org/dev/peps/pep-0517/>`_ and
`PEP-518 <https://www.python.org/dev/peps/pep-0518/>`_. To specify the virtual
environment Python version define use the :confval:`isolated_build_env` config
section.

.. confval:: isolated_build_env=str

.. versionadded:: 3.3.0

Name of the virtual environment used to create a source distribution from the
source tree. By **default ``.package``** is used.


Virtualenv test environment settings
------------------------------------
Expand Down
60 changes: 60 additions & 0 deletions doc/example/package.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
packaging
=========

Although one can use tox to develop and test applications one of its most popular
usage is to help library creators. Libraries need first to be packaged, so then
they can be installed inside a virtual environment for testing. To help with this
tox implements `PEP-517 <https://www.python.org/dev/peps/pep-0517/>`_ and
`PEP-518 <https://www.python.org/dev/peps/pep-0518/>`_. This means that by default
tox will build source distribution out of source trees. Before running test commands
``pip`` is used to install the source distribution inside the build environment.

To create a source distribution there are multiple tools out there and with ``PEP-517``
and ``PEP-518`` you can easily use your favorite one with tox. Historically tox
only supported ``setuptools``, and always used the tox host environment to build
a source distribution from the source tree. This is still the default behavior.
To opt out of this behaviour you need to set isolated builds to true.

setuptools
----------
Using the ``pyproject.toml`` file at the root folder (alongside ``setup.py``) one can specify
build requirements.

.. code-block:: toml
[build-system]
requires = [
"setuptools >= 35.0.2",
"setuptools_scm >= 2.0.0, <3"
]
build-backend = "setuptools.build_meta"
.. code-block:: ini
# tox.ini
[tox]
build_isolated = True
flit
----
`flit <https://flit.readthedocs.io/en/latest/>`_ requires ``Python 3``, however the generated source
distribution can be installed under ``python 2``. Furthermore it does not require a ``setup.py``
file as that information is also added to the ``pyproject.toml`` file.

.. code-block:: toml
[build-system]
requires = ["flit >= 1.1"]
build-backend = "flit.buildapi"
[tool.flit.metadata]
module = "package_toml_flit"
author = "Happy Harry"
author-email = "happy@harry.com"
home-page = "https://github.com/happy-harry/is"
.. code-block:: ini
# tox.ini
[tox]
build_isolated = True
1 change: 1 addition & 0 deletions doc/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ tox configuration and usage examples
:maxdepth: 2

example/basic.rst
example/package.rst
example/pytest.rst
example/unittest
example/nose.rst
Expand Down
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def main():
"py >= 1.4.17, <2",
"six >= 1.0.0, <2",
"virtualenv >= 1.11.2",
"toml >=0.9.4",
],
extras_require={
"testing": [
Expand All @@ -76,7 +77,11 @@ def main():
"pytest-xdist >= 1.22.2, <2",
"pytest-randomly >= 1.2.3, <2",
],
"docs": ["sphinx >= 1.7.5, < 2", "towncrier >= 18.5.0"],
"docs": [
"sphinx >= 1.7.5, < 2",
"towncrier >= 18.5.0",
"pygments-github-lexers >= 0.0.5",
],
},
classifiers=[
"Development Status :: 5 - Production/Stable",
Expand Down
17 changes: 14 additions & 3 deletions src/tox/_pytestplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def initproj(tmpdir):
setup.py
"""

def initproj_(nameversion, filedefs=None, src_root="."):
def initproj_(nameversion, filedefs=None, src_root=".", add_missing_setup_py=True):
if filedefs is None:
filedefs = {}
if not src_root:
Expand All @@ -297,7 +297,7 @@ def initproj_(nameversion, filedefs=None, src_root="."):

base.ensure(dir=1)
create_files(base, filedefs)
if not _filedefs_contains(base, filedefs, "setup.py"):
if not _filedefs_contains(base, filedefs, "setup.py") and add_missing_setup_py:
create_files(
base,
{
Expand All @@ -319,7 +319,18 @@ def initproj_(nameversion, filedefs=None, src_root="."):
)
if not _filedefs_contains(base, filedefs, src_root_path.join(name)):
create_files(
src_root_path, {name: {"__init__.py": "__version__ = {!r}".format(version)}}
src_root_path,
{
name: {
"__init__.py": textwrap.dedent(
'''
""" module {} """
__version__ = {!r}'''
)
.strip()
.format(name, version)
}
},
)
manifestlines = [
"include {}".format(p.relto(base)) for p in base.visit(lambda x: x.check(file=1))
Expand Down
23 changes: 21 additions & 2 deletions src/tox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,8 @@ def __init__(self, config, inipath): # noqa
config.logdir = config.toxworkdir.join("log")
self._make_thread_safe_path(config, "logdir", unique_id)

config.envlist, all_envs = self._getenvdata(reader)
self.parse_build_isolation(config, reader)
config.envlist, all_envs = self._getenvdata(reader, config)

# factors used in config or predefined
known_factors = self._list_section_factors("testenv")
Expand Down Expand Up @@ -1006,6 +1007,16 @@ def __init__(self, config, inipath): # noqa

config.skipsdist = reader.getbool("skipsdist", all_develop)

def parse_build_isolation(self, config, reader):
config.isolated_build = reader.getbool("isolated_build", False)
config.isolated_build_env = reader.getstring("isolated_build_env", ".package")
if config.isolated_build is True:
name = config.isolated_build_env
if name not in config.envconfigs:
config.envconfigs[name] = self.make_envconfig(
name, testenvprefix + name, reader._subs, config
)

def _make_thread_safe_path(self, config, attr, unique_id):
if config.option.parallel_safe_build:
path = getattr(config, attr)
Expand Down Expand Up @@ -1069,7 +1080,7 @@ def make_envconfig(self, name, section, subs, config, replace=True):
reader.addsubstitutions(**{env_attr.name: res})
return tc

def _getenvdata(self, reader):
def _getenvdata(self, reader, config):
candidates = (
self.config.option.env,
os.environ.get("TOXENV"),
Expand All @@ -1086,9 +1097,17 @@ def _getenvdata(self, reader):
if not all_envs:
all_envs.add("python")

package_env = config.isolated_build_env
if config.isolated_build is True and package_env in all_envs:
all_envs.remove(package_env)

if not env_list or "ALL" in env_list:
env_list = sorted(all_envs)

if config.isolated_build is True and package_env in env_list:
msg = "isolated_build_env {} cannot be part of envlist".format(package_env)
raise tox.exception.ConfigError(msg)

return env_list, all_envs


Expand Down
Loading

0 comments on commit ccdca4a

Please sign in to comment.