Skip to content

Commit

Permalink
Merge pull request #10435 from jdufresne/typing-unit
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Sep 26, 2021
2 parents 7d28893 + 60c274b commit b392833
Show file tree
Hide file tree
Showing 64 changed files with 2,052 additions and 1,273 deletions.
Empty file.
8 changes: 6 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ follow_imports = skip
[mypy-pip._vendor.requests.*]
follow_imports = skip

[mypy-tests.*]
# TODO: The following option should be removed at some point in the future.
# TODO: The following options should be removed at some point in the future.
[mypy-tests.conftest]
allow_untyped_defs = True
[mypy-tests.lib.*]
allow_untyped_defs = True
[mypy-tests.functional.*]
allow_untyped_defs = True

[tool:pytest]
Expand Down
14 changes: 12 additions & 2 deletions src/pip/_internal/req/req_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@
import shlex
import urllib.parse
from optparse import Values
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Tuple
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
Iterable,
Iterator,
List,
Optional,
Tuple,
)

from pip._internal.cli import cmdoptions
from pip._internal.exceptions import InstallationError, RequirementsFileParseError
Expand All @@ -27,7 +37,7 @@

__all__ = ["parse_requirements"]

ReqFileLines = Iterator[Tuple[int, str]]
ReqFileLines = Iterable[Tuple[int, str]]

LineParser = Callable[[str], Tuple[str, Values]]

Expand Down
4 changes: 2 additions & 2 deletions src/pip/_internal/req/req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
import uuid
import zipfile
from typing import Any, Dict, Iterable, List, Optional, Sequence, Union
from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union

from pip._vendor import pkg_resources
from pip._vendor.packaging.markers import Marker
Expand Down Expand Up @@ -103,7 +103,7 @@ def __init__(
global_options: Optional[List[str]] = None,
hash_options: Optional[Dict[str, List[str]]] = None,
constraint: bool = False,
extras: Iterable[str] = (),
extras: Collection[str] = (),
user_supplied: bool = False,
) -> None:
assert req is None or isinstance(req, Requirement), req
Expand Down
6 changes: 4 additions & 2 deletions src/pip/_internal/resolution/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from typing import Callable, List
from typing import Callable, List, Optional

from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet

InstallRequirementProvider = Callable[[str, InstallRequirement], InstallRequirement]
InstallRequirementProvider = Callable[
[str, Optional[InstallRequirement]], InstallRequirement
]


class BaseResolver:
Expand Down
4 changes: 2 additions & 2 deletions src/pip/_internal/resolution/resolvelib/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ def _iter_candidates_from_constraints(
def find_candidates(
self,
identifier: str,
requirements: Mapping[str, Iterator[Requirement]],
requirements: Mapping[str, Iterable[Requirement]],
incompatibilities: Mapping[str, Iterator[Candidate]],
constraint: Constraint,
prefers_installed: bool,
Expand Down Expand Up @@ -484,7 +484,7 @@ def make_requirement_from_candidate(
def make_requirement_from_spec(
self,
specifier: str,
comes_from: InstallRequirement,
comes_from: Optional[InstallRequirement],
requested_extras: Iterable[str] = (),
) -> Optional[Requirement]:
ireq = self._make_install_req_from_spec(specifier, comes_from)
Expand Down
1 change: 1 addition & 0 deletions tests/lib/requests_mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(self, contents):
self.url = None
self.headers = {"Content-Length": len(contents)}
self.history = []
self.from_cache = False


class MockConnection:
Expand Down
12 changes: 8 additions & 4 deletions tests/unit/resolution_resolvelib/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Iterator

import pytest

from pip._internal.cli.req_command import RequirementCommand
Expand All @@ -9,15 +11,17 @@
from pip._internal.models.search_scope import SearchScope
from pip._internal.models.selection_prefs import SelectionPreferences
from pip._internal.network.session import PipSession
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_tracker import get_requirement_tracker
from pip._internal.resolution.resolvelib.factory import Factory
from pip._internal.resolution.resolvelib.provider import PipProvider
from pip._internal.utils.temp_dir import TempDirectory, global_tempdir_manager
from tests.lib import TestData


@pytest.fixture
def finder(data):
def finder(data: TestData) -> Iterator[PackageFinder]:
session = PipSession()
scope = SearchScope([str(data.packages)], [])
collector = LinkCollector(session, scope)
Expand All @@ -27,7 +31,7 @@ def finder(data):


@pytest.fixture
def preparer(finder):
def preparer(finder: PackageFinder) -> Iterator[RequirementPreparer]:
session = PipSession()
rc = InstallCommand("x", "y")
o = rc.parse_args([])
Expand All @@ -48,7 +52,7 @@ def preparer(finder):


@pytest.fixture
def factory(finder, preparer):
def factory(finder: PackageFinder, preparer: RequirementPreparer) -> Iterator[Factory]:
yield Factory(
finder=finder,
preparer=preparer,
Expand All @@ -63,7 +67,7 @@ def factory(finder, preparer):


@pytest.fixture
def provider(factory):
def provider(factory: Factory) -> Iterator[PipProvider]:
yield PipProvider(
factory=factory,
constraints={},
Expand Down
34 changes: 25 additions & 9 deletions tests/unit/resolution_resolvelib/test_requirement.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from typing import Iterator, List, Tuple

import pytest
from pip._vendor.resolvelib import BaseReporter, Resolver

from pip._internal.resolution.resolvelib.base import Candidate, Constraint
from pip._internal.resolution.resolvelib.base import Candidate, Constraint, Requirement
from pip._internal.resolution.resolvelib.factory import Factory
from pip._internal.resolution.resolvelib.provider import PipProvider
from pip._internal.utils.urls import path_to_url
from tests.lib import TestData
from tests.lib.path import Path

# NOTE: All tests are prefixed `test_rlr` (for "test resolvelib resolver").
# This helps select just these tests using pytest's `-k` option, and
Expand All @@ -18,11 +24,11 @@


@pytest.fixture
def test_cases(data):
def data_file(name):
def test_cases(data: TestData) -> Iterator[List[Tuple[str, str, int]]]:
def data_file(name: str) -> Path:
return data.packages.joinpath(name)

def data_url(name):
def data_url(name: str) -> str:
return path_to_url(data_file(name))

test_cases = [
Expand All @@ -47,17 +53,23 @@ def data_url(name):
yield test_cases


def test_new_resolver_requirement_has_name(test_cases, factory):
def test_new_resolver_requirement_has_name(
test_cases: List[Tuple[str, str, int]], factory: Factory
) -> None:
"""All requirements should have a name"""
for spec, name, _ in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None)
assert req is not None
assert req.name == name


def test_new_resolver_correct_number_of_matches(test_cases, factory):
def test_new_resolver_correct_number_of_matches(
test_cases: List[Tuple[str, str, int]], factory: Factory
) -> None:
"""Requirements should return the correct number of candidates"""
for spec, _, match_count in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None)
assert req is not None
matches = factory.find_candidates(
req.name,
{req.name: [req]},
Expand All @@ -68,10 +80,13 @@ def test_new_resolver_correct_number_of_matches(test_cases, factory):
assert sum(1 for _ in matches) == match_count


def test_new_resolver_candidates_match_requirement(test_cases, factory):
def test_new_resolver_candidates_match_requirement(
test_cases: List[Tuple[str, str, int]], factory: Factory
) -> None:
"""Candidates returned from find_candidates should satisfy the requirement"""
for spec, _, _ in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None)
assert req is not None
candidates = factory.find_candidates(
req.name,
{req.name: [req]},
Expand All @@ -84,9 +99,10 @@ def test_new_resolver_candidates_match_requirement(test_cases, factory):
assert req.is_satisfied_by(c)


def test_new_resolver_full_resolve(factory, provider):
def test_new_resolver_full_resolve(factory: Factory, provider: PipProvider) -> None:
"""A very basic full resolve"""
req = factory.make_requirement_from_spec("simplewheel", comes_from=None)
r = Resolver(provider, BaseReporter())
assert req is not None
r: Resolver[Requirement, Candidate, str] = Resolver(provider, BaseReporter())
result = r.resolve([req])
assert set(result.mapping.keys()) == {"simplewheel"}
39 changes: 26 additions & 13 deletions tests/unit/resolution_resolvelib/test_resolver.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from typing import Dict, List, Optional, Tuple, cast
from unittest import mock

import pytest
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib.resolvers import Result
from pip._vendor.resolvelib.structs import DirectedGraph

from pip._internal.index.package_finder import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.resolvelib.resolver import (
Expand All @@ -14,29 +17,31 @@


@pytest.fixture()
def resolver(preparer, finder):
def resolver(preparer: RequirementPreparer, finder: PackageFinder) -> Resolver:
resolver = Resolver(
preparer=preparer,
finder=finder,
wheel_cache=None,
make_install_req=mock.Mock(),
use_user_site="not-used",
ignore_dependencies="not-used",
ignore_installed="not-used",
ignore_requires_python="not-used",
force_reinstall="not-used",
use_user_site=False,
ignore_dependencies=False,
ignore_installed=False,
ignore_requires_python=False,
force_reinstall=False,
upgrade_strategy="to-satisfy-only",
)
return resolver


def _make_graph(edges):
def _make_graph(
edges: List[Tuple[Optional[str], Optional[str]]]
) -> "DirectedGraph[Optional[str]]":
"""Build graph from edge declarations."""

graph = DirectedGraph()
graph: "DirectedGraph[Optional[str]]" = DirectedGraph()
for parent, child in edges:
parent = canonicalize_name(parent) if parent else None
child = canonicalize_name(child) if child else None
parent = cast(str, canonicalize_name(parent)) if parent else None
child = cast(str, canonicalize_name(child)) if child else None
for v in (parent, child):
if v not in graph:
graph.add(v)
Expand Down Expand Up @@ -76,12 +81,16 @@ def _make_graph(edges):
),
],
)
def test_new_resolver_get_installation_order(resolver, edges, ordered_reqs):
def test_new_resolver_get_installation_order(
resolver: Resolver,
edges: List[Tuple[Optional[str], Optional[str]]],
ordered_reqs: List[str],
) -> None:
graph = _make_graph(edges)

# Mapping values and criteria are not used in test, so we stub them out.
mapping = {vertex: None for vertex in graph if vertex is not None}
resolver._result = Result(mapping, graph, criteria=None)
resolver._result = Result(mapping, graph, criteria=None) # type: ignore

reqset = RequirementSet()
for r in ordered_reqs:
Expand Down Expand Up @@ -229,7 +238,11 @@ def test_new_resolver_get_installation_order(resolver, edges, ordered_reqs):
),
],
)
def test_new_resolver_topological_weights(name, edges, expected_weights):
def test_new_resolver_topological_weights(
name: str,
edges: List[Tuple[Optional[str], Optional[str]]],
expected_weights: Dict[Optional[str], int],
) -> None:
graph = _make_graph(edges)

weights = get_topological_weights(graph, len(expected_weights))
Expand Down
Loading

0 comments on commit b392833

Please sign in to comment.