Skip to content

Commit

Permalink
adds type hints to public APIs
Browse files Browse the repository at this point in the history
Closes #110
  • Loading branch information
ju1ius committed Nov 14, 2023
1 parent 30ae386 commit 8d0e8bf
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 30 deletions.
2 changes: 1 addition & 1 deletion observ/dict_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
}


class DictProxyBase(Proxy):
class DictProxyBase(Proxy[dict]):
def _orphaned_keydeps(self):
return set(proxy_db.attrs(self)["keydep"].keys()) - set(self.target.keys())

Expand Down
2 changes: 1 addition & 1 deletion observ/list_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
}


class ListProxyBase(Proxy):
class ListProxyBase(Proxy[list]):
pass


Expand Down
35 changes: 22 additions & 13 deletions observ/proxy.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from functools import partial
from typing import cast, Generic, TypedDict, TypeVar

from .proxy_db import proxy_db

T = TypeVar("T")

class Proxy:

class Proxy(Generic[T]):
"""
Proxy for an object/target.
Expand All @@ -16,9 +19,9 @@ class Proxy:
"""

__hash__ = None
__slots__ = ["target", "readonly", "shallow", "__weakref__"]
__slots__ = ("target", "readonly", "shallow", "__weakref__")

def __init__(self, target, readonly=False, shallow=False):
def __init__(self, target: T, readonly=False, shallow=False):
self.target = target
self.readonly = readonly
self.shallow = shallow
Expand All @@ -33,7 +36,7 @@ def __del__(self):
TYPE_LOOKUP = {}


def proxy(target, readonly=False, shallow=False):
def proxy(target: T, readonly=False, shallow=False) -> T:
"""
Returns a Proxy for the given object. If a proxy for the given
configuration already exists, it will return that instead of
Expand Down Expand Up @@ -67,14 +70,20 @@ def proxy(target, readonly=False, shallow=False):
return proxy_type(target, readonly=readonly, shallow=shallow)

if isinstance(target, tuple):
return tuple(proxy(x, readonly=readonly, shallow=shallow) for x in target)
return cast(
T, tuple(proxy(x, readonly=readonly, shallow=shallow) for x in target)
)

# We can't proxy a plain value
return target
return cast(T, target)


class Ref(TypedDict, Generic[T]):
value: T


def ref(target):
return proxy({"value": target})
def ref(target: T) -> Ref[T]:
return proxy(Ref(value=target))


reactive = proxy
Expand All @@ -83,7 +92,7 @@ def ref(target):
shallow_readonly = partial(proxy, shallow=True, readonly=True)


def to_raw(target):
def to_raw(target: Proxy[T] | T) -> T:
"""
Returns a raw object from which any trace of proxy has been replaced
with its wrapped target value.
Expand All @@ -92,15 +101,15 @@ def to_raw(target):
return to_raw(target.target)

if isinstance(target, list):
return [to_raw(t) for t in target]
return cast(T, [to_raw(t) for t in target])

if isinstance(target, dict):
return {key: to_raw(value) for key, value in target.items()}
return cast(T, {key: to_raw(value) for key, value in target.items()})

if isinstance(target, tuple):
return tuple(to_raw(t) for t in target)
return cast(T, tuple(to_raw(t) for t in target))

if isinstance(target, set):
return {to_raw(t) for t in target}
return cast(T, {to_raw(t) for t in target})

return target
2 changes: 1 addition & 1 deletion observ/set_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
}


class SetProxyBase(Proxy):
class SetProxyBase(Proxy[set]):
pass


Expand Down
9 changes: 6 additions & 3 deletions observ/store.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from functools import partial, wraps
from typing import Callable, Collection, TypeVar
from typing import Callable, Generic, TypeVar

import patchdiff

Expand Down Expand Up @@ -51,12 +51,15 @@ def decorator_computed(fn: T) -> T:
return decorator_computed(_fn)


class Store:
S = TypeVar("S")


class Store(Generic[S]):
"""
Store that tracks mutations to state in order to enable undo/redo functionality
"""

def __init__(self, state: Collection, strict=True):
def __init__(self, state: S, strict=True):
"""
Creates a store with the given state as the initial state.
When `strict` is False, calling mutations that do not result
Expand Down
23 changes: 12 additions & 11 deletions observ/watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,28 @@
from functools import partial, wraps
import inspect
from itertools import count
from typing import Any, Callable, Optional, TypeVar
from typing import Any, Callable, Generic, TypeVar
from weakref import ref, WeakSet

from .dep import Dep
from .dict_proxy import DictProxyBase
from .list_proxy import ListProxyBase
from .proxy import Proxy
from .scheduler import scheduler
from .set_proxy import SetProxyBase


T = TypeVar("T", bound=Callable[[], Any])
T = TypeVar("T")
Watchable = Callable[[], T] | T
WatchCallback = Callable[[], Any] | Callable[[T], Any] | Callable[[T, T], Any]


def watch(
fn: Callable[[], Any] | Proxy | list[Proxy],
callback: Optional[Callable] = None,
fn: Watchable[T],
callback: WatchCallback[T] | None = None,
sync: bool = False,
deep: bool | None = None,
immediate: bool = False,
):
) -> Watcher[T]:
watcher = Watcher(fn, sync=sync, lazy=False, deep=deep, callback=callback)
if immediate:
watcher.dirty = True
Expand All @@ -42,8 +43,8 @@ def watch(
watch_effect = partial(watch, immediate=False, deep=True, callback=None)


def computed(_fn=None, *, deep=True):
def decorator_computed(fn: T) -> T:
def computed(_fn: Callable[[], T] | None = None, *, deep=True) -> Callable[[], T]:
def decorator_computed(fn: Callable[[], T]) -> Callable[[], T]:
"""
Create a watcher for an expression.
Note: make sure fn doesn't need any arguments to run
Expand Down Expand Up @@ -113,7 +114,7 @@ class WrongNumberOfArgumentsError(TypeError):
pass


class Watcher:
class Watcher(Generic[T]):
__slots__ = [
"id",
"fn",
Expand All @@ -132,11 +133,11 @@ class Watcher:

def __init__(
self,
fn: Callable[[], Any] | Proxy | list[Proxy],
fn: Watchable[T],
sync: bool = False,
lazy: bool = True,
deep: bool | None = None,
callback: Callable = None,
callback: WatchCallback[T] | None = None,
) -> None:
"""
sync: Ignore the scheduler
Expand Down
4 changes: 4 additions & 0 deletions tests/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
"readonly",
"shallow",
"__weakref__",
# exclude attributes added by typing.Generic
"_is_protocol",
"__orig_bases__",
"__parameters__",
}


Expand Down

0 comments on commit 8d0e8bf

Please sign in to comment.