Skip to content

Commit

Permalink
Type checking (#253)
Browse files Browse the repository at this point in the history
* typecheck constraints

* stricter typechecking

* typechecking dependencies

* more mypy exceptions in tests

* more precise ignore-missing-imports

* unused import

* remove faulty constraint difference implementation

* 'unimplemented', rather than 'bad'

* say what type errors we're ignoring

* remove unnecessary annotation

* fix implementations on EmptyConstraint

* consistency in formatting of mypy exclusions
  • Loading branch information
dimbleby committed Jan 25, 2022
1 parent 20f89b6 commit c21fd67
Show file tree
Hide file tree
Showing 24 changed files with 177 additions and 160 deletions.
32 changes: 19 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,21 @@ known_first_party = "poetry.core"
known_third_party = ["poetry.core._vendor"]

[tool.mypy]
follow_imports = "silent"
strict = true
explicit_package_bases = true
namespace_packages = true
mypy_path = "src"
files = "src, tests"
exclude = "(?x)(^tests/.*/fixtures | ^src/poetry/core/_vendor)"

[[tool.mypy.overrides]]
module = [
'jsonschema.*',
'setuptools.*',
'tomlkit.*',
]
ignore_missing_imports = true

# The following whitelist is used to allow for incremental adoption
# of Mypy. Modules should be removed from this whitelist as and when
# their respective type errors have been addressed. No new modules
Expand All @@ -102,29 +110,27 @@ exclude = "(?x)(^tests/.*/fixtures | ^src/poetry/core/_vendor)"
module = [
# src modules
'poetry.core.masonry.builders.*',
'poetry.core.packages.constraints',
'poetry.core.packages.constraints.constraint',
'poetry.core.packages.constraints.empty_constraint',
'poetry.core.packages.constraints.multi_constraint',
'poetry.core.packages.constraints.union_constraint',
'poetry.core.packages.utils.utils',
'poetry.core.packages.dependency',
'poetry.core.packages.directory_dependency',
'poetry.core.packages.file_dependency',
'poetry.core.packages.package',
'poetry.core.packages.project_package',
'poetry.core.packages.url_dependency',
'poetry.core.packages.vcs_dependency',
'poetry.core.pyproject.*',
'poetry.core.semver.*',
'poetry.core.toml.*',
'poetry.core.version.*',
# test modules
'tests.conftest',
'tests.integration.test_pep517',
'tests.json.*',
'tests.masonry.*',
'tests.packages.*',
'tests.pyproject.*',
'tests.semver.*',
'tests.version.*',
'tests.spdx.*',
'tests.test_factory',
'tests.testutils.*',
'tests.integration/test_pep517',
'tests.utils.*',
'tests.vcs.*',
'tests.version.*',
]
ignore_errors = true

Expand Down
2 changes: 1 addition & 1 deletion src/poetry/core/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from poetry.core.exceptions.base import PoetryCoreException


__all__ = [clazz.__name__ for clazz in {PoetryCoreException}]
__all__ = ["PoetryCoreException"]
4 changes: 3 additions & 1 deletion src/poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,9 @@ def create_dependency(
return dependency

@classmethod
def validate(cls, config: dict, strict: bool = False) -> Dict[str, List[str]]:
def validate(
cls, config: Dict[str, Any], strict: bool = False
) -> Dict[str, List[str]]:
"""
Checks the validity of a configuration
"""
Expand Down
6 changes: 4 additions & 2 deletions src/poetry/core/json/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
import os

from typing import Any
from typing import Dict
from typing import List


Expand All @@ -12,7 +14,7 @@ class ValidationError(ValueError):
pass


def validate_object(obj: dict, schema_name: str) -> List[str]:
def validate_object(obj: Dict[str, Any], schema_name: str) -> List[str]:
schema = os.path.join(SCHEMA_DIR, f"{schema_name}.json")

if not os.path.exists(schema):
Expand All @@ -24,7 +26,7 @@ def validate_object(obj: dict, schema_name: str) -> List[str]:
from jsonschema import Draft7Validator

validator = Draft7Validator(schema)
validation_errors = sorted(validator.iter_errors(obj), key=lambda e: e.path)
validation_errors = sorted(validator.iter_errors(obj), key=lambda e: e.path) # type: ignore[no-any-return]

errors = []

Expand Down
7 changes: 6 additions & 1 deletion src/poetry/core/masonry/utils/module.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from typing import Dict
from typing import List
from typing import Optional


if TYPE_CHECKING:
from poetry.core.masonry.utils.include import Include


class ModuleOrPackageNotFound(ValueError):

pass
Expand Down Expand Up @@ -102,7 +107,7 @@ def file(self) -> Path:
return self._path

@property
def includes(self) -> List:
def includes(self) -> List["Include"]:
return self._includes

def is_package(self) -> bool:
Expand Down
21 changes: 13 additions & 8 deletions src/poetry/core/packages/constraints/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import re

from typing import Union

from poetry.core.packages.constraints.any_constraint import AnyConstraint
from poetry.core.packages.constraints.base_constraint import BaseConstraint
from poetry.core.packages.constraints.constraint import Constraint
Expand All @@ -11,14 +9,9 @@


BASIC_CONSTRAINT = re.compile(r"^(!?==?)?\s*([^\s]+?)\s*$")
ConstraintTypes = Union[
AnyConstraint, Constraint, UnionConstraint, EmptyConstraint, MultiConstraint
]


def parse_constraint(
constraints: str,
) -> Union[AnyConstraint, UnionConstraint, Constraint]:
def parse_constraint(constraints: str) -> BaseConstraint:
if constraints == "*":
return AnyConstraint()

Expand Down Expand Up @@ -64,3 +57,15 @@ def parse_single_constraint(constraint: str) -> Constraint:
return Constraint(version, op)

raise ValueError(f"Could not parse version constraint: {constraint}")


__all__ = [
"AnyConstraint",
"BaseConstraint",
"Constraint",
"EmptyConstraint",
"MultiConstraint",
"UnionConstraint",
"parse_constraint",
"parse_single_constraint",
]
20 changes: 7 additions & 13 deletions src/poetry/core/packages/constraints/any_constraint.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
from typing import TYPE_CHECKING

from poetry.core.packages.constraints.base_constraint import BaseConstraint
from poetry.core.packages.constraints.empty_constraint import EmptyConstraint


if TYPE_CHECKING:
from poetry.core.packages.constraints import ConstraintTypes # noqa


class AnyConstraint(BaseConstraint):
def allows(self, other: "ConstraintTypes") -> bool:
def allows(self, other: "BaseConstraint") -> bool:
return True

def allows_all(self, other: "ConstraintTypes") -> bool:
def allows_all(self, other: "BaseConstraint") -> bool:
return True

def allows_any(self, other: "ConstraintTypes") -> bool:
def allows_any(self, other: "BaseConstraint") -> bool:
return True

def difference(self, other: "ConstraintTypes") -> "ConstraintTypes":
def difference(self, other: "BaseConstraint") -> "BaseConstraint":
if other.is_any():
return EmptyConstraint()

return other
raise ValueError("Unimplemented constraint difference")

def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes":
def intersect(self, other: "BaseConstraint") -> "BaseConstraint":
return other

def union(self, other: "ConstraintTypes") -> "AnyConstraint":
def union(self, other: "BaseConstraint") -> "AnyConstraint":
return AnyConstraint()

def is_any(self) -> bool:
Expand Down
19 changes: 6 additions & 13 deletions src/poetry/core/packages/constraints/base_constraint.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
from typing import TYPE_CHECKING


if TYPE_CHECKING:
from poetry.core.packages.constraints import ConstraintTypes # noqa


class BaseConstraint:
def allows(self, other: "ConstraintTypes") -> bool:
def allows(self, other: "BaseConstraint") -> bool:
raise NotImplementedError()

def allows_all(self, other: "ConstraintTypes") -> bool:
def allows_all(self, other: "BaseConstraint") -> bool:
raise NotImplementedError()

def allows_any(self, other: "ConstraintTypes") -> bool:
def allows_any(self, other: "BaseConstraint") -> bool:
raise NotImplementedError()

def difference(self, other: "ConstraintTypes") -> "ConstraintTypes":
def difference(self, other: "BaseConstraint") -> "BaseConstraint":
raise NotImplementedError()

def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes":
def intersect(self, other: "BaseConstraint") -> "BaseConstraint":
raise NotImplementedError()

def union(self, other: "ConstraintTypes") -> "ConstraintTypes":
def union(self, other: "BaseConstraint") -> "BaseConstraint":
raise NotImplementedError()

def is_any(self) -> bool:
Expand Down
20 changes: 9 additions & 11 deletions src/poetry/core/packages/constraints/constraint.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import operator

from typing import TYPE_CHECKING
from typing import Any
from typing import Union

from poetry.core.packages.constraints.base_constraint import BaseConstraint
from poetry.core.packages.constraints.empty_constraint import EmptyConstraint


if TYPE_CHECKING:
from poetry.core.packages.constraints import ConstraintTypes # noqa


class Constraint(BaseConstraint):

OP_EQ = operator.eq
Expand All @@ -37,7 +32,10 @@ def version(self) -> str:
def operator(self) -> str:
return self._operator

def allows(self, other: "ConstraintTypes") -> bool:
def allows(self, other: "BaseConstraint") -> bool:
if not isinstance(other, Constraint):
raise ValueError("Unimplemented comparison of constraints")

is_equal_op = self._operator == "=="
is_non_equal_op = self._operator == "!="
is_other_equal_op = other.operator == "=="
Expand All @@ -58,13 +56,13 @@ def allows(self, other: "ConstraintTypes") -> bool:

return False

def allows_all(self, other: "ConstraintTypes") -> bool:
def allows_all(self, other: "BaseConstraint") -> bool:
if not isinstance(other, Constraint):
return other.is_empty()

return other == self

def allows_any(self, other: "ConstraintTypes") -> bool:
def allows_any(self, other: "BaseConstraint") -> bool:
if isinstance(other, Constraint):
is_non_equal_op = self._operator == "!="
is_other_non_equal_op = other.operator == "!="
Expand All @@ -75,14 +73,14 @@ def allows_any(self, other: "ConstraintTypes") -> bool:
return other.allows(self)

def difference(
self, other: "ConstraintTypes"
self, other: "BaseConstraint"
) -> Union["Constraint", "EmptyConstraint"]:
if other.allows(self):
return EmptyConstraint()

return self

def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes":
def intersect(self, other: "BaseConstraint") -> "BaseConstraint":
from poetry.core.packages.constraints.multi_constraint import MultiConstraint

if isinstance(other, Constraint):
Expand All @@ -102,7 +100,7 @@ def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes":

return other.intersect(self)

def union(self, other: "ConstraintTypes") -> "ConstraintTypes":
def union(self, other: "BaseConstraint") -> "BaseConstraint":
if isinstance(other, Constraint):
from poetry.core.packages.constraints.union_constraint import (
UnionConstraint,
Expand Down
31 changes: 14 additions & 17 deletions src/poetry/core/packages/constraints/empty_constraint.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
from typing import TYPE_CHECKING

from poetry.core.packages.constraints.base_constraint import BaseConstraint


if TYPE_CHECKING:
from poetry.core.packages.constraints import ConstraintTypes # noqa


class EmptyConstraint(BaseConstraint):

pretty_string = None

def matches(self, _: "ConstraintTypes") -> bool:
def matches(self, _: "BaseConstraint") -> bool:
return True

def is_empty(self) -> bool:
return True

def allows(self, other: "ConstraintTypes") -> bool:
def allows(self, other: "BaseConstraint") -> bool:
return False

def allows_all(self, other: "ConstraintTypes") -> bool:
return True
def allows_all(self, other: "BaseConstraint") -> bool:
return other.is_empty()

def allows_any(self, other: "ConstraintTypes") -> bool:
return True
def allows_any(self, other: "BaseConstraint") -> bool:
return False

def intersect(self, other: "BaseConstraint") -> "BaseConstraint":
return self

def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes":
return other
def difference(self, other: "BaseConstraint") -> "BaseConstraint":
return self

def difference(self, other: "ConstraintTypes") -> None:
return
def __eq__(self, other: object) -> bool:
if not isinstance(other, BaseConstraint):
return False

def __eq__(self, other: "ConstraintTypes") -> bool:
return other.is_empty()

def __str__(self) -> str:
Expand Down
Loading

0 comments on commit c21fd67

Please sign in to comment.