Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Commit

Permalink
Merge pull request #27 from dephell/local-repo
Browse files Browse the repository at this point in the history
Local repo
  • Loading branch information
orsinium committed Apr 17, 2019
2 parents 347976e + 4353081 commit d4286c3
Show file tree
Hide file tree
Showing 35 changed files with 594 additions and 75 deletions.
Binary file added assets/ehtim-graph.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
3 changes: 3 additions & 0 deletions dephell/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ._downloads import get_downloads_by_category, get_total_downloads
from ._editorconfig import make_editorconfig
from ._entrypoints import get_entrypoints
from ._git import git_tag, git_commit
from ._json import make_json
from ._package import get_package, get_packages, get_resolver
from ._python import get_python, get_python_env
Expand Down Expand Up @@ -35,6 +36,8 @@
'get_venv',
'get_version_from_file',
'get_version_from_project',
'git_tag',
'git_commit',
'make_bash_autocomplete',
'make_editorconfig',
'make_json',
Expand Down
37 changes: 37 additions & 0 deletions dephell/actions/_git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import subprocess
from logging import getLogger
from pathlib import Path
from typing import Iterable


logger = getLogger('dephell.actions.git')


def _run(command, project: Path) -> bool:
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=str(project),
)
if result.returncode != 0:
stderr = result.stderr.decode().strip()
if stderr:
logger.debug(stderr)
stdout = result.stdout.decode().strip()
if stdout:
logger.debug(stdout)
return False
return True


def git_commit(message: str, paths: Iterable[Path], project: Path) -> bool:
for path in paths:
ok = _run(['git', 'add', '--update', str(path)], project=project)
if not ok:
return False
return _run(['git', 'commit', '-m', message], project=project)


def git_tag(name: str, project: Path) -> bool:
return _run(['git', 'tag', name], project=project)
6 changes: 6 additions & 0 deletions dephell/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
from .autocomplete import AutocompleteCommand
from .deps_add import DepsAddCommand
from .deps_audit import DepsAuditCommand
from .deps_check import DepsCheckCommand
from .deps_convert import DepsConvertCommand
from .deps_install import DepsInstallCommand
from .deps_licenses import DepsLicensesCommand
from .deps_outdated import DepsOutdatedCommand
from .deps_sync import DepsSyncCommand
from .deps_tree import DepsTreeCommand
from .generate_authors import GenerateAuthorsCommand
from .generate_config import GenerateConfigCommand
Expand Down Expand Up @@ -38,10 +40,12 @@
'commands',
'DepsAddCommand',
'DepsAuditCommand',
'DepsCheckCommand',
'DepsConvertCommand',
'DepsInstallCommand',
'DepsLicensesCommand',
'DepsOutdatedCommand',
'DepsSyncCommand',
'DepsTreeCommand',
'GenerateAuthorsCommand',
'GenerateConfigCommand',
Expand Down Expand Up @@ -74,10 +78,12 @@
'autocomplete': AutocompleteCommand,
'deps add': DepsAddCommand,
'deps audit': DepsAuditCommand,
'deps check': DepsCheckCommand,
'deps convert': DepsConvertCommand,
'deps install': DepsInstallCommand,
'deps licenses': DepsLicensesCommand,
'deps outdated': DepsOutdatedCommand,
'deps sync': DepsSyncCommand,
'deps tree': DepsTreeCommand,
# 'deps remove': ...,
# 'deps sync': ...,
Expand Down
102 changes: 102 additions & 0 deletions dephell/commands/deps_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# built-in
from argparse import ArgumentParser

# app
from ..actions import attach_deps, get_python_env, make_json
from ..config import builders
from ..controllers import analize_conflict
from ..converters import CONVERTERS, InstalledConverter
from ..models import Requirement
from .base import BaseCommand


class DepsCheckCommand(BaseCommand):
"""Show difference between venv and project dependencies.
https://dephell.readthedocs.io/en/latest/cmd-deps-check.html
"""
@classmethod
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
prog='dephell deps check',
description=cls.__doc__,
)
builders.build_config(parser)
builders.build_to(parser)
builders.build_resolver(parser)
builders.build_api(parser)
builders.build_venv(parser)
builders.build_output(parser)
builders.build_other(parser)
return parser

def __call__(self) -> bool:
loader_config = self.config.get('to') or self.config['from']
self.logger.info('get dependencies', extra=dict(
format=loader_config['format'],
path=loader_config['path'],
))
loader = CONVERTERS[loader_config['format']]
resolver = loader.load_resolver(path=loader_config['path'])
attach_deps(resolver=resolver, config=self.config, merge=False)

# resolve
self.logger.info('build dependencies graph...')
resolved = resolver.resolve(silent=self.config['silent'])
if not resolved:
conflict = analize_conflict(resolver=resolver)
self.logger.warning('conflict was found')
print(conflict)
return False

# get executable
python = get_python_env(config=self.config)
self.logger.debug('choosen python', extra=dict(path=str(python.path)))

# get installed packages
installed_root = InstalledConverter().load(paths=python.lib_paths)
installed = {dep.name: str(dep.constraint).strip('=') for dep in installed_root.dependencies}

# filter deps by envs and markers
resolver.apply_envs(set(self.config['envs']))
resolver.apply_markers(python=python)

data = []
reqs = Requirement.from_graph(graph=resolver.graph, lock=True)
for req in reqs:
version = req.version.strip('=')
# not installed
if req.name not in installed:
data.append(dict(
action='install',
name=req.name,
installed=None,
locked=version,
))
continue
# installed the same version, skip
if version == installed[req.name]:
continue
# installed old version
data.append(dict(
action='update',
name=req.name,
installed=installed[req.name],
locked=version,
))
# obsolete
names = set(installed) - {req.name for req in reqs}
for name in names:
data.append(dict(
action='remove',
name=name,
installed=installed[name],
locked=None,
))

if data:
print(make_json(data=data, key=self.config.get('filter')))
return False

self.logger.info('all packages is up-to-date')
return True
71 changes: 45 additions & 26 deletions dephell/commands/deps_install.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# built-in
from argparse import ArgumentParser
from types import SimpleNamespace
from typing import Tuple

# app
from ..actions import attach_deps, get_python_env
Expand All @@ -16,6 +18,8 @@ class DepsInstallCommand(BaseCommand):
https://dephell.readthedocs.io/en/latest/cmd-deps-install.html
"""
sync = False

@classmethod
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
Expand Down Expand Up @@ -50,21 +54,53 @@ def __call__(self) -> bool:
print(conflict)
return False

# filter deps by envs
resolver.apply_envs(set(self.config['envs']))

# get executable
python = get_python_env(config=self.config)
self.logger.debug('choosen python', extra=dict(path=str(python.path)))

# filter deps by envs and markers
resolver.apply_envs(set(self.config['envs']))
resolver.apply_markers(python=python)

install, remove = self._get_install_remove(graph=resolver.graph, python=python)

# remove
manager = PackageManager(executable=python.path)
if remove:
self.logger.info('removing old packages...', extra=dict(
executable=python.path,
packages=len(remove),
))
code = manager.remove(reqs=remove)
if code != 0:
return False

# install
if install:
self.logger.info('installation...', extra=dict(
executable=python.path,
packages=len(install),
))
code = manager.install(reqs=install)
if code != 0:
return False

if not install and not remove:
self.logger.info('everything is up-to-date')
else:
self.logger.info('installed')
return True

def _get_install_remove(self, graph, python) -> Tuple[list, list]:
# get installed packages
installed_root = InstalledConverter().load(paths=python.lib_paths)
installed = {dep.name: str(dep.constraint).strip('=') for dep in installed_root.dependencies}

# plan what we will install and what we will remove
install = []
remove = []
for req in Requirement.from_graph(graph=resolver.graph, lock=True):
reqs = Requirement.from_graph(graph=graph, lock=True)
for req in reqs:
# not installed, install
if req.name not in installed:
install.append(req)
Expand All @@ -82,26 +118,9 @@ def __call__(self) -> bool:
remove.append(req)
install.append(req)

# remove
manager = PackageManager(executable=python.path)
if remove:
self.logger.info('removing old packages...', extra=dict(
executable=python.path,
packages=len(remove),
))
code = manager.remove(reqs=remove)
if code != 0:
return False

# install
if install:
self.logger.info('installation...', extra=dict(
executable=python.path,
packages=len(install),
))
code = manager.install(reqs=install)
if code != 0:
return False
# remove packages not in graph
if self.sync:
names = set(installed) - {req.name for req in reqs}
remove.extend(SimpleNamespace(name=name) for name in names)

self.logger.info('installed')
return True
return install, remove
31 changes: 31 additions & 0 deletions dephell/commands/deps_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# built-in
from argparse import ArgumentParser

# app
from ..config import builders
from .deps_install import DepsInstallCommand


class DepsSyncCommand(DepsInstallCommand):
"""Install project dependencies with removing all other packages from venv.
https://dephell.readthedocs.io/en/latest/cmd-deps-sync.html
"""
# DepsInstallCommand contains all logic for sync.
# There we just set `sync` flag and change some minor metainfo.
sync = True

@classmethod
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
prog='dephell deps sync',
description=cls.__doc__,
)
builders.build_config(parser)
builders.build_to(parser)
builders.build_resolver(parser)
builders.build_api(parser)
builders.build_venv(parser)
builders.build_output(parser)
builders.build_other(parser)
return parser
3 changes: 2 additions & 1 deletion dephell/commands/deps_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def _make_tree(cls, dep, *, level: int = 0) -> List[str]:
best=str(dep.group.best_release.version),
latest=str(dep.groups.releases[0].version),
)]
for subdep in sorted(dep.dependencies):
deps = {dep.name: dep for dep in dep.dependencies}.values() # drop duplicates
for subdep in sorted(deps):
lines.extend(cls._make_tree(subdep, level=level + 1))
return lines
4 changes: 1 addition & 3 deletions dephell/commands/package_releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# app
from ..actions import get_package, make_json
from ..config import builders
from ..repositories import WareHouseRepo
from .base import BaseCommand


Expand All @@ -28,8 +27,7 @@ def get_parser(cls) -> ArgumentParser:

def __call__(self) -> bool:
dep = get_package(self.args.name)
repo = WareHouseRepo()
releases = repo.get_releases(dep)
releases = dep.repo.get_releases(dep)

data = []
for release in releases:
Expand Down
4 changes: 1 addition & 3 deletions dephell/commands/package_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from ..actions import get_package, get_python_env, make_json, get_path_size, format_size
from ..config import builders
from ..converters import InstalledConverter
from ..repositories import WareHouseRepo
from .base import BaseCommand


Expand All @@ -29,8 +28,7 @@ def get_parser(cls) -> ArgumentParser:

def __call__(self) -> bool:
dep = get_package(self.args.name)
repo = WareHouseRepo()
releases = repo.get_releases(dep)
releases = dep.repo.get_releases(dep)

python = get_python_env(config=self.config)
self.logger.debug('choosen python', extra=dict(path=str(python.path)))
Expand Down

0 comments on commit d4286c3

Please sign in to comment.