-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #796 from sirosen/spartan-gare
Make GARE validation as simple as possible
- Loading branch information
Showing
9 changed files
with
271 additions
and
461 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
src/globus_sdk/experimental/auth_requirements_error/_serializable.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from __future__ import annotations | ||
|
||
import inspect | ||
import typing as t | ||
|
||
T = t.TypeVar("T", bound="Serializable") | ||
|
||
|
||
class Serializable: | ||
_EXCLUDE_VARS: t.ClassVar[tuple[str, ...]] = ("self", "extra") | ||
extra: dict[str, t.Any] | ||
|
||
@classmethod | ||
def _supported_fields(cls) -> list[str]: | ||
signature = inspect.signature(cls.__init__) | ||
return [ | ||
name | ||
for name in signature.parameters.keys() | ||
if name not in cls._EXCLUDE_VARS | ||
] | ||
|
||
@classmethod | ||
def from_dict(cls: type[T], data: dict[str, t.Any]) -> T: | ||
""" | ||
Instantiate from a dictionary. | ||
:param data: The dictionary to create the error from. | ||
:type data: dict | ||
""" | ||
|
||
# Extract any extra fields | ||
extras = {k: v for k, v in data.items() if k not in cls._supported_fields()} | ||
kwargs: dict[str, t.Any] = {"extra": extras} | ||
# Ensure required fields are supplied | ||
for field_name in cls._supported_fields(): | ||
kwargs[field_name] = data.get(field_name) | ||
|
||
return cls(**kwargs) | ||
|
||
def to_dict(self, include_extra: bool = False) -> dict[str, t.Any]: | ||
""" | ||
Render to a dictionary. | ||
:param include_extra: Whether to include stored extra (non-standard) fields in | ||
the returned dictionary. | ||
:type include_extra: bool | ||
""" | ||
result = {} | ||
|
||
# Set any authorization parameters | ||
for field in self._supported_fields(): | ||
value = getattr(self, field) | ||
if value is not None: | ||
if isinstance(value, Serializable): | ||
value = value.to_dict(include_extra=include_extra) | ||
result[field] = value | ||
|
||
# Set any extra fields | ||
if include_extra: | ||
result.update(self.extra) | ||
|
||
return result |
65 changes: 65 additions & 0 deletions
65
src/globus_sdk/experimental/auth_requirements_error/_validators.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from __future__ import annotations | ||
|
||
import typing as t | ||
|
||
from ._serializable import Serializable | ||
|
||
S = t.TypeVar("S", bound=Serializable) | ||
|
||
|
||
class ValidationError(ValueError): | ||
pass | ||
|
||
|
||
def str_(name: str, value: t.Any) -> str: | ||
if isinstance(value, str): | ||
return value | ||
raise ValidationError(f"'{name}' must be a string") | ||
|
||
|
||
def opt_str(name: str, value: t.Any) -> str | None: | ||
if value is None: | ||
return None | ||
if isinstance(value, str): | ||
return value | ||
raise ValidationError(f"'{name}' must be a string or null") | ||
|
||
|
||
def opt_bool(name: str, value: t.Any) -> bool | None: | ||
if value is None or isinstance(value, bool): | ||
return value | ||
raise ValidationError(f"'{name}' must be a bool or null") | ||
|
||
|
||
def str_list(name: str, value: t.Any) -> list[str]: | ||
if isinstance(value, list) and all(isinstance(s, str) for s in value): | ||
return value | ||
raise ValidationError(f"'{name}' must be a list of strings") | ||
|
||
|
||
def opt_str_list(name: str, value: t.Any) -> list[str] | None: | ||
if value is None: | ||
return None | ||
if isinstance(value, list) and all(isinstance(s, str) for s in value): | ||
return value | ||
raise ValidationError(f"'{name}' must be a list of strings or null") | ||
|
||
|
||
def opt_str_list_or_commasep(name: str, value: t.Any) -> list[str] | None: | ||
if value is None: | ||
return None | ||
if isinstance(value, str): | ||
value = value.split(",") | ||
if isinstance(value, list) and all(isinstance(s, str) for s in value): | ||
return value | ||
raise ValidationError( | ||
f"'{name}' must be a list of strings or a comma-delimited string or null" | ||
) | ||
|
||
|
||
def instance_or_dict(name: str, value: t.Any, cls: type[S]) -> S: | ||
if isinstance(value, cls): | ||
return value | ||
if isinstance(value, dict): | ||
return cls.from_dict(value) | ||
raise ValidationError(f"'{name}' must be a '{cls.__name__}' object or a dictionary") |
Oops, something went wrong.