layercall 1.2.9__tar.gz → 1.2.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {layercall-1.2.9 → layercall-1.2.12}/PKG-INFO +3 -1
- {layercall-1.2.9 → layercall-1.2.12}/README.md +2 -0
- {layercall-1.2.9 → layercall-1.2.12}/layercall/__init__.py +36 -6
- {layercall-1.2.9 → layercall-1.2.12}/layercall.egg-info/PKG-INFO +3 -1
- {layercall-1.2.9 → layercall-1.2.12}/pyproject.toml +1 -1
- {layercall-1.2.9 → layercall-1.2.12}/layercall/py.typed +0 -0
- {layercall-1.2.9 → layercall-1.2.12}/layercall.egg-info/SOURCES.txt +0 -0
- {layercall-1.2.9 → layercall-1.2.12}/layercall.egg-info/dependency_links.txt +0 -0
- {layercall-1.2.9 → layercall-1.2.12}/layercall.egg-info/top_level.txt +0 -0
- {layercall-1.2.9 → layercall-1.2.12}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: layercall
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.12
|
|
4
4
|
Summary: Official LayerCall client — score an IP, email, phone, domain or a whole signup for fraud in one call. Zero dependencies.
|
|
5
5
|
Author: LayerCall
|
|
6
6
|
License: MIT
|
|
@@ -69,7 +69,9 @@ different lever: a limited account state, a delayed payout, or a review queue.
|
|
|
69
69
|
```python
|
|
70
70
|
lc.score_ip("185.220.101.1")
|
|
71
71
|
lc.verify_email("someone@mailinator.com")
|
|
72
|
+
lc.score_email("someone@mailinator.com") # the same call, by the name people guess
|
|
72
73
|
lc.lookup_phone("+14155552671")
|
|
74
|
+
lc.score_phone("+14155552671") # the same call, likewise
|
|
73
75
|
lc.lookup_phone("4155552671", country="US")
|
|
74
76
|
lc.score_domain("example.com")
|
|
75
77
|
# score_device is the one method you cannot try without a browser: it needs a
|
|
@@ -50,7 +50,9 @@ different lever: a limited account state, a delayed payout, or a review queue.
|
|
|
50
50
|
```python
|
|
51
51
|
lc.score_ip("185.220.101.1")
|
|
52
52
|
lc.verify_email("someone@mailinator.com")
|
|
53
|
+
lc.score_email("someone@mailinator.com") # the same call, by the name people guess
|
|
53
54
|
lc.lookup_phone("+14155552671")
|
|
55
|
+
lc.score_phone("+14155552671") # the same call, likewise
|
|
54
56
|
lc.lookup_phone("4155552671", country="US")
|
|
55
57
|
lc.score_domain("example.com")
|
|
56
58
|
# score_device is the one method you cannot try without a browser: it needs a
|
|
@@ -32,7 +32,7 @@ import urllib.parse
|
|
|
32
32
|
import urllib.request
|
|
33
33
|
from typing import Any, Literal, Mapping, Sequence
|
|
34
34
|
|
|
35
|
-
__version__ = "1.2.
|
|
35
|
+
__version__ = "1.2.12"
|
|
36
36
|
# Verdict and Strictness are part of the signature — `verdict: Verdict` and
|
|
37
37
|
# `strictness: Strictness` appear on the public methods below — so a caller
|
|
38
38
|
# writing a type annotation needs them, and `from layercall import *` was not
|
|
@@ -51,11 +51,14 @@ class LayerCallError(Exception):
|
|
|
51
51
|
def __init__(
|
|
52
52
|
self,
|
|
53
53
|
message: str,
|
|
54
|
-
status: int,
|
|
54
|
+
status: int = 0,
|
|
55
55
|
code: str | None = None,
|
|
56
56
|
request_id: str | None = None,
|
|
57
57
|
) -> None:
|
|
58
|
+
# status defaults to 0 (no HTTP response) so a test can raise
|
|
59
|
+
# LayerCallError("boom") the way it can in the Node SDK.
|
|
58
60
|
super().__init__(message)
|
|
61
|
+
self.message = message
|
|
59
62
|
self.status = status
|
|
60
63
|
self.code = code
|
|
61
64
|
self.request_id = request_id
|
|
@@ -70,8 +73,16 @@ class LayerCallError(Exception):
|
|
|
70
73
|
"""Bad or revoked key."""
|
|
71
74
|
return self.status in (401, 403)
|
|
72
75
|
|
|
76
|
+
def __str__(self) -> str:
|
|
77
|
+
# print(err) is what a newcomer does first, and it showed only the
|
|
78
|
+
# message - not the status or code they are told to branch on.
|
|
79
|
+
bits = [f"HTTP {self.status}"] if self.status else []
|
|
80
|
+
if self.code:
|
|
81
|
+
bits.append(self.code)
|
|
82
|
+
return f"{self.message} ({', '.join(bits)})" if bits else self.message
|
|
83
|
+
|
|
73
84
|
def __repr__(self) -> str: # pragma: no cover - debugging aid
|
|
74
|
-
return f"LayerCallError(status={self.status}, code={self.code!r}, message={self!
|
|
85
|
+
return f"LayerCallError(status={self.status}, code={self.code!r}, message={self.message!r})"
|
|
75
86
|
|
|
76
87
|
|
|
77
88
|
class LayerCall:
|
|
@@ -240,6 +251,11 @@ class LayerCall:
|
|
|
240
251
|
query={"phone": phone, "country": country, "strictness": strictness},
|
|
241
252
|
)
|
|
242
253
|
|
|
254
|
+
# score_email / score_phone are the names a newcomer guesses, since the
|
|
255
|
+
# other identifiers are score_*. Aliases, so existing code keeps working.
|
|
256
|
+
score_email = verify_email
|
|
257
|
+
score_phone = lookup_phone
|
|
258
|
+
|
|
243
259
|
def score_domain(self, domain: str, *, strictness: Strictness | None = None) -> dict[str, Any]:
|
|
244
260
|
"""RDAP registration date, registrar, MX/SPF/DMARC, risky TLD."""
|
|
245
261
|
return self._request(
|
|
@@ -428,7 +444,9 @@ class LayerCall:
|
|
|
428
444
|
if v is not None
|
|
429
445
|
}
|
|
430
446
|
if not payload:
|
|
431
|
-
raise ValueError(
|
|
447
|
+
raise ValueError(
|
|
448
|
+
"score_user: provide at least one of ip, email, phone, domain or device_id."
|
|
449
|
+
)
|
|
432
450
|
return self._request("/v1/score/user", body=payload)
|
|
433
451
|
|
|
434
452
|
# -- custom rules -----------------------------------------------------
|
|
@@ -494,12 +512,24 @@ class LayerCall:
|
|
|
494
512
|
def batch(
|
|
495
513
|
self,
|
|
496
514
|
type: Literal["ip", "email", "phone", "domain"],
|
|
497
|
-
items: Sequence[str],
|
|
515
|
+
items: Sequence[str] | None = None,
|
|
498
516
|
*,
|
|
499
517
|
strictness: Strictness | None = None,
|
|
500
518
|
phone_country: str | None = None,
|
|
501
519
|
) -> dict[str, Any]:
|
|
502
|
-
"""Up to 500 values of one type. Each item carries its own error.
|
|
520
|
+
"""Up to 500 values of one type. Each item carries its own error.
|
|
521
|
+
|
|
522
|
+
lc.batch("email", ["a@x.com", "b@y.com"])
|
|
523
|
+
"""
|
|
524
|
+
# The natural first guess is a list of {"type", "value"} dicts, and it
|
|
525
|
+
# used to fail with a bare "missing 1 required positional argument".
|
|
526
|
+
# ValueError, not TypeError: the call-every-method test reads a
|
|
527
|
+
# TypeError as "this method can never reach the network".
|
|
528
|
+
if not isinstance(type, str) or items is None:
|
|
529
|
+
raise ValueError(
|
|
530
|
+
'batch takes the type once, then the values: '
|
|
531
|
+
'lc.batch("ip", ["8.8.8.8", "1.1.1.1"])'
|
|
532
|
+
)
|
|
503
533
|
if len(items) > 500:
|
|
504
534
|
raise ValueError(
|
|
505
535
|
f"batch: {len(items)} items exceeds the limit of 500 — send several batches."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: layercall
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.12
|
|
4
4
|
Summary: Official LayerCall client — score an IP, email, phone, domain or a whole signup for fraud in one call. Zero dependencies.
|
|
5
5
|
Author: LayerCall
|
|
6
6
|
License: MIT
|
|
@@ -69,7 +69,9 @@ different lever: a limited account state, a delayed payout, or a review queue.
|
|
|
69
69
|
```python
|
|
70
70
|
lc.score_ip("185.220.101.1")
|
|
71
71
|
lc.verify_email("someone@mailinator.com")
|
|
72
|
+
lc.score_email("someone@mailinator.com") # the same call, by the name people guess
|
|
72
73
|
lc.lookup_phone("+14155552671")
|
|
74
|
+
lc.score_phone("+14155552671") # the same call, likewise
|
|
73
75
|
lc.lookup_phone("4155552671", country="US")
|
|
74
76
|
lc.score_domain("example.com")
|
|
75
77
|
# score_device is the one method you cannot try without a browser: it needs a
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "layercall"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.12"
|
|
8
8
|
description = "Official LayerCall client — score an IP, email, phone, domain or a whole signup for fraud in one call. Zero dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|