Commit 10db97d8 by Sébastien Eustace

Add support for Python 2.7

parent 9b96d6db
......@@ -2,6 +2,11 @@
## [Unreleased]
### Added
- Added support for Python 2.7.
### Changes
- Improved dependency resolution time by using cache control.
......
__version__ = '0.7.1'
__version__ = '0.8.0a0'
from pathlib import Path
from typing import Any
from .locations import CONFIG_DIR
from .utils._compat import Path
from .utils.toml_file import TomlFile
from .utils.toml_file import TOMLFile
class Config:
def __init__(self, file: TomlFile):
def __init__(self, file): # type: (TomlFile) -> None
self._file = file
if not self._file.exists():
self._raw_content = {}
......@@ -33,7 +33,7 @@ class Config:
def content(self):
return self._content
def setting(self, setting_name: str) -> Any:
def setting(self, setting_name): # type: (str) -> Any
"""
Retrieve a setting value.
"""
......@@ -84,7 +84,7 @@ class Config:
self._file.write(self._content)
@classmethod
def create(cls, file, base_dir=None) -> 'Config':
def create(cls, file, base_dir=None): # type: (...) -> Config
if base_dir is None:
base_dir = CONFIG_DIR
......
......@@ -31,7 +31,7 @@ from .commands.debug import DebugResolveCommand
class Application(BaseApplication):
def __init__(self):
super().__init__('Poetry', __version__)
super(Application, self).__init__('Poetry', __version__)
self._poetry = None
self._skip_io_configuration = False
......@@ -47,10 +47,10 @@ class Application(BaseApplication):
return self._poetry
def reset_poetry(self) -> None:
def reset_poetry(self): # type: () -> None
self._poetry = None
def run(self, i=None, o=None) -> int:
def run(self, i=None, o=None): # type: (...) -> int
if i is None:
i = ArgvInput()
......@@ -62,13 +62,13 @@ class Application(BaseApplication):
self._skip_io_configuration = True
i = RawArgvInput()
return super().run(i, o)
return super(Application, self).run(i, o)
def do_run(self, i, o):
name = self.get_command_name(i)
if name not in ['run', 'script']:
return super().do_run(i, o)
return super(Application, self).do_run(i, o)
command = self.find(name)
......@@ -82,9 +82,9 @@ class Application(BaseApplication):
if self._skip_io_configuration:
return
super().configure_io(i, o)
super(Application, self).configure_io(i, o)
def get_default_commands(self) -> list:
def get_default_commands(self): # type: () -> list
commands = super(Application, self).get_default_commands()
commands += [
......
......@@ -103,7 +103,9 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
return status
def _determine_requirements(self, requires: List[str]) -> List[str]:
def _determine_requirements(self,
requires # type: List[str]
): # type: (...) -> List[str]
if not requires:
return []
......@@ -140,7 +142,7 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
def _find_best_version_for_package(self,
name,
required_version=None
) -> Tuple[str, str]:
): # type: (...) -> Tuple[str, str]
from poetry.version.version_selector import VersionSelector
selector = VersionSelector(self.poetry.pool)
......@@ -157,7 +159,7 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
selector.find_recommended_require_version(package)
)
def _parse_name_version_pairs(self, pairs: list) -> list:
def _parse_name_version_pairs(self, pairs): # type: (list) -> list
result = []
for i in range(len(pairs)):
......@@ -177,7 +179,7 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
return result
def _format_requirements(self, requirements: List[str]) -> dict:
def _format_requirements(self, requirements): # type: (List[str]) -> dict
requires = {}
requirements = self._parse_name_version_pairs(requirements)
for requirement in requirements:
......
......@@ -9,10 +9,10 @@ class Command(BaseCommand):
def poetry(self):
return self.get_application().poetry
def reset_poetry(self) -> None:
def reset_poetry(self): # type: () -> None
self.get_application().reset_poetry()
def run(self, i, o) -> int:
def run(self, i, o): # type: () -> int
"""
Initialize command.
"""
......
......@@ -38,13 +38,13 @@ To remove a repository (repo is a short alias for repositories):
def __init__(self):
from poetry.config import Config
super().__init__()
super(ConfigCommand, self).__init__()
self._config = Config.create('config.toml')
self._auth_config = Config.create('auth.toml')
def initialize(self, i, o):
super().initialize(i, o)
super(ConfigCommand, self).initialize(i, o)
# Create config file if it does not exist
if not self._config.file.exists():
......
......@@ -62,7 +62,7 @@ class DebugResolveCommand(Command):
)
)
def _determine_requirements(self, requires: List[str]) -> List[str]:
def _determine_requirements(self, requires): # type: (List[str]) -> List[str]
if not requires:
return []
......@@ -78,7 +78,7 @@ class DebugResolveCommand(Command):
return result
def _parse_name_version_pairs(self, pairs: list) -> list:
def _parse_name_version_pairs(self, pairs): # type: (list) -> list
result = []
for i in range(len(pairs)):
......@@ -98,7 +98,7 @@ class DebugResolveCommand(Command):
return result
def _format_requirements(self, requirements: List[str]) -> dict:
def _format_requirements(self, requirements): # type: (List[str]) -> dict
requires = {}
requirements = self._parse_name_version_pairs(requirements)
for requirement in requirements:
......
from pathlib import Path
from poetry.utils._compat import Path
from .command import Command
......
......@@ -14,7 +14,7 @@ class RunCommand(VenvCommand):
venv = self.venv
return venv.exec(*args)
return venv.execute(*args)
def merge_application_definition(self, merge_args=True):
if self._application is None \
......
# -*- coding: utf-8 -*-
from .venv_command import VenvCommand
......
......@@ -6,12 +6,12 @@ class VenvCommand(Command):
def __init__(self, name=None):
self._venv = None
super().__init__(name)
super(VenvCommand, self).__init__(name)
def initialize(self, i, o):
from poetry.utils.venv import Venv
super().initialize(i, o)
super(VenvCommand, self).initialize(i, o)
self._venv = Venv.create(o, self.poetry.package.name)
......
......@@ -5,7 +5,7 @@ from cleo.styles import OutputStyle
class PoetryStyle(CleoStyle):
def __init__(self, i, o):
super().__init__(i, o)
super(PoetryStyle, self).__init__(i, o)
self.output.get_formatter().add_style('warning', 'black', 'yellow')
self.output.get_formatter().add_style('question', 'blue')
......@@ -14,11 +14,11 @@ class PoetryStyle(CleoStyle):
type=OutputStyle.OUTPUT_NORMAL,
verbosity=OutputStyle.VERBOSITY_NORMAL):
if self.output.verbosity >= verbosity:
super().writeln(messages, type=type)
super(PoetryStyle, self).writeln(messages, type=type)
def write(self, messages,
newline=False,
type=OutputStyle.OUTPUT_NORMAL,
verbosity=OutputStyle.VERBOSITY_NORMAL):
if self.output.verbosity >= verbosity:
super().write(messages, newline=newline, type=type)
super(PoetryStyle, self).write(messages, newline=newline, type=type)
import sys
from typing import List
from typing import Union
from poetry.packages import Dependency
from poetry.packages import Locker
......@@ -26,10 +27,11 @@ class Installer:
def __init__(self,
io,
venv,
package: Package,
locker: Locker,
pool: Pool,
installed: InstalledRepository = None):
package, # type: Package
locker, # type: Locker
pool, # type: Pool
installed=None # type: (Union[InstalledRepository, None])
):
self._io = io
self._venv = venv
self._package = package
......@@ -48,7 +50,10 @@ class Installer:
self._extras = []
self._installer = self._get_installer()
self._installed_repository = installed or self._get_installed()
if installed is None:
installed = self._get_installed()
self._installed_repository = installed
@property
def installer(self):
......@@ -69,49 +74,49 @@ class Installer:
return 0
def dry_run(self, dry_run=True) -> 'Installer':
def dry_run(self, dry_run=True): # type: (bool) -> Installer
self._dry_run = dry_run
return self
def is_dry_run(self) -> bool:
def is_dry_run(self): # type: () -> bool
return self._dry_run
def verbose(self, verbose=True) -> 'Installer':
def verbose(self, verbose=True): # type: (bool) -> Installer
self._verbose = verbose
return self
def is_verbose(self) -> bool:
def is_verbose(self): # type: () -> bool
return self._verbose
def dev_mode(self, dev_mode=True) -> 'Installer':
def dev_mode(self, dev_mode=True): # type: (bool) -> Installer
self._dev_mode = dev_mode
return self
def is_dev_mode(self) -> bool:
def is_dev_mode(self): # type: () -> bool
return self._dev_mode
def update(self, update=True) -> 'Installer':
def update(self, update=True): # type: (bool) -> Installer
self._update = update
return self
def is_updating(self) -> bool:
def is_updating(self): # type: () -> bool
return self._update
def execute_operations(self, execute=True) -> 'Installer':
def execute_operations(self, execute=True): # type: (bool) -> Installer
self._execute_operations = execute
return self
def whitelist(self, packages: dict) -> 'Installer':
def whitelist(self, packages): # type: (dict) -> Installer
self._whitelist = packages
return self
def extras(self, extras: list) -> 'Installer':
def extras(self, extras): # type: (list) -> Installer
self._extras = extras
return self
......@@ -262,7 +267,7 @@ class Installer:
for op in ops:
self._execute(op)
def _execute(self, operation: Operation) -> None:
def _execute(self, operation): # type: (Operation) -> None
"""
Execute a given operation.
"""
......@@ -270,7 +275,7 @@ class Installer:
getattr(self, '_execute_{}'.format(method))(operation)
def _execute_install(self, operation: Install) -> None:
def _execute_install(self, operation): # type: (Install) -> None
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.writeln(
......@@ -296,7 +301,7 @@ class Installer:
self._installer.install(operation.package)
def _execute_update(self, operation: Update) -> None:
def _execute_update(self, operation): # type: (Update) -> None
source = operation.initial_package
target = operation.target_package
......@@ -327,7 +332,7 @@ class Installer:
self._installer.update(source, target)
def _execute_uninstall(self, operation: Uninstall) -> None:
def _execute_uninstall(self, operation): # type: (Uninstall) -> None
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.writeln(
......@@ -390,8 +395,8 @@ class Installer:
local_repo.add_package(package)
def _get_operations_from_lock(self,
locked_repository: Repository
) -> List[Operation]:
locked_repository # type: Repository
): # type: (...) -> List[Operation]
installed_repo = self._installed_repository
ops = []
......@@ -424,7 +429,10 @@ class Installer:
return ops
def _filter_operations(self, ops: List[Operation], repo: Repository) -> None:
def _filter_operations(self,
ops,
repo
): # type: (List[Operation], Repository) -> None
extra_packages = [p.name for p in
self._get_extra_packages(repo)]
for op in ops:
......@@ -513,8 +521,8 @@ class Installer:
return _extra_packages(extra_packages)
def _get_installer(self) -> BaseInstaller:
def _get_installer(self): # type: () -> BaseInstaller
return PipInstaller(self._venv, self._io)
def _get_installed(self) -> InstalledRepository:
def _get_installed(self): # type: () -> InstalledRepository
return InstalledRepository.load(self._venv)
......@@ -20,11 +20,11 @@ class NoopInstaller(BaseInstaller):
def removals(self):
return self._removals
def install(self, package) -> None:
def install(self, package):
self._installs.append(package)
def update(self, source, target) -> None:
def update(self, source, target):
self._updates.append((source, target))
def remove(self, package) -> None:
def remove(self, package):
self._removals.append(package)
......@@ -10,7 +10,7 @@ from .base_installer import BaseInstaller
class PipInstaller(BaseInstaller):
def __init__(self, venv: Venv, io):
def __init__(self, venv, io): # type: (Venv, ...) -> None
self._venv = venv
self._io = io
......@@ -54,10 +54,10 @@ class PipInstaller(BaseInstaller):
raise
def run(self, *args, **kwargs) -> str:
def run(self, *args, **kwargs): # type: (...) -> str
return self._venv.run('pip', *args, **kwargs)
def requirement(self, package, formatted=False) -> str:
def requirement(self, package, formatted=False):
if formatted and not package.source_type == 'git':
req = '{}=={}'.format(package.name, package.version)
for h in package.hashes:
......@@ -81,7 +81,9 @@ class PipInstaller(BaseInstaller):
'reqs.txt', '{}-{}'.format(package.name, package.version)
)
with open(fd, 'w') as f:
f.write(self.requirement(package, formatted=True))
try:
os.write(fd, self.requirement(package, formatted=True))
finally:
os.close(fd)
return name
......@@ -7,18 +7,18 @@ from poetry.console.styles.poetry import PoetryStyle
class NullIO(PoetryStyle):
def __init__(self):
super().__init__(ListInput([]), NullOutput())
super(NullIO, self).__init__(ListInput([]), NullOutput())
def is_quiet(self) -> bool:
def is_quiet(self): # type: () -> bool
return False
def is_verbose(self) -> bool:
def is_verbose(self): # type: () -> bool
return False
def is_very_verbose(self) -> bool:
def is_very_verbose(self): # type: () -> bool
return False
def is_debug(self) -> bool:
def is_debug(self): # type: () -> bool
return False
def writeln(self, *args, **kwargs):
......
......@@ -9,7 +9,7 @@ _LAYOUTS = {
}
def layout(name: str) -> Type[Layout]:
def layout(name): # type: (str) -> Type[Layout]
if name not in _LAYOUTS:
raise ValueError('Invalid layout')
......
......@@ -4,7 +4,7 @@ from poetry.utils.helpers import module_name
from poetry.vcs.git import Git
TESTS_DEFAULT = """from {package_name} import __version__
TESTS_DEFAULT = u"""from {package_name} import __version__
def test_version():
......@@ -45,7 +45,7 @@ class Layout(object):
git_config.get('user.name')
and git_config.get('user.email')
):
author = '{} <{}>'.format(
author = u'{} <{}>'.format(
git_config['user.name'],
git_config['user.email']
)
......
......@@ -2,7 +2,7 @@
from .layout import Layout
DEFAULT = """__version__ = '{version}'
DEFAULT = u"""__version__ = '{version}'
"""
......
......@@ -4,8 +4,9 @@ PEP-517 compliant buildsystem API
import logging
from pathlib import Path
from poetry import Poetry
from poetry.poetry import Poetry
from poetry.io import NullIO
from poetry.utils.venv import Venv
from .builders import SdistBuilder
from .builders import WheelBuilder
......@@ -38,6 +39,6 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
def build_sdist(sdist_directory, config_settings=None):
"""Builds an sdist, places it in sdist_directory"""
path = SdistBuilder(poetry, NullIO()).build(Path(sdist_directory))
path = SdistBuilder(poetry, Venv(), NullIO()).build(Path(sdist_directory))
return path.name
......@@ -16,7 +16,7 @@ class Builder:
self._venv = venv
self._io = io
def build(self, fmt: str):
def build(self, fmt):
if fmt not in self._FORMATS:
raise ValueError('Invalid format: {}'.format(fmt))
......
# -*- coding: utf-8 -*-
import os
import re
import shutil
import tempfile
from collections import defaultdict
from pathlib import Path
from contextlib import contextmanager
from poetry.semver.constraints import Constraint
from poetry.semver.constraints import MultiConstraint
from poetry.semver.version_parser import VersionParser
from poetry.utils._compat import Path
from poetry.vcs import get_vcs
from ..metadata import Metadata
......@@ -16,7 +20,7 @@ from ..utils.module import Module
AUTHOR_REGEX = re.compile('(?u)^(?P<name>[- .,\w\d\'’"()]+) <(?P<email>.+?)>$')
class Builder:
class Builder(object):
AVAILABLE_PYTHONS = {
'2',
......@@ -39,7 +43,7 @@ class Builder:
def build(self):
raise NotImplementedError()
def find_excluded_files(self) -> list:
def find_excluded_files(self): # type: () -> list
# Checking VCS
vcs = get_vcs(self._path)
if not vcs:
......@@ -58,7 +62,7 @@ class Builder:
return result
def find_files_to_add(self, exclude_build=True) -> list:
def find_files_to_add(self, exclude_build=True): # type: () -> list
"""
Finds all files to add to the tarball
......@@ -116,7 +120,7 @@ class Builder:
return sorted(to_add)
def convert_entry_points(self) -> dict:
def convert_entry_points(self): # type: () -> dict
result = defaultdict(list)
# Scripts -> Entry points
......@@ -134,7 +138,7 @@ class Builder:
return dict(result)
@classmethod
def convert_author(cls, author) -> dict:
def convert_author(cls, author): # type: () -> dict
m = AUTHOR_REGEX.match(author)
name = m.group('name')
......@@ -173,3 +177,18 @@ class Builder:
python_requires = str(constraint).replace(' ', '')
return python_requires
@classmethod
@contextmanager
def temporary_directory(cls, *args, **kwargs):
try:
from tempfile import TemporaryDirectory
with TemporaryDirectory(*args, **kwargs) as name:
yield name
except ImportError:
name = tempfile.mkdtemp(*args, **kwargs)
yield name
shutil.rmtree(name)
......@@ -4,8 +4,6 @@ import tarfile
import poetry.poetry
from contextlib import contextmanager
from tempfile import TemporaryDirectory
from types import SimpleNamespace
from .builder import Builder
from .sdist import SdistBuilder
......@@ -19,25 +17,22 @@ class CompleteBuilder(Builder):
# We will use it to build the wheel
sdist_builder = SdistBuilder(self._poetry, self._venv, self._io)
sdist_file = sdist_builder.build()
sdist_info = SimpleNamespace(builder=sdist_builder, file=sdist_file)
self._io.writeln('')
dist_dir = self._path / 'dist'
with self.unpacked_tarball(sdist_file) as tmpdir:
wheel_info = WheelBuilder.make_in(
WheelBuilder.make_in(
poetry.poetry.Poetry.create(tmpdir), self._venv, self._io, dist_dir,
original=self._poetry
)
return SimpleNamespace(wheel=wheel_info, sdist=sdist_info)
@classmethod
@contextmanager
def unpacked_tarball(cls, path):
tf = tarfile.open(str(path))
with TemporaryDirectory() as tmpdir:
with cls.temporary_directory() as tmpdir:
tf.extractall(tmpdir)
files = os.listdir(tmpdir)
......
from __future__ import unicode_literals
import os
import tarfile
......@@ -5,12 +7,13 @@ from collections import defaultdict
from copy import copy
from gzip import GzipFile
from io import BytesIO
from pathlib import Path
from posixpath import join as pjoin
from pprint import pformat
from typing import List
from poetry.packages import Dependency
from poetry.utils._compat import Path
from poetry.utils._compat import encode
from ..utils.helpers import normalize_file_permissions
......@@ -51,7 +54,7 @@ Author-email: {author_email}
class SdistBuilder(Builder):
def build(self, target_dir: Path = None) -> Path:
def build(self, target_dir=None): # type: (Path) -> Path
self._io.writeln(' - Building <info>sdist</info>')
if target_dir is None:
target_dir = self._path / 'dist'
......@@ -92,14 +95,14 @@ class SdistBuilder(Builder):
tar_info.size = len(setup)
tar.addfile(tar_info, BytesIO(setup))
pkg_info = PKG_INFO.format(
pkg_info = encode(PKG_INFO.format(
name=self._meta.name,
version=self._meta.version,
summary=self._meta.summary,
home_page=self._meta.home_page,
author=self._meta.author,
author_email=self._meta.author_email,
).encode('utf-8')
))
tar_info = tarfile.TarInfo(pjoin(tar_dir, 'PKG-INFO'))
tar_info.size = len(pkg_info)
......@@ -112,7 +115,7 @@ class SdistBuilder(Builder):
return target
def build_setup(self) -> bytes:
def build_setup(self): # type: () -> bytes
before, extra, after = [], [], []
# If we have a build script, use it
......@@ -155,7 +158,7 @@ class SdistBuilder(Builder):
extra.append("'python_requires': {!r},".format(python_requires))
return SETUP.format(
return encode(SETUP.format(
before='\n'.join(before),
name=self._meta.name,
version=self._meta.version,
......@@ -166,10 +169,10 @@ class SdistBuilder(Builder):
url=self._meta.home_page,
extra='\n '.join(extra),
after='\n'.join(after)
).encode('utf-8')
))
@classmethod
def find_packages(cls, path: str):
def find_packages(cls, path):
"""
Discover subpackages and data.
......@@ -219,8 +222,9 @@ class SdistBuilder(Builder):
@classmethod
def convert_dependencies(cls,
package,
dependencies: List[Dependency]):
package, # type: Package
dependencies # type: List[Dependency]
):
main = []
extras = defaultdict(list)
......
from __future__ import unicode_literals
import contextlib
import hashlib
import os
......@@ -13,12 +15,11 @@ except ImportError:
from base64 import urlsafe_b64encode
from io import StringIO
from pathlib import Path
from types import SimpleNamespace
from poetry.__version__ import __version__
from poetry.semver.constraints import Constraint
from poetry.semver.constraints import MultiConstraint
from poetry.utils._compat import Path
from ..utils.helpers import normalize_file_permissions
from ..utils.tags import get_abbr_impl
......@@ -39,7 +40,7 @@ Tag: {tag}
class WheelBuilder(Builder):
def __init__(self, poetry, venv, io, target_fp, original=None):
super().__init__(poetry, venv, io)
super(WheelBuilder, self).__init__(poetry, venv, io)
self._records = []
self._original_path = self._path
......@@ -51,26 +52,29 @@ class WheelBuilder(Builder):
compression=zipfile.ZIP_DEFLATED)
@classmethod
def make_in(cls, poetry, venv, io, directory, original=None) -> SimpleNamespace:
def make_in(cls, poetry, venv, io, directory, original=None):
# We don't know the final filename until metadata is loaded, so write to
# a temporary_file, and rename it afterwards.
(fd, temp_path) = tempfile.mkstemp(suffix='.whl',
dir=str(directory))
os.close(fd)
try:
with open(fd, 'w+b') as fp:
with open(temp_path, 'w+b') as fp:
wb = WheelBuilder(poetry, venv, io, fp, original=original)
wb.build()
wheel_path = directory / wb.wheel_filename
os.replace(temp_path, str(wheel_path))
if wheel_path.exists():
os.unlink(str(wheel_path))
os.rename(temp_path, str(wheel_path))
except:
os.unlink(temp_path)
raise
return SimpleNamespace(builder=wb, file=wheel_path)
@classmethod
def make(cls, poetry, venv, io) -> SimpleNamespace:
def make(cls, poetry, venv, io):
"""Build a wheel in the dist/ directory, and optionally upload it.
"""
dist_dir = poetry.file.parent / 'dist'
......@@ -79,9 +83,9 @@ class WheelBuilder(Builder):
except FileExistsError:
pass
return cls.make_in(poetry, venv, io, dist_dir)
cls.make_in(poetry, venv, io, dist_dir)
def build(self) -> None:
def build(self):
self._io.writeln(' - Building <info>wheel</info>')
try:
self._build()
......@@ -93,7 +97,7 @@ class WheelBuilder(Builder):
self._io.writeln(' - Built <fg=cyan>{}</>'.format(self.wheel_filename))
def _build(self) -> None:
def _build(self):
if self._package.build:
setup = self._path / 'setup.py'
......@@ -124,7 +128,7 @@ class WheelBuilder(Builder):
shutil.rmtree(str(self._path / pkg.name))
shutil.copytree(str(pkg), str(self._path / pkg.name))
def copy_module(self) -> None:
def copy_module(self):
if self._module.is_package():
files = self.find_files_to_add()
......@@ -164,16 +168,16 @@ class WheelBuilder(Builder):
# RECORD itself is recorded with no hash or size
f.write(self.dist_info + '/RECORD,,\n')
def find_excluded_files(self) -> list:
def find_excluded_files(self): # type: () -> list
# Checking VCS
return []
@property
def dist_info(self) -> str:
def dist_info(self): # type: () -> str
return self.dist_info_name(self._package.name, self._package.version)
@property
def wheel_filename(self) -> str:
def wheel_filename(self): # type: () -> str
return '{}-{}-{}.whl'.format(
re.sub("[^\w\d.]+", "_", self._package.pretty_name, flags=re.UNICODE),
re.sub("[^\w\d.]+", "_", self._package.version, flags=re.UNICODE),
......@@ -188,7 +192,7 @@ class WheelBuilder(Builder):
])
)
def dist_info_name(self, distribution, version) -> str:
def dist_info_name(self, distribution, version): # type: (...) -> str
escaped_name = re.sub("[^\w\d.]+", "_", distribution, flags=re.UNICODE)
escaped_version = re.sub("[^\w\d.]+", "_", version, flags=re.UNICODE)
......@@ -221,7 +225,7 @@ class WheelBuilder(Builder):
# RECORD
rel_path = rel_path.replace(os.sep, '/')
zinfo = zipfile.ZipInfo.from_file(full_path, rel_path)
zinfo = zipfile.ZipInfo(rel_path)
# Normalize permission bits to either 755 (executable) or 644
st_mode = os.stat(full_path).st_mode
......@@ -232,18 +236,20 @@ class WheelBuilder(Builder):
zinfo.external_attr |= 0x10 # MS-DOS directory flag
hashsum = hashlib.sha256()
with open(full_path, 'rb') as src, self._wheel_zip.open(zinfo,
'w') as dst:
with open(full_path, 'rb') as src:
while True:
buf = src.read(1024 * 8)
if not buf:
break
hashsum.update(buf)
dst.write(buf)
src.seek(0)
self._wheel_zip.writestr(zinfo, src.read())
size = os.stat(full_path).st_size
hash_digest = urlsafe_b64encode(hashsum.digest()).decode(
'ascii').rstrip('=')
hash_digest = urlsafe_b64encode(
hashsum.digest()
).decode('ascii').rstrip('=')
self._records.append((rel_path, hash_digest, size))
......
......@@ -38,7 +38,7 @@ class Metadata:
provides_extra = []
@classmethod
def from_package(cls, package) -> 'Metadata':
def from_package(cls, package): # type: (...) -> Metadata
meta = cls()
meta.name = canonicalize_name(package.name)
......
from pathlib import Path
from poetry.utils._compat import Path
from poetry.utils.helpers import module_name
......@@ -23,19 +22,19 @@ class Module:
raise ValueError("No file/folder found for package {}".format(name))
@property
def name(self) -> str:
def name(self): # type: () -> str
return self._name
@property
def path(self) -> Path:
def path(self): # type: () -> Path
return self._path
@property
def file(self) -> Path:
def file(self): # type: () -> Path
if self._is_package:
return self._path / '__init__.py'
else:
return self._path
def is_package(self) -> bool:
def is_package(self): # type: () -> bool
return self._is_package
......@@ -5,6 +5,7 @@ Base implementation taken from
https://github.com/pypa/wheel/blob/master/wheel/pep425tags.py
and adapted to work with poetry's venv util.
"""
from __future__ import unicode_literals
import distutils.util
import sys
......
......@@ -6,7 +6,7 @@ from ..conflict import Conflict
from ..dependency_graph import DependencyGraph
class SpecificationProvider:
class SpecificationProvider(object):
"""
Provides information about specifcations and dependencies to the resolver,
allowing the Resolver class to remain generic while still providing power
......@@ -18,14 +18,14 @@ class SpecificationProvider:
"""
@property
def name_for_explicit_dependency_source(self) -> str:
def name_for_explicit_dependency_source(self): # type: () -> str
return 'user-specified dependency'
@property
def name_for_locking_dependency_source(self) -> str:
def name_for_locking_dependency_source(self): # type: () -> str
return 'Lockfile'
def search_for(self, dependency: Any) -> List[Any]:
def search_for(self, dependency): # type: (Any) -> List[Any]
"""
Search for the specifications that match the given dependency.
......@@ -34,32 +34,34 @@ class SpecificationProvider:
"""
return []
def dependencies_for(self, specification: Any) -> List[Any]:
def dependencies_for(self, specification): # type: (Any) -> List[Any]
"""
Returns the dependencies of specification.
"""
return []
def is_requirement_satisfied_by(self,
requirement: Any,
activated: DependencyGraph,
spec: Any) -> bool:
requirement, # type: Any
activated, # type: DependencyGraph
spec # type: Any
): # type: (...) -> Any
"""
Determines whether the given requirement is satisfied by the given
spec, in the context of the current activated dependency graph.
"""
return True
def name_for(self, dependency: Any) -> str:
def name_for(self, dependency): # type: (Any) -> str
"""
Returns the name for the given dependency.
"""
return str(dependency)
def sort_dependencies(self,
dependencies: List[Any],
activated: DependencyGraph,
conflicts: Dict[str, List[Conflict]]) -> List[Any]:
dependencies, # type: List[Any]
activated, # type: DependencyGraph
conflicts # type: Dict[str, List[Conflict]]
): # type: (...) -> List[Any]
"""
Sort dependencies so that the ones
that are easiest to resolve are first.
......@@ -78,7 +80,7 @@ class SpecificationProvider:
)
)
def allow_missing(self, dependency) -> bool:
def allow_missing(self, dependency): # type: (Any) -> bool
"""
Returns whether this dependency, which has no possible matching
specifications, can safely be ignored.
......
import sys
class UI:
class UI(object):
def __init__(self, debug=False):
self._debug = debug
......@@ -11,22 +11,22 @@ class UI:
return sys.stdout
@property
def progress_rate(self) -> float:
def progress_rate(self): # type: () -> float
return 0.33
def is_debugging(self) -> bool:
def is_debugging(self): # type: () -> bool
return self._debug
def indicate_progress(self) -> None:
def indicate_progress(self): # type: () -> None
self.output.write('.')
def before_resolution(self) -> None:
def before_resolution(self): # type: () -> None
self.output.write('Resolving dependencies...\n')
def after_resolution(self) -> None:
def after_resolution(self): # type: () -> None
self.output.write('')
def debug(self, message, depth) -> None:
def debug(self, message, depth): # type: (...) -> None
if self.is_debugging():
debug_info = str(message)
debug_info = '\n'.join([
......
......@@ -17,7 +17,7 @@ class NoSuchDependencyError(ResolverError):
if sources:
message += ' depended upon by {}'.format(sources)
super().__init__(message)
super(NoSuchDependencyError, self).__init__(message)
class CircularDependencyError(ResolverError):
......@@ -48,7 +48,7 @@ class VersionConflict(ResolverError):
for c in conflict_requirements:
pairs.append((c, source))
super().__init__(
super(VersionConflict, self).__init__(
'Unable to satisfy the following requirements:\n\n'
'{}'.format(
'\n'.join('- "{}" required by "{}"'.format(r, d)
......
from typing import Any
class Action:
class Action(object):
def __init__(self):
self.previous = None
self.next = None
@property
def action_name(self) -> str:
def action_name(self): # type: () -> str
raise NotImplementedError()
def up(self, graph: 'DependencyGraph') -> Any:
def up(self, graph): # type: (DependencyGraph) -> Any
"""
Performs the action on the given graph.
"""
raise NotImplementedError()
def down(self, graph: 'DependencyGraph') -> None:
def down(self, graph): # type: (DependencyGraph) -> None
"""
Reverses the action on the given graph.
"""
......
......@@ -7,16 +7,6 @@ _NULL = object()
class AddVertex(Action):
def __init__(self, name, payload, root):
"""
:param name: The name of the vertex.
:type name: str
:param payload: The payload of he vertex
:type payload: Any
:param root: whether the vertex is root or not
:type root: bool
"""
super(AddVertex, self).__init__()
self._name = name
......
# -*- coding: utf-8 -*-
import logging
from copy import copy
......@@ -26,10 +27,11 @@ logger = logging.getLogger(__name__)
class Resolution:
def __init__(self,
provider: SpecificationProvider,
ui: UI,
requested: List[Any],
base: DependencyGraph):
provider, # type: SpecificationProvider
ui, # type: UI
requested, # type: List[Any]
base # type: DependencyGraph
):
self._provider = provider
self._ui = ui
self._requested = requested
......@@ -43,26 +45,26 @@ class Resolution:
self._started_at = None
@property
def provider(self) -> SpecificationProvider:
def provider(self): # type: () -> SpecificationProvider
return self._provider
@property
def ui(self) -> UI:
def ui(self): # type: () -> UI
return self._ui
@property
def requested(self) -> List[Any]:
def requested(self): # type: () -> List[Any]
return self._requested
@property
def base(self) -> DependencyGraph:
def base(self): # type: () -> DependencyGraph
return self._base
@property
def activated(self) -> DependencyGraph:
def activated(self): # type: () -> DependencyGraph
return self.state.activated
def resolve(self) -> DependencyGraph:
def resolve(self): # type: () -> DependencyGraph
"""
Resolve the original requested dependencies into a full
dependency graph.
......@@ -94,7 +96,7 @@ class Resolution:
finally:
self._end()
def _start(self) -> None:
def _start(self): # type: () -> None
"""
Set up the resolution process.
"""
......@@ -110,7 +112,7 @@ class Resolution:
self._handle_missing_or_push_dependency_state(self._initial_state())
def _resolve_activated_specs(self) -> DependencyGraph:
def _resolve_activated_specs(self): # type: () -> DependencyGraph
for vertex in self.activated.vertices.values():
if not vertex.payload:
continue
......@@ -132,7 +134,7 @@ class Resolution:
return self.activated
def _end(self) -> None:
def _end(self): # type: () -> None
"""
Ends the resolution process
"""
......@@ -147,7 +149,7 @@ class Resolution:
)
)
def _process_topmost_state(self) -> None:
def _process_topmost_state(self): # type: () -> None
"""
Processes the topmost available RequirementState on the stack.
"""
......@@ -162,7 +164,7 @@ class Resolution:
self._unwind_for_conflict()
@property
def possibility(self) -> PossibilitySet:
def possibility(self): # type: () -> PossibilitySet
"""
The current possibility that the resolution is trying.
"""
......@@ -170,7 +172,7 @@ class Resolution:
return self.state.possibilities[-1]
@property
def state(self) -> DependencyState:
def state(self): # type: () -> DependencyState
"""
The current state the resolution is operating upon.
"""
......@@ -178,14 +180,14 @@ class Resolution:
return self._states[-1]
@property
def name(self) -> str:
def name(self): # type: () -> str
return self.state.name
@property
def requirement(self) -> Any:
def requirement(self): # type: () -> Any
return self.state.requirement
def _initial_state(self) -> DependencyState:
def _initial_state(self): # type: () -> DependencyState
"""
Create the initial state for the resolution, based upon the
requested dependencies.
......@@ -221,7 +223,7 @@ class Resolution:
[]
)
def _unwind_for_conflict(self) -> None:
def _unwind_for_conflict(self): # type: () -> None
"""
Unwinds the states stack because a conflict has been encountered
"""
......@@ -260,7 +262,7 @@ class Resolution:
if uw.state_index < index
]
def _raise_error_unless_state(self, conflicts) -> None:
def _raise_error_unless_state(self, conflicts): # type: (dict) -> None
"""
Raise a VersionConflict error, or any underlying error,
if there is no current state
......@@ -278,7 +280,7 @@ class Resolution:
raise error
def _build_details_for_unwind(self) -> UnwindDetails:
def _build_details_for_unwind(self): # type: () -> UnwindDetails
"""
Return the details of the nearest index to which we could unwind.
"""
......@@ -333,7 +335,8 @@ class Resolution:
return current_detail
def _unwind_options_for_requirements(self, binding_requirements):
def _unwind_options_for_requirements(self, binding_requirements
): # type: (list) -> List[UnwindDetails]
unwind_details = []
trees = []
......@@ -504,7 +507,8 @@ class Resolution:
return satisfied
def _filter_possibilities_for_parent_unwind(self,
unwind_details: UnwindDetails):
unwind_details # type: UnwindDetails
):
"""
Filter a state's possibilities to remove any that would (eventually)
the requirements in the conflict we've just rewound from.
......@@ -619,7 +623,7 @@ class Resolution:
def _binding_requirement_in_set(self,
requirement,
possible_binding_requirements,
possibilities) -> bool:
possibilities): # type: () -> bool
"""
Return whether or not the given requirement is required
to filter out all elements of the list of possibilities.
......
......@@ -11,22 +11,24 @@ from .resolution import Resolution
class Resolver:
def __init__(self,
specification_provider: SpecificationProvider,
resolver_ui: UI):
specification_provider, # type: SpecificationProvider
resolver_ui # type: UI
):
self._specification_provider = specification_provider
self._resolver_ui = resolver_ui
@property
def specification_provider(self) -> SpecificationProvider:
def specification_provider(self): # type: () -> SpecificationProvider
return self._specification_provider
@property
def ui(self) -> UI:
def ui(self): # type: () -> UI
return self._resolver_ui
def resolve(self,
requested: List[Any],
base: Union[DependencyGraph, None] = None) -> DependencyGraph:
requested, # type: List[Any]
base=None # type: Union[DependencyGraph, None]
): # type: (...) -> DependencyGraph
if base is None:
base = DependencyGraph()
......
from collections import namedtuple
class UnwindDetails:
def __init__(self,
......
......@@ -42,7 +42,7 @@ class GenericConstraint(BaseConstraint):
self._version = version
@property
def supported_operators(self) -> list:
def supported_operators(self):
return list(self._trans_op_str.keys())
@property
......@@ -54,7 +54,7 @@ class GenericConstraint(BaseConstraint):
return self._string_operator
@property
def version(self) -> str:
def version(self):
return self._version
def matches(self, provider):
......
......@@ -9,14 +9,15 @@ from poetry.semver.version_parser import VersionParser
from .constraints.generic_constraint import GenericConstraint
class Dependency:
class Dependency(object):
def __init__(self,
name: str,
constraint: str,
optional: bool = False,
category: str = 'main',
allows_prereleases: bool = False):
name, # type: str
constraint, # type: str
optional=False, # type: bool
category='main', # type: str
allows_prereleases=False # type: bool
):
self._name = name.lower()
self._pretty_name = name
self._parser = VersionParser()
......@@ -67,7 +68,7 @@ class Dependency:
return self._python_versions
@python_versions.setter
def python_versions(self, value: str):
def python_versions(self, value):
self._python_versions = value
self._python_constraint = self._parser.parse_constraints(value)
......@@ -76,11 +77,11 @@ class Dependency:
return self._python_constraint
@property
def platform(self) -> str:
def platform(self):
return self._platform
@platform.setter
def platform(self, value: str):
def platform(self, value):
self._platform = value
self._platform_constraint = GenericConstraint.parse(value)
......@@ -89,11 +90,11 @@ class Dependency:
return self._platform_constraint
@property
def extras(self) -> list:
def extras(self): # type: () -> list
return self._extras
@property
def in_extras(self) -> list:
def in_extras(self): # type: () -> list
return self._in_extras
def allows_prereleases(self):
......@@ -105,7 +106,7 @@ class Dependency:
def is_vcs(self):
return False
def accepts(self, package: 'poetry.packages.Package') -> bool:
def accepts(self, package): # type: (poetry.packages.Package) -> bool
"""
Determines if the given package matches this dependency.
"""
......@@ -115,7 +116,7 @@ class Dependency:
and (not package.is_prerelease() or self.allows_prereleases())
)
def to_pep_508(self, with_extras=True) -> str:
def to_pep_508(self, with_extras=True): # type: (bool) -> str
requirement = self.pretty_name
if isinstance(self.constraint, MultiConstraint):
......
import json
import poetry.packages
import poetry.repositories
from hashlib import sha256
from pathlib import Path
from typing import List
from poetry.repositories import Repository
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile
......@@ -20,14 +20,14 @@ class Locker:
'source',
]
def __init__(self, lock: Path, local_config: dict):
def __init__(self, lock, local_config): # type: (Path, dict) -> None
self._lock = TomlFile(lock)
self._local_config = local_config
self._lock_data = None
self._content_hash = self._get_content_hash()
@property
def lock(self) -> TomlFile:
def lock(self): # type: () -> TomlFile
return self._lock
@property
......@@ -37,7 +37,7 @@ class Locker:
return self._lock_data
def is_locked(self) -> bool:
def is_locked(self): # type: () -> bool
"""
Checks whether the locker has been locked (lockfile found).
"""
......@@ -46,7 +46,7 @@ class Locker:
return 'package' in self.lock_data
def is_fresh(self) -> bool:
def is_fresh(self): # type: () -> bool
"""
Checks whether the lock file is still up to date with the current hash.
"""
......@@ -58,15 +58,16 @@ class Locker:
return False
def locked_repository(self, with_dev_reqs: bool = False) -> Repository:
def locked_repository(self, with_dev_reqs=False
): # type: (bool) -> poetry.repositories.Repository
"""
Searches and returns a repository of locked packages.
"""
if not self.is_locked():
return Repository()
return poetry.repositories.Repository()
lock_data = self.lock_data
packages = Repository()
packages = poetry.repositories.Repository()
if with_dev_reqs:
locked_packages = lock_data['package']
......@@ -106,7 +107,7 @@ class Locker:
return packages
def set_lock_data(self,
root, packages) -> bool:
root, packages): # type: () -> bool
hashes = {}
packages = self._lock_packages(packages)
# Retrieving hashes
......@@ -141,7 +142,7 @@ class Locker:
self._lock.write(data)
self._lock_data = None
def _get_content_hash(self) -> str:
def _get_content_hash(self): # type: () -> str
"""
Returns the sha256 hash of the sorted content of the composer file.
"""
......@@ -157,7 +158,7 @@ class Locker:
return content_hash
def _get_lock_data(self) -> dict:
def _get_lock_data(self): # type: () -> dict
if not self._lock.exists():
raise RuntimeError(
'No lockfile found. Unable to read locked packages'
......@@ -166,7 +167,8 @@ class Locker:
return self._lock.read(True)
def _lock_packages(self,
packages: List['poetry.packages.Package']) -> list:
packages
): # type: (List['poetry.packages.Package']) -> list
locked = []
for package in sorted(packages, key=lambda x: x.name):
......@@ -176,7 +178,8 @@ class Locker:
return locked
def _dump_package(self, package: 'poetry.packages.Package') -> dict:
def _dump_package(self, package
): # type: (poetry.packages.Package) -> dict
dependencies = {}
for dependency in package.requires:
if dependency.is_optional():
......
# -*- coding: utf-8 -*-
import copy
import re
from typing import Union
from poetry.semver.constraints import Constraint
......@@ -16,7 +19,7 @@ from .vcs_dependency import VCSDependency
AUTHOR_REGEX = re.compile('(?u)^(?P<name>[- .,\w\d\'’"()]+) <(?P<email>.+?)>$')
class Package:
class Package(object):
AVAILABLE_PYTHONS = {
'2',
......@@ -138,18 +141,18 @@ class Package:
return '{} {}'.format(self._pretty_version, self.source_reference)
@property
def authors(self) -> list:
def authors(self): # type: () -> list
return self._authors
@property
def author_name(self) -> str:
def author_name(self): # type: () -> str
return self._get_author()['name']
@property
def author_email(self) -> str:
def author_email(self): # type: () -> str
return self._get_author()['email']
def _get_author(self) -> dict:
def _get_author(self): # type: () -> dict
if not self._authors:
return {
'name': None,
......@@ -171,7 +174,7 @@ class Package:
return self._python_versions
@python_versions.setter
def python_versions(self, value: str):
def python_versions(self, value):
self._python_versions = value
self._python_constraint = self._parser.parse_constraints(value)
......@@ -180,11 +183,11 @@ class Package:
return self._python_constraint
@property
def platform(self) -> str:
def platform(self): # type: () -> str
return self._platform
@platform.setter
def platform(self, value: str):
def platform(self, value): # type: (str) -> None
self._platform = value
self._platform_constraint = GenericConstraint.parse(value)
......@@ -207,7 +210,7 @@ class Package:
@property
def all_classifiers(self):
classifiers = self.classifiers.copy()
classifiers = copy.copy(self.classifiers)
# Automatically set python classifiers
parser = VersionParser()
......@@ -241,9 +244,10 @@ class Package:
return self._stability != 'stable'
def add_dependency(self,
name: str,
constraint: Union[str, dict, None] = None,
category: str = 'main') -> Dependency:
name, # type: str
constraint=None, # type: Union[str, dict, None]
category='main' # type: str
): # type: (...) -> Dependency
if constraint is None:
constraint = '*'
......
import posixpath
import urllib.parse
import urllib.request
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
import re
from .utils import path_to_url
......@@ -79,23 +83,23 @@ class Link:
@property
def filename(self):
_, netloc, path, _, _ = urllib.parse.urlsplit(self.url)
_, netloc, path, _, _ = urlparse.urlsplit(self.url)
name = posixpath.basename(path.rstrip('/')) or netloc
name = urllib.parse.unquote(name)
name = urlparse.unquote(name)
assert name, ('URL %r produced no filename' % self.url)
return name
@property
def scheme(self):
return urllib.parse.urlsplit(self.url)[0]
return urlparse.urlsplit(self.url)[0]
@property
def netloc(self):
return urllib.parse.urlsplit(self.url)[1]
return urlparse.urlsplit(self.url)[1]
@property
def path(self):
return urllib.parse.unquote(urllib.parse.urlsplit(self.url)[2])
return urlparse.unquote(urlparse.urlsplit(self.url)[2])
def splitext(self):
return splitext(posixpath.basename(self.path.rstrip('/')))
......@@ -106,8 +110,8 @@ class Link:
@property
def url_without_fragment(self):
scheme, netloc, path, query, fragment = urllib.parse.urlsplit(self.url)
return urllib.parse.urlunsplit((scheme, netloc, path, query, None))
scheme, netloc, path, query, fragment = urlparse.urlsplit(self.url)
return urlparse.urlunsplit((scheme, netloc, path, query, None))
_egg_fragment_re = re.compile(r'[#&]egg=([^&]*)')
......
import os
import posixpath
import re
import urllib.parse
import urllib.request
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
try:
import urllib.request as urllib2
except ImportError:
import urllib2
BZ2_EXTENSIONS = ('.tar.bz2', '.tbz')
......@@ -33,7 +42,7 @@ def path_to_url(path):
quoted path parts.
"""
path = os.path.normpath(os.path.abspath(path))
url = urllib.parse.urljoin('file:', urllib.request.pathname2url(path))
url = urlparse.urljoin('file:', urllib2.pathname2url(path))
return url
......@@ -116,8 +125,6 @@ def convert_markers(markers):
requirements = {}
def _group(_groups, or_=False):
nonlocal requirements
for group in _groups:
if isinstance(group, tuple):
variable, op, value = group
......
......@@ -20,12 +20,12 @@ class VCSDependency(Dependency):
self._tag = tag
self._rev = rev
super().__init__(
super(VCSDependency, self).__init__(
name, '*', optional=optional, allows_prereleases=True
)
@property
def vcs(self) -> str:
def vcs(self):
return self._vcs
@property
......@@ -45,11 +45,11 @@ class VCSDependency(Dependency):
return self._rev
@property
def reference(self) -> str:
def reference(self): # type: () -> str
return self._branch or self._tag or self._rev
@property
def pretty_constraint(self) -> str:
def pretty_constraint(self): # type: () -> str
if self._branch:
what = 'branch'
version = self._branch
......@@ -62,7 +62,7 @@ class VCSDependency(Dependency):
return '{} {}'.format(what, version)
def is_vcs(self) -> bool:
def is_vcs(self): # type: () -> bool
return True
def accepts_prereleases(self):
......
import json
from __future__ import absolute_import
from __future__ import unicode_literals
from pathlib import Path
import json
import jsonschema
......@@ -12,6 +13,7 @@ from .packages import Package
from .repositories import Pool
from .repositories.pypi_repository import PyPiRepository
from .spdx import license_by_id
from .utils._compat import Path
from .utils.toml_file import TomlFile
......@@ -20,10 +22,11 @@ class Poetry:
VERSION = __version__
def __init__(self,
file: Path,
config: dict,
package: Package,
locker: Locker):
file, # type: Path
config, # type: dict
package, # type: Package
locker # type: Locker
):
self._file = TomlFile(file)
self._package = package
self._config = config
......@@ -42,23 +45,23 @@ class Poetry:
return self._file
@property
def package(self) -> Package:
def package(self): # type: () -> Package
return self._package
@property
def config(self) -> dict:
def config(self): # type: () -> dict
return self._config
@property
def locker(self) -> Locker:
def locker(self): # type: () -> Locker
return self._locker
@property
def pool(self) -> Pool:
def pool(self): # type: () -> Pool
return self._pool
@classmethod
def create(cls, cwd) -> 'Poetry':
def create(cls, cwd): # type: () -> Poetry
poetry_file = Path(cwd) / 'pyproject.toml'
if not poetry_file.exists():
......@@ -138,7 +141,7 @@ class Poetry:
return cls(poetry_file, local_config, package, locker)
@classmethod
def check(cls, config: dict, strict: bool = False):
def check(cls, config, strict=False): # type: (dict, bool) -> bool
"""
Checks the validity of a configuration
"""
......
......@@ -3,7 +3,7 @@ class SolverProblemError(Exception):
def __init__(self, error):
self._error = error
super().__init__(str(error))
super(SolverProblemError, self).__init__(str(error))
@property
def error(self):
......
......@@ -3,8 +3,8 @@ from .operation import Operation
class Install(Operation):
def __init__(self, package, reason: str = None) -> None:
super().__init__(reason)
def __init__(self, package, reason=None):
super(Install, self).__init__(reason)
self._package = package
......@@ -16,7 +16,7 @@ class Install(Operation):
def job_type(self):
return 'install'
def __str__(self) -> str:
def __str__(self):
return 'Installing {} ({})'.format(
self.package.pretty_name,
self.format_version(self.package)
......
# -*- coding: utf-8 -*-
from typing import Union
class Operation:
def __init__(self, reason: str = None) -> None:
class Operation(object):
def __init__(self, reason=None): # type: (Union[str, None]) -> None
self._reason = reason
self._skipped = False
self._skip_reason = None
@property
def job_type(self) -> str:
def job_type(self): # type: () -> str
raise NotImplementedError
@property
def reason(self) -> str:
def reason(self): # type: () -> str
return self._reason
@property
def skipped(self) -> bool:
def skipped(self): # type: () -> bool
return self._skipped
@property
def skip_reason(self):
def skip_reason(self): # type: () -> Union[str, None]
return self._skip_reason
def format_version(self, package) -> str:
def format_version(self, package): # type: (...) -> str
return package.full_pretty_version
def skip(self, reason: str) -> 'Operation':
def skip(self, reason): # type: (str) -> Operation
self._skipped = True
self._skip_reason = reason
......
......@@ -2,7 +2,6 @@ import os
import shutil
from functools import cmp_to_key
from pathlib import Path
from tempfile import mkdtemp
from typing import Dict
from typing import List
......@@ -19,6 +18,7 @@ from poetry.repositories import Pool
from poetry.semver import less_than
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile
from poetry.utils.venv import Venv
......@@ -29,7 +29,11 @@ class Provider(SpecificationProvider):
UNSAFE_PACKAGES = {'setuptools', 'distribute', 'pip'}
def __init__(self, package: Package, pool: Pool, io):
def __init__(self,
package, # type: Package
pool, # type: Pool
io
):
self._package = package
self._pool = pool
self._io = io
......@@ -38,24 +42,24 @@ class Provider(SpecificationProvider):
self._search_for = {}
@property
def pool(self) -> Pool:
def pool(self): # type: () -> Pool
return self._pool
@property
def name_for_explicit_dependency_source(self) -> str:
def name_for_explicit_dependency_source(self): # type: () -> str
return 'pyproject.toml'
@property
def name_for_locking_dependency_source(self) -> str:
def name_for_locking_dependency_source(self): # type: () -> str
return 'pyproject.lock'
def name_for(self, dependency: Dependency) -> str:
def name_for(self, dependency): # type: (Dependency) -> str
"""
Returns the name for the given dependency.
"""
return dependency.name
def search_for(self, dependency: Dependency) -> List[Package]:
def search_for(self, dependency): # type: (Dependency) -> List[Package]
"""
Search for the specifications that match the given dependency.
......@@ -86,7 +90,7 @@ class Provider(SpecificationProvider):
return self._search_for[dependency]
def search_for_vcs(self, dependency: VCSDependency) -> List[Package]:
def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package]
"""
Search for the specifications that match the given VCS dependency.
......@@ -171,7 +175,7 @@ class Provider(SpecificationProvider):
return [package]
def dependencies_for(self, package: Package):
def dependencies_for(self, package): # type: (Package) -> List[Dependency]
if package.source_type == 'git':
# Information should already be set
pass
......@@ -185,9 +189,10 @@ class Provider(SpecificationProvider):
]
def is_requirement_satisfied_by(self,
requirement: Dependency,
activated: DependencyGraph,
package: Package) -> bool:
requirement, # type: Dependency
activated, # type: DependencyGraph
package # type: Package
): # type: (...) -> bool
"""
Determines whether the given requirement is satisfied by the given
spec, in the context of the current activated dependency graph.
......@@ -210,9 +215,10 @@ class Provider(SpecificationProvider):
)
def sort_dependencies(self,
dependencies: List[Dependency],
activated: DependencyGraph,
conflicts: Dict[str, List[Conflict]]):
dependencies, # type: List[Dependency]
activated, # type: DependencyGraph
conflicts # type: Dict[str, List[Conflict]]
): # type: (...) -> List[Dependency]
return sorted(dependencies, key=lambda d: [
0 if activated.vertex_named(d.name).payload else 1,
0 if activated.vertex_named(d.name).root else 1,
......
......@@ -25,7 +25,7 @@ class Solver:
self._locked = locked
self._io = io
def solve(self, requested, fixed=None) -> List[Operation]:
def solve(self, requested, fixed=None): # type: (...) -> List[Operation]
resolver = Resolver(
Provider(self._package, self._pool, self._io),
UI(self._io)
......
......@@ -4,17 +4,17 @@ from poetry.mixology.contracts import UI as BaseUI
class UI(BaseUI):
def __init__(self, io: CleoStyle):
def __init__(self, io): # type: (CleoStyle) -> None
self._io = io
self._progress = None
super().__init__(self._io.is_debug())
super(UI, self).__init__(self._io.is_debug())
@property
def output(self):
return self._io
def before_resolution(self) -> None:
def before_resolution(self):
self._io.write('<info>Resolving dependencies</>')
if self.is_debugging():
......@@ -24,10 +24,10 @@ class UI(BaseUI):
if not self.is_debugging():
self._io.write('.')
def after_resolution(self) -> None:
def after_resolution(self):
self._io.new_line()
def debug(self, message, depth) -> None:
def debug(self, message, depth):
if self.is_debugging():
debug_info = str(message)
debug_info = '\n'.join([
......
class BaseRepository:
class BaseRepository(object):
SEARCH_FULLTEXT = 0
SEARCH_NAME = 1
......
......@@ -7,7 +7,7 @@ from .repository import Repository
class InstalledRepository(Repository):
@classmethod
def load(cls, venv: Venv) -> 'InstalledRepository':
def load(cls, venv): # type: (Venv) -> InstalledRepository
"""
Load installed packages.
......
......@@ -86,7 +86,8 @@ class LegacyRepository(PyPiRepository):
return packages
def package(self, name, version, extras=None) -> 'poetry.packages.Package':
def package(self, name, version, extras=None
): # type: (...) -> poetry.packages.Package
"""
Retrieve the release information.
......@@ -148,7 +149,7 @@ class LegacyRepository(PyPiRepository):
return package
def get_release_info(self, name: str, version: str) -> dict:
def get_release_info(self, name, version): # type: (str, str) -> dict
"""
Return the release information given a package name and a version.
......@@ -160,7 +161,7 @@ class LegacyRepository(PyPiRepository):
lambda: self._get_release_info(name, version)
)
def _get_release_info(self, name: str, version: str) -> dict:
def _get_release_info(self, name, version): # type: (str, str) -> dict
from pip.req import InstallRequirement
from pip.exceptions import InstallationError
......
......@@ -9,7 +9,7 @@ from .repository import Repository
class Pool(BaseRepository):
def __init__(self, repositories: Union[list, None] = None):
def __init__(self, repositories=None): # type: (Union[list, None]) -> None
if repositories is None:
repositories = []
......@@ -18,13 +18,13 @@ class Pool(BaseRepository):
for repository in repositories:
self.add_repository(repository)
super().__init__()
super(Pool, self).__init__()
@property
def repositories(self) -> List[Repository]:
def repositories(self): # type: () -> List[Repository]
return self._repositories
def add_repository(self, repository: Repository) -> 'Pool':
def add_repository(self, repository): # type: (Repository) -> Pool
"""
Adds a repository to the pool.
"""
......@@ -32,7 +32,7 @@ class Pool(BaseRepository):
return self
def configure(self, source: dict) -> 'Pool':
def configure(self, source): # type: (dict) -> Pool
"""
Configures a repository based on a source
specification and add it to the pool.
......
from pathlib import Path
from typing import List
from typing import Union
......@@ -13,6 +12,7 @@ from poetry.packages import Package
from poetry.semver.constraints import Constraint
from poetry.semver.constraints.base_constraint import BaseConstraint
from poetry.semver.version_parser import VersionParser
from poetry.utils._compat import Path
from poetry.version.markers import InvalidMarker
from .repository import Repository
......@@ -44,13 +44,13 @@ class PyPiRepository(Repository):
cache=FileCache(str(release_cache_dir / '_packages'))
)
super().__init__()
super(PyPiRepository, self).__init__()
def find_packages(self,
name: str,
constraint: Union[Constraint, str, None] = None,
extras: Union[list, None] = None
) -> List[Package]:
name, # type: str
constraint=None, # type: Union[Constraint, str, None]
extras=None # type: Union[list, None]
): # type: (...) -> List[Package]
"""
Find packages on the remote server.
"""
......@@ -79,9 +79,10 @@ class PyPiRepository(Repository):
return packages
def package(self,
name: str,
version: str,
extras: Union[list, None] = None) -> Package:
name, # type: str
version, # type: str
extras=None # type: (Union[list, None])
): # type: (...) -> Package
try:
index = self._packages.index(Package(name, version, version))
......@@ -153,7 +154,7 @@ class PyPiRepository(Repository):
return results
def get_package_info(self, name: str) -> dict:
def get_package_info(self, name): # type: (str) -> dict
"""
Return the package information given its name.
......@@ -168,14 +169,14 @@ class PyPiRepository(Repository):
lambda: self._get_package_info(name)
)
def _get_package_info(self, name: str) -> dict:
def _get_package_info(self, name): # type: (str) -> dict
data = self._get('pypi/{}/json'.format(name))
if data is None:
raise ValueError('Package [{}] not found.'.format(name))
return data
def get_release_info(self, name: str, version: str) -> dict:
def get_release_info(self, name, version): # type: (str, str) -> dict
"""
Return the release information given a package name and a version.
......@@ -190,7 +191,7 @@ class PyPiRepository(Repository):
lambda: self._get_release_info(name, version)
)
def _get_release_info(self, name: str, version: str) -> dict:
def _get_release_info(self, name, version): # type: (str, str) -> dict
json_data = self._get('pypi/{}/{}/json'.format(name, version))
if json_data is None:
raise ValueError('Package [{}] not found.'.format(name))
......@@ -210,7 +211,7 @@ class PyPiRepository(Repository):
return data
def _get(self, endpoint: str) -> Union[dict, None]:
def _get(self, endpoint): # type: (str) -> Union[dict, None]
json_response = self._session.get(self._url + endpoint)
if json_response.status_code == 404:
return None
......
import re
from poetry.semver.constraints import Constraint
from poetry.semver.constraints.base_constraint import BaseConstraint
from poetry.semver.version_parser import VersionParser
......@@ -54,33 +52,6 @@ class Repository(BaseRepository):
return packages
def search(self, query, mode=0):
regex = '(?i)(?:{})'.format('|'.join(re.split('\s+', query)))
matches = {}
for package in self.packages:
name = package.name
if name in matches:
continue
if (
re.match(regex, name) is not None
or (
mode == self.SEARCH_FULLTEXT
and isinstance(package, CompletePackage)
and re.match(regex, '')
)
):
matches[name] = {
'name': package.pretty_name,
'description': (package.description
if isinstance(package, CompletePackage)
else '')
}
return list(matches.values())
def has_package(self, package):
package_id = package.unique_name
......
class BaseConstraint:
class BaseConstraint(object):
def matches(self, provider):
raise NotImplementedError()
......@@ -35,7 +35,7 @@ class Constraint(BaseConstraint):
OP_NE: '!='
}
def __init__(self, operator: str, version: str):
def __init__(self, operator, version): # type: (str, str) -> None
if operator not in self.supported_operators:
raise ValueError(
'Invalid operator "{}" given, '
......@@ -50,7 +50,7 @@ class Constraint(BaseConstraint):
self._version = str(parse_version(version))
@property
def supported_operators(self) -> list:
def supported_operators(self): # type: () -> list
return list(self._trans_op_str.keys())
@property
......@@ -62,7 +62,7 @@ class Constraint(BaseConstraint):
return self._string_operator
@property
def version(self) -> str:
def version(self): # type: () -> str
return self._version
def matches(self, provider):
......@@ -75,7 +75,8 @@ class Constraint(BaseConstraint):
# turn matching around to find a match
return provider.matches(self)
def version_compare(self, a: str, b: str, operator: str) -> bool:
def version_compare(self, a, b, operator
): # type: (str, str, str) -> bool
if operator not in self._trans_op_str:
raise ValueError(
'Invalid operator "{}" given, '
......@@ -99,7 +100,7 @@ class Constraint(BaseConstraint):
return version_compare(a, b, operator)
def match_specific(self, provider: 'Constraint') -> bool:
def match_specific(self, provider): # type: (Constraint) -> bool
no_equal_op = self._trans_op_int[self._operator].replace('=', '')
provider_no_equal_op = self._trans_op_int[provider.operator].replace('=', '')
......
......@@ -5,7 +5,7 @@ from .constraint import Constraint
class WilcardConstraint(Constraint):
def __init__(self, constraint: str):
def __init__(self, constraint): # type: (str) -> None
m = re.match(
'^(!=|==)?v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$',
constraint
......@@ -18,7 +18,7 @@ class WilcardConstraint(Constraint):
else:
operator = m.group(1)
super().__init__(
super(WilcardConstraint, self).__init__(
operator,
'.'.join([g if g else '*' for g in m.groups()[1:]])
)
......@@ -64,7 +64,7 @@ class WilcardConstraint(Constraint):
def constraint(self):
return self._constraint
def matches(self, provider) -> bool:
def matches(self, provider): # type: (Constraint) -> bool
if isinstance(provider, self.__class__):
return self._constraint.matches(provider.constraint)
......
......@@ -85,7 +85,7 @@ def normalize_version(version):
raise ValueError('Invalid version string "{}"'.format(version))
def normalize_stability(stability: str) -> str:
def normalize_stability(stability): # type: (str) -> str
stability = stability.lower()
if stability == 'rc':
......@@ -94,7 +94,7 @@ def normalize_stability(stability: str) -> str:
return stability
def parse_stability(version: str) -> str:
def parse_stability(version): # type: (str) -> str
"""
Returns the stability of a version.
"""
......@@ -123,7 +123,7 @@ def parse_stability(version: str) -> str:
return 'stable'
def _expand_stability(stability: str) -> str:
def _expand_stability(stability): # type: (str) -> str
stability = stability.lower()
if stability == 'a':
......
import re
from typing import Tuple
from typing import Union
from .constraints.constraint import Constraint
from .constraints.base_constraint import BaseConstraint
from .constraints.empty_constraint import EmptyConstraint
from .constraints.multi_constraint import MultiConstraint
from .constraints.wildcard_constraint import WilcardConstraint
......@@ -19,7 +23,9 @@ class VersionParser:
'stable', 'RC', 'beta', 'alpha', 'dev'
]
def parse_constraints(self, constraints: str):
def parse_constraints(
self, constraints
): # type: (str) -> Union[Constraint, MultiConstraint]
"""
Parses a constraint string into
MultiConstraint and/or Constraint objects.
......@@ -88,7 +94,9 @@ class VersionParser:
return constraint
def _parse_constraint(self, constraint):
def _parse_constraint(
self, constraint
): # type: (str) -> Union[Tuple[BaseConstraint], Tuple[BaseConstraint, BaseConstraint]]
m = re.match('(?i)^v?[xX*](\.[xX*])*$', constraint)
if m:
return EmptyConstraint(),
......
import json
import os
from urllib.request import urlopen
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
class Updater:
......
......@@ -2,10 +2,11 @@
"""
A converter of python values to TOML Token instances.
"""
import codecs
import datetime
import re
from poetry.utils._compat import basestring
from .. import tokens
from ..errors import TOMLError
from ..tokens import Token
......@@ -47,7 +48,7 @@ def create_primitive_token(value, multiline_strings_allowed=True):
return tokens.Token(tokens.TYPE_FLOAT, u'{}'.format(value))
elif isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
return tokens.Token(tokens.TYPE_DATE, value.isoformat())
elif isinstance(value, str):
elif isinstance(value, basestring):
return create_string_token(value, multiline_strings_allowed=multiline_strings_allowed)
raise NotPrimitiveError("{} of type {}".format(value, type(value)))
......@@ -62,7 +63,7 @@ def create_string_token(text, bare_string_allowed=False, multiline_strings_allow
Raises ValueError on non-string input.
"""
if not isinstance(text, str):
if not isinstance(text, basestring):
raise ValueError('Given value must be a string')
if text == '':
......@@ -73,7 +74,7 @@ def create_string_token(text, bare_string_allowed=False, multiline_strings_allow
# If containing two or more newlines or is longer than 80 characters we'll use the multiline string format
return _create_multiline_string_token(text)
else:
return tokens.Token(tokens.TYPE_STRING, '"{}"'.format(_escape_single_line_quoted_string(text)))
return tokens.Token(tokens.TYPE_STRING, u'"{}"'.format(_escape_single_line_quoted_string(text)))
def _escape_single_line_quoted_string(text):
......@@ -121,6 +122,6 @@ def create_whitespace(source_substring):
def create_multiline_string(text, maximum_line_length=120):
def escape(t):
return t.replace('"""', '\"\"\"')
source_substring = '"""\n{}"""'.format('\\\n'.join(chunkate_string(escape(text), maximum_line_length)))
return t.replace(u'"""', u'\"\"\"')
source_substring = u'"""\n{}"""'.format(u'\\\n'.join(chunkate_string(escape(text), maximum_line_length)))
return Token(tokens.TYPE_MULTILINE_STRING, source_substring)
import sys
try:
import pathlib2
from pathlib2 import Path
except ImportError:
from pathlib import Path
try: # Python 2
long = long
unicode = unicode
basestring = basestring
except NameError: # Python 3
long = int
unicode = str
basestring = str
PY2 = sys.version_info[0] == 2
PY36 = sys.version_info >= (3, 6)
def decode(string, encodings=None):
if not PY2 and not isinstance(string, bytes):
return string
if PY2 and isinstance(string, unicode):
return string
encodings = encodings or ['utf-8', 'latin1', 'ascii']
for encoding in encodings:
try:
return string.decode(encoding)
except (UnicodeEncodeError, UnicodeDecodeError):
pass
return string.decode(encodings[0], errors='ignore')
def encode(string, encodings=None):
if not PY2 and isinstance(string, bytes):
return string
if PY2 and isinstance(string, str):
return string
encodings = encodings or ['utf-8', 'latin1', 'ascii']
for encoding in encodings:
try:
return string.encode(encoding)
except (UnicodeEncodeError, UnicodeDecodeError):
pass
return string.encode(encodings[0], errors='ignore')
......@@ -3,9 +3,9 @@ import re
_canonicalize_regex = re.compile('[-_.]+')
def canonicalize_name(name: str) -> str:
def canonicalize_name(name): # type: (str) -> str
return _canonicalize_regex.sub('-', name).lower()
def module_name(name: str) -> str:
def module_name(name): # type: (str) -> str
return canonicalize_name(name).replace('-', '_')
# -*- coding: utf-8 -*-
import toml
from pathlib import Path
from poetry.toml import dumps
from poetry.toml import loads
from poetry.toml import TOMLFile
from ._compat import Path
class TomlFile:
......@@ -16,14 +17,14 @@ class TomlFile:
def path(self):
return self._path
def read(self, raw=False) -> dict:
def read(self, raw=False): # type: (bool) -> dict
with self._path.open() as f:
if raw:
return toml.loads(f.read())
return loads(f.read())
def write(self, data) -> None:
def write(self, data): # type: (...) -> None
if not isinstance(data, TOMLFile):
data = toml.dumps(data)
else:
......
......@@ -6,13 +6,12 @@ import sysconfig
import warnings
from contextlib import contextmanager
from pathlib import Path
from subprocess import CalledProcessError
from venv import EnvBuilder
from poetry.config import Config
from poetry.locations import CACHE_DIR
from poetry.utils._compat import Path
from poetry.utils._compat import decode
class VenvError(Exception):
......@@ -22,15 +21,15 @@ class VenvError(Exception):
class VenvCommandError(VenvError):
def __init__(self, e: CalledProcessError):
def __init__(self, e): # type: (CalledProcessError) -> None
message = 'Command {} errored with the following output: \n{}'.format(
e.cmd, e.output.decode()
)
super().__init__(message)
super(VenvCommandError, self).__init__(message)
class Venv:
class Venv(object):
def __init__(self, venv=None):
self._venv = venv
......@@ -48,7 +47,7 @@ class Venv:
self._python_implementation = None
@classmethod
def create(cls, io, name=None) -> 'Venv':
def create(cls, io, name=None): # type: (...) -> Venv
if 'VIRTUAL_ENV' not in os.environ:
# Not in a virtualenv
# Checking if we need to create one
......@@ -86,8 +85,8 @@ class Venv:
name, str(venv_path)
)
)
builder = EnvBuilder(with_pip=True)
builder.create(str(venv))
cls.build(str(venv))
else:
if io.is_very_verbose():
io.writeln(
......@@ -117,26 +116,41 @@ class Venv:
return cls(venv)
@classmethod
def build(cls, path):
try:
from venv import EnvBuilder
builder = EnvBuilder(with_pip=True)
build = builder.create
except ImportError:
# We fallback on virtualenv for Python 2.7
from virtualenv import create_environment
build = create_environment
build(path)
@property
def venv(self):
return self._venv
@property
def python(self) -> str:
def python(self): # type: () -> str
"""
Path to current python executable
"""
return self._bin('python')
@property
def pip(self) -> str:
def pip(self): # type: () -> str
"""
Path to current pip executable
"""
return self._bin('pip')
@property
def version_info(self) -> tuple:
def version_info(self): # type: () -> tuple
if self._version_info is not None:
return self._version_info
......@@ -201,7 +215,7 @@ class Venv:
return value
def run(self, bin: str, *args, **kwargs):
def run(self, bin, *args, **kwargs):
"""
Run a command inside the virtual env.
"""
......@@ -248,9 +262,9 @@ class Venv:
except CalledProcessError as e:
raise VenvCommandError(e)
return output.decode()
return decode(output)
def exec(self, bin, *args, **kwargs):
def execute(self, bin, *args, **kwargs):
if not self.is_venv():
return subprocess.call([bin] + list(args))
else:
......@@ -287,7 +301,7 @@ class Venv:
if shell in ('bash', 'zsh', 'fish'):
return shell
def _bin(self, bin) -> str:
def _bin(self, bin): # type: (str) -> str
"""
Return path to the given executable.
"""
......@@ -296,23 +310,23 @@ class Venv:
return str(self._bin_dir / bin) + ('.exe' if self._windows else '')
def is_venv(self) -> bool:
def is_venv(self): # type: () -> bool
return self._venv is not None
class NullVenv(Venv):
def __init__(self, execute=False):
super().__init__()
super(NullVenv, self).__init__()
self.executed = []
self._execute = execute
def run(self, bin: str, *args):
def run(self, bin, *args):
self.executed.append([bin] + list(args))
if self._execute:
return super().run(bin, *args)
return super(NullVenv, self).run(bin, *args)
def _bin(self, bin):
return bin
from pathlib import Path
from poetry.utils._compat import Path
from .git import Git
def get_vcs(directory: Path):
def get_vcs(directory): # type: (Path) -> Git
directory = directory.resolve()
for p in [directory] + list(directory.parents):
......
# -*- coding: utf-8 -*-
import re
import subprocess
from poetry.utils._compat import decode
class GitConfig:
def __init__(self):
config_list = subprocess.check_output(
config_list = decode(subprocess.check_output(
['git', 'config', '-l'],
stderr=subprocess.STDOUT
).decode()
))
self._config = {}
......@@ -31,13 +35,13 @@ class Git:
self._work_dir = work_dir
@property
def config(self) -> GitConfig:
def config(self): # type: () -> GitConfig
return self._config
def clone(self, repository, dest) -> str:
def clone(self, repository, dest): # type: (...) -> str
return self.run('clone', repository, dest)
def checkout(self, rev, folder=None) -> str:
def checkout(self, rev, folder=None): # type: (...) -> str
args = []
if folder is None and self._work_dir:
folder = self._work_dir
......@@ -54,7 +58,7 @@ class Git:
return self.run(*args)
def rev_parse(self, rev, folder=None) -> str:
def rev_parse(self, rev, folder=None): # type: (...) -> str
args = []
if folder is None and self._work_dir:
folder = self._work_dir
......@@ -71,7 +75,7 @@ class Git:
return self.run(*args)
def get_ignored_files(self, folder=None) -> list:
def get_ignored_files(self, folder=None): # type: (...) -> list
args = []
if folder is None and self._work_dir:
folder = self._work_dir
......@@ -89,8 +93,8 @@ class Git:
return output.split('\n')
def run(self, *args) -> str:
return subprocess.check_output(
def run(self, *args): # type: (...) -> str
return decode(subprocess.check_output(
['git'] + list(args),
stderr=subprocess.STDOUT
).decode()
))
......@@ -25,7 +25,9 @@ _trans_op = {
}
def parse(version: str, strict: bool = False) -> Union[Version, LegacyVersion]:
def parse(version, # type: str
strict=False # type: bool
): # type:(...) -> Union[Version, LegacyVersion]
"""
Parse the given version string and return either a :class:`Version` object
or a LegacyVersion object depending on if the given version is
......@@ -42,7 +44,8 @@ def parse(version: str, strict: bool = False) -> Union[Version, LegacyVersion]:
return LegacyVersion(version)
def version_compare(version1: str, version2: str, operator) -> bool:
def version_compare(version1, version2, operator
): # type: (str, str, str) -> bool
if operator in _trans_op:
operator = _trans_op[operator]
elif operator in _trans_op.values():
......
......@@ -5,7 +5,11 @@ from __future__ import absolute_import, division, print_function
import string
import re
import urllib.parse as urlparse
try:
import urllib.parse as urlparse
except ImportError:
from urlparse import urlparse
from pyparsing import (
stringStart, stringEnd, originalTextFor, ParseException
......
......@@ -14,9 +14,9 @@ class VersionSelector(object):
self._parser = parser
def find_best_candidate(self,
package_name: str,
target_package_version: Union[str, None] = None
) -> Union[Package, bool]:
package_name, # type: str
target_package_version=None # type: Union[str, None]
): # type: (...) -> Union[Package, bool]
"""
Given a package name and optional version,
returns the latest Package that matches
......
[tool.poetry]
name = "poetry"
version = "0.7.1"
version = "0.8.0a0"
description = "Python dependency management and packaging made easy."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
......@@ -22,7 +22,7 @@ classifiers = [
# Requirements
[tool.poetry.dependencies]
python = "^3.4"
python = "~2.7 || ^3.4"
cleo = "^0.6"
requests = "^2.18"
toml = "^0.9"
......@@ -37,8 +37,12 @@ cachecontrol = { version = "^0.12.4", extras = ["filecache"] }
# zipfile36 is needed for Python 3.4 and 3.5
zipfile36 = { version = "^0.1", python = ">=3.4 <3.6" }
# The typing module is not in the stdlib in Python 3.4
typing = { version = "^3.6", python = "~3.4" }
# The typing module is not in the stdlib in Python 2.7 and 3.4
typing = { version = "^3.6", python = "~2.7 || ~3.4" }
# Use pathlib2 and virtualenv for Python 2.7
pathlib2 = { version = "^2.3", python = "~2.7" }
virtualenv = { version = "^15.2", python = "~2.7" }
# cachecontrol dependencies are badly set
# and do not appear in PyPI JSON API
......
......@@ -17,7 +17,7 @@ python-versions = "*"
platform = "*"
[package.requirements]
python = ">= 2.7.0.0, < 2.8.0.0"
python = ">= 2.4.0.0, < 2.5.0.0"
[[package]]
name = "C"
......@@ -32,7 +32,7 @@ platform = "*"
D = "^1.2"
[package.requirements]
python = ">= 3.6.0.0, < 4.0.0.0"
python = ">= 2.7.0.0, < 2.8.0.0 || >= 3.6.0.0, < 4.0.0.0"
[[package]]
name = "D"
......@@ -44,7 +44,7 @@ python-versions = "*"
platform = "*"
[package.requirements]
python = ">= 3.6.0.0, < 4.0.0.0"
python = ">= 2.7.0.0, < 2.8.0.0 || >= 3.6.0.0, < 4.0.0.0"
[metadata]
python-versions = "~2.7 || ^3.4"
......
......@@ -17,7 +17,7 @@ python-versions = "*"
platform = "*"
[package.requirements]
platform = "win32"
platform = "custom"
[[package]]
name = "C"
......
......@@ -22,11 +22,11 @@ version = "1.2"
description = ""
category = "main"
optional = false
python-versions = "^3.6"
python-versions = "~2.7 || ^3.6"
platform = "*"
[metadata]
python-versions = "^3.4"
python-versions = "~2.7 || ^3.4"
platform = "*"
content-hash = "123456789"
......
from __future__ import unicode_literals
import pytest
import toml
import sys
from pathlib import Path
from poetry.installation import Installer as BaseInstaller
from poetry.installation.noop_installer import NoopInstaller
......@@ -11,6 +10,8 @@ from poetry.packages import Locker as BaseLocker
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils._compat import Path
from poetry.utils._compat import PY2
from poetry.utils.venv import NullVenv
from tests.helpers import get_dependency
......@@ -23,6 +24,13 @@ class Installer(BaseInstaller):
return NoopInstaller()
class CustomInstalledRepository(InstalledRepository):
@classmethod
def load(cls, venv):
return cls()
class Locker(BaseLocker):
def __init__(self):
......@@ -34,48 +42,45 @@ class Locker(BaseLocker):
def written_data(self):
return self._written_data
def locked(self, is_locked=True) -> 'Locker':
def locked(self, is_locked=True):
self._locked = is_locked
return self
def mock_lock_data(self, data) -> None:
def mock_lock_data(self, data):
self._lock_data = data
def is_locked(self) -> bool:
def is_locked(self):
return self._locked
def is_fresh(self) -> bool:
def is_fresh(self):
return True
def _get_content_hash(self) -> str:
def _get_content_hash(self):
return '123456789'
def _write_lock_data(self, data) -> None:
def _write_lock_data(self, data):
for package in data['package']:
package['python-versions'] = str(package['python-versions'])
package['platform'] = str(package['platform'])
python_versions = str(package['python-versions'])
platform = str(package['platform'])
if PY2:
python_versions = python_versions.decode()
platform = platform.decode()
if 'requirements' in package:
requirements = {}
for key, value in package['requirements'].items():
requirements[key.decode()] = value.decode()
package['requirements'] = requirements
package['python-versions'] = python_versions
package['platform'] = platform
if not package['dependencies']:
del package['dependencies']
self._written_data = data
@pytest.fixture(autouse=True)
def setup():
# Mock python version and platform to get reliable tests
original = sys.version_info
original_platform = sys.platform
sys.version_info = (3, 6, 3, 'final', 0)
sys.platform = 'darwin'
yield
sys.version_info = original
sys.platform = original_platform
@pytest.fixture()
def package():
return get_package('root', '1.0')
......@@ -96,12 +101,7 @@ def pool(repo):
@pytest.fixture()
def installed():
original = InstalledRepository.load
InstalledRepository.load = lambda _: InstalledRepository()
yield
InstalledRepository.load = original
return CustomInstalledRepository()
@pytest.fixture()
......@@ -257,12 +257,12 @@ def test_add_with_sub_dependencies(installer, locker, repo, package):
def test_run_with_python_versions(installer, locker, repo, package):
package.python_versions = '^3.4'
package.python_versions = '~2.7 || ^3.4'
package_a = get_package('A', '1.0')
package_b = get_package('B', '1.1')
package_c12 = get_package('C', '1.2')
package_c12.python_versions = '^3.6'
package_c12.python_versions = '~2.7 || ^3.6'
package_c13 = get_package('C', '1.3')
package_c13.python_versions = '~3.3'
......@@ -298,17 +298,20 @@ def test_run_with_optional_and_python_restricted_dependencies(installer, locker,
repo.add_package(package_d)
package.add_dependency('A', {'version': '~1.0', 'optional': True})
package.add_dependency('B', {'version': '^1.0', 'python': '~2.7'})
package.add_dependency('C', {'version': '^1.0', 'python': '^3.6'})
package.add_dependency('B', {'version': '^1.0', 'python': '~2.4'})
package.add_dependency('C', {'version': '^1.0', 'python': '~2.7 || ^3.6'})
installer.run()
expected = fixture('with-optional-dependencies')
import json
print(json.dumps(locker.written_data, indent=2, sort_keys=True))
print(json.dumps(expected, indent=2, sort_keys=True))
assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs:
# C,D since the mocked python version is not compatible
# C,D since python version is not compatible
# with B's python constraint and A is optional
assert len(installer.installs) == 2
assert installer.installs[0].name == 'd'
......@@ -330,7 +333,7 @@ def test_run_with_optional_and_platform_restricted_dependencies(installer, locke
repo.add_package(package_d)
package.add_dependency('A', {'version': '~1.0', 'optional': True})
package.add_dependency('B', {'version': '^1.0', 'platform': 'win32'})
package.add_dependency('B', {'version': '^1.0', 'platform': 'custom'})
package.add_dependency('C', {'version': '^1.0', 'platform': 'darwin'})
installer.run()
......
......@@ -16,7 +16,7 @@ static PyMethodDef module_methods[] = {
{NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"extended",
......@@ -28,17 +28,31 @@ static struct PyModuleDef moduledef = {
NULL,
NULL,
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_extended(void)
#else
init_extended(void)
#endif
{
PyObject *module;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule3("extended", module_methods, NULL);
#endif
if (module == NULL)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pytest
import re
import shutil
import tarfile
import zipfile
from pathlib import Path
from poetry import __version__
from poetry.io import NullIO
from poetry.masonry.builders import CompleteBuilder
from poetry.poetry import Poetry
from poetry.utils._compat import Path
from poetry.utils._compat import decode
from poetry.utils.venv import NullVenv
fixtures_dir = Path(__file__).parent / 'fixtures'
......@@ -44,7 +47,7 @@ def test_wheel_c_extension():
assert 'extended-0.1/build.py' in tar.getnames()
assert 'extended-0.1/extended/extended.c' in tar.getnames()
whl = list((module_path / 'dist').glob('extended-0.1-cp3*-cp3*m-*.whl'))[0]
whl = list((module_path / 'dist').glob('extended-0.1-cp*-cp*m-*.whl'))[0]
assert whl.exists()
......@@ -58,13 +61,13 @@ def test_wheel_c_extension():
assert has_compiled_extension
try:
wheel_data = zip.read('extended-0.1.dist-info/WHEEL').decode()
wheel_data = decode(zip.read('extended-0.1.dist-info/WHEEL'))
assert re.match("""(?m)^\
Wheel-Version: 1.0
Generator: poetry {}
Root-Is-Purelib: false
Tag: cp3\d-cp3\dm-.+
Tag: cp[23]\d-cp[23]\dm-.+
$""".format(__version__), wheel_data) is not None
finally:
zip.close()
......@@ -85,13 +88,13 @@ def test_complete():
try:
entry_points = zip.read('my_package-1.2.3.dist-info/entry_points.txt')
assert entry_points.decode() == """\
assert decode(entry_points.decode()) == """\
[console_scripts]
my-2nd-script=my_package:main2
my-script=my_package:main
"""
wheel_data = zip.read('my_package-1.2.3.dist-info/WHEEL').decode()
wheel_data = decode(zip.read('my_package-1.2.3.dist-info/WHEEL'))
assert wheel_data == """\
Wheel-Version: 1.0
......@@ -99,7 +102,7 @@ Generator: poetry {}
Root-Is-Purelib: true
Tag: py3-none-any
""".format(__version__)
wheel_data = zip.read('my_package-1.2.3.dist-info/METADATA').decode()
wheel_data = decode(zip.read('my_package-1.2.3.dist-info/METADATA'))
assert wheel_data == """\
Metadata-Version: 2.1
......
......@@ -3,12 +3,11 @@ import pytest
import shutil
import tarfile
from pathlib import Path
from poetry.io import NullIO
from poetry.masonry.builders.sdist import SdistBuilder
from poetry.packages import Package
from poetry.poetry import Poetry
from poetry.utils._compat import Path
from poetry.utils.venv import NullVenv
from tests.helpers import get_dependency
......
import pytest
import shutil
from pathlib import Path
from poetry.io import NullIO
from poetry.masonry.builders import WheelBuilder
from poetry.poetry import Poetry
from poetry.utils._compat import Path
from poetry.utils.venv import NullVenv
......
import json
from pathlib import Path
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import Path
class MockRepository(PyPiRepository):
......@@ -10,9 +9,12 @@ class MockRepository(PyPiRepository):
FIXTURES = Path(__file__).parent / 'fixtures' / 'pypi.org' / 'json'
def __init__(self):
super().__init__(url='http://foo.bar', disable_cache=True)
super(MockRepository, self).__init__(
url='http://foo.bar',
disable_cache=True
)
def _get(self, url: str) -> dict:
def _get(self, url):
fixture = self.FIXTURES / 'requests.json'
with fixture.open() as f:
......
import toml
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from pathlib import Path
import toml
from poetry.poetry import Poetry
from poetry.utils._compat import Path
fixtures_dir = Path(__file__).parent / 'fixtures'
......
# -*- coding: utf-8 -*-
from poetry.toml import dumps
from poetry.toml import loads
......
import pytest
from pathlib import Path
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile
......
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