Skip to content

Commit

Permalink
Allow inline tables in includes to be similarly functional to the `pa…
Browse files Browse the repository at this point in the history
…ckage` directive.

Unify common logic between WheelBuilder and Builder. Move SDistBuilder logic from Builder to SDistBuilder.
Resolves: #8
  • Loading branch information
kasteph committed Apr 25, 2020
1 parent 8f36036 commit e662057
Show file tree
Hide file tree
Showing 14 changed files with 298 additions and 86 deletions.
13 changes: 12 additions & 1 deletion poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,18 @@ def create_poetry(self, cwd=None): # type: (Optional[Path]) -> Poetry
package.build_config = build or {}

if "include" in local_config:
package.include = local_config["include"]
package.include = []

for include in local_config["include"]:
if not isinstance(include, dict):
include = {"path": include}

formats = include.get("format", [])
if formats and not isinstance(formats, list):
formats = [formats]
include["format"] = formats

package.include.append(include)

if "exclude" in local_config:
package.exclude = local_config["exclude"]
Expand Down
49 changes: 41 additions & 8 deletions poetry/core/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,26 +73,43 @@
],
"properties": {
"include": {
"type": "string",
"description": "What to include in the package."
"$ref": "#/definitions/include-path"
},
"from": {
"type": "string",
"description": "Where the source directory of the package resides."
},
"format": {
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
],
"description": "The format(s) for which the package must be included."
"$ref": "#/definitions/package-formats"
}
}
}
},
"include": {
"type": "array",
"description": "A list of files and folders to include."
"description": "A list of files and folders to include.",
"items": {
"anyOf": [
{
"$ref": "#/definitions/include-path"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"path"
],
"properties": {
"path": {
"$ref": "#/definitions/include-path"
},
"format": {
"$ref": "#/definitions/package-formats"
}
}
}
]
}
},
"exclude": {
"type": "array",
Expand Down Expand Up @@ -189,6 +206,22 @@
"type": "string"
}
},
"include-path": {
"type": "string",
"description": "Path to file or directory to include."
},
"package-format": {
"type": "string",
"enum": ["sdist", "wheel"],
"description": "A Python packaging format."
},
"package-formats": {
"oneOf": [
{"$ref": "#/definitions/package-format"},
{"type": "array", "items": {"$ref": "#/definitions/package-format"}}
],
"description": "The format(s) for which the package must be included."
},
"dependencies": {
"type": "object",
"patternProperties": {
Expand Down
128 changes: 98 additions & 30 deletions poetry/core/masonry/builders/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

from collections import defaultdict
from contextlib import contextmanager
from typing import Set
from typing import Optional
from typing import Union
from typing import Set

from poetry.core.utils._compat import Path
from poetry.core.utils._compat import to_str
Expand All @@ -17,7 +18,6 @@
from ..utils.module import Module
from ..utils.package_include import PackageInclude


AUTHOR_REGEX = re.compile(r"(?u)^(?P<name>[- .,\w\d'’\"()]+) <(?P<email>.+?)>$")

METADATA_BASE = """\
Expand All @@ -37,7 +37,7 @@ class Builder(object):

def __init__(
self, poetry, ignore_packages_formats=False
): # type: ("Poetry", bool) -> None
): # type: ("Poetry", "Env", "IO", bool) -> None
self._poetry = poetry
self._package = poetry.package
self._path = poetry.file.parent
Expand All @@ -60,12 +60,27 @@ def __init__(

packages.append(p)

includes = []
for include in self._package.include:
formats = include.get("format", [])

if (
formats
and self.format
and self.format not in formats
and not ignore_packages_formats
):
continue

includes.append(include)

self._module = Module(
self._package.name,
self._path.as_posix(),
packages=packages,
includes=self._package.include,
includes=includes,
)

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

def build(self):
Expand Down Expand Up @@ -113,23 +128,53 @@ def is_excluded(self, filepath): # type: (Union[str, Path]) -> bool

return False

def find_files_to_add(self, exclude_build=True): # type: (bool) -> list
def find_files_to_add(
self, exclude_build=True
): # type: (bool) -> Set[BuildIncludeFile]
"""
Finds all files to add to the tarball
"""
to_add = []
to_add = set()

for include in self._module.includes:
include.refresh()
formats = include.formats or ["sdist"]

for file in include.elements:
if "__pycache__" in str(file):
continue

if file.is_dir():
if self.format in formats:
for current_file in file.glob("**/*"):
include_file = BuildIncludeFile(
path=current_file, source_root=self._path
)

if (
include_file.relative_to_source_root() not in to_add
and not current_file.is_dir()
and not self.is_excluded(
include_file.relative_to_source_root()
)
):
to_add.add(include_file)
continue

file = file.relative_to(self._path)
if (
isinstance(include, PackageInclude)
and include.source
and self.format == "wheel"
):
source_root = include.base
else:
source_root = self._path

include_file = BuildIncludeFile(path=file, source_root=source_root)

if self.is_excluded(file) and isinstance(include, PackageInclude):
if self.is_excluded(
include_file.relative_to_source_root()
) and isinstance(include, PackageInclude):
continue

if file.suffix == ".pyc":
Expand All @@ -140,31 +185,19 @@ def find_files_to_add(self, exclude_build=True): # type: (bool) -> list
continue

logger.debug(" - Adding: {}".format(str(file)))
to_add.append(file)

# Include project files
logger.debug(" - Adding: pyproject.toml")
to_add.append(Path("pyproject.toml"))

# If a license file exists, add it
for license_file in self._path.glob("LICENSE*"):
logger.debug(" - Adding: {}".format(license_file.relative_to(self._path)))
to_add.append(license_file.relative_to(self._path))

# If a README is specified we need to include it
# to avoid errors
if "readme" in self._poetry.local_config:
readme = self._path / self._poetry.local_config["readme"]
if readme.exists():
logger.debug(" - Adding: {}".format(readme.relative_to(self._path)))
to_add.append(readme.relative_to(self._path))

# If a build script is specified and explicitely required
to_add.add(include_file)

# If a build script is specified and explicitly required
# we add it to the list of files
if self._package.build_script and not exclude_build:
to_add.append(Path(self._package.build_script))
to_add.add(
BuildIncludeFile(
path=self._path / Path(self._package.build_script),
source_root=self._path,
)
)

return sorted(to_add)
return to_add

def get_metadata_content(self): # type: () -> bytes
content = METADATA_BASE.format(
Expand Down Expand Up @@ -268,3 +301,38 @@ def temporary_directory(cls, *args, **kwargs):
yield name

shutil.rmtree(name)


class BuildIncludeFile:
def __init__(
self,
path, # type: Path
source_root=None, # type: Optional[Path]
):
"""
:param path: a full path to the file to be included
:param source_root: the root path to resolve to
"""
self.path = Path(path).resolve()
self.source_root = None if not source_root else Path(source_root).resolve()
if not self.path.is_absolute() and self.source_root:
self.path = (self.source_root / self.path).resolve()

def __eq__(self, other): # type: (Union[BuildIncludeFile, Path]) -> bool
if hasattr(other, "path"):
return self.path == other.path
return self.path == other

def __ne__(self, other): # type: (Union[BuildIncludeFile, Path]) -> bool
return not self.__eq__(other)

def __hash__(self):
return hash(self.path)

def __repr__(self): # type: () -> str
return str(self.path)

def relative_to_source_root(self): # type(): -> Path
if self.source_root is not None:
return self.path.relative_to(self.source_root)
return self.path
42 changes: 37 additions & 5 deletions poetry/core/masonry/builders/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from posixpath import join as pjoin
from pprint import pformat
from typing import Iterator
from typing import List

from poetry.core.utils._compat import Path
from poetry.core.utils._compat import decode
Expand All @@ -22,6 +23,7 @@
from ..utils.helpers import normalize_file_permissions
from ..utils.package_include import PackageInclude
from .builder import Builder
from .builder import BuildIncludeFile


SETUP = """\
Expand Down Expand Up @@ -74,15 +76,15 @@ def build(self, target_dir=None): # type: (Path) -> Path

files_to_add = self.find_files_to_add(exclude_build=False)

for relpath in files_to_add:
path = self._path / relpath
for file in files_to_add:
tar_info = tar.gettarinfo(
str(path), arcname=pjoin(tar_dir, str(relpath))
str(file.path),
arcname=pjoin(tar_dir, str(file.relative_to_source_root())),
)
tar_info = self.clean_tarinfo(tar_info)

if tar_info.isreg():
with path.open("rb") as f:
with file.path.open("rb") as f:
tar.addfile(tar_info, f)
else:
tar.addfile(tar_info) # Symlinks & ?
Expand All @@ -105,7 +107,6 @@ def build(self, target_dir=None): # type: (Path) -> Path
gz.close()

logger.info(" - Built <comment>{}</comment>".format(target.name))

return target

def build_setup(self): # type: () -> bytes
Expand Down Expand Up @@ -302,6 +303,37 @@ def find_nearest_pkg(rel_path):

return pkgdir, sorted(packages), pkg_data

def find_files_to_add(
self, exclude_build=False
): # type: (bool) -> List[BuildIncludeFile]
to_add = super(SdistBuilder, self).find_files_to_add(exclude_build)

# Include project files
logger.debug(" - Adding: pyproject.toml")
to_add.add(
BuildIncludeFile(
path=self._path / Path("pyproject.toml"), source_root=self._path
)
)

# If a license file exists, add it
for license_file in self._path.glob("LICENSE*"):
license_file = BuildIncludeFile(path=license_file, source_root=self._path)
logger.debug(" - Adding: {}".format(license_file.relative_to_source_root()))
to_add.add(license_file)

# If a README is specified we need to include it to avoid errors
if "readme" in self._poetry.local_config:
readme = BuildIncludeFile(
path=self._path / self._poetry.local_config["readme"],
source_root=self._path,
)
if readme.path.exists():
logger.debug(" - Adding: {}".format(readme.relative_to_source_root()))
to_add.add(readme)

return sorted(list(to_add), key=lambda x: x.relative_to_source_root())

@classmethod
def convert_dependencies(cls, package, dependencies):
main = []
Expand Down
Loading

0 comments on commit e662057

Please sign in to comment.