Commit 2c53da3f by Sébastien Eustace

Improve locking and installation

parent b6c1c279
...@@ -47,15 +47,6 @@ class Installer: ...@@ -47,15 +47,6 @@ class Installer:
local_repo = Repository() local_repo = Repository()
self._do_install(local_repo) self._do_install(local_repo)
if self._update and self._write_lock:
updated_lock = self._locker.set_lock_data(
self._package,
local_repo.packages
)
if updated_lock:
self._io.writeln('<info>Writing lock file</>')
return 0 return 0
def dry_run(self, dry_run=True) -> 'Installer': def dry_run(self, dry_run=True) -> 'Installer':
...@@ -99,7 +90,7 @@ class Installer: ...@@ -99,7 +90,7 @@ class Installer:
locked_repository = Repository() locked_repository = Repository()
# initialize locked repo if we are installing from lock # initialize locked repo if we are installing from lock
if not self._update or self._locker.is_locked(): if not self._update or self._locker.is_locked():
locked_repository = self._locker.locked_repository(self._dev_mode) locked_repository = self._locker.locked_repository(True)
if self._update: if self._update:
self._io.writeln('<info>Updating dependencies</>') self._io.writeln('<info>Updating dependencies</>')
...@@ -121,15 +112,9 @@ class Installer: ...@@ -121,15 +112,9 @@ class Installer:
self._io.new_line() self._io.new_line()
# Execute operations # Execute operations
if not ops: if not ops and self._execute_operations:
self._io.writeln('Nothing to install or update') self._io.writeln('Nothing to install or update')
# extract dev packages and mark them to be skipped
# if it's a --no-dev install or update
# we also force them to be uninstalled
# if they are present in the local repo
# TODO
if ops and (self._execute_operations or self._dry_run): if ops and (self._execute_operations or self._dry_run):
installs = [] installs = []
updates = [] updates = []
...@@ -166,6 +151,30 @@ class Installer: ...@@ -166,6 +151,30 @@ class Installer:
elif op.job_type == 'update': elif op.job_type == 'update':
local_repo.add_package(op.target_package) local_repo.add_package(op.target_package)
# Adding untouched locked package
# to local_repo
if self._update:
for locked in locked_repository.packages:
untouched = True
for local_pkg in local_repo.packages:
if locked.name == local_pkg.name:
untouched = False
if untouched:
local_repo.add_package(locked)
# Writing lock before installing
if self._update and self._write_lock:
updated_lock = self._locker.set_lock_data(
self._package,
local_repo.packages
)
if updated_lock:
self._io.writeln('<info>Writing lock file</>')
self._io.writeln('')
for op in ops:
self._execute(op) self._execute(op)
def _execute(self, operation: Operation) -> None: def _execute(self, operation: Operation) -> None:
...@@ -177,10 +186,11 @@ class Installer: ...@@ -177,10 +186,11 @@ class Installer:
getattr(self, f'_execute_{method}')(operation) getattr(self, f'_execute_{method}')(operation)
def _execute_install(self, operation: Install) -> None: def _execute_install(self, operation: Install) -> None:
self._io.writeln( if self._execute_operations or self.is_dry_run():
f' - Installing <info>{operation.package.name}</> ' self._io.writeln(
f'(<comment>{operation.package.full_pretty_version}</>)' f' - Installing <info>{operation.package.name}</> '
) f'(<comment>{operation.package.full_pretty_version}</>)'
)
if not self._execute_operations: if not self._execute_operations:
return return
...@@ -191,11 +201,12 @@ class Installer: ...@@ -191,11 +201,12 @@ class Installer:
source = operation.target_package source = operation.target_package
target = operation.target_package target = operation.target_package
self._io.writeln( if self._execute_operations or self.is_dry_run():
f' - Updating <info>{target.name}</> ' self._io.writeln(
f'(<comment>{source.pretty_version}</>' f' - Updating <info>{target.name}</> '
f' -> <comment>{target.pretty_version}</>)' f'(<comment>{source.pretty_version}</>'
) f' -> <comment>{target.pretty_version}</>)'
)
if not self._execute_operations: if not self._execute_operations:
return return
...@@ -203,10 +214,11 @@ class Installer: ...@@ -203,10 +214,11 @@ class Installer:
self._installer.update(source, target) self._installer.update(source, target)
def _execute_uninstall(self, operation: Uninstall) -> None: def _execute_uninstall(self, operation: Uninstall) -> None:
self._io.writeln( if self._execute_operations or self.is_dry_run():
f' - Removing <info>{operation.package.name}</> ' self._io.writeln(
f'(<comment>{operation.package.full_pretty_version}</>)' f' - Removing <info>{operation.package.name}</> '
) f'(<comment>{operation.package.full_pretty_version}</>)'
)
if not self._execute_operations: if not self._execute_operations:
return return
...@@ -224,7 +236,9 @@ class Installer: ...@@ -224,7 +236,9 @@ class Installer:
for installed in installed_repo.packages: for installed in installed_repo.packages:
if locked.name == installed.name: if locked.name == installed.name:
is_installed = True is_installed = True
if locked.version != installed.version: if locked.category == 'dev' and not self.is_dev_mode():
ops.append(Uninstall(locked))
elif locked.version != installed.version:
ops.append(Update( ops.append(Update(
installed, locked installed, locked
)) ))
......
...@@ -14,7 +14,7 @@ class PipInstaller: ...@@ -14,7 +14,7 @@ class PipInstaller:
self.run('install', self.requirement(target), '--no-deps', '-U') self.run('install', self.requirement(target), '--no-deps', '-U')
def remove(self, package): def remove(self, package):
self.run('uninstall', package.name) self.run('uninstall', package.name, '-y')
def run(self, *args) -> str: def run(self, *args) -> str:
return self._venv.run('pip', *args) return self._venv.run('pip', *args)
......
...@@ -3,7 +3,7 @@ from poetry.semver.version_parser import VersionParser ...@@ -3,7 +3,7 @@ from poetry.semver.version_parser import VersionParser
class Dependency: class Dependency:
def __init__(self, name, constraint, optional=False): def __init__(self, name, constraint, optional=False, category='main'):
self._name = name.lower() self._name = name.lower()
try: try:
self._constraint = VersionParser().parse_constraints(constraint) self._constraint = VersionParser().parse_constraints(constraint)
...@@ -12,6 +12,7 @@ class Dependency: ...@@ -12,6 +12,7 @@ class Dependency:
self._pretty_constraint = constraint self._pretty_constraint = constraint
self._optional = optional self._optional = optional
self._category = category
@property @property
def name(self): def name(self):
...@@ -29,6 +30,10 @@ class Dependency: ...@@ -29,6 +30,10 @@ class Dependency:
def pretty_name(self): def pretty_name(self):
return '{} ({})'.format(self._name, self._pretty_constraint) return '{} ({})'.format(self._name, self._pretty_constraint)
@property
def category(self):
return self._category
def accepts_prereleases(self): def accepts_prereleases(self):
return False return False
......
...@@ -74,34 +74,35 @@ class Locker: ...@@ -74,34 +74,35 @@ class Locker:
return packages return packages
for info in locked_packages: for info in locked_packages:
packages.add_package( package = poetry.packages.Package(
poetry.packages.Package( info['name'],
info['name'], info['version'], info['version'] info['version'],
) info['version']
) )
package.category = info['category']
package.optional = info['optional']
package.hashes = info['checksum']
package.python_versions = info['python-versions']
packages.add_package(package)
return packages return packages
def set_lock_data(self, def set_lock_data(self,
root, packages, root, packages) -> bool:
python_versions=None, platform=None) -> bool:
lock = { lock = {
'root': { 'root': {
'name': root.name, 'name': root.name,
'version': root.version, 'version': root.version,
'python_versions': python_versions or '*' 'python_versions': root.python_versions,
'platform': root.platform
}, },
'packages': None, 'packages': self._lock_packages(packages),
'metadata': { 'metadata': {
'content-hash': self._content_hash 'content-hash': self._content_hash
} }
} }
if platform is not None:
lock['root']['platform'] = platform
lock['packages'] = self._lock_packages(packages)
if not self.is_locked() or lock != self.lock_data: if not self.is_locked() or lock != self.lock_data:
self._lock.write(lock) self._lock.write(lock)
self._lock_data = None self._lock_data = None
...@@ -156,6 +157,7 @@ class Locker: ...@@ -156,6 +157,7 @@ class Locker:
'category': package.category, 'category': package.category,
'optional': package.optional, 'optional': package.optional,
'python-versions': package.python_versions, 'python-versions': package.python_versions,
'platform': package.platform,
'checksum': package.hashes 'checksum': package.hashes
} }
......
...@@ -63,7 +63,7 @@ class Package: ...@@ -63,7 +63,7 @@ class Package:
self.hashes = [] self.hashes = []
self.optional = False self.optional = False
self.python_versions = '*' self.python_versions = '*'
self.platform = None self.platform = '*'
@property @property
def name(self): def name(self):
...@@ -111,7 +111,7 @@ class Package: ...@@ -111,7 +111,7 @@ class Package:
def is_prerelease(self): def is_prerelease(self):
return self._stability != 'stable' return self._stability != 'stable'
def add_dependency(self, name, constraint=None, dev=False): def add_dependency(self, name, constraint=None, category='main'):
if constraint is None: if constraint is None:
constraint = '*' constraint = '*'
...@@ -122,11 +122,11 @@ class Package: ...@@ -122,11 +122,11 @@ class Package:
else: else:
version = constraint['version'] version = constraint['version']
optional = constraint.get('optional', False) optional = constraint.get('optional', False)
dependency = Dependency(name, version, optional=optional) dependency = Dependency(name, version, optional=optional, category=category)
else: else:
dependency = Dependency(name, constraint) dependency = Dependency(name, constraint, category=category)
if dev: if category == 'dev':
self.dev_requires.append(dependency) self.dev_requires.append(dependency)
else: else:
self.requires.append(dependency) self.requires.append(dependency)
......
...@@ -61,7 +61,7 @@ class Poetry: ...@@ -61,7 +61,7 @@ class Poetry:
if 'dev-dependencies' in local_config: if 'dev-dependencies' in local_config:
for name, constraint in local_config['dev-dependencies'].items(): for name, constraint in local_config['dev-dependencies'].items():
package.add_dependency(name, constraint, dev=True) package.add_dependency(name, constraint, category='dev')
locker = Locker(poetry_file.with_suffix('.lock'), poetry_file) locker = Locker(poetry_file.with_suffix('.lock'), poetry_file)
......
...@@ -29,6 +29,14 @@ class Solver: ...@@ -29,6 +29,14 @@ class Solver:
packages = [v.payload for v in graph.vertices.values()] packages = [v.payload for v in graph.vertices.values()]
# Setting categories
for vertex in graph.vertices.values():
tags = self._get_categories_for_vertex(vertex, requested)
if 'main' in tags:
vertex.payload.category = 'main'
else:
vertex.payload.category = 'dev'
operations = [] operations = []
for package in packages: for package in packages:
installed = False installed = False
...@@ -56,3 +64,16 @@ class Solver: ...@@ -56,3 +64,16 @@ class Solver:
operations.append(Uninstall(pkg)) operations.append(Uninstall(pkg))
return list(reversed(operations)) return list(reversed(operations))
def _get_categories_for_vertex(self, vertex, requested):
tags = []
if not vertex.incoming_edges:
# Original dependency
for req in requested:
if req.name == vertex.name:
tags.append(req.category)
else:
for edge in vertex.incoming_edges:
tags += self._get_categories_for_vertex(edge.origin, requested)
return tags
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