Skip to content

Commit 79a6adf

Browse files
committed
Improve our Pyright type coverage score
1 parent 56f3d74 commit 79a6adf

File tree

6 files changed

+65
-61
lines changed

6 files changed

+65
-61
lines changed

pyproject.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,6 @@ source-include = [
6767
[tool.ruff.lint]
6868
select = ["ALL"]
6969
ignore = [
70-
# Skip type annotation on **_
71-
"ANN003",
72-
7370
# Redundant as the formatter handles missing trailing commas.
7471
"COM812",
7572

@@ -95,7 +92,8 @@ ignorelist = ["id"]
9592

9693
[tool.ruff.lint.per-file-ignores]
9794
"docs/*" = ["ALL"]
98-
"src/minfraud/models.py" = [ "PLR0913" ]
95+
"src/minfraud/models.py" = ["ANN401", "PLR0913"]
96+
"src/minfraud/webservice.py" = ["ANN401"]
9997
"tests/*" = ["ANN201", "D"]
10098

10199
[tool.tox]

src/minfraud/models.py

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
import json
6-
from typing import TYPE_CHECKING
6+
from typing import TYPE_CHECKING, Any
77

88
import geoip2.models
99
import geoip2.records
@@ -23,7 +23,7 @@ def __hash__(self) -> int:
2323
# This is not particularly efficient, but I don't expect it to be used much.
2424
return hash(json.dumps(self.to_dict(), sort_keys=True))
2525

26-
def to_dict(self) -> dict: # noqa: C901
26+
def to_dict(self) -> dict[str, Any]: # noqa: C901
2727
"""Return a dict of the object suitable for serialization."""
2828
result = {}
2929
for key, value in self.__dict__.items():
@@ -100,7 +100,7 @@ class GeoIP2Location(geoip2.records.Location):
100100
`RFC 3339 <https://tools.ietf.org/html/rfc3339>`_. For instance, the
101101
local time in Boston might be returned as 2015-04-27T19:17:24-04:00."""
102102

103-
def __init__(self, *args, **kwargs) -> None: # noqa: ANN002
103+
def __init__(self, *args: Any, **kwargs: Any) -> None:
104104
"""Initialize a GeoIP2Location instance."""
105105
self.local_time = kwargs.get("local_time")
106106
super().__init__(*args, **kwargs)
@@ -131,11 +131,11 @@ def __init__(
131131
self,
132132
locales: Sequence[str] | None,
133133
*,
134-
country: dict | None = None,
135-
location: dict | None = None,
134+
country: dict[str, Any] | None = None,
135+
location: dict[str, Any] | None = None,
136136
risk: float | None = None,
137-
risk_reasons: list[dict] | None = None,
138-
**kwargs,
137+
risk_reasons: list[dict[str, Any]] | None = None,
138+
**kwargs: Any,
139139
) -> None:
140140
"""Initialize an IPAddress instance."""
141141
# For raw attribute
@@ -161,7 +161,7 @@ class ScoreIPAddress(_Serializable):
161161
"""This field contains the risk associated with the IP address. The value
162162
ranges from 0.01 to 99. A higher score indicates a higher risk."""
163163

164-
def __init__(self, *, risk: float | None = None, **_) -> None:
164+
def __init__(self, *, risk: float | None = None, **_: Any) -> None:
165165
"""Initialize a ScoreIPAddress instance."""
166166
self.risk = risk
167167

@@ -197,7 +197,7 @@ def __init__(
197197
matches_provided_name: bool | None = None,
198198
phone_number: str | None = None,
199199
matches_provided_phone_number: bool | None = None,
200-
**_,
200+
**_: Any,
201201
) -> None:
202202
"""Initialize an Issuer instance."""
203203
self.name = name
@@ -239,7 +239,7 @@ def __init__(
239239
id: str | None = None,
240240
last_seen: str | None = None,
241241
local_time: str | None = None,
242-
**_,
242+
**_: Any,
243243
) -> None:
244244
"""Initialize a Device instance."""
245245
self.confidence = confidence
@@ -277,7 +277,7 @@ def __init__(
277277
action: str | None = None,
278278
reason: str | None = None,
279279
rule_label: str | None = None,
280-
**_,
280+
**_: Any,
281281
) -> None:
282282
"""Initialize a Disposition instance."""
283283
self.action = action
@@ -293,7 +293,7 @@ class EmailDomain(_Serializable):
293293
was first seen by MaxMind. This is expressed using the ISO 8601 date
294294
format."""
295295

296-
def __init__(self, *, first_seen: str | None = None, **_) -> None:
296+
def __init__(self, *, first_seen: str | None = None, **_: Any) -> None:
297297
"""Initialize an EmailDomain instance."""
298298
self.first_seen = first_seen
299299

@@ -325,7 +325,7 @@ class Email(_Serializable):
325325

326326
def __init__(
327327
self,
328-
domain: dict | None = None,
328+
domain: dict[str, Any] | None = None,
329329
first_seen: str | None = None,
330330
is_disposable: bool | None = None, # noqa: FBT001
331331
is_free: bool | None = None, # noqa: FBT001
@@ -378,7 +378,7 @@ class CreditCard(_Serializable):
378378

379379
def __init__(
380380
self,
381-
issuer: dict | None = None,
381+
issuer: dict[str, Any] | None = None,
382382
country: str | None = None,
383383
brand: str | None = None,
384384
is_business: bool | None = None, # noqa: FBT001
@@ -432,7 +432,7 @@ def __init__(
432432
longitude: float | None = None,
433433
distance_to_ip_location: int | None = None,
434434
is_in_ip_country: bool | None = None,
435-
**_,
435+
**_: Any,
436436
) -> None:
437437
"""Initialize a BillingAddress instance."""
438438
self.is_postal_in_city = is_postal_in_city
@@ -487,7 +487,7 @@ def __init__(
487487
is_in_ip_country: bool | None = None,
488488
is_high_risk: bool | None = None,
489489
distance_to_billing_address: int | None = None,
490-
**_,
490+
**_: Any,
491491
) -> None:
492492
"""Initialize a ShippingAddress instance."""
493493
self.is_postal_in_city = is_postal_in_city
@@ -538,7 +538,7 @@ def __init__(
538538
matches_postal: bool | None = None,
539539
network_operator: str | None = None,
540540
number_type: str | None = None,
541-
**_,
541+
**_: Any,
542542
) -> None:
543543
"""Initialize a Phone instance."""
544544
self.country = country
@@ -573,7 +573,7 @@ def __init__(
573573
code: str | None = None,
574574
warning: str | None = None,
575575
input_pointer: str | None = None,
576-
**_,
576+
**_: Any,
577577
) -> None:
578578
"""Initialize a ServiceWarning instance."""
579579
self.code = code
@@ -717,7 +717,7 @@ def __init__(
717717
shipping_address: float | None = None,
718718
shipping_address_distance_to_ip_location: float | None = None,
719719
time_of_day: float | None = None,
720-
**_,
720+
**_: Any,
721721
) -> None:
722722
"""Initialize a Subscores instance."""
723723
self.avs_result = avs_result
@@ -831,7 +831,7 @@ def __init__(
831831
*,
832832
code: str | None = None,
833833
reason: str | None = None,
834-
**_,
834+
**_: Any,
835835
) -> None:
836836
"""Initialize a Reason instance."""
837837
self.code = code
@@ -855,8 +855,8 @@ def __init__(
855855
self,
856856
*,
857857
multiplier: float,
858-
reasons: list | None = None,
859-
**_,
858+
reasons: list[dict[str, Any]] | None = None,
859+
**_: Any,
860860
) -> None:
861861
"""Initialize a RiskScoreReason instance."""
862862
self.multiplier = multiplier
@@ -948,23 +948,23 @@ def __init__(
948948
self,
949949
locales: Sequence[str],
950950
*,
951-
billing_address: dict | None = None,
952-
billing_phone: dict | None = None,
953-
credit_card: dict | None = None,
954-
disposition: dict | None = None,
951+
billing_address: dict[str, Any] | None = None,
952+
billing_phone: dict[str, Any] | None = None,
953+
credit_card: dict[str, Any] | None = None,
954+
disposition: dict[str, Any] | None = None,
955955
funds_remaining: float,
956-
device: dict | None = None,
957-
email: dict | None = None,
956+
device: dict[str, Any] | None = None,
957+
email: dict[str, Any] | None = None,
958958
id: str,
959-
ip_address: dict | None = None,
959+
ip_address: dict[str, Any] | None = None,
960960
queries_remaining: int,
961961
risk_score: float,
962-
shipping_address: dict | None = None,
963-
shipping_phone: dict | None = None,
964-
subscores: dict | None = None,
965-
warnings: list[dict] | None = None,
966-
risk_score_reasons: list[dict] | None = None,
967-
**_,
962+
shipping_address: dict[str, Any] | None = None,
963+
shipping_phone: dict[str, Any] | None = None,
964+
subscores: dict[str, Any] | None = None,
965+
warnings: list[dict[str, Any]] | None = None,
966+
risk_score_reasons: list[dict[str, Any]] | None = None,
967+
**_: Any,
968968
) -> None:
969969
"""Initialize a Factors instance."""
970970
self.billing_address = BillingAddress(**(billing_address or {}))
@@ -1056,21 +1056,21 @@ def __init__(
10561056
self,
10571057
locales: Sequence[str],
10581058
*,
1059-
billing_address: dict | None = None,
1060-
billing_phone: dict | None = None,
1061-
credit_card: dict | None = None,
1062-
device: dict | None = None,
1063-
disposition: dict | None = None,
1064-
email: dict | None = None,
1059+
billing_address: dict[str, Any] | None = None,
1060+
billing_phone: dict[str, Any] | None = None,
1061+
credit_card: dict[str, Any] | None = None,
1062+
device: dict[str, Any] | None = None,
1063+
disposition: dict[str, Any] | None = None,
1064+
email: dict[str, Any] | None = None,
10651065
funds_remaining: float,
10661066
id: str,
1067-
ip_address: dict | None = None,
1067+
ip_address: dict[str, Any] | None = None,
10681068
queries_remaining: int,
10691069
risk_score: float,
1070-
shipping_address: dict | None = None,
1071-
shipping_phone: dict | None = None,
1072-
warnings: list[dict] | None = None,
1073-
**_,
1070+
shipping_address: dict[str, Any] | None = None,
1071+
shipping_phone: dict[str, Any] | None = None,
1072+
warnings: list[dict[str, Any]] | None = None,
1073+
**_: Any,
10741074
) -> None:
10751075
"""Initialize an Insights instance."""
10761076
self.billing_address = BillingAddress(**(billing_address or {}))
@@ -1128,14 +1128,14 @@ class Score(_Serializable):
11281128
def __init__(
11291129
self,
11301130
*,
1131-
disposition: dict | None = None,
1131+
disposition: dict[str, Any] | None = None,
11321132
funds_remaining: float,
11331133
id: str,
1134-
ip_address: dict | None = None,
1134+
ip_address: dict[str, Any] | None = None,
11351135
queries_remaining: int,
11361136
risk_score: float,
1137-
warnings: list[dict] | None = None,
1138-
**_,
1137+
warnings: list[dict[str, Any]] | None = None,
1138+
**_: Any,
11391139
) -> None:
11401140
"""Initialize a Score instance."""
11411141
self.disposition = Disposition(**(disposition or {}))

src/minfraud/validation.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import urllib.parse
1515
import uuid
1616
from decimal import Decimal
17+
from typing import Any as AnyType
1718

1819
from email_validator import validate_email
1920
from voluptuous import (
@@ -327,7 +328,7 @@ def _uri(s: str) -> str:
327328
return s
328329

329330

330-
validate_transaction = Schema(
331+
validate_transaction: Schema = Schema(
331332
{
332333
"account": {
333334
"user_id": str,
@@ -459,7 +460,7 @@ def _validate_at_least_one_identifier_field(report: dict) -> bool:
459460
return True
460461

461462

462-
def validate_report(report: dict) -> bool:
463+
def validate_report(report: dict[str, AnyType]) -> bool:
463464
"""Validate minFraud Transaction Report fields."""
464465
_validate_report_schema(report)
465466
_validate_at_least_one_identifier_field(report)

src/minfraud/webservice.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import json
66
from functools import partial
7-
from typing import TYPE_CHECKING, Any, Callable, cast
7+
from typing import TYPE_CHECKING, Any, cast
88

99
import aiohttp
1010
import aiohttp.http
@@ -26,7 +26,7 @@
2626
if TYPE_CHECKING:
2727
import sys
2828
import types
29-
from collections.abc import Sequence
29+
from collections.abc import Callable, Sequence
3030

3131
from requests.models import Response
3232

@@ -78,7 +78,7 @@ def _handle_success(
7878
self,
7979
raw_body: str,
8080
uri: str,
81-
model_class: Callable,
81+
model_class: Callable[..., Score | Factors | Insights],
8282
) -> Score | Factors | Insights:
8383
"""Handle successful response."""
8484
try:

tests/test_validation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
import unittest
44
from decimal import Decimal
5-
from typing import Any, Callable
5+
from typing import TYPE_CHECKING, Any
66

77
from voluptuous import MultipleInvalid
88

99
from minfraud.validation import validate_report, validate_transaction
1010

11+
if TYPE_CHECKING:
12+
from collections.abc import Callable
13+
1114

1215
class ValidationBase(unittest.TestCase):
1316
def setup_transaction(self, transaction: dict[str, Any]) -> None:

tests/test_webservice.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import unittest
88
from functools import partial
9-
from typing import TYPE_CHECKING, Any, Callable, cast
9+
from typing import TYPE_CHECKING, Any, cast
1010

1111
import pytest
1212

@@ -23,6 +23,8 @@
2323
from minfraud.webservice import AsyncClient, Client
2424

2525
if TYPE_CHECKING:
26+
from collections.abc import Callable
27+
2628
from pytest_httpserver import HTTPServer
2729

2830
minfraud.webservice._SCHEME = "http" # noqa: SLF001

0 commit comments

Comments
 (0)