Skip to content

[WIP] Make serializer interface streaming #20

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

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[**]
charset = utf-8
end_of_line = lf

indent_size = 4
indent_style = tab
max_line_length = 100
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ matrix:
include:
- python: 3.7
env: TOXENV=py37
- python: 3.8
env: TOXENV=py38
- python: 3.7
env: TOXENV=styleck
- python: 3.7
env: TOXENV=typeck

install:
- pip install tox
# - pip install -r requirements.txt

script: tox
6 changes: 5 additions & 1 deletion datastore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"BinaryNullDatastore", "BinaryDictDatastore",
"ObjectNullDatastore", "ObjectDictDatastore",
"Query", "Cursor",
"SerializerAdapter",
"SerializerAdapter", "SerializingError",
"SerializeError", "ParseError",

"abc", "typing", "util"
)
Expand All @@ -38,6 +39,9 @@

# import core.serialize
from .core.serialize import SerializerAdapter
from .core.serialize import SerializingError
from .core.serialize import SerializeError
from .core.serialize import ParseError


### Exposed submodules ###
Expand Down
13 changes: 13 additions & 0 deletions datastore/abc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
__all__ = (
"BinaryDatastore",
"ObjectDatastore",

"BinaryAdapter",
"ObjectAdapter",

"ReceiveChannel",
"ReceiveStream",

"Serializer",
)

from .core.binarystore import Datastore as BinaryDatastore
from .core.objectstore import Datastore as ObjectDatastore

Expand Down
13 changes: 9 additions & 4 deletions datastore/adapter/_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

import datastore

T_co = typing.TypeVar("T_co", covariant=True)

#XXX: Maybe retry describing this cooperative multiple inheritance scheme if these are ever added:
#
# * https://github.com/python/mypy/issues/7191 (Mixin classes in general)
# * https://github.com/python/mypy/issues/7790 (Associated types)
# * https://github.com/python/mypy/issues/7791 (Types of generic classes)
DS = typing.TypeVar("DS", datastore.abc.BinaryDatastore, datastore.abc.ObjectDatastore)
DA = typing.TypeVar("DA", datastore.abc.BinaryAdapter, datastore.abc.ObjectAdapter)
RT = typing.TypeVar("RT", datastore.abc.ReceiveStream, datastore.abc.ReceiveChannel)
DS = typing.TypeVar("DS", datastore.abc.BinaryDatastore,
datastore.abc.ObjectDatastore[T_co]) # type: ignore[valid-type] # noqa: F821
DA = typing.TypeVar("DA", datastore.abc.BinaryAdapter,
datastore.abc.ObjectAdapter[T_co, T_co]) # type: ignore[valid-type] # noqa: F821, E501
RT = typing.TypeVar("RT", datastore.abc.ReceiveStream,
datastore.abc.ReceiveChannel[T_co]) # type: ignore[valid-type] # noqa: F821
RV = typing.TypeVar("RV", bytes, typing.List[T_co]) # type: ignore[valid-type] # noqa: F821


# Workaround for https://github.com/python/mypy/issues/708
Expand All @@ -29,6 +33,7 @@ def __set__(self, oself: typing.Any, value: T) -> None:

class DatastoreCollectionMixin(typing.Generic[DS]):
"""Represents a collection of datastores."""
__slots__ = ()

_stores: typing.List[DS]

Expand Down
7 changes: 5 additions & 2 deletions datastore/adapter/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import datastore


__all__ = ["ObjectDirectorySupport", "ObjectDatastore"]
__all__ = ("ObjectDirectorySupport", "ObjectDatastore")


T_co = typing.TypeVar("T_co", covariant=True)
Expand Down Expand Up @@ -153,6 +152,10 @@ class ObjectDatastore(
>>> rds.get(a)
[]
"""
__slots__ = ()

FORWARD_CONTAINS = True
FORWARD_GET_ALL = True


async def _put(self, key: datastore.Key, value: datastore.abc.ReceiveChannel[T_co]) -> None:
Expand Down
122 changes: 85 additions & 37 deletions datastore/adapter/keytransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
import datastore

from . import _support
from ._support import DS, RT
from ._support import DS, RT, RV, T_co


__all__ = [
__all__ = (
"BinaryAdapter",
"ObjectAdapter",

Expand All @@ -18,14 +17,14 @@

"BinaryNestedPathAdapter",
"ObjectNestedPathAdapter",
]
)



KEY_TRANSFORM_T = typing.Callable[[datastore.Key], datastore.Key]


class _Adapter(typing.Generic[DS]):
class _Adapter(typing.Generic[DS, RT, RV]):
"""Represents a simple DatastoreAdapter that applies a transform on all incoming
keys. For example:

Expand All @@ -46,59 +45,72 @@ class _Adapter(typing.Generic[DS]):
>>> ds.get(datastore.Key('/c/b/a'))
None
"""
__slots__ = ()

FORWARD_CONTAINS = True
FORWARD_GET_ALL = True

key_transform_fn: _support.FunctionProperty[KEY_TRANSFORM_T]


@typing.no_type_check
def __init__(self, *args, key_transform: KEY_TRANSFORM_T = (lambda k: k), **kwargs):
"""Initializes KeyTransformDatastore with `keytransform` function."""
self.key_transform_fn = key_transform
super().__init__(*args, **kwargs)
super().__init__(*args, **kwargs) # type: ignore[call-arg] # noqa: F821


@typing.no_type_check
async def get(self, key: datastore.Key) -> RT:
"""Return the object named by keytransform(key)."""
return await super().get(self.key_transform_fn(key))
return await super().get(self.key_transform_fn(key)) # type: ignore[misc] # noqa: F821


async def get_all(self, key: datastore.Key) -> RV:
"""Return the object named by keytransform(key)."""
return await super().get_all(self.key_transform_fn(key)) # type: ignore[misc] # noqa: F821


@typing.no_type_check
async def _put(self, key: datastore.Key, value: RT) -> None:
"""Stores the object names by keytransform(key)."""
await super()._put(self.key_transform_fn(key), value)
await super()._put(self.key_transform_fn(key), value) # type: ignore[misc] # noqa: F821


@typing.no_type_check
async def delete(self, key: datastore.Key) -> None:
"""Removes the object named by keytransform(key)."""
await super().delete(self.key_transform_fn(key))
await super().delete(self.key_transform_fn(key)) # type: ignore[misc] # noqa: F821


@typing.no_type_check
async def contains(self, key: datastore.Key) -> bool:
"""Returns whether the object named by key is in this datastore."""
return await super().contains(self.key_transform_fn(key))
return await super().contains(self.key_transform_fn(key)) # type: ignore[misc] # noqa: F821


@typing.no_type_check
async def query(self, query: datastore.Query) -> datastore.Cursor:
"""Returns a sequence of objects matching criteria expressed in `query`"""
query = query.copy()
query.key = self.key_transform_fn(query.key)
return await super().query(query)

return await super().query(query) # type: ignore[misc] # noqa: F821

class BinaryAdapter(_Adapter[datastore.abc.BinaryDatastore], datastore.abc.BinaryAdapter):
...

class BinaryAdapter(
_Adapter[datastore.abc.BinaryDatastore, datastore.abc.ReceiveStream, bytes],
datastore.abc.BinaryAdapter
):
__slots__ = ("key_transform_fn",)

class ObjectAdapter(_Adapter[datastore.abc.ObjectDatastore], datastore.abc.ObjectAdapter):
...

class ObjectAdapter(
typing.Generic[T_co],
_Adapter[
datastore.abc.ObjectDatastore[T_co],
datastore.abc.ReceiveChannel[T_co],
typing.List[T_co]
],
datastore.abc.ObjectAdapter[T_co, T_co]
):
__slots__ = ("key_transform_fn",)


class _LowercaseKeyAdapter(_Adapter[DS], typing.Generic[DS]):
class _LowercaseKeyAdapter(_Adapter[DS, RT, RV], typing.Generic[DS, RT, RV]):
"""Represents a simple DatastoreAdapter that lowercases all incoming keys.

For example:
Expand All @@ -121,6 +133,7 @@ class _LowercaseKeyAdapter(_Adapter[DS], typing.Generic[DS]):
>>> lds.get(datastore.Key('HeLlO'))
'world'
"""
__slots__ = ()

def __init__(self, *args, **kwargs):
"""Initializes KeyTransformDatastore with `key_transform` function."""
Expand All @@ -132,16 +145,27 @@ def lowercase_key(cls, key: datastore.Key) -> datastore.Key:
return datastore.Key(str(key).lower())


class BinaryLowercaseKeyAdapter(_LowercaseKeyAdapter[datastore.abc.BinaryDatastore], datastore.abc.BinaryAdapter): # noqa: E501
...
class BinaryLowercaseKeyAdapter(
_LowercaseKeyAdapter[datastore.abc.BinaryDatastore, datastore.abc.ReceiveStream, bytes],
datastore.abc.BinaryAdapter
):
__slots__ = ("key_transform_fn",)


class ObjectLowercaseKeyAdapter(_LowercaseKeyAdapter[datastore.abc.ObjectDatastore], datastore.abc.ObjectAdapter): # noqa: E501
...
class ObjectLowercaseKeyAdapter(
typing.Generic[T_co],
_LowercaseKeyAdapter[
datastore.abc.ObjectDatastore[T_co],
datastore.abc.ReceiveChannel[T_co],
typing.List[T_co]
],
datastore.abc.ObjectAdapter[T_co, T_co]
):
__slots__ = ("key_transform_fn",)



class _NamespaceAdapter(_Adapter[DS], typing.Generic[DS]):
class _NamespaceAdapter(_Adapter[DS, RT, RV], typing.Generic[DS, RT, RV]):
"""Represents a simple DatastoreAdapter that namespaces all incoming keys.
For example:

Expand All @@ -163,6 +187,7 @@ class _NamespaceAdapter(_Adapter[DS], typing.Generic[DS]):
>>> ds.get(datastore.Key('/a/b/c/d'))
'cd'
"""
__slots__ = ()

namespace: datastore.Key

Expand All @@ -176,16 +201,27 @@ def namespace_key(self, key: datastore.Key) -> datastore.Key:
return self.namespace.child(key)


class BinaryNamespaceAdapter(_NamespaceAdapter[datastore.abc.BinaryDatastore], datastore.abc.BinaryAdapter): # noqa: E501
...
class BinaryNamespaceAdapter(
_NamespaceAdapter[datastore.abc.BinaryDatastore, datastore.abc.ReceiveStream, bytes],
datastore.abc.BinaryAdapter
):
__slots__ = ("key_transform_fn", "namespace",)


class ObjectNamespaceAdapter(_NamespaceAdapter[datastore.abc.ObjectDatastore], datastore.abc.ObjectAdapter): # noqa: E501
...
class ObjectNamespaceAdapter(
typing.Generic[T_co],
_NamespaceAdapter[
datastore.abc.ObjectDatastore[T_co],
datastore.abc.ReceiveChannel[T_co],
typing.List[T_co]
],
datastore.abc.ObjectAdapter[T_co, T_co]
):
__slots__ = ("key_transform_fn", "namespace",)



class _NestedPathAdapter(_Adapter[DS], typing.Generic[DS]):
class _NestedPathAdapter(_Adapter[DS, RT, RV], typing.Generic[DS, RT, RV]):
"""Represents a simple DatastoreAdapter that shards/namespaces incoming keys.

Incoming keys are sharded into nested namespaces. The idea is to use the key
Expand All @@ -210,6 +246,7 @@ class _NestedPathAdapter(_Adapter[DS], typing.Generic[DS]):
>>> ds.get(datastore.Key('/ab/ca/bc/abc'))
2
"""
__slots__ = ()

_default_depth: int = 3
_default_length: int = 2
Expand Down Expand Up @@ -284,9 +321,20 @@ def nested_path(path: str, depth: int, length: int) -> str:
return '/'.join(components)


class BinaryNestedPathAdapter(_NestedPathAdapter[datastore.abc.BinaryDatastore], datastore.abc.BinaryAdapter): # noqa: E501
...
class BinaryNestedPathAdapter(
_NestedPathAdapter[datastore.abc.BinaryDatastore, datastore.abc.ReceiveStream, bytes],
datastore.abc.BinaryAdapter
):
__slots__ = ("key_transform_fn", "nest_depth", "nest_length", "nest_keyfn")


class ObjectNestedPathAdapter(_NestedPathAdapter[datastore.abc.ObjectDatastore], datastore.abc.ObjectAdapter): # noqa: E501
...
class ObjectNestedPathAdapter(
typing.Generic[T_co],
_NestedPathAdapter[
datastore.abc.ObjectDatastore[T_co],
datastore.abc.ReceiveChannel[T_co],
typing.List[T_co]
],
datastore.abc.ObjectAdapter[T_co, T_co]
):
__slots__ = ("key_transform_fn", "nest_depth", "nest_length", "nest_keyfn")
Loading