Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support build system requires and enable use of custom executable for build script execution #58

Closed
wants to merge 2 commits into from
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
39 changes: 38 additions & 1 deletion poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
from typing import Optional

from .json import validate_object
from .packages import DirectoryDependency
from .packages import FileDependency
from .packages import dependency_from_pep_508
from .packages.dependency import Dependency
from .packages.project_package import ProjectPackage
from .poetry import Poetry
from .pyproject import PyProjectTOML
from .spdx import license_by_id
from .utils._compat import Path
from .utils.helpers import canonicalize_name


class Factory(object):
Expand All @@ -21,7 +25,10 @@ class Factory(object):

def create_poetry(self, cwd=None): # type: (Optional[Path]) -> Poetry
poetry_file = self.locate(cwd)
local_config = PyProjectTOML(path=poetry_file).poetry_config

pyproject = PyProjectTOML(path=poetry_file)
local_config = pyproject.poetry_config
build_system = pyproject.build_system

# Checking validity
check_result = self.validate(local_config)
Expand Down Expand Up @@ -87,6 +94,36 @@ def create_poetry(self, cwd=None): # type: (Optional[Path]) -> Poetry

package.add_dependency(name, constraint, category="dev")

for requirement in build_system.requires:
dependency = None
try:
dependency = dependency_from_pep_508(requirement)
except ValueError:
# PEP 517 requires can be path if not PEP 508
path = Path(requirement)
try:
if path.is_file():
dependency = FileDependency(
name=canonicalize_name(path.name), path=path
)
elif path.is_dir():
dependency = DirectoryDependency(
name=canonicalize_name(path.name), path=path
)
except OSError:
# compatibility Python < 3.8
# https://docs.python.org/3/library/pathlib.html#methods
pass

if dependency is None:
# skip since we could not determine requirement
continue

if dependency.name not in {"poetry", "poetry-core"}:
package.add_dependency(
dependency.name, dependency.constraint, category="build"
)

extras = local_config.get("extras", {})
for extra_name, requirements in extras.items():
package.extras[extra_name] = []
Expand Down
11 changes: 9 additions & 2 deletions poetry/core/masonry/builder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from typing import Optional
from typing import Union

from poetry.core.utils._compat import Path

from .builders.complete import CompleteBuilder
from .builders.sdist import SdistBuilder
from .builders.wheel import WheelBuilder
Expand All @@ -10,10 +15,12 @@ class Builder:
def __init__(self, poetry):
self._poetry = poetry

def build(self, fmt):
def build(
self, fmt, executable=None
): # type: (str, Optional[Union[str, Path]]) -> None
if fmt not in self._FORMATS:
raise ValueError("Invalid format: {}".format(fmt))

builder = self._FORMATS[fmt](self._poetry)
builder = self._FORMATS[fmt](self._poetry, executable=executable)

return builder.build()
10 changes: 8 additions & 2 deletions poetry/core/masonry/builders/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import re
import shutil
import sys
import tempfile

from collections import defaultdict
Expand Down Expand Up @@ -40,13 +41,14 @@ class Builder(object):
format = None

def __init__(
self, poetry, ignore_packages_formats=False
): # type: ("Poetry", bool) -> None
self, poetry, ignore_packages_formats=False, executable=None
): # type: ("Poetry", bool, Optional[Union[Path, str]]) -> None
self._poetry = poetry
self._package = poetry.package
self._path = poetry.file.parent
self._original_path = self._path
self._excluded_files = None
self._executable = Path(executable or sys.executable) # type: Path

packages = []
for p in self._package.packages:
Expand Down Expand Up @@ -87,6 +89,10 @@ def __init__(

self._meta = Metadata.from_package(self._package)

@property
def executable(self): # type: () -> Path
return self._executable

def build(self):
raise NotImplementedError()

Expand Down
10 changes: 8 additions & 2 deletions poetry/core/masonry/builders/complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ def build(self):

with self.unpacked_tarball(sdist_file) as tmpdir:
WheelBuilder.make_in(
Factory().create_poetry(tmpdir), dist_dir, original=self._poetry
Factory().create_poetry(tmpdir),
dist_dir,
original=self._poetry,
executable=self.executable,
)
else:
with self.unpacked_tarball(sdist_file) as tmpdir:
WheelBuilder.make_in(
Factory().create_poetry(tmpdir), dist_dir, original=self._poetry
Factory().create_poetry(tmpdir),
dist_dir,
original=self._poetry,
executable=self.executable,
)

@classmethod
Expand Down
25 changes: 16 additions & 9 deletions poetry/core/masonry/builders/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import shutil
import stat
import subprocess
import sys
import tempfile
import zipfile

Expand Down Expand Up @@ -43,8 +42,8 @@
class WheelBuilder(Builder):
format = "wheel"

def __init__(self, poetry, target_dir=None, original=None):
super(WheelBuilder, self).__init__(poetry)
def __init__(self, poetry, target_dir=None, original=None, executable=None):
super(WheelBuilder, self).__init__(poetry, executable=executable)

self._records = []
self._original_path = self._path
Expand All @@ -53,16 +52,18 @@ def __init__(self, poetry, target_dir=None, original=None):
self._original_path = original.file.parent

@classmethod
def make_in(cls, poetry, directory=None, original=None):
wb = WheelBuilder(poetry, target_dir=directory, original=original)
def make_in(cls, poetry, directory=None, original=None, executable=None):
wb = WheelBuilder(
poetry, target_dir=directory, original=original, executable=executable
)
wb.build()

return wb.wheel_filename

@classmethod
def make(cls, poetry):
def make(cls, poetry, executable=None):
"""Build a wheel in the dist/ directory, and optionally upload it."""
cls.make_in(poetry)
cls.make_in(poetry, executable=executable)

def build(self):
logger.info("Building wheel")
Expand Down Expand Up @@ -146,12 +147,18 @@ def _build(self, wheel):

def _run_build_command(self, setup):
subprocess.check_call(
[sys.executable, str(setup), "build", "-b", str(self._path / "build")]
[
self.executable.as_posix(),
str(setup),
"build",
"-b",
str(self._path / "build"),
]
)

def _run_build_script(self, build_script):
logger.debug("Executing build script: {}".format(build_script))
subprocess.check_call([sys.executable, build_script])
subprocess.check_call([self.executable.as_posix(), build_script])

def _copy_module(self, wheel): # type: (zipfile.ZipFile) -> None
to_add = self.find_files_to_add()
Expand Down
8 changes: 7 additions & 1 deletion poetry/core/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def __init__(self, name, version, pretty_version=None):

self.requires = []
self.dev_requires = []
self.build_requires = []
self.extras = {}
self.requires_extras = []

Expand Down Expand Up @@ -164,7 +165,7 @@ def maintainer_email(self): # type: () -> str

@property
def all_requires(self):
return self.requires + self.dev_requires
return self.requires + self.dev_requires + self.build_requires

def _get_author(self): # type: () -> dict
if not self._authors:
Expand Down Expand Up @@ -404,6 +405,8 @@ def add_dependency(

if category == "dev":
self.dev_requires.append(dependency)
elif category == "build":
self.build_requires.append(dependency)
else:
self.requires.append(dependency)

Expand Down Expand Up @@ -447,6 +450,9 @@ def clone(self): # type: () -> Package
for dep in self.dev_requires:
clone.dev_requires.append(dep)

for dep in self.build_requires:
clone.build_requires.append(dep)

return clone

def __hash__(self):
Expand Down
22 changes: 22 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,22 @@
[build-system]
requires = [
"poetry-core",
"Cython~=0.29.6",
]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "poetry-cython-example"
version = "0.1.0"
description = ""
authors = []
include = [{ path = "project/**/*.so", format = "wheel" }]

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

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

[tool.poetry.dev-dependencies]
19 changes: 19 additions & 0 deletions tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,22 @@ def test_create_poetry_fails_on_invalid_configuration():
- 'description' is a required property
"""
assert expected == str(e.value)


def test_create_poetry_with_build_system_requires():
poetry = Factory().create_poetry(
fixtures_dir / "project_with_build_system_requires"
)
package = poetry.package

assert package.name == "poetry-cython-example"
assert package.version.text == "0.1.0"

assert not package.dev_requires
assert not package.requires

assert len(package.build_requires) == 1

dependency = package.build_requires[0]
assert dependency.category == "build"
assert dependency.to_pep_508() == "cython (>=0.29.6,<0.30.0)"