Skip to content

Commit

Permalink
Support build dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
abn committed Aug 10, 2020
1 parent de355ea commit 2591618
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 3 deletions.
13 changes: 10 additions & 3 deletions poetry/console/commands/build.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from cleo import option

from .env_command import EnvCommand
from poetry.console.commands.installer_command import InstallerCommand


class BuildCommand(EnvCommand):
class BuildCommand(InstallerCommand):

name = "build"
description = "Builds a package, as a tarball and a wheel by default."
Expand Down Expand Up @@ -33,4 +33,11 @@ def handle(self):
)

builder = Builder(self.poetry)
builder.build(fmt)

executable = None
if self.poetry.package.build_requires:
# ensure build requirements are available if specified
self.installer.categories({"build"}).run()
executable = self.installer.env.python

builder.build(fmt, executable=executable)
4 changes: 4 additions & 0 deletions poetry/installation/base_installer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
class BaseInstaller:
@property
def env(self):
return getattr(self, "_env", None)

def install(self, package):
raise NotImplementedError

Expand Down
37 changes: 37 additions & 0 deletions poetry/installation/installer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Iterable
from typing import List
from typing import Optional
from typing import Union
Expand All @@ -11,6 +12,7 @@
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils.env import Env
from poetry.utils.extras import get_extra_package_names
from poetry.utils.helpers import canonicalize_name

Expand Down Expand Up @@ -47,10 +49,12 @@ def __init__(
self._verbose = False
self._write_lock = True
self._dev_mode = True
self._build_mode = True
self._execute_operations = True
self._lock = False

self._whitelist = []
self._categories = set()

self._extras = []

Expand All @@ -66,6 +70,10 @@ def __init__(

self._installed_repository = installed

@property
def env(self): # type: () -> Env
return self._env

@property
def executor(self):
return self._executor
Expand Down Expand Up @@ -132,6 +140,14 @@ def dev_mode(self, dev_mode=True): # type: (bool) -> Installer
def is_dev_mode(self): # type: () -> bool
return self._dev_mode

def build_mode(self, build_mode=True): # type: (bool) -> Installer
self._build_mode = build_mode

return self

def is_build_mode(self): # type: () -> bool
return self._build_mode or self._dev_mode

def update(self, update=True): # type: (bool) -> Installer
self._update = update

Expand Down Expand Up @@ -163,6 +179,13 @@ def whitelist(self, packages): # type: (dict) -> Installer

return self

def categories(
self, categories=None
): # type: (Optional[Iterable[str]]) -> Installer
self._categories = {category for category in categories or []}

return self

def extras(self, extras): # type: (list) -> Installer
self._extras = extras

Expand Down Expand Up @@ -241,6 +264,10 @@ def _do_install(self, local_repo):
root = root.clone()
del root.dev_requires[:]

if not self.is_build_mode():
root = root.clone()
del root.build_requires[:]

if self._io.is_verbose():
self._io.write_line("")
self._io.write_line(
Expand Down Expand Up @@ -455,6 +482,8 @@ def _get_operations_from_lock(
is_installed = True
if locked.category == "dev" and not self.is_dev_mode():
ops.append(Uninstall(locked))
elif locked.category == "build" and not self.is_build_mode():
ops.append(Uninstall(locked))
elif locked.optional and locked.name not in extra_packages:
# Installed but optional and not requested in extras
ops.append(Uninstall(locked))
Expand Down Expand Up @@ -506,11 +535,19 @@ def _filter_operations(
if package.name not in extra_packages:
op.skip("Not required")

if self._categories and package.category not in self._categories:
op.skip("Category ({}) not enabled".format(package.category))

# If the package is a dev package and dev packages
# are not requested, we skip it
if package.category == "dev" and not self.is_dev_mode():
op.skip("Dev dependencies not requested")

# If the package is a build package and build packages
# are not requested, we skip it
if package.category == "build" and not self.is_build_mode():
op.skip("Build dependencies not requested")

def _get_extra_packages(self, repo): # type: (Repository) -> List[str]
"""
Returns all package names required by extras.
Expand Down
2 changes: 2 additions & 0 deletions poetry/puzzle/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ def _get_tags_for_package(self, package, graph, depth=0):

if "main" in categories:
category = "main"
elif "build" in categories:
category = "build"
else:
category = "dev"

Expand Down
83 changes: 83 additions & 0 deletions tests/console/commands/test_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import re

import pytest

from tests.helpers import get_package


@pytest.fixture
def command_tester_with_build_requires(
repo, make_poetry, make_installer_command_tester
):
repo.add_package(get_package("cython", "0.29.6"))
poetry = make_poetry("project_with_build_system_requires")
return make_installer_command_tester(poetry, "build")


@pytest.fixture
def command_tester(project_directory, make_poetry, make_installer_command_tester):
return make_installer_command_tester(make_poetry(project_directory), "build")


def test_build_project_complete(command_tester):
tester = command_tester
command_tester.execute()

assert tester._command.installer.executor.installations_count == 0

output = tester.io.fetch_output()

assert "Writing lock file" not in output
assert "Building sdist" in output
assert "Built simple-project-1.2.3.tar.gz" in output
assert "Building wheel" in output
assert re.search(r"Built simple_project-1\.2\.3-.*\.whl", output) is not None


def test_build_project_sdist(command_tester):
tester = command_tester
command_tester.execute("-f sdist")

assert tester._command.installer.executor.installations_count == 0

output = tester.io.fetch_output()

assert "Writing lock file" not in output
assert "Building sdist" in output
assert "Built simple-project-1.2.3.tar.gz" in output
assert "Building wheel" not in output
assert re.search(r"Built simple_project-1\.2\.3-.*\.whl", output) is None


def test_build_project_wheel(command_tester):
tester = command_tester
command_tester.execute("-f wheel")

assert tester._command.installer.executor.installations_count == 0

output = tester.io.fetch_output()

assert "Writing lock file" not in output
assert "Building sdist" not in output
assert "Built simple-project-1.2.3.tar.gz" not in output
assert "Building wheel" in output
assert re.search(r"Built simple_project-1\.2\.3-.*\.whl", output) is not None


def test_build_project_with_build_requires(command_tester_with_build_requires):
tester = command_tester_with_build_requires
tester.execute()

assert tester._command.installer.executor.installations_count == 1

package = tester._command.installer.executor._installs[0]
assert package.name == "cython"
assert package.version.text == "0.29.6"

output = tester.io.fetch_output()

assert "Writing lock file" in output
assert "Building sdist" in output
assert "Built project-1.2.3.tar.gz" in output
assert "Building wheel" in output
assert re.search(r"Built project-1\.2\.3-.*\.whl", output) is not None
2 changes: 2 additions & 0 deletions tests/fixtures/project_with_build_system_requires/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
My Package
==========
Empty file.
Empty file.
19 changes: 19 additions & 0 deletions tests/fixtures/project_with_build_system_requires/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[tool.poetry]
name = "project"
version = "1.2.3"
description = "Some description."
authors = []
license = "MIT"

[tool.poetry.build]
script = "build.py"
generate-setup-file = false

[tool.poetry.dependencies]
python = "^3.7"

[build-system]
requires = [
"poetry-core",
"Cython>=0.29.6",
]

0 comments on commit 2591618

Please sign in to comment.