Include recursive JSON type checks

This commit is contained in:
Olivia Appleton 2022-10-18 19:39:00 -04:00
parent a02f088c0b
commit 6dae1fb0f9
GPG Key ID: AF65A9CA0FF7FD69
11 changed files with 38 additions and 25 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.6.0.107
current_version = 0.6.0.108
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.(?P<build>\d+)
serialize = {major}.{minor}.{patch}.{build}

View File

@ -14,7 +14,9 @@ from src.rule import get_rule
from src.util import DictDeserializable, explain_abstract, get_client
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Mapping, Optional
from typing import Any, Optional
from pymanifold.types import JSONDict
from src import Rule
from src.consts import OutcomeType
@ -98,7 +100,7 @@ class ManifoldRequest(DictDeserializable):
if self.answers is None or len(self.answers) < 2 or any(len(x) < 1 for x in self.answers):
raise ValueError("Invalid answers list")
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> JSONDict:
state = deepcopy(self.__dict__)
state['description'].pop('processed', None)
return {
@ -135,7 +137,7 @@ class CreationRequest:
self.manifold.description["processed"] = True
@classmethod
def from_dict(cls, obj: Mapping[str, Any]) -> 'CreationRequest':
def from_dict(cls, obj: JSONDict) -> 'CreationRequest':
"""Take a dictionary and return an instance of the associated class."""
obj = dict(obj)
manifold = ManifoldRequest.from_dict(obj.pop('manifold'))

View File

@ -2,7 +2,7 @@
name = ManifoldMarketManager
author = Olivia Appleton
author_email = Liv@OliviaAppleton.com
version = 0.6.0.107
version = 0.6.0.108
description = Tools and market management for manifold.markets
long_description = file: README.md
long_description_content_type = text/markdown

BIN
src.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 674 KiB

After

Width:  |  Height:  |  Size: 630 KiB

@ -1 +1 @@
Subproject commit c59a0d3f1375b54e21c56cc99e55688d8401c89f
Subproject commit 3159de40a59b04dab78fdfefa14a59e0615f3083

View File

@ -178,7 +178,7 @@ register_converter("Rule", loads)
register_adapter(market.Market, dumps)
register_converter("Market", loads)
VERSION = "0.6.0.107"
VERSION = "0.6.0.108"
__version_info__ = tuple(int(x) for x in VERSION.split('.'))
__all__ = [
"__version_info__", "VERSION", "DoResolveRule", "ResolutionValueRule", "Rule", "Market", "get_client", "rule",

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from pickle import loads, dumps
from pickle import dumps, loads
from typing import cast
from cryptography.fernet import Fernet

View File

@ -14,9 +14,12 @@ from ..util import round_sig_figs
from . import ResolutionValueRule, get_rule
if TYPE_CHECKING: # pragma: no cover
from typing import Any, ClassVar, Mapping, Sequence
from typing import Any, ClassVar, Sequence
from pymanifold.types import JSONDict
from ..market import Market
from ..util import ModJSONDict
SENTINEL_STUB = "A programatic explanation was not provided"
@ -54,10 +57,11 @@ class UnaryRule(AbstractRule[T]):
self.child.explain_specific(market, indent + 1, sig_figs)
@classmethod
def from_dict(cls, env: Mapping[str, Any]) -> 'UnaryRule[T]':
def from_dict(cls, env: ModJSONDict) -> 'UnaryRule[T]':
"""Take a dictionary and return an instance of the associated class."""
env_copy = dict(env)
type_, kwargs = env["child"]
child: tuple[str, ModJSONDict] = env["child"] # type: ignore[assignment]
type_, kwargs = child
env_copy["child"] = get_rule(type_).from_dict(kwargs)
return super().from_dict(env_copy)
@ -70,11 +74,12 @@ class BinaryRule(AbstractRule[T]):
rule2: Rule[T]
@classmethod
def from_dict(cls, env: Mapping[str, Any]) -> 'BinaryRule[T]':
def from_dict(cls, env: ModJSONDict) -> 'BinaryRule[T]':
"""Take a dictionary and return an instance of the associated class."""
env_copy = dict(env)
for name in ('rule1', 'rule2'):
type_, kwargs = env[name]
child: tuple[str, ModJSONDict] = env[name] # type: ignore[assignment]
type_, kwargs = child
env_copy[name] = get_rule(type_).from_dict(kwargs)
return super().from_dict(env_copy)
@ -98,13 +103,14 @@ class VariadicRule(AbstractRule[T]):
rules: list[Rule[T]] = Factory(list)
@classmethod
def from_dict(cls, env: Mapping[str, Any]) -> 'VariadicRule[T]':
def from_dict(cls, env: ModJSONDict) -> 'VariadicRule[T]':
"""Take a dictionary and return an instance of the associated class."""
env_copy = dict(env)
arr = env.get("rules", [])
env_copy["rules"] = [None] * len(arr)
arr: Sequence[tuple[str, ModJSONDict]] = env.get("rules", []) # type: ignore[assignment]
rules: list[None | Rule[Any]] = [None] * len(arr)
for idx, (type_, kwargs) in enumerate(arr):
env_copy["rules"][idx] = get_rule(type_).from_dict(kwargs)
rules[idx] = get_rule(type_).from_dict(kwargs)
env_copy["rules"] = rules
return super().from_dict(env_copy)
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
@ -129,7 +135,7 @@ class ResolveRandomSeed(ResolutionValueRule):
method: str = 'random'
rounds: int = 1
args: Sequence[Any] = ()
kwargs: dict[str, Any] = Factory(dict)
kwargs: JSONDict = Factory(dict)
def _value(self, market: Market) -> Any:
source = Random(self.seed)

View File

@ -17,6 +17,8 @@ from .abstract import BinaryRule, ResolveRandomSeed, UnaryRule, VariadicRule
if TYPE_CHECKING: # pragma: no cover
from typing import Any, ClassVar, DefaultDict, Literal, MutableSequence
from pymanifold.types import JSONDict, JSONType
from ..consts import FreeResponseResolution, MultipleChoiceResolution
from ..market import Market
@ -252,14 +254,14 @@ class ResolveMultipleValues(ResolutionValueRule):
return ret
@classmethod
def from_dict(cls, env: Mapping[str, Any]) -> 'ResolveMultipleValues':
def from_dict(cls, env: dict[str, JSONType | Rule]) -> 'ResolveMultipleValues':
"""Take a dictionary and return an instance of the associated class."""
env_copy: dict[str, Any] = dict(env)
shares: MutableSequence[tuple[ResolutionValueRule | tuple[str, dict[str, Any]], float]] = env.get('shares', [])
env_copy: JSONDict = dict(env)
shares: MutableSequence[tuple[ResolutionValueRule | tuple[str, JSONDict], float]] = env.get('shares', [])
new_shares = []
for rule, weight in shares:
try:
type_, kwargs = cast(Tuple[str, Dict[str, Any]], rule)
type_, kwargs = cast(Tuple[str, JSONDict], rule)
new_rule = get_rule(type_).from_dict(kwargs)
new_shares.append((new_rule, weight))
except Exception:

View File

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, cast
from attrs import define
from pymanifold.lib import ManifoldClient
from pymanifold.types import JSONDict
from ... import Rule
@ -31,7 +32,7 @@ class ManifoldUserRule(Rule[float]):
def _value(self, market: Market) -> float:
user = ManifoldClient()._get_user_raw(self.user)
return cast(float, user[self.attr][self.field])
return cast(float, cast(JSONDict, user[self.attr])[self.field])
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return f"{' ' * indent}- Resolves to the current {self.field} {self.attr_desc} user {self.user}.\n"

View File

@ -21,10 +21,12 @@ from pymanifold.utils.math import number_to_prob_cpmm1 # noqa: F401
from .consts import EnvironmentVariable, Outcome
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Callable, Collection, Iterable, Mapping, MutableSequence, Type, TypeVar
from typing import Any, Callable, Collection, Iterable, Mapping, MutableSequence, Sequence, Type, TypeVar, Union
from . import Market, Rule
ModJSONType = Union[int, float, bool, str, None, Rule[Any], Sequence['ModJSONType'], Mapping[str, 'ModJSONType']]
ModJSONDict = Mapping[str, ModJSONType]
T = TypeVar("T")
@ -32,7 +34,7 @@ class DictDeserializable:
"""A port of PyManifold's DictDeserializable that does not check against the signature."""
@classmethod
def from_dict(cls: Type[T], env: dict[str, Any]) -> T:
def from_dict(cls: Type[T], env: ModJSONDict) -> T:
"""Take a dictionary and return an instance of the associated class."""
return cls(**env)