-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Flesh out more of jsonschema stubs #7950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
from collections.abc import Iterable | ||
from typing import Any | ||
from collections.abc import Callable, Iterable | ||
from typing import Any, TypeVar | ||
|
||
_F = TypeVar("_F", bound=Callable[..., Any]) | ||
|
||
class FormatChecker: | ||
checkers: Any | ||
checkers: dict[str, tuple[Callable[[Any], bool], Exception | tuple[Exception, ...]]] | ||
|
||
def __init__(self, formats: Iterable[str] | None = ...) -> None: ... | ||
def checks(self, format, raises=...): ... | ||
cls_checks: Any | ||
def check(self, instance, format) -> None: ... | ||
def conforms(self, instance, format) -> bool: ... | ||
def checks(self, format: str, raises: Exception | tuple[Exception, ...] = ...) -> Callable[[_F], _F]: ... | ||
@classmethod | ||
def cls_checks(cls, format: str, raises: Exception | tuple[Exception, ...] = ...) -> Callable[[_F], _F]: ... | ||
def check(self, instance: Any, format: str) -> None: ... | ||
def conforms(self, instance: Any, format: str) -> bool: ... | ||
|
||
draft3_format_checker: FormatChecker | ||
draft4_format_checker: FormatChecker | ||
|
@@ -16,28 +20,28 @@ draft7_format_checker: FormatChecker | |
draft201909_format_checker: FormatChecker | ||
draft202012_format_checker: FormatChecker | ||
|
||
def is_email(instance) -> bool: ... | ||
def is_ipv4(instance) -> bool: ... | ||
def is_ipv6(instance) -> bool: ... | ||
def is_email(instance: Any) -> bool: ... | ||
def is_ipv4(instance: Any) -> bool: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like this function often returns an Looks like the same goes for several other of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh wow, I completely missed that! I'd like to fix the runtime logic to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm okay with that 😄 I guessed that that was probably the case! |
||
def is_ipv6(instance: Any) -> bool: ... | ||
|
||
# is_host_name is only defined if fqdn is installed. | ||
def is_host_name(instance) -> bool: ... | ||
def is_idn_host_name(instance) -> bool: ... | ||
def is_uri(instance) -> bool: ... | ||
def is_uri_reference(instance) -> bool: ... | ||
def is_iri(instance) -> bool: ... | ||
def is_iri_reference(instance) -> bool: ... | ||
def is_datetime(instance) -> bool: ... | ||
def is_time(instance) -> bool: ... | ||
def is_regex(instance) -> bool: ... | ||
def is_date(instance) -> bool: ... | ||
def is_draft3_time(instance) -> bool: ... | ||
def is_css_color_code(instance) -> bool: ... | ||
def is_css21_color(instance) -> bool: ... | ||
def is_json_pointer(instance) -> bool: ... | ||
def is_relative_json_pointer(instance) -> bool: ... | ||
def is_uri_template(instance) -> bool: ... | ||
def is_host_name(instance: Any) -> bool: ... | ||
def is_idn_host_name(instance: Any) -> bool: ... | ||
def is_uri(instance: Any) -> bool: ... | ||
def is_uri_reference(instance: Any) -> bool: ... | ||
def is_iri(instance: Any) -> bool: ... | ||
def is_iri_reference(instance: Any) -> bool: ... | ||
def is_datetime(instance: Any) -> bool: ... | ||
def is_time(instance: Any) -> bool: ... | ||
def is_regex(instance: Any) -> bool: ... | ||
def is_date(instance: Any) -> bool: ... | ||
def is_draft3_time(instance: Any) -> bool: ... | ||
def is_css_color_code(instance: Any) -> bool: ... | ||
def is_css21_color(instance: Any) -> bool: ... | ||
def is_json_pointer(instance: Any) -> bool: ... | ||
def is_relative_json_pointer(instance: Any) -> bool: ... | ||
def is_uri_template(instance: Any) -> bool: ... | ||
|
||
# is_duration is only defined if isoduration is installed. | ||
def is_duration(instance) -> bool: ... | ||
def is_uuid(instance) -> bool: ... | ||
def is_duration(instance: Any) -> bool: ... | ||
def is_uuid(instance: Any) -> bool: ... |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -1,40 +1,50 @@ | ||||||||
from _typeshed import Self | ||||||||
from collections import deque | ||||||||
from collections.abc import Callable, Container, Iterable, Sequence | ||||||||
from typing import Any | ||||||||
|
||||||||
WEAK_MATCHES: Any | ||||||||
STRONG_MATCHES: Any | ||||||||
from jsonschema import _utils, protocols | ||||||||
|
||||||||
_RelevanceFuncType = Callable[[ValidationError], Any] | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Functions passed to the Line 1295 in 2d2b34c
Suggested change
(You'll have to import it from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, that's why I wanted this but decided against adding class _Comparable(Protocol):
def __lt__(self, other: Any) -> bool: ... to the stubs. I'll make the change, with great enthusiasm. (Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm rooting for it 🤞 but I think it's still too new to add it to |
||||||||
|
||||||||
WEAK_MATCHES: frozenset[str] | ||||||||
STRONG_MATCHES: frozenset[str] | ||||||||
|
||||||||
class _Error(Exception): | ||||||||
message: Any | ||||||||
path: Any | ||||||||
schema_path: Any | ||||||||
context: Any | ||||||||
cause: Any | ||||||||
validator: Any | ||||||||
message: str | ||||||||
path: deque[str] | ||||||||
relative_path: deque[str] | ||||||||
schema_path: deque[str] | ||||||||
relative_schema_path: deque[str] | ||||||||
context: list[ValidationError] | None | ||||||||
cause: Exception | None | ||||||||
validator: protocols.Validator | None | ||||||||
validator_value: Any | ||||||||
instance: Any | ||||||||
schema: Any | ||||||||
parent: Any | ||||||||
parent: _Error | None | ||||||||
def __init__( | ||||||||
self, | ||||||||
message, | ||||||||
validator=..., | ||||||||
path=..., | ||||||||
message: str, | ||||||||
validator: _utils.Unset | None | protocols.Validator = ..., | ||||||||
path: Sequence[str] = ..., | ||||||||
cause: Any | None = ..., | ||||||||
context=..., | ||||||||
context: Sequence[ValidationError] = ..., | ||||||||
validator_value=..., | ||||||||
instance=..., | ||||||||
schema=..., | ||||||||
schema_path=..., | ||||||||
parent: Any | None = ..., | ||||||||
instance: Any = ..., | ||||||||
schema: Any = ..., | ||||||||
schema_path: Sequence[str] = ..., | ||||||||
parent: _Error | None = ..., | ||||||||
) -> None: ... | ||||||||
@classmethod | ||||||||
def create_from(cls, other): ... | ||||||||
def create_from(cls: type[Self], other: _Error) -> Self: ... | ||||||||
@property | ||||||||
def absolute_path(self): ... | ||||||||
def absolute_path(self) -> Sequence[str]: ... | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We generally prefer to be as precise as possible with return types in typeshed, so that would imply using The same point applies to
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, I considered annotating it as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is incorrect (at least with jsonschema 4.4.0) because path entries may be indexes into a JSON array. For example: import jsonschema
SCHEMA = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {"type": "string"},
}
document = ["a", 12345, "c", "d"]
try:
jsonschema.validate(document, SCHEMA)
except jsonschema.ValidationError as e:
print(repr(e))
for path_entry in e.absolute_path:
print(repr(path_entry), type(path_entry))
I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes! Apologies for the oversight. These can be ints in precisely the case you mention. I'll double-check for other cases of this. (I'm not a typeshed maintainer, but I'll also go 👍 your PR.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not to worry, glad it was straightforward to sort out! |
||||||||
@property | ||||||||
def absolute_schema_path(self): ... | ||||||||
def absolute_schema_path(self) -> Sequence[str]: ... | ||||||||
@property | ||||||||
def json_path(self): ... | ||||||||
def json_path(self) -> str: ... | ||||||||
def _contents(self) -> dict[str, Any]: ... | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be a bit involved, but you could consider returning a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to punt on this for now, and just add a TODO comment to make the type more precise with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure |
||||||||
|
||||||||
class ValidationError(_Error): ... | ||||||||
class SchemaError(_Error): ... | ||||||||
|
@@ -68,8 +78,8 @@ class ErrorTree: | |||||||
@property | ||||||||
def total_errors(self): ... | ||||||||
|
||||||||
def by_relevance(weak=..., strong=...): ... | ||||||||
def by_relevance(weak: Container[str] = ..., strong: Container[str] = ...) -> _RelevanceFuncType: ... | ||||||||
|
||||||||
relevance: Any | ||||||||
relevance: _RelevanceFuncType | ||||||||
|
||||||||
def best_match(errors, key=...): ... | ||||||||
def best_match(errors: Iterable[ValidationError], key: _RelevanceFuncType = ...): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm reading the source code correctly, I think this (and several other of the
is_foo
functions here) can accept literally any type. In general, for functions like these, we prefer usingobject
instead ofAny
for parameter annotations.Any
is sort of an "escape hatch" to be used as a last resort, or for when the true type of a parameter isn't really expressible in the current type system.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll switch this.
Aside/tangent: using
object
in similar functions (e.g.ensure_is_foo(x: object) -> Foo
) can be less nice thanAny
because of how it responds to type narrowing. After you write enough "assert-is-X" utilities, it becomes a habit to useAny
in basically any case in which one could useobject
. This is all off-topic, but I wonder if changing the way thatobject
narrows would make it more comfortable/normal for typing users like myself to use it. When I first ran into this, I found it very surprising thatisinstance(x, str)
does not narrowx: object
tostr
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not true:
https://mypy-play.net/?mypy=latest&python=3.10&gist=5104b5bedec0b6dd05bd8b6cf23fc1ef
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. Hmm... I know I've run into unexpected issues in which
object
doesn't narrow butAny
does. But clearly it wasn't such a simple case. If I see this again, I'll make sure to pay closer attention.Sorry for the inaccuracy and distraction.