Skip to content

Commit

Permalink
Merge branch 'develop': v1.3.0. Safe kwargs for all AliceObjects
Browse files Browse the repository at this point in the history
+ store raw json in models
  • Loading branch information
mahenzon committed Feb 18, 2020
2 parents 5efad29 + 0ce3594 commit acb3d05
Show file tree
Hide file tree
Showing 24 changed files with 133 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Python tests
on: [push]

jobs:
build:
unit-tests:

runs-on: ubuntu-latest

Expand Down
2 changes: 1 addition & 1 deletion aioalice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())


__version__ = '1.2.5'
__version__ = '1.3.0'
12 changes: 7 additions & 5 deletions aioalice/types/alice_request.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from attr import attrs, attrib
from aiohttp.web import Request as WebRequest
from aioalice.utils import ensure_cls
from . import AliceObject, Meta, Session, \
Card, Request, Response, AliceResponse
from ..utils import ensure_cls


@attrs
Expand All @@ -18,7 +18,7 @@ def _response(self, response):
return AliceResponse(
response=response,
session=self.session.base,
version=self.version
version=self.version,
)

def response(self, responose_or_text, **kwargs):
Expand Down Expand Up @@ -53,8 +53,9 @@ def response_big_image(self, text, image_id, title, description, button=None, **
"""
return self._response(
Response(
text, **kwargs,
text,
card=Card.big_image(image_id, title, description, button),
**kwargs,
)
)

Expand All @@ -71,7 +72,8 @@ def response_items_list(self, text, header, items, footer=None, **kwargs):
"""
return self._response(
Response(
text, **kwargs,
card=Card.items_list(header, items, footer)
text,
card=Card.items_list(header, items, footer),
**kwargs,
)
)
3 changes: 2 additions & 1 deletion aioalice/types/alice_response.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from attr import attrs, attrib
from aioalice.utils import ensure_cls

from . import AliceObject, BaseSession, Response
from ..utils import ensure_cls


@attrs
Expand Down
17 changes: 14 additions & 3 deletions aioalice/types/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
from attr import attrs, asdict
from attr import attrs, attrib, asdict


@attrs
class AliceObject(object):
class AliceObject:
"""AliceObject is base class for all Alice requests related objects"""

_raw_kwargs = attrib(factory=dict, init=False)
"""
here the raw JSON (dict) will be stored
for using with compatible API
"""

def to_json(self):
return asdict(self)
data = asdict(self, filter=filter_to_json)
return data


def filter_to_json(attr, value) -> bool:
return attr.name != '_raw_kwargs'
6 changes: 3 additions & 3 deletions aioalice/types/card.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from attr import attrs, attrib

from aioalice.utils import ensure_cls
from aioalice.utils.helper import Helper, HelperMode, Item
from . import AliceObject, MediaButton, Image, CardHeader, CardFooter
from ..utils import ensure_cls
from ..utils.helper import Helper, HelperMode, Item


@attrs
Expand Down Expand Up @@ -31,7 +31,7 @@ def check(self, attribute, value):
"ItemsList" — с галереей из нескольких изображений
"""
if value not in CardType.all():
raise ValueError(f'Card type must be "BigImage" or "ItemsList", not "{value}"')
raise ValueError(f'Card type must be "BigImage" or "ItemsList", not {value!r}')

@classmethod
def big_image(cls, image_id, title, description, button=None):
Expand Down
2 changes: 1 addition & 1 deletion aioalice/types/card_footer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from attr import attrs, attrib

from aioalice.utils import ensure_cls
from . import AliceObject, MediaButton
from ..utils import ensure_cls


@attrs
Expand Down
4 changes: 2 additions & 2 deletions aioalice/types/entity.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import logging
from attr import attrs, attrib

from aioalice.utils import ensure_cls
from aioalice.utils.helper import Helper, HelperMode, Item
from . import AliceObject, EntityTokens, EntityValue
from ..utils import ensure_cls
from ..utils.helper import Helper, HelperMode, Item

log = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion aioalice/types/image.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from attr import attrs, attrib

from aioalice.utils import ensure_cls
from . import AliceObject, MediaButton
from ..utils import ensure_cls


@attrs
Expand Down
3 changes: 0 additions & 3 deletions aioalice/types/interfaces.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from attr import attrs, attrib

from aioalice.utils import safe_kwargs
from . import AliceObject


@safe_kwargs
@attrs
class Interfaces(AliceObject):
"""Interfaces object"""
Expand Down
3 changes: 3 additions & 0 deletions aioalice/types/markup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from attr import attrs, attrib

from . import AliceObject
from ..utils import safe_kwargs


@safe_kwargs
@attrs
class Markup(AliceObject):
"""Markup object"""
Expand Down
3 changes: 1 addition & 2 deletions aioalice/types/meta.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from attr import attrs, attrib

from aioalice.utils import safe_kwargs, ensure_cls
from . import AliceObject, Interfaces
from ..utils import ensure_cls


@safe_kwargs
@attrs
class Meta(AliceObject):
"""Meta object"""
Expand Down
8 changes: 6 additions & 2 deletions aioalice/types/natural_language_understanding.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Natural Language Understanding: https://medium.com/@lola.com/nlp-vs-nlu-whats-the-difference-d91c06780992
"""
Natural Language Understanding: https://medium.com/@lola.com/nlp-vs-nlu-whats-the-difference-d91c06780992
"""

from attr import attrs, attrib
from aioalice.utils import ensure_cls

from . import AliceObject, Entity
from ..utils import ensure_cls


@attrs
Expand Down
5 changes: 2 additions & 3 deletions aioalice/types/request.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from attr import attrs, attrib

from aioalice.utils import safe_kwargs, ensure_cls
from aioalice.utils.helper import Helper, HelperMode, Item
from . import AliceObject, Markup, NaturalLanguageUnderstanding
from ..utils import ensure_cls
from ..utils.helper import Helper, HelperMode, Item


@safe_kwargs
@attrs
class Request(AliceObject):
"""Request object"""
Expand Down
3 changes: 2 additions & 1 deletion aioalice/types/response.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from attr import attrs, attrib
from aioalice.utils import ensure_cls

from . import AliceObject, Card, Button
from ..utils import ensure_cls


@attrs
Expand Down
2 changes: 1 addition & 1 deletion aioalice/types/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ def base(self):
return BaseSession(
self.session_id,
self.message_id,
self.user_id
self.user_id,
)
14 changes: 13 additions & 1 deletion aioalice/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,26 @@


def ensure_cls(klass):
from ..types.base import AliceObject
safe_cls = safe_kwargs(klass) if issubclass(klass, AliceObject) else klass

def converter(val):
if val is None:
return
if isinstance(val, dict):
return klass(**val)
return safe_cls(**val)
if isinstance(val, list):
return [converter(v) for v in val]
if not isinstance(val, klass):
return klass(val)
return val
return converter


__all__ = [
"exceptions",
"json",
"generate_json_payload",
"safe_kwargs",
"ensure_cls",
]
9 changes: 6 additions & 3 deletions aioalice/utils/json.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
try:
import rapidjson as json
import simplejson as json
except ImportError:
try:
import ujson as json
import rapidjson as json
except ImportError:
import json
try:
import ujson as json
except ImportError:
import json
20 changes: 16 additions & 4 deletions aioalice/utils/safe_kwargs.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
# https://gist.github.com/surik00/a6c2804a2d18a2ab75630bb5d93693c8
"""
https://gist.github.com/mahenzon/a6c2804a2d18a2ab75630bb5d93693c8
"""

import inspect
import functools
from inspect import isclass, getfullargspec


def safe_kwargs(func_or_class):
spec = inspect.getfullargspec(func_or_class)
from ..types.base import AliceObject

spec = getfullargspec(func_or_class)
all_args = spec.args

save_raw_kwargs = isclass(func_or_class) and issubclass(func_or_class, AliceObject)

@functools.wraps(func_or_class)
def wrap(*args, **kwargs):
accepted_kwargs = {k: v for k, v in kwargs.items() if k in all_args}
return func_or_class(*args, **accepted_kwargs)
res = func_or_class(*args, **accepted_kwargs)

if save_raw_kwargs:
# saving all kwargs for access to unexpected attrs
res._raw_kwargs.update(kwargs)

return res

return wrap
11 changes: 11 additions & 0 deletions deploy_to_pypi.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

# remove old builds
rm ./dist/*

# build
python setup.py sdist
python setup.py bdist_wheel

# upload to PYPI
twine upload dist/*
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

wheel>=0.33.4
twine>=1.13.0
pytest==5.3.5
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
if sys.version_info < MINIMAL_PY_VERSION:
raise RuntimeError('aioAlice works only with Python {}+'.format('.'.join(map(str, MINIMAL_PY_VERSION))))

__version__ = '1.2.5'
__version__ = '1.3.0'


def get_description():
Expand Down
31 changes: 31 additions & 0 deletions tests/_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,34 @@
},
'version': '1.0'
}


REQUEST_WITH_EXTRA_KWARGS = {
'meta': {
'client_id': 'JS/1.0',
'locale': 'ru_RU',
'timezone': 'Europe/Moscow',
'interfaces': {
'screen': {},
},
'_city_ru': 'Москва',
},
'request': {
'command': '',
'original_utterance': 'Запусти навык qwerty',
'type': 'SimpleUtterance',
'nlu': {
'tokens': ['запусти', 'навык', 'qwerty'],
'entities': [],
},
},
'session': {
'session_id': '4b124ca8-19c4-4ec5-75ca-24f96ef5718e',
'user_id': '8e4156d21488cac9b7a7175a9374e63a74bb6ddd46cfbe34cf9dfb60c30c7bfb',
'skill_id': 'f5f39790-2ee1-4744-8345-ee8229dadd58',
'new': True,
'message_id': 0,
'deploy_tokens': {},
},
'version': '1.0',
}
12 changes: 7 additions & 5 deletions tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
EXPECTED_ALICE_RESPONSE_ITEMS_LIST_WITH_BUTTON, \
DATA_FROM_STATION, REQUEST_WITH_NLU, ENTITY_TOKEN, \
ENTITY_VALUE, ENTITY, ENTITY_INTEGER, NLU, \
PING_REQUEST_1, REQUEST_NEW_INTERFACES
PING_REQUEST_1, REQUEST_NEW_INTERFACES, REQUEST_WITH_EXTRA_KWARGS


TestAliceRequest = partial(types.AliceRequest, None) # original_request: https://github.com/surik00/aioalice/pull/2/
Expand Down Expand Up @@ -196,7 +196,7 @@ def test_response2(self):
response = types.Response(RESPONSE2['text'], buttons=['Hi!'])
self._assert_payload(response, RESPONSE2)

def _test_alice_request(self, arq, dct):
def _test_alice_request(self, arq: types.AliceRequest, dct: dict):
self.assertEqual(arq.version, dct['version'])
self._test_session(arq.session, dct['session'])
self._test_request(arq.request, dct['request'])
Expand Down Expand Up @@ -346,6 +346,8 @@ def test_request_new_fields_in_interfaces(self):
alice_request = TestAliceRequest(**REQUEST_NEW_INTERFACES)
self._test_alice_request(alice_request, REQUEST_NEW_INTERFACES)


if __name__ == '__main__':
unittest.main()
def test_model_inits_ok_with_extra_kwargs(self):
alice_request: types.AliceRequest = TestAliceRequest(**REQUEST_WITH_EXTRA_KWARGS)
self._test_alice_request(alice_request, REQUEST_WITH_EXTRA_KWARGS)
assert alice_request.session._raw_kwargs['deploy_tokens'] is REQUEST_WITH_EXTRA_KWARGS['session']['deploy_tokens']
assert alice_request.meta._raw_kwargs['_city_ru'] is REQUEST_WITH_EXTRA_KWARGS['meta']['_city_ru']

0 comments on commit acb3d05

Please sign in to comment.