Commit 135ef5d5 by Sébastien Eustace

Add compatibility with Python 3.4 and 3.5

parent ac73c538
...@@ -2,6 +2,11 @@ ...@@ -2,6 +2,11 @@
## [Unreleased] ## [Unreleased]
### Added
- Added compatibility with Python 3.4 and 3.5.
### Changed ### Changed
- Improved dependency resolution to avoid unnecessary operations. - Improved dependency resolution to avoid unnecessary operations.
......
...@@ -42,7 +42,9 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -42,7 +42,9 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
for name in packages: for name in packages:
for key in poetry_content[section]: for key in poetry_content[section]:
if key.lower() == name.lower(): if key.lower() == name.lower():
raise ValueError(f'Package {name} is already present') raise ValueError(
'Package {} is already present'.format(name)
)
requirements = self._determine_requirements(packages) requirements = self._determine_requirements(packages)
requirements = self._format_requirements(requirements) requirements = self._format_requirements(requirements)
...@@ -118,7 +120,8 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -118,7 +120,8 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
requirement['name'] = name requirement['name'] = name
self.line( self.line(
f'Using version <info>{version}</> for <info>{name}</>' 'Using version <info>{}</> for <info>{}</>'
.format(version, name)
) )
else: else:
# check that the specified version/constraint exists # check that the specified version/constraint exists
...@@ -129,7 +132,9 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -129,7 +132,9 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
requirement['name'] = name requirement['name'] = name
result.append(f'{requirement["name"]} {requirement["version"]}') result.append(
'{} {}'.format(requirement['name'], requirement['version'])
)
return result return result
...@@ -143,7 +148,7 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -143,7 +148,7 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
if not package: if not package:
# TODO: find similar # TODO: find similar
raise ValueError( raise ValueError(
f'Could not find a matching version of package {name}' 'Could not find a matching version of package {}'.format(name)
) )
return ( return (
......
...@@ -17,8 +17,10 @@ class BuildCommand(VenvCommand): ...@@ -17,8 +17,10 @@ class BuildCommand(VenvCommand):
fmt = self.option('format') fmt = self.option('format')
package = self.poetry.package package = self.poetry.package
self.line(f'Building <info>{package.pretty_name}</> ' self.line(
f'(<comment>{package.version}</>)') 'Building <info>{}</> (<comment>{}</>)'
.format(package.pretty_name, package.version)
)
builder = Builder(self.poetry, self.venv, self.output) builder = Builder(self.poetry, self.venv, self.output)
builder.build(fmt) builder.build(fmt)
from cleo import Command as BaseCommand from cleo import Command as BaseCommand
from cleo.inputs import ListInput
from poetry.poetry import Poetry from poetry.poetry import Poetry
......
...@@ -77,10 +77,13 @@ To remove a repository (repo is a short alias for repositories): ...@@ -77,10 +77,13 @@ To remove a repository (repo is a short alias for repositories):
if self._config.setting('repositories') is not None: if self._config.setting('repositories') is not None:
value = self._config.setting('repositories') value = self._config.setting('repositories')
else: else:
repo = self._config.setting(f'repositories.{m.group(1)}') repo = self._config.setting(
'repositories.{}'.format(m.group(1))
)
if repo is None: if repo is None:
raise ValueError( raise ValueError(
f'There is no {m.group(1)} repository defined' 'There is no {} repository defined'
.format(m.group(1))
) )
value = repo value = repo
...@@ -115,18 +118,26 @@ To remove a repository (repo is a short alias for repositories): ...@@ -115,18 +118,26 @@ To remove a repository (repo is a short alias for repositories):
raise ValueError('You cannot remove the [repositories] section') raise ValueError('You cannot remove the [repositories] section')
if self.option('unset'): if self.option('unset'):
repo = self._config.setting(f'repositories.{m.group(1)}') repo = self._config.setting(
'repositories.{}'.format(m.group(1))
)
if repo is None: if repo is None:
raise ValueError(f'There is no {m.group(1)} repository defined') raise ValueError(
'There is no {} repository defined'.format(m.group(1))
)
self._config.remove_property(f'repositories.{m.group(1)}') self._config.remove_property(
'repositories.{}'.format(m.group(1))
)
return 0 return 0
if len(values) == 1: if len(values) == 1:
url = values[0] url = values[0]
self._config.add_property(f'repositories.{m.group(1)}.url', url) self._config.add_property(
'repositories.{}.url'.format(m.group(1)), url
)
return 0 return 0
...@@ -139,12 +150,16 @@ To remove a repository (repo is a short alias for repositories): ...@@ -139,12 +150,16 @@ To remove a repository (repo is a short alias for repositories):
m = re.match('^(http-basic)\.(.+)', self.argument('key')) m = re.match('^(http-basic)\.(.+)', self.argument('key'))
if m: if m:
if self.option('unset'): if self.option('unset'):
if not self._auth_config.setting(f'{m.group(1)}.{m.group(2)}'): if not self._auth_config.setting('{}.{}'.format(m.group(1), m.group(2))):
raise ValueError( raise ValueError(
f'There is no {m.group(2)} {m.group(1)} defined' 'There is no {} {} defined'.format(
m.group(2), m.group(1)
)
) )
self._auth_config.remove_property(f'{m.group(1)}.{m.group(2)}') self._auth_config.remove_property(
'{}.{}'.format(m.group(1), m.group(2))
)
return 0 return 0
...@@ -154,14 +169,17 @@ To remove a repository (repo is a short alias for repositories): ...@@ -154,14 +169,17 @@ To remove a repository (repo is a short alias for repositories):
# Only username, so we prompt for password # Only username, so we prompt for password
password = self.secret('Password:') password = self.secret('Password:')
elif len(values) != 2: elif len(values) != 2:
raise ValueError(f'Expected one or two arguments ' raise ValueError(
f'(username, password), got {len(values)}') 'Expected one or two arguments '
'(username, password), got {}'.format(len(values))
)
else: else:
username = values[0] username = values[0]
password = values[1] password = values[1]
self._auth_config.add_property( self._auth_config.add_property(
f'{m.group(1)}.{m.group(2)}', { '{}.{}'.format(m.group(1), m.group(2)),
{
'username': username, 'username': username,
'password': password 'password': password
} }
...@@ -169,7 +187,9 @@ To remove a repository (repo is a short alias for repositories): ...@@ -169,7 +187,9 @@ To remove a repository (repo is a short alias for repositories):
return 0 return 0
raise ValueError(f'Setting {self.argument("key")} does not exist') raise ValueError(
'Setting {} does not exist'.format(self.argument("key"))
)
def _handle_single_value(self, key, callbacks, values): def _handle_single_value(self, key, callbacks, values):
validator, normalizer = callbacks validator, normalizer = callbacks
...@@ -180,7 +200,7 @@ To remove a repository (repo is a short alias for repositories): ...@@ -180,7 +200,7 @@ To remove a repository (repo is a short alias for repositories):
value = values[0] value = values[0]
if not validator(value): if not validator(value):
raise RuntimeError( raise RuntimeError(
f'"{value}" is an invalid value for {key}' '"{}" is an invalid value for {}'.format(value, key)
) )
self._config.add_property(key, normalizer(value)) self._config.add_property(key, normalizer(value))
...@@ -215,8 +235,12 @@ To remove a repository (repo is a short alias for repositories): ...@@ -215,8 +235,12 @@ To remove a repository (repo is a short alias for repositories):
for val in value for val in value
] ]
value = f'[{", ".join(value)}]' value = '[{}]'.format(", ".join(value))
value = json.dumps(value) value = json.dumps(value)
self.line(f'[<comment>{(k or "") + key}</comment>] <info>{value}</info>') self.line(
'[<comment>{}</comment>] <info>{}</info>'.format(
(k or "") + key, value
)
)
...@@ -55,8 +55,12 @@ class DebugResolveCommand(Command): ...@@ -55,8 +55,12 @@ class DebugResolveCommand(Command):
for op in ops: for op in ops:
package = op.package package = op.package
self.line(f' - <info>{package.name}</info> ' self.line(
f'(<comment>{package.version}</comment>)') ' - <info>{}</info> (<comment>{}</comment>)'
.format(
package.name, package.version
)
)
def _determine_requirements(self, requires: List[str]) -> List[str]: def _determine_requirements(self, requires: List[str]) -> List[str]:
if not requires: if not requires:
...@@ -68,7 +72,9 @@ class DebugResolveCommand(Command): ...@@ -68,7 +72,9 @@ class DebugResolveCommand(Command):
if 'version' not in requirement: if 'version' not in requirement:
requirement['version'] = '*' requirement['version'] = '*'
result.append(f'{requirement["name"]} {requirement["version"]}') result.append(
'{} {}'.format(requirement['name'], requirement['version'])
)
return result return result
......
...@@ -41,7 +41,7 @@ list of installed packages ...@@ -41,7 +41,7 @@ list of installed packages
break break
if not found: if not found:
raise ValueError(f'Package {name} not found') raise ValueError('Package {} not found'.format(name))
for key in requirements: for key in requirements:
del poetry_content[section][key] del poetry_content[section][key]
......
...@@ -62,7 +62,7 @@ lists all packages available.""" ...@@ -62,7 +62,7 @@ lists all packages available."""
break break
if not pkg: if not pkg:
raise ValueError(f'Package {package} not found') raise ValueError('Package {} not found'.format(package))
if self.option('tree'): if self.option('tree'):
self.display_package_tree(pkg, installed_repo) self.display_package_tree(pkg, installed_repo)
...@@ -70,9 +70,9 @@ lists all packages available.""" ...@@ -70,9 +70,9 @@ lists all packages available."""
return 0 return 0
rows = [ rows = [
['<info>name</>', f' : <fg=cyan>{pkg.pretty_name}</>'], ['<info>name</>', ' : <fg=cyan>{}</>'.format(pkg.pretty_name)],
['<info>version</>', f' : <comment>{pkg.pretty_version}</>'], ['<info>version</>', ' : <comment>{}</>'.format(pkg.pretty_version)],
['<info>description</>', f' : {pkg.description}'], ['<info>description</>', ' : {}'.format(pkg.description)],
] ]
table.add_rows(rows) table.add_rows(rows)
...@@ -82,8 +82,12 @@ lists all packages available.""" ...@@ -82,8 +82,12 @@ lists all packages available."""
self.line('') self.line('')
self.line('<info>dependencies</info>') self.line('<info>dependencies</info>')
for dependency in pkg.requires: for dependency in pkg.requires:
self.line(f' - {dependency.pretty_name} ' self.line(
f'<comment>{dependency.pretty_constraint}</>') ' - {} <comment>{}</>'.format(
dependency.pretty_name,
dependency.pretty_constraint
)
)
return 0 return 0
...@@ -109,9 +113,11 @@ lists all packages available.""" ...@@ -109,9 +113,11 @@ lists all packages available."""
write_description = name_length + version_length + latest_length + 24 <= width write_description = name_length + version_length + latest_length + 24 <= width
for locked in locked_packages: for locked in locked_packages:
line = f'<fg=cyan>{locked.pretty_name:{name_length}}</>' line = '<fg=cyan>{:{}}</>'.format(locked.pretty_name, name_length)
if write_version: if write_version:
line += f' {locked.full_pretty_version:{version_length}}' line += ' {:{}}'.format(
locked.full_pretty_version, version_length
)
if show_latest and write_latest: if show_latest and write_latest:
latest = latest_packages[locked.pretty_name] latest = latest_packages[locked.pretty_name]
...@@ -122,7 +128,9 @@ lists all packages available.""" ...@@ -122,7 +128,9 @@ lists all packages available."""
elif update_status == 'update-possible': elif update_status == 'update-possible':
color = 'yellow' color = 'yellow'
line += f' <fg={color}>{latest.version:{latest_length}}</>' line += ' <fg={}>{:{}}</>'.format(
color, latest.version, latest_length
)
if self.option('outdated') and update_status == 'up-to-date': if self.option('outdated') and update_status == 'up-to-date':
continue continue
...@@ -140,8 +148,8 @@ lists all packages available.""" ...@@ -140,8 +148,8 @@ lists all packages available."""
self.line(line) self.line(line)
def display_package_tree(self, package, installed_repo): def display_package_tree(self, package, installed_repo):
self.write(f'<info>{package.pretty_name}</info>') self.write('<info>{}</info>'.format(package.prett_name))
self.line(f' {package.pretty_version} {package.description}') self.line(' {} {}'.format(package.pretty_version, package.description))
dependencies = package.requires dependencies = package.requires
dependencies = sorted(dependencies, key=lambda x: x.name) dependencies = sorted(dependencies, key=lambda x: x.name)
...@@ -155,8 +163,12 @@ lists all packages available.""" ...@@ -155,8 +163,12 @@ lists all packages available."""
level = 1 level = 1
color = self.colors[level] color = self.colors[level]
info = f'{tree_bar}── <{color}>{dependency.name}</{color}> ' \ info = '{tree_bar}── <{color}>{name}</{color}> {constraint}'.format(
f'{dependency.pretty_constraint}' tree_bar=tree_bar,
color=color,
name=dependency.name,
constraint=dependency.pretty_constraint
)
self._write_tree_line(info) self._write_tree_line(info)
tree_bar = tree_bar.replace('└', ' ') tree_bar = tree_bar.replace('└', ' ')
...@@ -196,8 +208,13 @@ lists all packages available.""" ...@@ -196,8 +208,13 @@ lists all packages available."""
if dependency.name in current_tree: if dependency.name in current_tree:
circular_warn = '(circular dependency aborted here)' circular_warn = '(circular dependency aborted here)'
info = f'{tree_bar}── <{color}>{dependency.name}</{color}> ' \ info = '{tree_bar}── <{color}>{name}</{color}> {constraint} {warn}'.format(
f'{dependency.pretty_constraint} {circular_warn}' tree_bar=tree_bar,
color=color,
name=dependency.name,
constraint=dependency.pretty_constraint,
warn=circular_warn
)
self._write_tree_line(info) self._write_tree_line(info)
tree_bar = tree_bar.replace('└', ' ') tree_bar = tree_bar.replace('└', ' ')
...@@ -231,7 +248,9 @@ lists all packages available.""" ...@@ -231,7 +248,9 @@ lists all packages available."""
name = package.name name = package.name
selector = VersionSelector(self.poetry.pool) selector = VersionSelector(self.poetry.pool)
return selector.find_best_candidate(name, f'>={package.version}') return selector.find_best_candidate(
name, '>={}'.format(package.version)
)
def get_update_status(self, latest, package): def get_update_status(self, latest, package):
if latest.full_pretty_version == package.full_pretty_version: if latest.full_pretty_version == package.full_pretty_version:
......
...@@ -16,7 +16,9 @@ class VenvCommand(Command): ...@@ -16,7 +16,9 @@ class VenvCommand(Command):
self._venv = Venv.create(o, self.poetry.package.name) self._venv = Venv.create(o, self.poetry.package.name)
if self._venv.is_venv() and o.is_verbose(): if self._venv.is_venv() and o.is_verbose():
o.writeln(f'Using virtualenv: <comment>{self._venv.venv}</>') o.writeln(
'Using virtualenv: <comment>{}</>'.format(self._venv.venv)
)
@property @property
def venv(self): def venv(self):
......
...@@ -125,7 +125,9 @@ class Installer: ...@@ -125,7 +125,9 @@ class Installer:
# Checking extras # Checking extras
for extra in self._extras: for extra in self._extras:
if extra not in self._package.extras: if extra not in self._package.extras:
raise ValueError(f'Extra [{extra}] is not specified.') raise ValueError(
'Extra [{}] is not specified.'.format(extra)
)
self._io.writeln('<info>Updating dependencies</>') self._io.writeln('<info>Updating dependencies</>')
fixed = [] fixed = []
...@@ -181,7 +183,9 @@ class Installer: ...@@ -181,7 +183,9 @@ class Installer:
for extra in self._extras: for extra in self._extras:
if extra not in self._locker.lock_data.get('extras', {}): if extra not in self._locker.lock_data.get('extras', {}):
raise ValueError(f'Extra [{extra}] is not specified.') raise ValueError(
'Extra [{}] is not specified.'.format(extra)
)
# If we are installing from lock # If we are installing from lock
# Filter the operations by comparing it with what is # Filter the operations by comparing it with what is
...@@ -213,26 +217,35 @@ class Installer: ...@@ -213,26 +217,35 @@ class Installer:
if op.job_type == 'install': if op.job_type == 'install':
installs.append( installs.append(
f'{op.package.pretty_name}' '{}:{}'.format(
f':{op.package.full_pretty_version}' op.package.pretty_name,
op.package.full_pretty_version
)
) )
elif op.job_type == 'update': elif op.job_type == 'update':
updates.append( updates.append(
f'{op.target_package.pretty_name}' '{}:{}'.format(
f':{op.target_package.full_pretty_version}' op.target_package.pretty_name,
op.target_package.full_pretty_version
)
) )
elif op.job_type == 'uninstall': elif op.job_type == 'uninstall':
uninstalls.append( uninstalls.append(op.package.pretty_name)
f'{op.package.pretty_name}'
)
self._io.new_line() self._io.new_line()
self._io.writeln( self._io.writeln(
'Package operations: ' 'Package operations: '
f'<info>{len(installs)}</> install{"" if len(installs) == 1 else "s"}, ' '<info>{}</> install{}, '
f'<info>{len(updates)}</> update{"" if len(updates) == 1 else "s"}, ' '<info>{}</> update{}, '
f'<info>{len(uninstalls)}</> removal{"" if len(uninstalls) == 1 else "s"}' '<info>{}</> removal{}'
f'{", <info>{}</> skipped".format(len(skipped)) if skipped and self.is_verbose() else ""}' '{}'.format(
len(installs), '' if len(installs) == 1 else 's',
len(updates), '' if len(updates) == 1 else 's',
len(uninstalls), '' if len(uninstalls) == 1 else 's',
', <info>{}</> skipped'.format(
len(skipped)
) if skipped and self.is_verbose() else ''
)
) )
self._io.new_line() self._io.new_line()
...@@ -256,22 +269,27 @@ class Installer: ...@@ -256,22 +269,27 @@ class Installer:
""" """
method = operation.job_type method = operation.job_type
getattr(self, f'_execute_{method}')(operation) getattr(self, '_execute_{}'.format(method))(operation)
def _execute_install(self, operation: Install) -> None: def _execute_install(self, operation: Install) -> None:
if operation.skipped: if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()): if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.writeln( self._io.writeln(
f' - Skipping <info>{operation.package.pretty_name}</> ' ' - Skipping <info>{}</> (<comment>{}</>) {}'.format(
f'(<comment>{operation.package.full_pretty_version}</>) ' operation.package.pretty_name,
f'{operation.skip_reason}') operation.package.full_pretty_version,
operation.skip_reason
)
)
return return
if self._execute_operations or self.is_dry_run(): if self._execute_operations or self.is_dry_run():
self._io.writeln( self._io.writeln(
f' - Installing <info>{operation.package.pretty_name}</> ' ' - Installing <info>{}</> (<comment>{}</>)'.format(
f'(<comment>{operation.package.full_pretty_version}</>)' operation.package.pretty_name,
operation.package.full_pretty_version
)
) )
if not self._execute_operations: if not self._execute_operations:
...@@ -286,17 +304,23 @@ class Installer: ...@@ -286,17 +304,23 @@ class Installer:
if operation.skipped: if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()): if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.writeln( self._io.writeln(
f' - Skipping <info>{target.pretty_name}</> ' ' - Skipping <info>{}</> (<comment>{}</>) {}'.format(
f'(<comment>{target.full_pretty_version}</>) ' target.pretty_name,
f'{operation.skip_reason}') target.full_pretty_version,
operation.skip_reason
)
)
return return
if self._execute_operations or self.is_dry_run(): if self._execute_operations or self.is_dry_run():
self._io.writeln( self._io.writeln(
f' - Updating <info>{target.pretty_name}</> ' ' - Updating <info>{}</> (<comment>{}</> -> <comment>{}</>)'
f'(<comment>{source.pretty_version}</>' .format(
f' -> <comment>{target.pretty_version}</>)' target.pretty_name,
source.pretty_version,
target.pretty_version
)
) )
if not self._execute_operations: if not self._execute_operations:
...@@ -307,8 +331,10 @@ class Installer: ...@@ -307,8 +331,10 @@ class Installer:
def _execute_uninstall(self, operation: Uninstall) -> None: def _execute_uninstall(self, operation: Uninstall) -> None:
if self._execute_operations or self.is_dry_run(): if self._execute_operations or self.is_dry_run():
self._io.writeln( self._io.writeln(
f' - Removing <info>{operation.package.pretty_name}</> ' ' - Removing <info>{}</> (<comment>{}</>)'.format(
f'(<comment>{operation.package.full_pretty_version}</>)' operation.package.pretty_name,
operation.package.full_pretty_version
)
) )
if not self._execute_operations: if not self._execute_operations:
......
...@@ -59,22 +59,27 @@ class PipInstaller(BaseInstaller): ...@@ -59,22 +59,27 @@ class PipInstaller(BaseInstaller):
def requirement(self, package, formatted=False) -> str: def requirement(self, package, formatted=False) -> str:
if formatted and not package.source_type == 'git': if formatted and not package.source_type == 'git':
req = f'{package.name}=={package.version}' req = '{}=={}'.format(package.name, package.version)
for h in package.hashes: for h in package.hashes:
req += f' --hash sha256:{h}' req += ' --hash sha256:{}'.format(h)
req += '\n' req += '\n'
return req return req
if package.source_type == 'git': if package.source_type == 'git':
return f'git+{package.source_url}@{package.source_reference}' \ return 'git+{}@{}#egg={}'.format(
f'#egg={package.name}' package.source_url,
package.source_reference,
package.name
)
return f'{package.name}=={package.version}' return '{}=={}'.format(package.name, package.version)
def create_temporary_requirement(self, package): def create_temporary_requirement(self, package):
fd, name = tempfile.mkstemp('reqs.txt', f'{package.name}-{package.version}') fd, name = tempfile.mkstemp(
'reqs.txt', '{}-{}'.format(package.name, package.version)
)
with open(fd, 'w') as f: with open(fd, 'w') as f:
f.write(self.requirement(package, formatted=True)) f.write(self.requirement(package, formatted=True))
......
...@@ -18,7 +18,7 @@ class Builder: ...@@ -18,7 +18,7 @@ class Builder:
def build(self, fmt: str): def build(self, fmt: str):
if fmt not in self._FORMATS: if fmt not in self._FORMATS:
raise ValueError(f'Invalid format: {fmt}') raise ValueError('Invalid format: {}'.format(fmt))
builder = self._FORMATS[fmt](self._poetry, self._venv, self._io) builder = self._FORMATS[fmt](self._poetry, self._venv, self._io)
......
...@@ -84,14 +84,14 @@ class Builder: ...@@ -84,14 +84,14 @@ class Builder:
continue continue
self._io.writeln( self._io.writeln(
f' - Adding: <comment>{str(file)}</comment>', ' - Adding: <comment>{}</comment>'.format(str(file)),
verbosity=self._io.VERBOSITY_VERY_VERBOSE verbosity=self._io.VERBOSITY_VERY_VERBOSE
) )
to_add.append(file) to_add.append(file)
# Include project files # Include project files
self._io.writeln( self._io.writeln(
f' - Adding: <comment>pyproject.toml</comment>', ' - Adding: <comment>pyproject.toml</comment>',
verbosity=self._io.VERBOSITY_VERY_VERBOSE verbosity=self._io.VERBOSITY_VERY_VERBOSE
) )
to_add.append(Path('pyproject.toml')) to_add.append(Path('pyproject.toml'))
...@@ -102,7 +102,9 @@ class Builder: ...@@ -102,7 +102,9 @@ class Builder:
readme = self._path / self._poetry.config['readme'] readme = self._path / self._poetry.config['readme']
if readme.exists(): if readme.exists():
self._io.writeln( self._io.writeln(
f' - Adding: <comment>{readme.relative_to(self._path)}</comment>', ' - Adding: <comment>{}</comment>'.format(
readme.relative_to(self._path)
),
verbosity=self._io.VERBOSITY_VERY_VERBOSE verbosity=self._io.VERBOSITY_VERY_VERBOSE
) )
to_add.append(readme.relative_to(self._path)) to_add.append(readme.relative_to(self._path))
...@@ -119,12 +121,15 @@ class Builder: ...@@ -119,12 +121,15 @@ class Builder:
# Scripts -> Entry points # Scripts -> Entry points
for name, ep in self._poetry.config.get('scripts', {}).items(): for name, ep in self._poetry.config.get('scripts', {}).items():
result['console_scripts'].append(f'{name} = {ep}') result['console_scripts'].append('{} = {}'.format(name, ep))
# Plugins -> entry points # Plugins -> entry points
for groupname, group in self._poetry.config.get('plugins', {}).items(): for groupname, group in self._poetry.config.get('plugins', {}).items():
for name, ep in sorted(group.items()): for name, ep in sorted(group.items()):
result[groupname].append(f'{name} = {ep}') result[groupname].append('{} = {}'.format(name, ep))
for groupname in result:
result[groupname] = sorted(result[groupname])
return dict(result) return dict(result)
...@@ -152,7 +157,9 @@ class Builder: ...@@ -152,7 +157,9 @@ class Builder:
for version in sorted(self.AVAILABLE_PYTHONS): for version in sorted(self.AVAILABLE_PYTHONS):
if python_constraint.matches(Constraint('=', version)): if python_constraint.matches(Constraint('=', version)):
classifiers.append(f'Programming Language :: Python :: {version}') classifiers.append(
'Programming Language :: Python :: {}'.format(version)
)
return classifiers return classifiers
......
...@@ -59,14 +59,17 @@ class SdistBuilder(Builder): ...@@ -59,14 +59,17 @@ class SdistBuilder(Builder):
if not target_dir.exists(): if not target_dir.exists():
target_dir.mkdir(parents=True) target_dir.mkdir(parents=True)
target = target_dir / f'{self._package.pretty_name}' \ target = target_dir / '{}-{}.tar.gz'.format(
f'-{self._package.version}.tar.gz' self._package.pretty_name, self._package.version
)
gz = GzipFile(target.as_posix(), mode='wb') gz = GzipFile(target.as_posix(), mode='wb')
tar = tarfile.TarFile(target.as_posix(), mode='w', fileobj=gz, tar = tarfile.TarFile(target.as_posix(), mode='w', fileobj=gz,
format=tarfile.PAX_FORMAT) format=tarfile.PAX_FORMAT)
try: try:
tar_dir = f'{self._package.pretty_name}-{self._package.version}' tar_dir = '{}-{}'.format(
self._package.pretty_name, self._package.version
)
files_to_add = self.find_files_to_add(exclude_build=False) files_to_add = self.find_files_to_add(exclude_build=False)
...@@ -74,7 +77,7 @@ class SdistBuilder(Builder): ...@@ -74,7 +77,7 @@ class SdistBuilder(Builder):
path = self._path / relpath path = self._path / relpath
tar_info = tar.gettarinfo( tar_info = tar.gettarinfo(
str(path), str(path),
arcname=pjoin(tar_dir, relpath) arcname=pjoin(tar_dir, str(relpath))
) )
tar_info = self.clean_tarinfo(tar_info) tar_info = self.clean_tarinfo(tar_info)
...@@ -105,7 +108,7 @@ class SdistBuilder(Builder): ...@@ -105,7 +108,7 @@ class SdistBuilder(Builder):
tar.close() tar.close()
gz.close() gz.close()
self._io.writeln(f' - Built <fg=cyan>{target.name}</>') self._io.writeln(' - Built <fg=cyan>{}</>'.format(target.name))
return target return target
...@@ -115,7 +118,7 @@ class SdistBuilder(Builder): ...@@ -115,7 +118,7 @@ class SdistBuilder(Builder):
# If we have a build script, use it # If we have a build script, use it
if self._package.build: if self._package.build:
after += [ after += [
f'from {self._package.build.split(".")[0]} import *', 'from {} import *'.format(self._package.build.split('.')[0]),
'build(setup_kwargs)' 'build(setup_kwargs)'
] ]
......
...@@ -5,7 +5,11 @@ import re ...@@ -5,7 +5,11 @@ import re
import tempfile import tempfile
import shutil import shutil
import stat import stat
import zipfile
try:
import zipfile36 as zipfile
except ImportError:
import zipfile
from base64 import urlsafe_b64encode from base64 import urlsafe_b64encode
from io import StringIO from io import StringIO
...@@ -87,7 +91,7 @@ class WheelBuilder(Builder): ...@@ -87,7 +91,7 @@ class WheelBuilder(Builder):
finally: finally:
self._wheel_zip.close() self._wheel_zip.close()
self._io.writeln(f' - Built <fg=cyan>{self.wheel_filename}</>') self._io.writeln(' - Built <fg=cyan>{}</>'.format(self.wheel_filename))
def _build(self) -> None: def _build(self) -> None:
if self._package.build: if self._package.build:
...@@ -289,37 +293,37 @@ class WheelBuilder(Builder): ...@@ -289,37 +293,37 @@ class WheelBuilder(Builder):
Write out metadata in the 2.x format (email like) Write out metadata in the 2.x format (email like)
""" """
fp.write('Metadata-Version: 2.1\n') fp.write('Metadata-Version: 2.1\n')
fp.write(f'Name: {self._meta.name}\n') fp.write('Name: {}\n'.format(self._meta.name))
fp.write(f'Version: {self._meta.version}\n') fp.write('Version: {}\n'.format(self._meta.version))
fp.write(f'Summary: {self._meta.summary}\n') fp.write('Summary: {}\n'.format(self._meta.summary))
fp.write(f'Home-page: {self._meta.home_page or "UNKNOWN"}\n') fp.write('Home-page: {}\n'.format(self._meta.home_page or 'UNKNOWN'))
fp.write(f'License: {self._meta.license or "UNKOWN"}\n') fp.write('License: {}\n'.format(self._meta.license or 'UNKOWN'))
# Optional fields # Optional fields
if self._meta.keywords: if self._meta.keywords:
fp.write(f"Keywords: {self._meta.keywords}\n") fp.write("Keywords: {}\n".format(self._meta.keywords))
if self._meta.author: if self._meta.author:
fp.write(f'Author: {self._meta.author}\n') fp.write('Author: {}\n'.format(self._meta.author))
if self._meta.author_email: if self._meta.author_email:
fp.write(f'Author-email: {self._meta.author_email}\n') fp.write('Author-email: {}\n'.format(self._meta.author_email))
if self._meta.requires_python: if self._meta.requires_python:
fp.write(f'Requires-Python: {self._meta.requires_python}\n') fp.write('Requires-Python: {}\n'.format(self._meta.requires_python))
for classifier in self._meta.classifiers: for classifier in self._meta.classifiers:
fp.write(f'Classifier: {classifier}\n') fp.write('Classifier: {}\n'.format(classifier))
for extra in self._meta.provides_extra: for extra in sorted(self._meta.provides_extra):
fp.write(f'Provides-Extra: {extra}\n') fp.write('Provides-Extra: {}\n'.format(extra))
for dep in self._meta.requires_dist: for dep in sorted(self._meta.requires_dist):
fp.write(f'Requires-Dist: {dep}\n') fp.write('Requires-Dist: {}\n'.format(dep))
if self._meta.description_content_type: if self._meta.description_content_type:
fp.write(f'Description-Content-Type: ' fp.write('Description-Content-Type: '
f'{self._meta.description_content_type}\n') '{}\n'.format(self._meta.description_content_type))
if self._meta.description is not None: if self._meta.description is not None:
fp.write('\n' + self._meta.description + '\n') fp.write('\n' + self._meta.description + '\n')
...@@ -44,15 +44,20 @@ class Publisher: ...@@ -44,15 +44,20 @@ class Publisher:
def publish(self, repository_name): def publish(self, repository_name):
if repository_name: if repository_name:
self._io.writeln( self._io.writeln(
f'Publishing <info>{self._package.pretty_name}</info> ' 'Publishing <info>{}</info> (<comment>{}</comment>) '
f'(<comment>{self._package.pretty_version}</comment>) ' 'to <fg=cyan>{}</>'.format(
f'to <fg=cyan>{repository_name}</>' self._package.pretty_name,
self._package.pretty_version,
repository_name
)
) )
else: else:
self._io.writeln( self._io.writeln(
f'Publishing <info>{self._package.pretty_name}</info> ' 'Publishing <info>{}</info> (<comment>{}</comment>) '
f'(<comment>{self._package.pretty_version}</comment>) ' 'to <fg=cyan>PyPI</>'.format(
f'to <fg=cyan>PyPI</>' self._package.pretty_name,
self._package.pretty_version
)
) )
if not repository_name: if not repository_name:
...@@ -76,7 +81,7 @@ class Publisher: ...@@ -76,7 +81,7 @@ class Publisher:
or repository_name not in config['repositories'] or repository_name not in config['repositories']
): ):
raise RuntimeError( raise RuntimeError(
f'Repository {repository_name} is not defined' 'Repository {} is not defined'.format(repository_name)
) )
url = config['repositories'][repository_name]['url'] url = config['repositories'][repository_name]['url']
...@@ -119,7 +124,9 @@ class Publisher: ...@@ -119,7 +124,9 @@ class Publisher:
Register a package to a repository. Register a package to a repository.
""" """
dist = self._poetry.file.parent / 'dist' dist = self._poetry.file.parent / 'dist'
file = dist / f'{self._package.name}-{self._package.version}.tar.gz' file = dist / '{}-{}.tar.gz'.format(
self._package.name, self._package.version
)
if not file.exists(): if not file.exists():
raise RuntimeError( raise RuntimeError(
...@@ -240,12 +247,22 @@ class Publisher: ...@@ -240,12 +247,22 @@ class Publisher:
def _upload(self, session, url): def _upload(self, session, url):
dist = self._poetry.file.parent / 'dist' dist = self._poetry.file.parent / 'dist'
packages = dist.glob(f'{self._package.name}-{self._package.version}*') packages = dist.glob(
'{}-{}*'.format(self._package.name, self._package.version)
)
files = ( files = (
i for i in packages if ( i for i in packages if (
i.match(f'{self._package.name}-{self._package.version}-*.whl') i.match(
'{}-{}-*.whl'.format(
self._package.name, self._package.version
)
)
or or
i.match(f'{self._package.name}-{self._package.version}.tar.gz') i.match(
'{}-{}.tar.gz'.format(
self._package.name, self._package.version
)
)
) )
) )
...@@ -254,15 +271,6 @@ class Publisher: ...@@ -254,15 +271,6 @@ class Publisher:
resp = self._upload_file(session, url, file) resp = self._upload_file(session, url, file)
# Bug 92. If we get a redirect we should abort because something seems
# funky. The behaviour is not well defined and redirects being issued
# by PyPI should never happen in reality. This should catch malicious
# redirects as well.
if resp.is_redirect:
raise RuntimeError(
('"{0}" attempted to redirect to "{1}" during upload.'
' Aborting...').format(url, resp.headers["location"]))
resp.raise_for_status() resp.raise_for_status()
def _upload_file(self, session, url, file): def _upload_file(self, session, url, file):
...@@ -328,7 +336,7 @@ class Publisher: ...@@ -328,7 +336,7 @@ class Publisher:
return 'sdist' return 'sdist'
raise ValueError( raise ValueError(
f'Unknown distribution format {"".join(exts)}' 'Unknown distribution format {}'.format(''.join(exts))
) )
@staticmethod @staticmethod
...@@ -344,5 +352,5 @@ class Publisher: ...@@ -344,5 +352,5 @@ class Publisher:
@staticmethod @staticmethod
def _make_user_agent_string(): def _make_user_agent_string():
return user_agent( return user_agent(
'twine', __version__, 'poetry', __version__,
) )
...@@ -13,4 +13,4 @@ class PossibilitySet: ...@@ -13,4 +13,4 @@ class PossibilitySet:
return '[{}]'.format(', '.join([str(p) for p in self.possibilities])) return '[{}]'.format(', '.join([str(p) for p in self.possibilities]))
def __repr__(self): def __repr__(self):
return f'<PossibilitySet {str(self)}>' return '<PossibilitySet {}>'.format(str(self))
...@@ -77,9 +77,11 @@ class Resolution: ...@@ -77,9 +77,11 @@ class Resolution:
self._indicate_progress() self._indicate_progress()
if hasattr(self.state, 'pop_possibility_state'): if hasattr(self.state, 'pop_possibility_state'):
self._debug( self._debug(
f'Creating possibility state for ' 'Creating possibility state for {} ({} remaining)'
f'{str(self.state.requirement)} ' .format(
f'({len(self.state.possibilities)} remaining)' str(self.state.requirement),
len(self.state.possibilities)
)
) )
s = self.state.pop_possibility_state() s = self.state.pop_possibility_state()
if s: if s:
...@@ -99,9 +101,10 @@ class Resolution: ...@@ -99,9 +101,10 @@ class Resolution:
self._started_at = datetime.now() self._started_at = datetime.now()
self._debug( self._debug(
f'Starting resolution ({self._started_at})\n' 'Starting resolution ({})\nRequested dependencies: {}'.format(
f'Requested dependencies: ' self._started_at,
f'{[str(d) for d in self._original_requested]}' [str(d) for d in self._original_requested]
)
) )
self._ui.before_resolution() self._ui.before_resolution()
...@@ -138,8 +141,10 @@ class Resolution: ...@@ -138,8 +141,10 @@ class Resolution:
self._ui.after_resolution() self._ui.after_resolution()
self._debug( self._debug(
f'Finished resolution ({self._iteration_counter} steps) ' 'Finished resolution ({} steps) '
f'in {elapsed:.3f} seconds' 'in {:.3f} seconds'.format(
self._iteration_counter, elapsed
)
) )
def _process_topmost_state(self) -> None: def _process_topmost_state(self) -> None:
...@@ -734,7 +739,7 @@ class Resolution: ...@@ -734,7 +739,7 @@ class Resolution:
def _attempt_to_activate(self): def _attempt_to_activate(self):
self._debug( self._debug(
f'Attempting to activate {str(self.possibility)}', 'Attempting to activate {}'.format(str(self.possibility)),
self.state.depth, self.state.depth,
) )
existing_vertex = self.activated.vertex_named(self.state.name) existing_vertex = self.activated.vertex_named(self.state.name)
...@@ -778,7 +783,7 @@ class Resolution: ...@@ -778,7 +783,7 @@ class Resolution:
else: else:
self._create_conflict() self._create_conflict()
self._debug( self._debug(
f'Unsatisfied by existing spec ({str(vertex.payload)})', 'Unsatisfied by existing spec ({})'.format(str(vertex.payload)),
self.state.depth self.state.depth
) )
self._unwind_for_conflict() self._unwind_for_conflict()
...@@ -805,7 +810,7 @@ class Resolution: ...@@ -805,7 +810,7 @@ class Resolution:
del self.state.conflicts[self.name] del self.state.conflicts[self.name]
self._debug( self._debug(
f'Activated {self.state.name} at {str(self.possibility)}', 'Activated {} at {}'.format(self.state.name, str(self.possibility)),
self.state.depth self.state.depth
) )
self.activated.set_payload(self.state.name, self.possibility) self.activated.set_payload(self.state.name, self.possibility)
...@@ -816,8 +821,8 @@ class Resolution: ...@@ -816,8 +821,8 @@ class Resolution:
possibility_set.latest_version possibility_set.latest_version
) )
self._debug( self._debug(
f'Requiring nested dependencies ' 'Requiring nested dependencies '
f'({", ".join([str(d) for d in nested_dependencies])})', '({})'.format(', '.join([str(d) for d in nested_dependencies])),
self.state.depth self.state.depth
) )
......
...@@ -42,8 +42,11 @@ class ResolutionState: ...@@ -42,8 +42,11 @@ class ResolutionState:
return cls(None, [], DependencyGraph(), None, None, 0, {}, []) return cls(None, [], DependencyGraph(), None, None, 0, {}, [])
def __repr__(self): def __repr__(self):
return f'<{self.__class__.__name__} {self._name} ' \ return '<{} {} ({})>'.format(
f'({str(self.requirement)})>' self.__class__.__name__,
self._name,
str(self.requirement)
)
class PossibilityState(ResolutionState): class PossibilityState(ResolutionState):
......
import os import os
import re import re
from poetry.semver.version_parser import VersionParser
from poetry.version.markers import Marker
from poetry.version.requirements import Requirement from poetry.version.requirements import Requirement
from .dependency import Dependency from .dependency import Dependency
...@@ -40,8 +38,8 @@ def dependency_from_pep_508(name): ...@@ -40,8 +38,8 @@ def dependency_from_pep_508(name):
if not is_installable_dir(p): if not is_installable_dir(p):
raise ValueError( raise ValueError(
"Directory %r is not installable. File 'setup.py' " "Directory {!r} is not installable. File 'setup.py' "
"not found." % name "not found.".format(name)
) )
link = Link(path_to_url(p)) link = Link(path_to_url(p))
elif is_archive_file(p): elif is_archive_file(p):
...@@ -61,7 +59,7 @@ def dependency_from_pep_508(name): ...@@ -61,7 +59,7 @@ def dependency_from_pep_508(name):
link.filename link.filename
) )
if not m: if not m:
raise ValueError(f'Invalid wheel name: {link.filename}') raise ValueError('Invalid wheel name: {}'.format(link.filename))
name = m.group('name') name = m.group('name')
version = m.group('ver') version = m.group('ver')
...@@ -101,7 +99,7 @@ def dependency_from_pep_508(name): ...@@ -101,7 +99,7 @@ def dependency_from_pep_508(name):
elif op == '!=': elif op == '!=':
version += '.*' version += '.*'
ands.append(f'{op}{version}') ands.append('{}{}'.format(op, version))
ors.append(' '.join(ands)) ors.append(' '.join(ands))
...@@ -115,7 +113,7 @@ def dependency_from_pep_508(name): ...@@ -115,7 +113,7 @@ def dependency_from_pep_508(name):
if op == '==': if op == '==':
op = '' op = ''
ands.append(f'{op}{platform}') ands.append('{}{}'.format(op, platform))
ors.append(' '.join(ands)) ors.append(' '.join(ands))
......
...@@ -30,8 +30,11 @@ class GenericConstraint(BaseConstraint): ...@@ -30,8 +30,11 @@ class GenericConstraint(BaseConstraint):
def __init__(self, operator, version): def __init__(self, operator, version):
if operator not in self._trans_op_str: if operator not in self._trans_op_str:
raise ValueError( raise ValueError(
f'Invalid operator "{operator}" given, ' 'Invalid operator "{}" given, '
f'expected one of: {", ".join(self.supported_operators)}' 'expected one of: {}'
.format(
operator, ', '.join(self.supported_operators)
)
) )
self._operator = self._trans_op_str[operator] self._operator = self._trans_op_str[operator]
......
...@@ -116,7 +116,7 @@ class Dependency: ...@@ -116,7 +116,7 @@ class Dependency:
) )
def to_pep_508(self, with_extras=True) -> str: def to_pep_508(self, with_extras=True) -> str:
requirement = f'{self.pretty_name}' requirement = self.pretty_name
if isinstance(self.constraint, MultiConstraint): if isinstance(self.constraint, MultiConstraint):
requirement += ' ({})'.format(','.join( requirement += ' ({})'.format(','.join(
...@@ -147,9 +147,9 @@ class Dependency: ...@@ -147,9 +147,9 @@ class Dependency:
if markers: if markers:
if len(markers) > 1: if len(markers) > 1:
markers = ['({})'.format(m) for m in markers] markers = ['({})'.format(m) for m in markers]
requirement += f'; {" and ".join(markers)}' requirement += '; {}'.format(' and '.join(markers))
else: else:
requirement += f'; {markers[0]}' requirement += '; {}'.format(markers[0])
return requirement return requirement
...@@ -166,7 +166,7 @@ class Dependency: ...@@ -166,7 +166,7 @@ class Dependency:
glue = ' and ' glue = ' and '
if constraint.is_disjunctive(): if constraint.is_disjunctive():
parts = [ parts = [
f'({part[1]})' if part[0] else f'{part[1]}' '({})'.format(part[1]) if part[0] else part[1]
for part in parts for part in parts
] ]
glue = ' or ' glue = ' or '
...@@ -175,7 +175,9 @@ class Dependency: ...@@ -175,7 +175,9 @@ class Dependency:
marker = glue.join(parts) marker = glue.join(parts)
else: else:
marker = f'{name} {constraint.string_operator} "{constraint.version}"' marker = '{} {} "{}"'.format(
name, constraint.string_operator, constraint.version
)
return marker return marker
...@@ -201,7 +203,9 @@ class Dependency: ...@@ -201,7 +203,9 @@ class Dependency:
return hash((self._name, self._pretty_constraint)) return hash((self._name, self._pretty_constraint))
def __str__(self): def __str__(self):
return f'{self._pretty_name} ({self._pretty_constraint})' return '{} ({})'.format(
self._pretty_name, self._pretty_constraint
)
def __repr__(self): def __repr__(self):
return f'<Dependency {str(self)}>' return '<Dependency {}>'.format(str(self))
...@@ -207,7 +207,7 @@ class Package: ...@@ -207,7 +207,7 @@ class Package:
if python_constraint.matches(constraint): if python_constraint.matches(constraint):
classifiers.append( classifiers.append(
f'Programming Language :: Python :: {version}' 'Programming Language :: Python :: {}'.format(version)
) )
return classifiers return classifiers
......
...@@ -60,7 +60,7 @@ class VCSDependency(Dependency): ...@@ -60,7 +60,7 @@ class VCSDependency(Dependency):
what = 'rev' what = 'rev'
version = self._rev version = self._rev
return f'{what} {version}' return '{} {}'.format(what, version)
def is_vcs(self) -> bool: def is_vcs(self) -> bool:
return True return True
......
...@@ -62,13 +62,13 @@ class Poetry: ...@@ -62,13 +62,13 @@ class Poetry:
if not poetry_file.exists(): if not poetry_file.exists():
raise RuntimeError( raise RuntimeError(
f'Poetry could not find a pyproject.toml file in {cwd}' 'Poetry could not find a pyproject.toml file in {}'.format(cwd)
) )
local_config = TomlFile(poetry_file.as_posix()).read(True) local_config = TomlFile(poetry_file.as_posix()).read(True)
if 'tool' not in local_config or 'poetry' not in local_config['tool']: if 'tool' not in local_config or 'poetry' not in local_config['tool']:
raise RuntimeError( raise RuntimeError(
f'[tool.poetry] section not found in {poetry_file.name}' '[tool.poetry] section not found in {}'.format(poetry_file.name)
) )
local_config = local_config['tool']['poetry'] local_config = local_config['tool']['poetry']
...@@ -145,7 +145,8 @@ class Poetry: ...@@ -145,7 +145,8 @@ class Poetry:
/ 'json' / 'schemas' / 'poetry-schema.json' / 'json' / 'schemas' / 'poetry-schema.json'
) )
schema = json.loads(schema.read_text()) with schema.open() as f:
schema = json.loads(f.read())
try: try:
jsonschema.validate( jsonschema.validate(
...@@ -155,7 +156,10 @@ class Poetry: ...@@ -155,7 +156,10 @@ class Poetry:
except jsonschema.ValidationError as e: except jsonschema.ValidationError as e:
message = e.message message = e.message
if e.path: if e.path:
message = f"[{'.'.join(e.path)}] {message}" message = "[{}] {}".format(
'.'.join(e.path),
message
)
raise InvalidProjectFile(message) raise InvalidProjectFile(message)
......
...@@ -18,6 +18,10 @@ class Update(Operation): ...@@ -18,6 +18,10 @@ class Update(Operation):
return self._target_package return self._target_package
@property @property
def package(self):
return self._target_package
@property
def job_type(self): def job_type(self):
return 'update' return 'update'
......
...@@ -93,9 +93,13 @@ class Provider(SpecificationProvider): ...@@ -93,9 +93,13 @@ class Provider(SpecificationProvider):
and get the information we need by checking out the specified reference. and get the information we need by checking out the specified reference.
""" """
if dependency.vcs != 'git': if dependency.vcs != 'git':
raise ValueError(f'Unsupported VCS dependency {dependency.vcs}') raise ValueError(
'Unsupported VCS dependency {}'.format(dependency.vcs)
)
tmp_dir = Path(mkdtemp(prefix=f'pypoetry-git-{dependency.name}')) tmp_dir = Path(
mkdtemp(prefix='pypoetry-git-{}'.format(dependency.name))
)
try: try:
git = Git() git = Git()
......
...@@ -107,7 +107,15 @@ class Solver: ...@@ -107,7 +107,15 @@ class Solver:
break break
return list(reversed(operations)) requested_names = [r.name for r in requested]
return sorted(
operations,
key=lambda o: (
1 if not o.package.name not in requested_names else 0,
o.package.name
)
)
def _get_tags_for_vertex(self, vertex, requested): def _get_tags_for_vertex(self, vertex, requested):
tags = { tags = {
......
...@@ -38,7 +38,7 @@ class LegacyRepository(PyPiRepository): ...@@ -38,7 +38,7 @@ class LegacyRepository(PyPiRepository):
'stores': { 'stores': {
'releases': { 'releases': {
'driver': 'file', 'driver': 'file',
'path': Path(CACHE_DIR) / 'cache' / 'repositories' / name 'path': str(self._cache_dir)
}, },
'packages': { 'packages': {
'driver': 'dict' 'driver': 'dict'
......
...@@ -53,7 +53,7 @@ class Pool(BaseRepository): ...@@ -53,7 +53,7 @@ class Pool(BaseRepository):
def has_package(self, package): def has_package(self, package):
raise NotImplementedError() raise NotImplementedError()
def package(self, name, version) -> Union['poetry.packages.Package', None]: def package(self, name, version):
package = poetry.packages.Package(name, version, version) package = poetry.packages.Package(name, version, version)
if package in self._packages: if package in self._packages:
return self._packages[self._packages.index(package)] return self._packages[self._packages.index(package)]
...@@ -70,7 +70,7 @@ class Pool(BaseRepository): ...@@ -70,7 +70,7 @@ class Pool(BaseRepository):
def find_packages(self, def find_packages(self,
name, name,
constraint=None, constraint=None,
extras=None) -> List['poetry.packages.Package']: extras=None):
for repository in self._repositories: for repository in self._repositories:
packages = repository.find_packages(name, constraint, extras=extras) packages = repository.find_packages(name, constraint, extras=extras)
if packages: if packages:
......
...@@ -21,13 +21,14 @@ class PyPiRepository(Repository): ...@@ -21,13 +21,14 @@ class PyPiRepository(Repository):
def __init__(self, url='https://pypi.org/', disable_cache=False): def __init__(self, url='https://pypi.org/', disable_cache=False):
self._url = url self._url = url
self._disable_cache = disable_cache self._disable_cache = disable_cache
release_cache_dir = Path(CACHE_DIR) / 'cache' / 'repositories' / 'pypi'
self._cache = CacheManager({ self._cache = CacheManager({
'default': 'releases', 'default': 'releases',
'serializer': 'json', 'serializer': 'json',
'stores': { 'stores': {
'releases': { 'releases': {
'driver': 'file', 'driver': 'file',
'path': Path(CACHE_DIR) / 'cache' / 'repositories' / 'pypi' 'path': str(release_cache_dir)
}, },
'packages': { 'packages': {
'driver': 'dict' 'driver': 'dict'
...@@ -155,14 +156,14 @@ class PyPiRepository(Repository): ...@@ -155,14 +156,14 @@ class PyPiRepository(Repository):
return self._get_package_info(name) return self._get_package_info(name)
return self._cache.store('packages').remember_forever( return self._cache.store('packages').remember_forever(
f'{name}', name,
lambda: self._get_package_info(name) lambda: self._get_package_info(name)
) )
def _get_package_info(self, name: str) -> dict: def _get_package_info(self, name: str) -> dict:
data = self._get(self._url + f'pypi/{name}/json') data = self._get('pypi/{}/json'.format(name))
if data is None: if data is None:
raise ValueError(f'Package [{name}] not found.') raise ValueError('Package [{}] not found.'.format(name))
return data return data
...@@ -177,14 +178,14 @@ class PyPiRepository(Repository): ...@@ -177,14 +178,14 @@ class PyPiRepository(Repository):
return self._get_release_info(name, version) return self._get_release_info(name, version)
return self._cache.remember_forever( return self._cache.remember_forever(
f'{name}:{version}', '{}:{}'.format(name, version),
lambda: self._get_release_info(name, version) lambda: self._get_release_info(name, version)
) )
def _get_release_info(self, name: str, version: str) -> dict: def _get_release_info(self, name: str, version: str) -> dict:
json_data = self._get(self._url + f'pypi/{name}/{version}/json') json_data = self._get('pypi/{}/{}/json'.format(name, version))
if json_data is None: if json_data is None:
raise ValueError(f'Package [{name}] not found.') raise ValueError('Package [{}] not found.'.format(name))
info = json_data['info'] info = json_data['info']
data = { data = {
...@@ -201,8 +202,8 @@ class PyPiRepository(Repository): ...@@ -201,8 +202,8 @@ class PyPiRepository(Repository):
return data return data
def _get(self, url: str) -> Union[dict, None]: def _get(self, endpoint: str) -> Union[dict, None]:
json_response = get(url) json_response = get(self._url + endpoint)
if json_response.status_code == 404: if json_response.status_code == 404:
return None return None
......
...@@ -38,8 +38,11 @@ class Constraint(BaseConstraint): ...@@ -38,8 +38,11 @@ class Constraint(BaseConstraint):
def __init__(self, operator: str, version: str): def __init__(self, operator: str, version: str):
if operator not in self.supported_operators: if operator not in self.supported_operators:
raise ValueError( raise ValueError(
f'Invalid operator "{operator}" given, ' 'Invalid operator "{}" given, '
f'expected one of: {", ".join(self.supported_operators)}' 'expected one of: {}'
.format(
operator, ', '.join(self.supported_operators)
)
) )
self._operator = self._trans_op_str[operator] self._operator = self._trans_op_str[operator]
...@@ -75,8 +78,11 @@ class Constraint(BaseConstraint): ...@@ -75,8 +78,11 @@ class Constraint(BaseConstraint):
def version_compare(self, a: str, b: str, operator: str) -> bool: def version_compare(self, a: str, b: str, operator: str) -> bool:
if operator not in self._trans_op_str: if operator not in self._trans_op_str:
raise ValueError( raise ValueError(
f'Invalid operator "{operator}" given, ' 'Invalid operator "{}" given, '
f'expected one of: {", ".join(self.supported_operators)}' 'expected one of: {}'
.format(
operator, ', '.join(self.supported_operators)
)
) )
# If we can't normalize the version # If we can't normalize the version
......
...@@ -46,14 +46,14 @@ class WilcardConstraint(Constraint): ...@@ -46,14 +46,14 @@ class WilcardConstraint(Constraint):
self._constraint = Constraint('>=', high_version) self._constraint = Constraint('>=', high_version)
else: else:
self._constraint = parser.parse_constraints( self._constraint = parser.parse_constraints(
f'<{low_version} || >={high_version}' '<{} || >={}'.format(low_version, high_version)
) )
else: else:
if low_version == '0.0.0.0': if low_version == '0.0.0.0':
self._constraint = Constraint('<', high_version) self._constraint = Constraint('<', high_version)
else: else:
self._constraint = parser.parse_constraints( self._constraint = parser.parse_constraints(
f'>={low_version},<{high_version}' '>={},<{}'.format(low_version, high_version)
) )
@property @property
......
...@@ -28,10 +28,12 @@ def normalize_version(version): ...@@ -28,10 +28,12 @@ def normalize_version(version):
version version
) )
if m: if m:
version = f'{m.group(1)}' \ version = '{}{}{}{}'.format(
f'{m.group(2) if m.group(2) else ".0"}' \ m.group(1),
f'{m.group(3) if m.group(3) else ".0"}' \ m.group(2) if m.group(2) else '.0',
f'{m.group(4) if m.group(4) else ".0"}' m.group(3) if m.group(3) else '.0',
m.group(4) if m.group(4) else '.0',
)
index = 5 index = 5
else: else:
# Some versions have the form M.m.p-\d+ # Some versions have the form M.m.p-\d+
...@@ -43,10 +45,12 @@ def normalize_version(version): ...@@ -43,10 +45,12 @@ def normalize_version(version):
version version
) )
if m: if m:
version = f'{m.group(1)}' \ version = '{}{}{}{}'.format(
f'{m.group(2) if m.group(2) else ".0"}' \ m.group(1),
f'{m.group(3) if m.group(3) else ".0"}' \ m.group(2) if m.group(2) else '.0',
f'{m.group(4) if m.group(4) else ".0"}' m.group(3) if m.group(3) else '.0',
m.group(4) if m.group(4) else '.0',
)
index = 5 index = 5
else: else:
# Match date(time) based versioning # Match date(time) based versioning
...@@ -69,15 +73,16 @@ def normalize_version(version): ...@@ -69,15 +73,16 @@ def normalize_version(version):
# stable releases # stable releases
return version return version
version = f'{version}' \ version = '{}-{}'.format(version, _expand_stability(m.group(index)))
f'-{_expand_stability(m.group(index))}'
if m.group(index + 1): if m.group(index + 1):
version = f'{version}.{m.group(index + 1).lstrip(".-")}' version = '{}.{}'.format(
version, m.group(index + 1).lstrip('.-')
)
return version return version
raise ValueError(f'Invalid version string "{version}"') raise ValueError('Invalid version string "{}"'.format(version))
def normalize_stability(stability: str) -> str: def normalize_stability(stability: str) -> str:
......
...@@ -19,7 +19,9 @@ class CascadeDict: ...@@ -19,7 +19,9 @@ class CascadeDict:
""" """
Returns another instance with one more dict cascaded at the end. Returns another instance with one more dict cascaded at the end.
""" """
return CascadeDict(*self._internal_dicts, one_more_dict) dicts = self._internal_dicts + one_more_dict
return CascadeDict(*dicts)
def __getitem__(self, item): def __getitem__(self, item):
for d in self._internal_dicts: for d in self._internal_dicts:
......
import sys
PY36 = sys.version_info >= (3, 6)
...@@ -17,10 +17,11 @@ class TomlFile: ...@@ -17,10 +17,11 @@ class TomlFile:
return self._path return self._path
def read(self, raw=False) -> dict: def read(self, raw=False) -> dict:
if raw: with self._path.open() as f:
return toml.loads(self._path.read_text()) if raw:
return toml.loads(f.read())
return loads(self._path.read_text()) return loads(f.read())
def write(self, data) -> None: def write(self, data) -> None:
if not isinstance(data, TOMLFile): if not isinstance(data, TOMLFile):
...@@ -28,7 +29,8 @@ class TomlFile: ...@@ -28,7 +29,8 @@ class TomlFile:
else: else:
data = dumps(data) data = dumps(data)
self._path.write_text(data) with self._path.open('w') as f:
f.write(data)
def __getattr__(self, item): def __getattr__(self, item):
return getattr(self._path, item) return getattr(self._path, item)
...@@ -23,8 +23,9 @@ class VenvError(Exception): ...@@ -23,8 +23,9 @@ class VenvError(Exception):
class VenvCommandError(VenvError): class VenvCommandError(VenvError):
def __init__(self, e: CalledProcessError): def __init__(self, e: CalledProcessError):
message = f'Command {e.cmd} errored with the following output: \n' \ message = 'Command {} errored with the following output: \n{}'.format(
f'{e.output.decode()}' e.cmd, e.output.decode()
)
super().__init__(message) super().__init__(message)
...@@ -64,7 +65,9 @@ class Venv: ...@@ -64,7 +65,9 @@ class Venv:
if not name: if not name:
name = Path.cwd().name name = Path.cwd().name
name = f'{name}-py{".".join([str(v) for v in sys.version_info[:2]])}' name = '{}-py{}'.format(
name, '.'.join([str(v) for v in sys.version_info[:2]])
)
venv = venv_path / name venv = venv_path / name
if not venv.exists(): if not venv.exists():
...@@ -79,13 +82,17 @@ class Venv: ...@@ -79,13 +82,17 @@ class Venv:
return cls() return cls()
io.writeln( io.writeln(
f'Creating virtualenv <info>{name}</> in {str(venv_path)}' 'Creating virtualenv <info>{}</> in {}'.format(
name, str(venv_path)
)
) )
builder = EnvBuilder(with_pip=True) builder = EnvBuilder(with_pip=True)
builder.create(str(venv)) builder.create(str(venv))
else: else:
if io.is_very_verbose(): if io.is_very_verbose():
io.writeln(f'Virtualenv <info>{name}</> already exists.') io.writeln(
'Virtualenv <info>{}</> already exists.'.format(name)
)
os.environ['VIRTUAL_ENV'] = str(venv) os.environ['VIRTUAL_ENV'] = str(venv)
...@@ -177,8 +184,8 @@ class Venv: ...@@ -177,8 +184,8 @@ class Venv:
try: try:
value = self.run( value = self.run(
'python', '-c', 'python', '-c',
f'"import sysconfig; ' '"import sysconfig; '
f'print(sysconfig.get_config_var(\'{var}\'))"', 'print(sysconfig.get_config_var(\'{}\'))"'.format(var),
shell=True shell=True
).strip() ).strip()
except VenvCommandError as e: except VenvCommandError as e:
......
...@@ -66,4 +66,4 @@ class VersionSelector(object): ...@@ -66,4 +66,4 @@ class VersionSelector(object):
else: else:
return pretty_version return pretty_version
return f'^{version}' return '^{}'.format(version)
...@@ -17,16 +17,18 @@ keywords = ["packaging", "dependency", "poetry"] ...@@ -17,16 +17,18 @@ keywords = ["packaging", "dependency", "poetry"]
# Requirements # Requirements
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.4"
cleo = "^0.6" cleo = "^0.6"
requests = "^2.18" requests = "^2.18"
toml = "^0.9" toml = "^0.9"
cachy = "^0.1.0" cachy = "^0.1.1"
pip-tools = "^1.11" pip-tools = "^1.11"
requests-toolbelt = "^0.8.0" requests-toolbelt = "^0.8.0"
jsonschema = "^2.6" jsonschema = "^2.6"
pyrsistent = "^0.14.2" pyrsistent = "^0.14.2"
pyparsing = "^2.2" pyparsing = "^2.2"
zipfile36 = { version = "^0.1", python = ">=3.4 <3.6" }
typing = { version = "^3.6", python = "~3.4" }
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^3.4" pytest = "^3.4"
......
...@@ -120,9 +120,10 @@ def installer(package, pool, locker, venv, installed): ...@@ -120,9 +120,10 @@ def installer(package, pool, locker, venv, installed):
def fixture(name): def fixture(name):
file = Path(__file__).parent / 'fixtures' / f'{name}.test' file = Path(__file__).parent / 'fixtures' / '{}.test'.format(name)
return toml.loads(file.read_text()) with file.open() as f:
return toml.loads(f.read())
def test_run_no_dependencies(installer, locker): def test_run_no_dependencies(installer, locker):
......
...@@ -48,7 +48,7 @@ def test_wheel_c_extension(): ...@@ -48,7 +48,7 @@ def test_wheel_c_extension():
assert whl.exists() assert whl.exists()
zip = zipfile.ZipFile(whl) zip = zipfile.ZipFile(str(whl))
has_compiled_extension = False has_compiled_extension = False
for name in zip.namelist(): for name in zip.namelist():
...@@ -80,7 +80,7 @@ def test_complete(): ...@@ -80,7 +80,7 @@ def test_complete():
assert whl.exists assert whl.exists
zip = zipfile.ZipFile(whl) zip = zipfile.ZipFile(str(whl))
try: try:
entry_points = zip.read('my_package-1.2.3.dist-info/entry_points.txt') entry_points = zip.read('my_package-1.2.3.dist-info/entry_points.txt')
...@@ -93,12 +93,12 @@ my-script=my_package:main ...@@ -93,12 +93,12 @@ my-script=my_package:main
""" """
wheel_data = zip.read('my_package-1.2.3.dist-info/WHEEL').decode() wheel_data = zip.read('my_package-1.2.3.dist-info/WHEEL').decode()
assert wheel_data == f"""\ assert wheel_data == """\
Wheel-Version: 1.0 Wheel-Version: 1.0
Generator: poetry {__version__} Generator: poetry {}
Root-Is-Purelib: true Root-Is-Purelib: true
Tag: py3-none-any Tag: py3-none-any
""" """.format(__version__)
wheel_data = zip.read('my_package-1.2.3.dist-info/METADATA').decode() wheel_data = zip.read('my_package-1.2.3.dist-info/METADATA').decode()
assert wheel_data == """\ assert wheel_data == """\
......
...@@ -99,8 +99,8 @@ def test_make_setup(): ...@@ -99,8 +99,8 @@ def test_make_setup():
] ]
assert ns['entry_points'] == { assert ns['entry_points'] == {
'console_scripts': [ 'console_scripts': [
'my-script = my_package:main',
'my-2nd-script = my_package:main2', 'my-2nd-script = my_package:main2',
'my-script = my_package:main',
] ]
} }
assert ns['extras_require'] == { assert ns['extras_require'] == {
......
...@@ -165,9 +165,9 @@ def test_install_with_deps_in_order(solver, repo): ...@@ -165,9 +165,9 @@ def test_install_with_deps_in_order(solver, repo):
ops = solver.solve(request) ops = solver.solve(request)
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_c},
]) ])
...@@ -239,8 +239,8 @@ def test_solver_sets_categories(solver, repo): ...@@ -239,8 +239,8 @@ def test_solver_sets_categories(solver, repo):
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c}, {'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
]) ])
assert package_c.category == 'dev' assert package_c.category == 'dev'
...@@ -273,8 +273,8 @@ def test_solver_respects_root_package_python_versions(solver, repo, package): ...@@ -273,8 +273,8 @@ def test_solver_respects_root_package_python_versions(solver, repo, package):
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c}, {'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
]) ])
...@@ -326,8 +326,8 @@ def test_solver_solves_optional_and_compatible_packages(solver, repo, package): ...@@ -326,8 +326,8 @@ def test_solver_solves_optional_and_compatible_packages(solver, repo, package):
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c}, {'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
]) ])
...@@ -356,8 +356,8 @@ def test_solver_solves_while_respecting_root_platforms(solver, repo, package): ...@@ -356,8 +356,8 @@ def test_solver_solves_while_respecting_root_platforms(solver, repo, package):
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c10}, {'job': 'install', 'package': package_c10},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
]) ])
...@@ -384,8 +384,8 @@ def test_solver_does_not_return_extras_if_not_requested(solver, repo): ...@@ -384,8 +384,8 @@ def test_solver_does_not_return_extras_if_not_requested(solver, repo):
ops = solver.solve(request) ops = solver.solve(request)
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
]) ])
...@@ -414,8 +414,8 @@ def test_solver_returns_extras_if_requested(solver, repo): ...@@ -414,8 +414,8 @@ def test_solver_returns_extras_if_requested(solver, repo):
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c}, {'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
]) ])
...@@ -442,9 +442,9 @@ def test_solver_returns_prereleases_if_requested(solver, repo): ...@@ -442,9 +442,9 @@ def test_solver_returns_prereleases_if_requested(solver, repo):
ops = solver.solve(request) ops = solver.solve(request)
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c_dev},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_c_dev},
]) ])
...@@ -471,7 +471,7 @@ def test_solver_does_not_return_prereleases_if_not_requested(solver, repo): ...@@ -471,7 +471,7 @@ def test_solver_does_not_return_prereleases_if_not_requested(solver, repo):
ops = solver.solve(request) ops = solver.solve(request)
check_solver_result(ops, [ check_solver_result(ops, [
{'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_a}, {'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_c},
]) ])
...@@ -26,12 +26,15 @@ def test_poetry(): ...@@ -26,12 +26,15 @@ def test_poetry():
assert package.python_versions == '~2.7 || ^3.6' assert package.python_versions == '~2.7 || ^3.6'
assert str(package.python_constraint) == '>= 2.7.0.0, < 2.8.0.0 || >= 3.6.0.0, < 4.0.0.0' assert str(package.python_constraint) == '>= 2.7.0.0, < 2.8.0.0 || >= 3.6.0.0, < 4.0.0.0'
dependencies = package.requires dependencies = {}
cleo = dependencies[0] for dep in package.requires:
dependencies[dep.name] = dep
cleo = dependencies['cleo']
assert cleo.pretty_constraint == '^0.6' assert cleo.pretty_constraint == '^0.6'
assert not cleo.is_optional() assert not cleo.is_optional()
pendulum = dependencies[1] pendulum = dependencies['pendulum']
assert pendulum.pretty_constraint == 'branch 2.0' assert pendulum.pretty_constraint == 'branch 2.0'
assert pendulum.is_vcs() assert pendulum.is_vcs()
assert pendulum.vcs == 'git' assert pendulum.vcs == 'git'
...@@ -39,14 +42,14 @@ def test_poetry(): ...@@ -39,14 +42,14 @@ def test_poetry():
assert pendulum.source == 'https://github.com/sdispater/pendulum.git' assert pendulum.source == 'https://github.com/sdispater/pendulum.git'
assert pendulum.allows_prereleases() assert pendulum.allows_prereleases()
requests = dependencies[2] requests = dependencies['requests']
assert requests.pretty_constraint == '^2.18' assert requests.pretty_constraint == '^2.18'
assert not requests.is_vcs() assert not requests.is_vcs()
assert not requests.allows_prereleases() assert not requests.allows_prereleases()
assert requests.is_optional() assert requests.is_optional()
assert requests.extras == ['security'] assert requests.extras == ['security']
pathlib2 = dependencies[3] pathlib2 = dependencies['pathlib2']
assert pathlib2.pretty_constraint == '^2.2' assert pathlib2.pretty_constraint == '^2.2'
assert pathlib2.python_versions == '~2.7' assert pathlib2.python_versions == '~2.7'
assert not pathlib2.is_optional() assert not pathlib2.is_optional()
...@@ -56,6 +59,7 @@ def test_poetry(): ...@@ -56,6 +59,7 @@ def test_poetry():
def test_check(): def test_check():
complete = fixtures_dir / 'complete.toml' complete = fixtures_dir / 'complete.toml'
content = toml.loads(complete.read_text())['tool']['poetry'] with complete.open() as f:
content = toml.loads(f.read())['tool']['poetry']
assert Poetry.check(content) assert Poetry.check(content)
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