Hello community, here is the log from the commit of package python-dephell for openSUSE:Factory checked in at 2020-07-06 16:30:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-dephell (Old) and /work/SRC/openSUSE:Factory/.python-dephell.new.3060 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-dephell" Mon Jul 6 16:30:02 2020 rev:13 rq:818851 version:0.8.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-dephell/python-dephell.changes 2020-05-28 09:18:09.209080694 +0200 +++ /work/SRC/openSUSE:Factory/.python-dephell.new.3060/python-dephell.changes 2020-07-06 16:32:23.199732494 +0200 @@ -1,0 +2,15 @@ +Sun Jul 5 10:18:13 UTC 2020 - Sebastian Wagner <sebix+novell....@sebix.at> + +- update to version 0.8.3: + - The only noticeable change is an ability to provide a custom CA bundle via --ca flag. + - Fewer network requests + - Fix envs filtering in `deps convert` + - test and fix `deps add` + - Fixes #425: `vendor download` doesn't do anything with private pypi + - fix path resolving 4 register + - Fix Converter.lock default value for subclasses + - Small fixes in requesting warehouse (simple and API) + - fix typo in examples +- check: disable a failing test which depends on the (not packaged) docs. + +------------------------------------------------------------------- Old: ---- dephell-0.8.2.tar.gz New: ---- dephell-0.8.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-dephell.spec ++++++ --- /var/tmp/diff_new_pack.X3sToa/_old 2020-07-06 16:32:24.755737274 +0200 +++ /var/tmp/diff_new_pack.X3sToa/_new 2020-07-06 16:32:24.759737287 +0200 @@ -28,7 +28,7 @@ %bcond_with test %endif Name: python-dephell%{psuffix} -Version: 0.8.2 +Version: 0.8.3 Release: 0 Summary: Dependency resolution for Python License: MIT @@ -172,7 +172,8 @@ %if %{with test} # Emulate Travis, which disables tests which expect a git repository export TRAVIS_OS_NAME=1 -%pytest --no-network +# test_params_all_described requires the docs to be packaged https://github.com/dephell/dephell/pull/448 +%pytest --no-network -k 'not test_params_all_described' %endif %if ! %{with test} ++++++ dephell-0.8.2.tar.gz -> dephell-0.8.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/PKG-INFO new/dephell-0.8.3/PKG-INFO --- old/dephell-0.8.2/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell-0.8.3/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: dephell -Version: 0.8.2 +Version: 0.8.3 Summary: Dependency resolution for Python Project-URL: Homepage, https://dephell.org/ Project-URL: Repository, https://github.com/dephell/dephell @@ -317,6 +317,7 @@ .. code-block:: bash + dephell self auth upload.pypi.org my-username my-password dephell project upload These are some of the most useful commands. See `documentation <https://dephell.org/docs/>`_ for more details. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/README.md new/dephell-0.8.3/README.md --- old/dephell-0.8.2/README.md 2020-03-19 16:37:32.000000000 +0100 +++ new/dephell-0.8.3/README.md 2020-03-19 16:41:14.000000000 +0100 @@ -248,6 +248,7 @@ Now, we can upload these packages to [PyPI](https://pypi.org/): ```bash +dephell self auth upload.pypi.org my-username my-password dephell project upload ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/README.rst new/dephell-0.8.3/README.rst --- old/dephell-0.8.2/README.rst 2020-03-19 16:38:17.000000000 +0100 +++ new/dephell-0.8.3/README.rst 2020-04-28 08:29:15.000000000 +0200 @@ -288,6 +288,7 @@ .. code-block:: bash + dephell self auth upload.pypi.org my-username my-password dephell project upload These are some of the most useful commands. See `documentation <https://dephell.org/docs/>`_ for more details. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/__init__.py new/dephell-0.8.3/dephell/__init__.py --- old/dephell-0.8.2/dephell/__init__.py 2020-03-19 16:37:51.000000000 +0100 +++ new/dephell-0.8.3/dephell/__init__.py 2020-04-28 08:29:08.000000000 +0200 @@ -13,6 +13,6 @@ """ -__version__ = '0.8.2' +__version__ = '0.8.3' __author__ = 'Gram (@orsinium)' __license__ = 'MIT' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/actions/_package.py new/dephell-0.8.3/dephell/actions/_package.py --- old/dephell-0.8.2/dephell/actions/_package.py 2019-12-19 12:08:27.000000000 +0100 +++ new/dephell-0.8.3/dephell/actions/_package.py 2020-04-27 12:28:33.000000000 +0200 @@ -22,6 +22,5 @@ def get_resolver(reqs: Iterable[str] = None) -> Resolver: - root = PIPConverter(lock=False).loads_resolver('\n'.join(reqs)) - root.name = 'root' - return root + resolver = PIPConverter(lock=False).loads_resolver('\n'.join(reqs)) + return resolver diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/cache.py new/dephell-0.8.3/dephell/cache.py --- old/dephell-0.8.2/dephell/cache.py 2019-11-19 13:50:34.000000000 +0100 +++ new/dephell-0.8.3/dephell/cache.py 2020-04-22 14:17:27.000000000 +0200 @@ -16,7 +16,7 @@ def __init__(self, *keys, ttl: int = -1): self.path = Path(config['cache']['path'], *keys) if self.ext: - self.path = self.path.with_suffix(self.ext) + self.path = self.path.with_name(self.path.name + self.ext) self.ttl = ttl self._check_ttl() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/commands/deps_add.py new/dephell-0.8.3/dephell/commands/deps_add.py --- old/dephell-0.8.2/dephell/commands/deps_add.py 2019-12-19 18:13:43.000000000 +0100 +++ new/dephell-0.8.3/dephell/commands/deps_add.py 2020-04-27 12:28:33.000000000 +0200 @@ -38,6 +38,7 @@ # get new deps new_resolver = get_resolver(reqs=self.args.name) new_root = new_resolver.graph._roots[0] + new_root.name = 'new-root' # set envs for dep in new_root.dependencies: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/commands/deps_convert.py new/dephell-0.8.3/dephell/commands/deps_convert.py --- old/dephell-0.8.2/dephell/commands/deps_convert.py 2019-12-19 18:13:43.000000000 +0100 +++ new/dephell-0.8.3/dephell/commands/deps_convert.py 2020-04-28 08:23:30.000000000 +0200 @@ -66,15 +66,14 @@ # filter out deps by `--envs` if self.config.get('envs'): - if len(resolver.graph._layers) == 1: - for root in resolver.graph._roots: - for dep in root.dependencies: - dep.applied = True - resolver.graph.add(dep) - for root in resolver.graph._roots: - root.applied = True - resolver.apply_envs(set(self.config['envs'])) - resolver.graph._layers = resolver.graph._layers[:1] + if not should_be_resolved: + resolver.graph.fast_apply() + resolver.apply_envs(envs=set(self.config['envs']), deep=should_be_resolved) + # If it's not a lockfile, we should drop all dependencies except direct ones. + # While `fast_apply` doesn't produce such dependencies, we want to be sure + # that we can't ever have them in the output. Trust no one. + if not should_be_resolved: + resolver.graph._layers = resolver.graph._layers[:2] # dump self.logger.debug('dump dependencies...', extra=dict( @@ -82,10 +81,10 @@ path=self.config['to']['path'], )) - dumper_kwargs = { - 'reqs': Requirement.from_graph(resolver.graph, lock=dumper.lock), - 'project': resolver.graph.metainfo, - } + dumper_kwargs = dict( + reqs=Requirement.from_graph(resolver.graph, lock=dumper.lock), + project=resolver.graph.metainfo, + ) if self.config['to']['path'] == 'stdout': print(dumper.dumps(**dumper_kwargs)) else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/commands/deps_tree.py new/dephell-0.8.3/dephell/commands/deps_tree.py --- old/dephell-0.8.2/dephell/commands/deps_tree.py 2019-12-19 18:13:43.000000000 +0100 +++ new/dephell-0.8.3/dephell/commands/deps_tree.py 2020-04-27 19:06:45.000000000 +0200 @@ -35,7 +35,10 @@ if self.args.type == 'pretty': for dep in sorted(resolver.graph.get_layer(1)): - print('\n'.join(self._make_tree(dep))) + if not dep.applied: + continue + content = '\n'.join(self._make_tree(dep)) + print(self._colorize(content)) return True if self.args.type == 'json': @@ -63,14 +66,35 @@ @classmethod def _make_tree(cls, dep, *, level: int = 0) -> List[str]: - lines = ['{level}- {name} [required: {constraint}, locked: {best}, latest: {latest}]'.format( + best = dep.group.best_release.version + latest = dep.group.best_release.version + if best == latest: + template = '{level}- {name} [required: `{constraint}`, latest: `{latest}`]' + else: + template = '{level}- {name} [required: `{constraint}`, locked: `{best}`, latest: `{latest}`]' + lines = [template.format( level=' ' * level, name=dep.name, constraint=str(dep.constraint) or '*', - best=str(dep.group.best_release.version), - latest=str(dep.groups.releases[0].version), + best=str(best), + latest=str(latest), )] 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 + + @staticmethod + def _colorize(content: str) -> str: + try: + import pygments + import pygments.lexers + import pygments.formatters + except ImportError: + return content + content = pygments.highlight( + code=content, + lexer=pygments.lexers.MarkdownLexer(), + formatter=pygments.formatters.TerminalFormatter(), + ) + return content.strip() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/commands/project_register.py new/dephell-0.8.3/dephell/commands/project_register.py --- old/dephell-0.8.2/dephell/commands/project_register.py 2020-03-16 17:51:40.000000000 +0100 +++ new/dephell-0.8.3/dephell/commands/project_register.py 2020-04-27 11:43:00.000000000 +0200 @@ -69,6 +69,7 @@ return True def _register(self, project_path: Path, lib_path: Path = None) -> bool: + project_path = project_path.resolve() self._make_egg_info( project_path=project_path, from_format=self.config['from']['format'], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/config/builders.py new/dephell-0.8.3/dephell/config/builders.py --- old/dephell-0.8.2/dephell/config/builders.py 2020-03-18 10:58:30.000000000 +0100 +++ new/dephell-0.8.3/dephell/config/builders.py 2020-04-27 19:06:45.000000000 +0200 @@ -88,6 +88,7 @@ other_group.add_argument('--project', help='path to the current project') other_group.add_argument('--bin', help='path to the dir for installing scripts') + other_group.add_argument('--ca', help='path to CA_BUNDLE file for SSL verification.') other_group.add_argument('--envs', nargs='*', help='environments (main, dev) or extras to install') other_group.add_argument('--tests', nargs='*', help='paths to test files') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/config/scheme.py new/dephell-0.8.3/dephell/config/scheme.py --- old/dephell-0.8.2/dephell/config/scheme.py 2020-03-18 10:58:30.000000000 +0100 +++ new/dephell-0.8.3/dephell/config/scheme.py 2020-04-27 19:06:45.000000000 +0200 @@ -115,6 +115,7 @@ ), 'project': dict(type='string', required=True), 'bin': dict(type='string', required=True), + 'ca': dict(type='string', required=False), 'envs': dict(type='list', schema=dict(type='string'), required=False, empty=False), 'tests': dict(type='list', schema=dict(type='string'), required=True), 'versioning': dict(type='string', required=True, allowed=get_schemes()), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/controllers/_graph.py new/dephell-0.8.3/dephell/controllers/_graph.py --- old/dephell-0.8.2/dephell/controllers/_graph.py 2019-12-30 11:44:13.000000000 +0100 +++ new/dephell-0.8.3/dephell/controllers/_graph.py 2020-04-27 19:06:45.000000000 +0200 @@ -118,7 +118,7 @@ raise KeyError('cannot find any parent for dependency: ' + str(dep.name)) def get_leafs(self, level: Optional[int] = None) -> tuple: - """Get deps that isn't applied yet + """Get deps that aren't applied yet """ layers = self._layers if level is not None: @@ -193,6 +193,19 @@ )) return parents + def fast_apply(self) -> bool: + """Apply only the first layer. + """ + if len(self._layers) != 1: + return False + for root in self._roots: + for dep in root.dependencies: + dep.applied = True + self.add(dep) + for root in self._roots: + root.applied = True + return True + def draw(self, path: str = '.dephell_report', suffix: str = '') -> None: dot = graphviz.Digraph( name=self._roots[0].name + suffix, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/controllers/_resolver.py new/dephell-0.8.3/dephell/controllers/_resolver.py --- old/dephell-0.8.2/dephell/controllers/_resolver.py 2020-01-27 18:20:00.000000000 +0100 +++ new/dephell-0.8.3/dephell/controllers/_resolver.py 2020-04-28 08:23:30.000000000 +0200 @@ -22,9 +22,9 @@ self.graph = graph self.mutator = mutator - def apply(self, parent): + def apply(self, parent, recursive: bool = False): """ - Returns conflicting (incompatible) dependency + Returns conflicting (incompatible) dependency. """ for new_dep in parent.dependencies: other_dep = self.graph.get(new_dep.name) @@ -45,6 +45,10 @@ other_dep += new_dep except TypeError: # conflict happened return other_dep + # `recursive` used only in re-application of dependencies, + # when the graph already was built before. + if recursive: + self.apply(other_dep, recursive=True) # check if not other_dep.compat: return other_dep @@ -133,7 +137,12 @@ self.unapply(dep) dep.group = group - def apply_envs(self, envs: set) -> None: + def apply_envs(self, envs: set, deep: bool = True) -> None: + """Filter out dependencies from the graph by the given envs. + + deep: Helps to avoid fetching dependencies (hence the network requests). + Set it to False for not resolved graph to make filtering faster. + """ if not any(root.dependencies for root in self.graph.get_layer(0)): logger.debug('no dependencies, nothing to filter') return @@ -152,18 +161,22 @@ # and ignored in Requirement.from_graph. # It's bad behavior because deps of this dep can be required for other # deps that won't be unapplied. - self.unapply(dep, soft=True) + if deep: + self.unapply(dep, soft=True) dep.applied = False # Some child deps can be unapplied from other child deps, but we need them. # For example, if we need A, but don't need B, and A and B depends on C, - # then C will be unapplied from B. Let's return B in the graph by re-applying A. + # then C will be unapplied from B. Let's return B in the graph by reapplying A. for dep in self.graph: if not dep.applied: continue if not (dep.envs | dep.inherited_envs) & envs: continue - self.apply(dep) + logger.debug('reapply', extra=dict(dep=dep.name, envs=envs)) + if deep: + self.apply(dep, recursive=True) + dep.applied = True def apply_markers(self, python) -> None: implementation = python.implementation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/conda.py new/dephell-0.8.3/dephell/converters/conda.py --- old/dephell-0.8.2/dephell/converters/conda.py 2019-11-19 13:50:34.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/conda.py 2020-04-27 11:43:00.000000000 +0200 @@ -4,6 +4,7 @@ from typing import Optional # external +import attr from dephell_discover import Root as PackageRoot from dephell_specifier import RangeSpecifier from packaging.utils import canonicalize_name @@ -16,6 +17,7 @@ from .base import BaseConverter +@attr.s() class CondaConverter(BaseConverter): def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if isinstance(path, str): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/egginfo.py new/dephell-0.8.3/dephell/converters/egginfo.py --- old/dephell-0.8.2/dephell/converters/egginfo.py 2020-03-19 13:33:18.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/egginfo.py 2020-04-27 11:43:00.000000000 +0200 @@ -2,14 +2,16 @@ from collections import defaultdict from email.parser import Parser from itertools import chain +from logging import getLogger from pathlib import Path from typing import Dict, Optional # external +import attr from dephell_discover import Root as PackageRoot from dephell_links import parse_link from dephell_markers import Markers -from packaging.requirements import Requirement as PackagingRequirement +from packaging.requirements import InvalidRequirement, Requirement as PackagingRequirement # app from ..constants import DOWNLOAD_FIELD, HOMEPAGE_FIELD @@ -20,6 +22,7 @@ class _Reader: + logger = getLogger('dephell.converters.egginfo') def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if isinstance(path, str): @@ -151,7 +154,15 @@ # dependencies deps = [] for req in cls._get_list(info, 'Requires-Dist'): - req = PackagingRequirement(req) + try: + req = PackagingRequirement(req) + except InvalidRequirement: + cls.logger.warning('invalid requirement', extra=dict( + requirement=req, + package_name=root.name, + package_version=root.version, + )) + continue deps.extend(DependencyMaker.from_requirement( source=root, req=req, @@ -391,9 +402,10 @@ return line +@attr.s() class EggInfoConverter(_Reader, _Writer, BaseConverter): """ PEP-314, PEP-345, PEP-566 https://packaging.python.org/specifications/core-metadata/ """ - lock = False + lock = attr.ib(type=bool, default=False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/flit.py new/dephell-0.8.3/dephell/converters/flit.py --- old/dephell-0.8.2/dephell/converters/flit.py 2020-03-19 13:33:18.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/flit.py 2020-04-27 11:43:00.000000000 +0200 @@ -4,6 +4,7 @@ from typing import Optional # external +import attr import tomlkit from dephell_discover import Root as PackageRoot from dephell_specifier import RangeSpecifier @@ -17,8 +18,9 @@ from .egginfo import EggInfoConverter +@attr.s() class FlitConverter(BaseConverter): - lock = False + lock = attr.ib(type=bool, default=False) def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if not content: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/imports.py new/dephell-0.8.3/dephell/converters/imports.py --- old/dephell-0.8.2/dephell/converters/imports.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/imports.py 2020-04-27 11:43:00.000000000 +0200 @@ -4,6 +4,7 @@ from typing import Dict, List, Set # external +import attr from dephell_discover import Root as PackageRoot # app @@ -23,8 +24,9 @@ CACHE_TTL = 3600 * 24 * 30 # 30 days +@attr.s() class ImportsConverter(BaseConverter): - lock = True + lock = attr.ib(type=bool, default=True) def can_parse(self, path: Path, content: str = None) -> bool: if isinstance(path, str): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/installed.py new/dephell-0.8.3/dephell/converters/installed.py --- old/dephell-0.8.2/dephell/converters/installed.py 2019-12-27 14:56:27.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/installed.py 2020-04-27 11:43:00.000000000 +0200 @@ -4,6 +4,7 @@ from typing import Iterable, Union # external +import attr from dephell_discover import Root as PackageRoot from packaging.utils import canonicalize_name @@ -15,8 +16,9 @@ from .wheel import WheelConverter +@attr.s() class InstalledConverter(BaseConverter): - lock = True + lock = attr.ib(type=bool, default=True) _blacklist = { 'pkg-resources', # https://bugs.launchpad.net/ubuntu/+source/python-pip/+bug/1635463 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/pip.py new/dephell-0.8.3/dephell/converters/pip.py --- old/dephell-0.8.2/dephell/converters/pip.py 2020-03-19 09:29:18.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/pip.py 2020-04-27 11:43:00.000000000 +0200 @@ -5,6 +5,7 @@ from typing import Optional # external +import attr from dephell_discover import Root as PackageRoot from dephell_links import DirLink, FileLink from pip._internal.req import parse_requirements @@ -33,6 +34,7 @@ from pip._internal.network.session import PipSession +@attr.s() class PIPConverter(BaseConverter): sep = ' \\\n ' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/pipfile.py new/dephell-0.8.3/dephell/converters/pipfile.py --- old/dephell-0.8.2/dephell/converters/pipfile.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/pipfile.py 2020-04-27 11:43:00.000000000 +0200 @@ -3,6 +3,7 @@ from typing import List, Optional # external +import attr import tomlkit from dephell_discover import Root as PackageRoot from dephell_pythons import Pythons @@ -19,8 +20,10 @@ VCS_LIST = ('git', 'svn', 'hg', 'bzr') +@attr.s() class PIPFileConverter(BaseConverter): - lock = False + lock = attr.ib(type=bool, default=False) + fields = ( 'version', 'editable', 'extras', 'markers', 'ref', 'vcs', 'index', 'hashes', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/pipfilelock.py new/dephell-0.8.3/dephell/converters/pipfilelock.py --- old/dephell-0.8.2/dephell/converters/pipfilelock.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/pipfilelock.py 2020-04-27 11:43:00.000000000 +0200 @@ -6,6 +6,7 @@ from typing import Optional # external +import attr from dephell_discover import Root as PackageRoot from dephell_pythons import Pythons from dephell_specifier import RangeSpecifier @@ -22,8 +23,9 @@ # https://github.com/pypa/pipfile/blob/master/pipfile/api.py +@attr.s() class PIPFileLockConverter(PIPFileConverter): - lock = True + lock = attr.ib(type=bool, default=True) def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if isinstance(path, str): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/poetry.py new/dephell-0.8.3/dephell/converters/poetry.py --- old/dephell-0.8.2/dephell/converters/poetry.py 2020-03-18 14:11:02.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/poetry.py 2020-04-27 19:06:45.000000000 +0200 @@ -4,6 +4,7 @@ from typing import List, Optional # external +import attr import tomlkit from dephell_discover import Root as PackageRoot from dephell_specifier import RangeSpecifier @@ -16,8 +17,10 @@ from .base import BaseConverter +@attr.s() class PoetryConverter(BaseConverter): - lock = False + lock = attr.ib(type=bool, default=False) + fields = ( 'version', 'python', 'platform', 'allows-prereleases', 'optional', 'extras', 'develop', @@ -30,7 +33,7 @@ if isinstance(path, str): path = Path(path) if content: - return '[tool.poetry]' in content + return '[tool.poetry]' in content or '[tool.poetry.' in content return path.name in ('poetry.toml', 'pyproject.toml') def loads(self, content) -> RootDependency: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/poetrylock.py new/dephell-0.8.3/dephell/converters/poetrylock.py --- old/dephell-0.8.2/dephell/converters/poetrylock.py 2019-11-19 13:50:34.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/poetrylock.py 2020-04-27 11:43:00.000000000 +0200 @@ -4,6 +4,7 @@ from typing import List, Optional # external +import attr import tomlkit from dephell_discover import Root as PackageRoot from dephell_links import DirLink @@ -16,8 +17,10 @@ from .base import BaseConverter +@attr.s() class PoetryLockConverter(BaseConverter): - lock = True + lock = attr.ib(type=bool, default=True) + fields = ( 'category', 'description', 'name', 'marker', 'optional', 'python-versions', 'version', 'dependencies', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/pyproject.py new/dephell-0.8.3/dephell/converters/pyproject.py --- old/dephell-0.8.2/dephell/converters/pyproject.py 2019-12-19 18:13:43.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/pyproject.py 2020-04-27 11:43:00.000000000 +0200 @@ -3,6 +3,7 @@ from typing import Optional # external +import attr from dephell_discover import Root as PackageRoot from packaging.requirements import Requirement from tomlkit import document, dumps, parse @@ -13,8 +14,9 @@ from .base import BaseConverter +@attr.s() class PyProjectConverter(BaseConverter): - lock = False + lock = attr.ib(type=bool, default=False) def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if isinstance(path, str): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/sdist.py new/dephell-0.8.3/dephell/converters/sdist.py --- old/dephell-0.8.2/dephell/converters/sdist.py 2020-01-23 19:49:00.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/sdist.py 2020-04-27 11:43:00.000000000 +0200 @@ -25,7 +25,7 @@ # ratio of tests and project size after which tests will be excluded from sdist ratio = attr.ib(type=Optional[float], default=None) - lock = False + lock = attr.ib(type=bool, default=False) def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if content is not None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/setuppy.py new/dephell-0.8.3/dephell/converters/setuppy.py --- old/dephell-0.8.2/dephell/converters/setuppy.py 2020-03-19 13:33:18.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/setuppy.py 2020-04-27 11:43:00.000000000 +0200 @@ -6,6 +6,7 @@ from typing import Optional # external +import attr from dephell_discover import Root as PackageRoot from dephell_links import DirLink, FileLink, URLLink, VCSLink, parse_link from dephell_setuptools import read_setup @@ -54,8 +55,9 @@ """ +@attr.s() class SetupPyConverter(BaseConverter): - lock = False + lock = attr.ib(type=bool, default=False) def can_parse(self, path: Path, content: Optional[str] = None) -> bool: if isinstance(path, str): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/converters/wheel.py new/dephell-0.8.3/dephell/converters/wheel.py --- old/dephell-0.8.2/dephell/converters/wheel.py 2019-12-30 11:44:13.000000000 +0100 +++ new/dephell-0.8.3/dephell/converters/wheel.py 2020-04-27 11:43:00.000000000 +0200 @@ -8,6 +8,7 @@ from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo # external +import attr from dephell_archive import ArchivePath from dephell_discover import Root as PackageRoot @@ -207,9 +208,10 @@ return content + ('\n{},,'.format(path)) +@attr.s() class WheelConverter(_Reader, _Writer, BaseConverter): """ PEP-0427 https://www.python.org/dev/peps/pep-0427/ """ - lock = False + lock = attr.ib(type=bool, default=False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/models/dependency.py new/dephell-0.8.3/dephell/models/dependency.py --- old/dephell-0.8.2/dephell/models/dependency.py 2019-12-19 18:13:43.000000000 +0100 +++ new/dephell-0.8.3/dephell/models/dependency.py 2020-04-22 14:17:27.000000000 +0200 @@ -193,7 +193,7 @@ marker = Markers(str(self.marker)) if self.envs - {'main'}: - extra_markers = {'extra == "{}"'.format(env) for env in self.envs - {'main'}} + extra_markers = {'extra == {!r}'.format(env) for env in self.envs - {'main'}} marker &= Markers(' or '.join(extra_markers)) if marker: result += '; ' + str(marker) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/models/groups.py new/dephell-0.8.3/dephell/models/groups.py --- old/dephell-0.8.2/dephell/models/groups.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/models/groups.py 2020-04-28 08:23:30.000000000 +0200 @@ -12,6 +12,9 @@ loop = asyncio.get_event_loop() +# How many latest releases should go in their own group. +# Lesser value -> less network requests per package and more mutations. +ONE_GROUP_RELEASES = 1 def get_key(release) -> str: @@ -164,12 +167,15 @@ self.actualize(group=group) yield group - # load first group + # load the first two groups if not self._loaded_groups: - release = self.releases[0] - self._load_release_deps(release) - self._loaded_releases_count += 1 - yield self._make_group([release]) + for i in range(ONE_GROUP_RELEASES): + if len(self.releases) <= i: + return + release = self.releases[i] + self._load_release_deps(release) + self._loaded_releases_count += 1 + yield self._make_group([release]) # load new groups prev_key = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/models/requirement.py new/dephell-0.8.3/dephell/models/requirement.py --- old/dephell-0.8.2/dephell/models/requirement.py 2020-01-27 18:20:00.000000000 +0100 +++ new/dephell-0.8.3/dephell/models/requirement.py 2020-04-27 19:06:45.000000000 +0200 @@ -29,16 +29,15 @@ extras = defaultdict(list) roots = [root.name for root in graph.get_layer(0)] - # if roots wasn't applied then apply them - if len(graph._layers) == 1: - for root in graph._roots: - for dep in root.dependencies: - graph.add(dep) + # if roots weren't applied, apply them + graph.fast_apply() # get all nodes for layer in reversed(graph._layers[1:]): # skip roots for dep in sorted(layer): - if dep.constraint.empty: + if not dep.used: + continue + if not dep.applied: continue if dep.extra is None: req = cls(dep=dep, lock=lock, roots=roots) @@ -219,7 +218,10 @@ @staticmethod def _get_comparable_dict(dep) -> dict: - excluded = {'constraint', 'repo', 'link', 'marker', 'license', 'inherited_envs', 'locations'} + excluded = { + 'constraint', 'repo', 'link', 'marker', 'license', + 'inherited_envs', 'locations', 'applied', + } result = attr.asdict(dep, recurse=True, filter=lambda x, _: x.name not in excluded) result['constraint'] = str(dep.constraint) if dep.marker: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/networking.py new/dephell-0.8.3/dephell/networking.py --- old/dephell-0.8.2/dephell/networking.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/networking.py 2020-04-27 19:06:45.000000000 +0200 @@ -1,27 +1,38 @@ # built-in +from functools import partial, update_wrapper +from logging import getLogger from ssl import create_default_context +from time import sleep # external import certifi import requests -from aiohttp import ClientSession, TCPConnector +from aiohttp import ClientError, ClientSession, TCPConnector # app from . import __version__ +from .config import config USER_AGENT = 'DepHell/{version}'.format(version=__version__) +logger = getLogger('dephell.networking') def aiohttp_session(*, auth=None, **kwargs): - headers = dict() + headers = {'User-Agent': USER_AGENT} if auth: headers['Authorization'] = auth.encode() - ssl_context = create_default_context(cafile=certifi.where()) + + # setup SSL + cafile = config.get('ca') + if not cafile: + cafile = certifi.where() + ssl_context = create_default_context(cafile=cafile) try: connector = TCPConnector(ssl=ssl_context) except TypeError: connector = TCPConnector(ssl_context=ssl_context) + return ClientSession(headers=headers, connector=connector, **kwargs) @@ -29,10 +40,37 @@ session = requests.Session() if auth: session.auth = auth + + # setup SSL + cafile = config.get('ca') + if cafile: + session.verify = cafile + + # set headers if headers is None: headers = dict() headers.setdefault('User-Agent', USER_AGENT) session.headers = headers if kwargs: session.__dict__.update(kwargs) + return session + + +def aiohttp_repeat(func=None, *, count: int = 4): + if func is None: + return partial(func, count=count) + + async def wrapper(*args, **kwargs): + for pause in range(1, count + 1): + try: + return await func(*args, **kwargs) + except ClientError: + if pause == count: + raise + logger.debug('aiohttp payload error, repeating...', exc_info=True) + sleep(pause) + raise RuntimeError('unreachable') + + wrapper = update_wrapper(wrapper=wrapper, wrapped=func) + return wrapper diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/repositories/_warehouse/_api.py new/dephell-0.8.3/dephell/repositories/_warehouse/_api.py --- old/dephell-0.8.2/dephell/repositories/_warehouse/_api.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/repositories/_warehouse/_api.py 2020-04-28 08:23:30.000000000 +0200 @@ -38,6 +38,8 @@ 'summary', 'version', } +# Do not download too big files +SIZE_LIMIT = 2 * 1024 * 1024 # 2 Mb @attr.s() @@ -284,8 +286,11 @@ for converter, checker in rules: for file_info in files_info: + if file_info.get('size') > SIZE_LIMIT: + continue if not checker(file_info): continue + logger.debug('downloading file...', extra=dict(url=file_info['url'])) try: return await self._download_and_parse( url=file_info['url'], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/repositories/_warehouse/_base.py new/dephell-0.8.3/dephell/repositories/_warehouse/_base.py --- old/dephell-0.8.2/dephell/repositories/_warehouse/_base.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/dephell/repositories/_warehouse/_base.py 2020-04-27 19:44:05.000000000 +0200 @@ -13,7 +13,7 @@ # app from ...cached_property import cached_property from ...constants import WAREHOUSE_DOMAINS -from ...networking import aiohttp_session +from ...networking import aiohttp_repeat, aiohttp_session from ..base import Interface @@ -99,22 +99,23 @@ )) try: - dep_extra = req.marker and Markers(req.marker).extra + dep_extras = req.marker and Markers(req.marker).get_strings('extra') except ValueError: # unsupported operation for version marker python_version: in - dep_extra = None + dep_extras = set() # it's not extra and we want not extra too - if dep_extra is None and extra is None: + if not dep_extras and extra is None: result.append(req) continue # it's extra, but we want not the extra # or it's not the extra, but we want extra. - if dep_extra is None or extra is None: + if not dep_extras or extra is None: continue # it's extra and we want this extra - elif dep_extra == extra: - result.append(req) - continue + for dep_extra in dep_extras: + if dep_extra == extra: + result.append(req) + break return tuple(result) @@ -136,6 +137,7 @@ deps.append(str(dep)) return tuple(deps) + @aiohttp_repeat async def _download(self, *, url: str, path: Path) -> None: async with aiohttp_session(auth=self.auth) as session: async with session.get(url) as response: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell/repositories/_warehouse/_simple.py new/dephell-0.8.3/dephell/repositories/_warehouse/_simple.py --- old/dephell-0.8.2/dephell/repositories/_warehouse/_simple.py 2020-03-18 10:58:30.000000000 +0100 +++ new/dephell-0.8.3/dephell/repositories/_warehouse/_simple.py 2020-04-27 12:28:33.000000000 +0200 @@ -53,9 +53,19 @@ releases_info = dict() for link in links: name, version = self._parse_name(link['name']) - if canonicalize_name(name) != dep.name: + if canonicalize_name(name) != canonicalize_name(dep.base_name): + logger.warning('bad dist name', extra=dict( + dist_name=link['name'], + package_name=dep.base_name, + reason='package name does not match', + )) continue if not version: + logger.warning('bad dist name', extra=dict( + dist_name=link['name'], + package_name=dep.base_name, + reason='no version specified', + )) continue if version not in releases_info: @@ -112,6 +122,9 @@ raise NotImplementedError async def download(self, name: str, version: str, path: Path) -> bool: + if not isinstance(version, str): + version = str(version) + links = self._get_links(name=name) good_links = [] for link in links: @@ -142,11 +155,13 @@ ttl=config['cache']['ttl'], ) links = cache.load() - if links: - return links + if links is not None: + yield from links + return dep_url = posixpath.join(self.url, quote(name)) + '/' with requests_session() as session: + logger.debug('getting dep info from simple repo', extra=dict(url=dep_url)) response = session.get(dep_url, auth=self.auth) if response.status_code == 404: raise PackageNotFoundError(package=name, url=dep_url) @@ -164,17 +179,19 @@ python = tag.get('data-requires-python') fragment = parse_qs(parsed.fragment) - yield dict( + link = dict( url=urljoin(dep_url, link), name=parsed.path.strip('/').split('/')[-1], python=html.unescape(python) if python else '*', digest=fragment['sha256'][0] if 'sha256' in fragment else None, ) + links.append(link) + yield link cache.dump(links) return links - async def _get_deps_from_links(self, name, version): + async def _get_deps_from_links(self, name: str, version): from ...converters import SDistConverter, WheelConverter links = self._get_links(name=name) @@ -183,7 +200,7 @@ link_name, link_version = self._parse_name(link['name']) if canonicalize_name(link_name) != name: continue - if link_version != version: + if link_version != str(version): continue good_links.append(link) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/dephell.egg-info/PKG-INFO new/dephell-0.8.3/dephell.egg-info/PKG-INFO --- old/dephell-0.8.2/dephell.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell-0.8.3/dephell.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: dephell -Version: 0.8.2 +Version: 0.8.3 Summary: Dependency resolution for Python Project-URL: Homepage, https://dephell.org/ Project-URL: Repository, https://github.com/dephell/dephell @@ -317,6 +317,7 @@ .. code-block:: bash + dephell self auth upload.pypi.org my-username my-password dephell project upload These are some of the most useful commands. See `documentation <https://dephell.org/docs/>`_ for more details. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/setup.cfg new/dephell-0.8.3/setup.cfg --- old/dephell-0.8.2/setup.cfg 2019-11-19 13:50:34.000000000 +0100 +++ new/dephell-0.8.3/setup.cfg 2020-04-22 14:17:27.000000000 +0200 @@ -11,3 +11,8 @@ .pytest_cache build setup.py + +[tool:pytest] +addopts = --strict-markers +markers = + allow_hosts: allow network requests to specified hostnames. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/setup.py new/dephell-0.8.3/setup.py --- old/dephell-0.8.2/setup.py 2020-03-19 16:38:16.000000000 +0100 +++ new/dephell-0.8.3/setup.py 2020-04-28 08:29:15.000000000 +0200 @@ -21,7 +21,7 @@ setup( long_description=readme, name='dephell', - version='0.8.2', + version='0.8.3', description='Dependency resolution for Python', python_requires='>=3.6', project_urls={ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/helpers.py new/dephell-0.8.3/tests/helpers.py --- old/dephell-0.8.2/tests/helpers.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/tests/helpers.py 2020-04-27 19:06:45.000000000 +0200 @@ -71,7 +71,15 @@ return root_dep -def check(root, resolved=True, missed=None, **deps): +def set_envs(root: RootDependency, dep_name: str, envs: str) -> None: + for dep in root.dependencies: + if dep.name == dep_name: + dep.envs = envs + return + raise RuntimeError('cannot find dep') + + +def check(root: RootDependency, resolved: bool = True, missed=None, envs: set = None, **deps): resolver = Resolver( graph=Graph(root), mutator=Mutator(), @@ -82,6 +90,9 @@ ): result = resolver.resolve(debug=True, silent=True) + if envs is not None: + resolver.apply_envs(envs=envs) + reqs = Requirement.from_graph(resolver.graph, lock=True) reqs = {req.name: req for req in reqs} @@ -104,4 +115,4 @@ if missed: for name in missed: - assert name not in reqs + assert name not in reqs, '{} must be missed but it is not'.format(name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/requirements/setup.py new/dephell-0.8.3/tests/requirements/setup.py --- old/dephell-0.8.2/tests/requirements/setup.py 2020-03-19 13:33:18.000000000 +0100 +++ new/dephell-0.8.3/tests/requirements/setup.py 2020-04-27 11:43:00.000000000 +0200 @@ -5,7 +5,6 @@ # external from setuptools import setup - here = path.abspath(path.dirname(__file__)) root = path.dirname(path.dirname(here)) with open(path.join(root, 'README.md'), encoding='utf-8') as f: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/test_commands/test_deps_add.py new/dephell-0.8.3/tests/test_commands/test_deps_add.py --- old/dephell-0.8.2/tests/test_commands/test_deps_add.py 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell-0.8.3/tests/test_commands/test_deps_add.py 2020-04-27 12:28:33.000000000 +0200 @@ -0,0 +1,33 @@ +# built-in +from pathlib import Path + +# external +import pytest + +# project +from dephell.commands import DepsAddCommand +from dephell.config import Config + + +@pytest.mark.allow_hosts() +def test_deps_add_command(temp_path: Path, capsys): + reqs_path = temp_path / 'requirements.txt' + reqs_path.write_text('six==1.12.0') + + config = Config() + config.attach({ + 'level': 'WARNING', + 'silent': True, + 'nocolors': True, + 'from': dict(format='pip', path=reqs_path), + }) + + command = DepsAddCommand(argv=['jinja2==2.0'], config=config) + result = command() + + captured = capsys.readouterr() + print(captured.err) + print(captured.out) + assert result is True + + assert set(reqs_path.read_text().split()) == {'six==1.12.0', 'jinja2==2.0'} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/test_controllers/test_safety.py new/dephell-0.8.3/tests/test_controllers/test_safety.py --- old/dephell-0.8.2/tests/test_controllers/test_safety.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/tests/test_controllers/test_safety.py 2020-04-22 14:17:27.000000000 +0200 @@ -9,5 +9,5 @@ def test_safety(): safety = Safety() vulns = safety.get('django', '1.11.0') - assert len(vulns) == 5 + assert len(vulns) == 13 assert {vuln.name for vuln in vulns} == {'django'} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/test_docs.py new/dephell-0.8.3/tests/test_docs.py --- old/dephell-0.8.2/tests/test_docs.py 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell-0.8.3/tests/test_docs.py 2020-04-28 08:23:30.000000000 +0200 @@ -0,0 +1,15 @@ +# built-in +from pathlib import Path + +# project +from dephell.config.scheme import SCHEME + + +def test_params_all_described(): + undocumented = {'and', 'auth', 'vars', 'command'} + path = Path(__file__).parent.parent / 'docs' / 'params.md' + content = path.read_text() + for key in SCHEME: + if key in undocumented: + continue + assert '`--' + key in content diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/test_repositories/test_warehouse_api.py new/dephell-0.8.3/tests/test_repositories/test_warehouse_api.py --- old/dephell-0.8.2/tests/test_repositories/test_warehouse_api.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/tests/test_repositories/test_warehouse_api.py 2020-04-27 12:28:33.000000000 +0200 @@ -5,6 +5,7 @@ # external import pytest +from packaging.version import Version # project from dephell.constants import DEFAULT_WAREHOUSE @@ -110,8 +111,9 @@ assert client._default_headers['authorization'] == 'Basic Z3JhbTp0ZXN0' +@pytest.mark.parametrize('version', ['0.1.2', Version('0.1.2')]) def test_download(asyncio_mock, temp_cache, fixtures_path: Path, temp_path: Path, - requirements_path: Path): + requirements_path: Path, version): pypi_url = 'https://custom.pypi.org/pypi/' json_response = (fixtures_path / 'warehouse-api-release.json').read_text() json_content = json.loads(json_response) @@ -123,7 +125,7 @@ asyncio_mock.get(file_url, body=file_content) repo = WarehouseAPIRepo(name='pypi', url=pypi_url) - coroutine = repo.download(name='dephell-shells', version='0.1.2', path=temp_path) + coroutine = repo.download(name='dephell-shells', version=version, path=temp_path) result = loop.run_until_complete(asyncio.gather(coroutine))[0] assert result is True assert (temp_path / file_name).exists() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/test_repositories/test_warehouse_simple.py new/dephell-0.8.3/tests/test_repositories/test_warehouse_simple.py --- old/dephell-0.8.2/tests/test_repositories/test_warehouse_simple.py 2019-12-17 15:33:35.000000000 +0100 +++ new/dephell-0.8.3/tests/test_repositories/test_warehouse_simple.py 2020-04-27 12:28:33.000000000 +0200 @@ -6,6 +6,7 @@ # external import pytest +from packaging.version import Version # project from dephell.constants import DEFAULT_WAREHOUSE @@ -128,8 +129,9 @@ assert requests_mock.last_request.headers['Authorization'] == 'Basic Z3JhbTp0ZXN0' +@pytest.mark.parametrize('version', ['0.1.2', Version('0.1.2')]) def test_download(requests_mock, asyncio_mock, temp_cache, fixtures_path: Path, - temp_path: Path, requirements_path: Path): + temp_path: Path, requirements_path: Path, version): pypi_url = 'https://custom.pypi.org/pypi/' text_response = (fixtures_path / 'warehouse-simple.html').read_text() file_url = re.findall( @@ -143,7 +145,7 @@ asyncio_mock.get(file_url, body=file_content) repo = WarehouseSimpleRepo(name='pypi', url=pypi_url) - coroutine = repo.download(name='dephell-shells', version='0.1.2', path=temp_path) + coroutine = repo.download(name='dephell-shells', version=version, path=temp_path) result = loop.run_until_complete(asyncio.gather(coroutine))[0] assert result is True assert (temp_path / file_name).exists() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell-0.8.2/tests/test_resolving/test_apply_envs.py new/dephell-0.8.3/tests/test_resolving/test_apply_envs.py --- old/dephell-0.8.2/tests/test_resolving/test_apply_envs.py 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell-0.8.3/tests/test_resolving/test_apply_envs.py 2020-04-28 08:23:30.000000000 +0200 @@ -0,0 +1,145 @@ +# project +from dephell.controllers import Graph, Mutator, Resolver +from dephell.converters import PIPConverter +from dephell.models import Requirement + +# app +from ..helpers import Fake, check, make_root, set_envs + + +def fast_filter(root, *, deep=True): + resolver = Resolver( + graph=Graph(root), + mutator=Mutator(), + ) + resolver.graph.fast_apply() + resolver.apply_envs(envs={'main'}, deep=deep) + reqs = Requirement.from_graph(resolver.graph, lock=False) + return {req.name: req for req in reqs} + + +def test_direct_dependencies(): + root = make_root( + root=Fake('', 'a', 'b'), + a=(Fake('1.0'), ), + b=(Fake('1.0'), ), + ) + set_envs(root, 'a', {'main'}) + set_envs(root, 'b', {'dev'}) + check(root=root, a='==1.0', missed=['b'], envs={'main'}) + + +def test_subdependencies(): + root = make_root( + root=Fake('', 'a', 'b'), + a=(Fake('1.0'), ), + b=(Fake('1.0', 'c'), ), + c=(Fake('1.0'), ), + ) + set_envs(root, 'a', {'main'}) + set_envs(root, 'b', {'dev'}) + check(root=root, a='==1.0', missed=['b', 'c'], envs={'main'}) + + +def test_reapply(): + root = make_root( + root=Fake('', 'a', 'b'), + a=(Fake('1.0', 'c'), ), + b=(Fake('1.0', 'c'), ), + c=(Fake('1.0'), ), + ) + set_envs(root, 'a', {'main'}) + set_envs(root, 'b', {'dev'}) + check(root=root, a='==1.0', c='==1.0', missed=['b'], envs={'main'}) + + +def test_unapply_twice(): + root = make_root( + root=Fake('', 'a', 'b', 'c'), + a=(Fake('1.0'), ), + b=(Fake('1.0', 'd'), ), + c=(Fake('1.0', 'd'), ), + d=(Fake('1.0'), ), + ) + set_envs(root, 'a', {'main'}) + set_envs(root, 'b', {'dev'}) + set_envs(root, 'c', {'dev'}) + check(root=root, a='==1.0', missed=['b', 'c', 'd'], envs={'main'}) + + +def test_with_real_names(): + root = make_root( + root=Fake('', 'bandit', 'boltons'), + bandit=(Fake('1.0', 'colorama'), ), + boltons=(Fake('1.0'), ), + colorama=(Fake('1.0'), ), + ) + set_envs(root, 'boltons', {'main'}) + set_envs(root, 'bandit', {'dev'}) + check(root=root, boltons='==1.0', missed=['bandit', 'colorama'], envs={'main'}) + + +def test_deep_dependencies(): + root = make_root( + root=Fake('', 'apiwrapper', 'sphinx', 'certifi'), + sphinx=(Fake('1.0', 'requests'), ), + apiwrapper=(Fake('1.0', 'requests'), ), + requests=(Fake('1.0', 'certifi'), ), + certifi=(Fake('1.0'), ), + ) + set_envs(root, 'sphinx', {'main'}) + set_envs(root, 'apiwrapper', {'dev'}) + set_envs(root, 'certifi', {'dev'}) + check(root=root, sphinx='==1.0', certifi='==1.0', requests='==1.0', missed=['apiwrapper'], envs={'main'}) + + +def test_very_deep_dependencies_reapply(): + root = make_root( + root=Fake('', 'a', 'b'), + a=(Fake('1.0', 'c'), ), + b=(Fake('1.0', 'c'), ), + + c=(Fake('1.0', 'd'), ), + d=(Fake('1.0', 'e'), ), + e=(Fake('1.0'), ), + ) + set_envs(root, 'a', {'main'}) + set_envs(root, 'b', {'dev'}) + check(root=root, a='==1.0', c='==1.0', d='==1.0', e='==1.0', missed=['b'], envs={'main'}) + + +def test_dependencies_unapply_twice_and_reapply(): + root = make_root( + root=Fake('', 'requests', 'sphinx', 'certifi'), + requests=(Fake('1.0', 'certifi'), ), + sphinx=(Fake('1.0', 'requests'), ), + certifi=(Fake('1.0'), ), + ) + set_envs(root, 'sphinx', {'main'}) + set_envs(root, 'requests', {'dev'}) + set_envs(root, 'certifi', {'dev'}) + check(root=root, sphinx='==1.0', requests='==1.0', certifi='==1.0', missed=[], envs={'main'}) + + +def test_direct_dependencies_without_resolving(): + root = make_root( + root=Fake('', 'a', 'b'), + a=(Fake('1.0'), ), + b=(Fake('1.0'), ), + ) + set_envs(root, 'a', {'main'}) + set_envs(root, 'b', {'dev'}) + + reqs = fast_filter(root) + assert set(reqs) == {'a'} + + +def test_not_deep(): + """If deep is False, filtering must work and no network requests can be made. + """ + loader = PIPConverter(lock=False) + root = loader.loads(content='sphinx\nrequests') + root.dependencies[0].envs = {'main'} + root.dependencies[1].envs = {'dev'} + reqs = fast_filter(root, deep=False) + assert set(reqs) == {'sphinx'}