Commit 95c0a9a0 by Sébastien Eustace

Add the build command with a tarball builder (sdist)

parent cdb2c621
__version__ = '0.3.0' __version__ = '0.4.0-beta'
...@@ -7,6 +7,7 @@ from poetry.utils.venv import Venv ...@@ -7,6 +7,7 @@ from poetry.utils.venv import Venv
from .commands import AboutCommand from .commands import AboutCommand
from .commands import AddCommand from .commands import AddCommand
from .commands import BuildCommand
from .commands import InstallCommand from .commands import InstallCommand
from .commands import LockCommand from .commands import LockCommand
from .commands import NewCommand from .commands import NewCommand
...@@ -45,6 +46,7 @@ class Application(BaseApplication): ...@@ -45,6 +46,7 @@ class Application(BaseApplication):
return commands + [ return commands + [
AboutCommand(), AboutCommand(),
AddCommand(), AddCommand(),
BuildCommand(),
InstallCommand(), InstallCommand(),
LockCommand(), LockCommand(),
NewCommand(), NewCommand(),
......
from .about import AboutCommand from .about import AboutCommand
from .add import AddCommand from .add import AddCommand
from .build import BuildCommand
from .install import InstallCommand from .install import InstallCommand
from .lock import LockCommand from .lock import LockCommand
from .new import NewCommand from .new import NewCommand
......
from .command import Command
from poetry.masonry import Builder
class BuildCommand(Command):
"""
Builds a package, as a tarball and a wheel by default.
build
{ --f|format=* : Limit the format to either wheel or sdist}
"""
def handle(self):
fmt = 'all'
if self.option('format'):
fmt = self.option('format')
builder = Builder(self.poetry)
builder.build(fmt)
# -*- coding: utf-8 -*-
import re
import toml import toml
from poetry.utils.helpers import module_name
from poetry.vcs.git import Git from poetry.vcs.git import Git
_canonicalize_regex = re.compile(r"[-_.]+")
TESTS_DEFAULT = """from {package_name} import __version__ TESTS_DEFAULT = """from {package_name} import __version__
...@@ -21,7 +16,7 @@ class Layout(object): ...@@ -21,7 +16,7 @@ class Layout(object):
def __init__(self, project, version='0.1.0', readme_format='md', author=None): def __init__(self, project, version='0.1.0', readme_format='md', author=None):
self._project = project self._project = project
self._package_name = self._canonicalize_name(project).replace('-', '_') self._package_name = module_name(project)
self._version = version self._version = version
self._readme_format = readme_format self._readme_format = readme_format
self._dependencies = {} self._dependencies = {}
...@@ -118,6 +113,3 @@ class Layout(object): ...@@ -118,6 +113,3 @@ class Layout(object):
with poetry.open('w') as f: with poetry.open('w') as f:
f.write(content) f.write(content)
def _canonicalize_name(self, name: str) -> str:
return _canonicalize_regex.sub("-", name).lower()
"""
This module handles the packaging and publishing
of python projects.
A lot of the code used here has been taken from
`flit <https://github.com/takluyver/flit>`__ and adapted
to work with the poetry codebase, so kudos to them for showing the way.
"""
from .builder import Builder
from .builders import CompleteBuilder
from .builders import SdistBuilder
from .builders import WheelBuilder
class Builder:
_FORMATS = {
'sdist': SdistBuilder,
'wheel': WheelBuilder,
'all': CompleteBuilder
}
def __init__(self, poetry):
self._poetry = poetry
def build(self, fmt: str):
if fmt not in self._FORMATS:
raise ValueError(f'Invalid format: {fmt}')
builder = self._FORMATS[fmt](self._poetry)
return builder.build()
from .complete import CompleteBuilder
from .sdist import SdistBuilder
from .wheel import WheelBuilder
from .sdist import SdistBuilder
class CompleteBuilder:
def __init__(self, poetry):
self._poetry = poetry
def build(self):
# We start by building the tarball
# We will use it to build the wheel
sdist_builder = SdistBuilder(self._poetry)
sdist_file = sdist_builder.build()
class WheelBuilder:
def __init__(self):
pass
def normalize_file_permissions(st_mode):
"""
Normalizes the permission bits in the st_mode field from stat to 644/755
Popular VCSs only track whether a file is executable or not. The exact
permissions can vary on systems with different umasks. Normalising
to 644 (non executable) or 755 (executable) makes builds more reproducible.
"""
# Set 644 permissions, leaving higher bits of st_mode unchanged
new_mode = (st_mode | 0o644) & ~0o133
if st_mode & 0o100:
new_mode |= 0o111 # Executable: 644 -> 755
return new_mode
from pathlib import Path
from poetry.utils.helpers import module_name
class Module:
def __init__(self, name, directory='.'):
self._name = module_name(name)
# It must exist either as a .py file or a directory, but not both
pkg_dir = Path(directory, self._name)
py_file = Path(directory, self._name + '.py')
if pkg_dir.is_dir() and py_file.is_file():
raise ValueError("Both {} and {} exist".format(pkg_dir, py_file))
elif pkg_dir.is_dir():
self._path = pkg_dir
self._is_package = True
elif py_file.is_file():
self._path = py_file
self._is_package = False
else:
raise ValueError("No file/folder found for package {}".format(name))
@property
def name(self) -> str:
return self._name
@property
def path(self) -> Path:
return self._path
@property
def file(self) -> Path:
if self._is_package:
return self._path / '__init__.py'
else:
return self._path
def is_package(self) -> bool:
return self._is_package
...@@ -36,15 +36,6 @@ class Package: ...@@ -36,15 +36,6 @@ class Package:
def __init__(self, name, version, pretty_version=None): def __init__(self, name, version, pretty_version=None):
""" """
Creates a new in memory package. Creates a new in memory package.
:param name: The package's name
:type name: str
:param version: The package's version
:type version: str
:param pretty_version: The package's non-normalized version
:type pretty_version: str
""" """
self._pretty_name = name self._pretty_name = name
self._name = name.lower() self._name = name.lower()
...@@ -57,6 +48,12 @@ class Package: ...@@ -57,6 +48,12 @@ class Package:
self._stability = parse_stability(version) self._stability = parse_stability(version)
self._dev = self._stability == 'dev' self._dev = self._stability == 'dev'
self._authors = []
self.homepage = None
self.repository_url = None
self.keywords = []
self.source_type = '' self.source_type = ''
self.source_reference = '' self.source_reference = ''
self.source_url = '' self.source_url = ''
...@@ -112,6 +109,10 @@ class Package: ...@@ -112,6 +109,10 @@ class Package:
return '{} {}'.format(self._pretty_version, self.source_reference) return '{} {}'.format(self._pretty_version, self.source_reference)
@property @property
def authors(self) -> list:
return self._authors
@property
def python_versions(self): def python_versions(self):
return self._python_versions return self._python_versions
......
...@@ -74,6 +74,13 @@ class Poetry: ...@@ -74,6 +74,13 @@ class Poetry:
version = normalize_version(pretty_version) version = normalize_version(pretty_version)
package = Package(name, version, pretty_version) package = Package(name, version, pretty_version)
for author in local_config['authors']:
package.authors.append(author)
package.description = local_config.get('description', '')
package.homepage = local_config.get('homepage')
package.repository_url = local_config.get('repository')
if 'platform' in local_config: if 'platform' in local_config:
package.platform = local_config['platform'] package.platform = local_config['platform']
......
import re
_canonicalize_regex = re.compile('[-_.]+')
def canonicalize_name(name: str) -> str:
return _canonicalize_regex.sub('-', name).lower()
def module_name(name: str) -> str:
return canonicalize_name(name).replace('-', '_')
...@@ -30,5 +30,5 @@ class TomlFile: ...@@ -30,5 +30,5 @@ class TomlFile:
self._path.write_text(data) self._path.write_text(data)
def exists(self) -> bool: def __getattr__(self, item):
return self._path.exists() return getattr(self._path, item)
from pathlib import Path
from .git import Git
def get_vcs(directory: Path):
directory = directory.resolve()
for p in [directory] + list(directory.parents):
if (p / '.git').is_dir():
return Git()
...@@ -50,6 +50,13 @@ class Git: ...@@ -50,6 +50,13 @@ class Git:
'rev-parse', rev 'rev-parse', rev
) )
def get_ignored_files(self) -> list:
output = self.run(
'ls-files', '--others', '-i', '--exclude-standard'
)
return output.split('\n')
def run(self, *args) -> str: def run(self, *args) -> str:
return subprocess.check_output( return subprocess.check_output(
['git'] + list(args), ['git'] + list(args),
......
[tool.poetry] [tool.poetry]
name = "poetry" name = "poetry"
version = "0.3.0" version = "0.4.0-beta"
description = "Python dependency management and packaging made easy." description = "Python dependency management and packaging made easy."
authors = [ authors = [
"Sébastien Eustace <sebastien@eustace.io>" "Sébastien Eustace <sebastien@eustace.io>"
......
[tool.poetry]
name = "my-package"
version = "1.2.3"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
keywords = ["packaging", "dependency", "poetry"]
# Requirements
[tool.poetry.dependencies]
python = "^3.6"
cleo = "^0.6"
[tool.poetry.dev-dependencies]
pytest = "~3.4"
[tool.poetry.scripts]
my-script = "my_package:main"
import ast
from pathlib import Path
from poetry import Poetry
from poetry.masonry.builders.sdist import SdistBuilder
from tests.helpers import get_dependency
def project(name):
return Path(__file__).parent / 'fixtures' / name
def test_convert_dependencies():
result = SdistBuilder.convert_dependencies([
get_dependency('A', '^1.0'),
get_dependency('B', '~1.0'),
get_dependency('C', '1.2.3'),
])
main = [
'A>=1.0.0.0,<2.0.0.0',
'B>=1.0.0.0,<1.1.0.0',
'C==1.2.3.0',
]
extras = []
assert result == (main, extras)
dependency_with_python = get_dependency('A', '^1.0')
dependency_with_python.python_versions = '^3.4'
result = SdistBuilder.convert_dependencies([
dependency_with_python,
get_dependency('B', '~1.0'),
get_dependency('C', '1.2.3'),
])
main = [
'B>=1.0.0.0,<1.1.0.0',
'C==1.2.3.0',
]
extras = [
'A>=1.0.0.0,<2.0.0.0; python_version>=3.4.0.0,<4.0.0.0',
]
assert result == (main, extras)
def test_make_setup():
poetry = Poetry.create(project('complete'))
builder = SdistBuilder(poetry)
setup = builder.build_setup()
setup_ast = ast.parse(setup)
setup_ast.body = [n for n in setup_ast.body if isinstance(n, ast.Assign)]
ns = {}
exec(compile(setup_ast, filename="setup.py", mode="exec"), ns)
assert ns['packages'] == [
'my_package',
'my_package.sub_pkg1',
'my_package.sub_pkg2'
]
assert ns['install_requires'] == [
'cleo>=0.6.0.0,<0.7.0.0'
]
assert ns['entry_points'] == {
'console_scripts': ['my-script = my_package:main']
}
def test_find_files_to_add():
poetry = Poetry.create(project('complete'))
builder = SdistBuilder(poetry)
result = builder.find_files_to_add()
assert result == [
Path('my_package/__init__.py'),
Path('my_package/sub_pkg1/__init__.py'),
Path('my_package/data1/test.json'),
Path('my_package/sub_pkg2/__init__.py'),
Path('my_package/sub_pkg2/data2/data.json'),
]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment