Commit 31b9dbf9 by Sébastien Eustace

Change poetry.lock format

parent 85950d71
...@@ -7,6 +7,14 @@ ...@@ -7,6 +7,14 @@
- Added `remove` command. - Added `remove` command.
- Added basic support for VCS (git) dependencies. - Added basic support for VCS (git) dependencies.
### Changed
- Changed `poetry.lock` format.
### Fixed
- Fixed dependencies solving that would lead to dependencies not being written to lock.
## [0.1.0] - 2018-02-28 ## [0.1.0] - 2018-02-28
......
class BaseInstaller:
def install(self, package):
raise NotImplementedError
def update(self, source, target):
raise NotImplementedError
def remove(self, package):
raise NotImplementedError
...@@ -11,6 +11,7 @@ from poetry.puzzle.operations.operation import Operation ...@@ -11,6 +11,7 @@ from poetry.puzzle.operations.operation import Operation
from poetry.repositories import Repository from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.installed_repository import InstalledRepository
from .base_installer import BaseInstaller
from .pip_installer import PipInstaller from .pip_installer import PipInstaller
...@@ -35,7 +36,7 @@ class Installer: ...@@ -35,7 +36,7 @@ class Installer:
self._whitelist = {} self._whitelist = {}
self._installer = PipInstaller(self._io.venv, self._io) self._installer = self._get_installer()
def run(self): def run(self):
# Force update if there is no lock file present # Force update if there is no lock file present
...@@ -289,3 +290,6 @@ class Installer: ...@@ -289,3 +290,6 @@ class Installer:
ops.append(Install(locked)) ops.append(Install(locked))
return ops return ops
def _get_installer(self) -> BaseInstaller:
return PipInstaller(self._io.venv, self._io)
from .base_installer import BaseInstaller
class NoopInstaller(BaseInstaller):
def install(self, package) -> None:
pass
def update(self, source, target) -> None:
pass
def remove(self, package) -> None:
pass
from poetry.utils.venv import Venv from poetry.utils.venv import Venv
from .base_installer import BaseInstaller
class PipInstaller:
class PipInstaller(BaseInstaller):
def __init__(self, venv: Venv, io): def __init__(self, venv: Venv, io):
self._venv = venv self._venv = venv
......
from .null_io import NullIO
from poetry.utils.venv import Venv
class NullVenv(Venv):
def __init__(self):
super().__init__()
self.executed = []
def run(self, bin: str, *args):
self.executed.append([bin] + list(args))
def _bin(self, bin):
return bin
class NullIO:
def __init__(self):
self._venv = NullVenv()
@property
def venv(self) -> NullVenv:
return self._venv
def is_quiet(self) -> bool:
return False
def is_verbose(self) -> bool:
return False
def is_very_verbose(self) -> bool:
return False
def is_debug(self) -> bool:
return False
def writeln(self, *args, **kwargs):
pass
def write(self, *args, **kwargs):
pass
def new_line(self, *args, **kwargs):
pass
...@@ -48,7 +48,7 @@ class Locker: ...@@ -48,7 +48,7 @@ class Locker:
if not self._lock.exists(): if not self._lock.exists():
return False return False
return 'packages' in self.lock_data return 'package' in self.lock_data
def is_fresh(self) -> bool: def is_fresh(self) -> bool:
""" """
...@@ -72,10 +72,10 @@ class Locker: ...@@ -72,10 +72,10 @@ class Locker:
packages = Repository() packages = Repository()
if with_dev_reqs: if with_dev_reqs:
locked_packages = lock_data['packages'] locked_packages = lock_data['package']
else: else:
locked_packages = [ locked_packages = [
p for p in lock_data['packages'] if p['category'] == 'main' p for p in lock_data['package'] if p['category'] == 'main'
] ]
if not locked_packages: if not locked_packages:
...@@ -89,7 +89,7 @@ class Locker: ...@@ -89,7 +89,7 @@ class Locker:
) )
package.category = info['category'] package.category = info['category']
package.optional = info['optional'] package.optional = info['optional']
package.hashes = info['checksum'] package.hashes = lock_data['metadata']['hashes'][info['name']]
package.python_versions = info['python-versions'] package.python_versions = info['python-versions']
if 'source' in info: if 'source' in info:
...@@ -103,27 +103,34 @@ class Locker: ...@@ -103,27 +103,34 @@ class Locker:
def set_lock_data(self, def set_lock_data(self,
root, packages) -> bool: root, packages) -> bool:
hashes = {}
packages = self._lock_packages(packages)
# Retrieving hashes
for package in packages:
hashes[package['name']] = package['hashes']
del package['hashes']
lock = { lock = {
'root': { 'package': packages,
'name': root.name,
'version': root.version,
'python_versions': root.python_versions,
'platform': root.platform
},
'packages': self._lock_packages(packages),
'metadata': { 'metadata': {
'content-hash': self._content_hash 'python-versions': root.python_versions,
'platform': root.platform,
'content-hash': self._content_hash,
'hashes': hashes,
} }
} }
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._write_lock_data(lock)
self._lock_data = None
return True return True
return False return False
def _write_lock_data(self, data):
self._lock.write(data)
self._lock_data = None
def _get_content_hash(self) -> str: def _get_content_hash(self) -> str:
""" """
Returns the sha256 hash of the sorted content of the composer file. Returns the sha256 hash of the sorted content of the composer file.
...@@ -153,7 +160,8 @@ class Locker: ...@@ -153,7 +160,8 @@ class Locker:
return self._lock.read() return self._lock.read()
def _lock_packages(self, packages: List['poetry.packages.Package']) -> list: def _lock_packages(self,
packages: List['poetry.packages.Package']) -> list:
locked = [] locked = []
for package in sorted(packages, key=lambda x: x.name): for package in sorted(packages, key=lambda x: x.name):
...@@ -171,7 +179,7 @@ class Locker: ...@@ -171,7 +179,7 @@ class Locker:
'optional': package.optional, 'optional': package.optional,
'python-versions': package.python_versions, 'python-versions': package.python_versions,
'platform': package.platform, 'platform': package.platform,
'checksum': package.hashes, 'hashes': package.hashes,
} }
if package.source_type: if package.source_type:
......
...@@ -36,6 +36,10 @@ class Poetry: ...@@ -36,6 +36,10 @@ class Poetry:
def repository(self) -> PyPiRepository: def repository(self) -> PyPiRepository:
return self._repository return self._repository
@property
def installer(self):
return self._instal
@classmethod @classmethod
def create(cls, cwd) -> 'Poetry': def create(cls, cwd) -> 'Poetry':
poetry_file = Path(cwd) / 'poetry.toml' poetry_file = Path(cwd) / 'poetry.toml'
......
package = []
[metadata]
python-versions = "*"
platform = "*"
content-hash = "123456789"
[metadata.hashes]
[[package]]
name = "A"
version = "1.0"
category = "main"
optional = false
python-versions = "*"
platform = "*"
[metadata]
python-versions = "*"
platform = "*"
content-hash = "123456789"
[metadata.hashes]
"A" = []
[[package]]
name = "A"
version = "1.0"
category = "main"
optional = false
python-versions = "*"
platform = "*"
[[package]]
name = "B"
version = "1.1"
category = "main"
optional = false
python-versions = "*"
platform = "*"
[metadata]
python-versions = "*"
platform = "*"
content-hash = "123456789"
[metadata.hashes]
"A" = []
"B" = []
import pytest
import toml
from pathlib import Path
from poetry.installation import Installer as BaseInstaller
from poetry.installation.noop_installer import NoopInstaller
from poetry.io import NullIO
from poetry.packages import Locker as BaseLocker
from poetry.repositories import Repository
from tests.helpers import get_package
class Installer(BaseInstaller):
def _get_installer(self):
return NoopInstaller()
class Locker(BaseLocker):
def __init__(self):
self._written_data = None
self._locked = False
self._content_hash = self._get_content_hash()
@property
def written_data(self):
return self._written_data
def locked(self, is_locked=True) -> 'Locker':
self._locked = is_locked
return self
def mock_lock_data(self, data) -> None:
self._lock_data = data
def is_locked(self) -> bool:
return self._locked
def _get_content_hash(self) -> str:
return '123456789'
def _write_lock_data(self, data) -> None:
self._written_data = data
@pytest.fixture()
def package():
return get_package('root', '1.0')
@pytest.fixture()
def repo():
return Repository()
@pytest.fixture()
def locker():
return Locker()
def fixture(name):
file = Path(__file__).parent / 'fixtures' / f'{name}.test'
return toml.loads(file.read_text())
@pytest.fixture()
def installer(package, repo, locker):
return Installer(NullIO(), package, locker, repo)
def test_run_no_dependencies(installer, locker):
installer.run()
expected = fixture('no-dependencies')
assert locker.written_data == expected
def test_run_with_dependencies(installer, locker, repo, package):
package_a = get_package('A', '1.0')
package_b = get_package('B', '1.1')
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency('A', '~1.0')
package.add_dependency('B', '^1.0')
installer.run()
expected = fixture('with-dependencies')
assert locker.written_data == expected
def test_run_whitelist_add(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data({
'package': [{
'name': 'A',
'version': '1.0',
'category': 'main',
'optional': False,
'platform': '*',
'python-versions': '*',
'checksum': []
}],
'metadata': {
'python-versions': '*',
'platform': '*',
'content-hash': '123456789',
'hashes': {
'A': []
}
}
})
package_a = get_package('A', '1.0')
package_a_new = get_package('A', '1.1')
package_b = get_package('B', '1.1')
repo.add_package(package_a)
repo.add_package(package_a_new)
repo.add_package(package_b)
package.add_dependency('A', '~1.0')
package.add_dependency('B', '^1.0')
installer.update(True)
installer.whitelist({'B': '^1.1'})
installer.run()
expected = fixture('with-dependencies')
assert locker.written_data == expected
def test_run_whitelist_remove(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data({
'package': [{
'name': 'A',
'version': '1.0',
'category': 'main',
'optional': False,
'platform': '*',
'python-versions': '*',
'checksum': []
}, {
'name': 'B',
'version': '1.1',
'category': 'main',
'optional': False,
'platform': '*',
'python-versions': '*',
'checksum': []
}],
'metadata': {
'python-versions': '*',
'platform': '*',
'content-hash': '123456789',
'hashes': {
'A': [],
'B': []
}
}
})
package_a = get_package('A', '1.0')
package_b = get_package('B', '1.1')
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency('A', '~1.0')
installer.update(True)
installer.whitelist({'B': '^1.1'})
installer.run()
expected = fixture('remove')
assert locker.written_data == expected
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