From c2ca589c1d9d62f96fdb850113b327b9979c91d8 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Fri, 4 Oct 2019 09:40:04 +0300 Subject: [PATCH 01/12] Create deploy script --- deploy_to_pypi.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 deploy_to_pypi.sh diff --git a/deploy_to_pypi.sh b/deploy_to_pypi.sh new file mode 100644 index 0000000..59730dc --- /dev/null +++ b/deploy_to_pypi.sh @@ -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/* From 4cac8e1a2e42ce135fd14d82f1b1d7d91e47209f Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Mon, 17 Feb 2020 22:16:43 +0200 Subject: [PATCH 02/12] Create publish_to_pypi.yml (cherry picked from commit ceb5a5f2012d849e1a2ba74d5c187bed9df61b5a) --- .github/workflows/publish_to_pypi.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/publish_to_pypi.yml diff --git a/.github/workflows/publish_to_pypi.yml b/.github/workflows/publish_to_pypi.yml new file mode 100644 index 0000000..94e4b65 --- /dev/null +++ b/.github/workflows/publish_to_pypi.yml @@ -0,0 +1,27 @@ +name: Publish Python 🐍 distributions 📦 to PyPI + +on: + release: + types: [created] + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.6 + uses: actions/setup-python@v1 + with: + python-version: '3.6' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* From ca3910e51df8b119a933a157d2b21b05dcee1405 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Mon, 17 Feb 2020 22:38:35 +0200 Subject: [PATCH 03/12] Add pytest to dev requirements --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index ab2a666..a62c9b8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,3 +2,4 @@ wheel>=0.33.4 twine>=1.13.0 +pytest==5.3.5 From 2f0f7ef5612ff78916f9d2803de7a789da293cd2 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 18:43:43 +0200 Subject: [PATCH 04/12] Add simplejson default support --- aioalice/utils/json.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/aioalice/utils/json.py b/aioalice/utils/json.py index dfb6c52..ad98409 100644 --- a/aioalice/utils/json.py +++ b/aioalice/utils/json.py @@ -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 From 2669f4b313bfea55328c9dd64d35bb849f46e30d Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 18:46:12 +0200 Subject: [PATCH 05/12] Add __all__ to utils/__init__.py --- aioalice/utils/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/aioalice/utils/__init__.py b/aioalice/utils/__init__.py index 3e612d0..4191436 100644 --- a/aioalice/utils/__init__.py +++ b/aioalice/utils/__init__.py @@ -16,3 +16,12 @@ def converter(val): return klass(val) return val return converter + + +__all__ = [ + "exceptions", + "json", + "generate_json_payload", + "safe_kwargs", + "ensure_cls", +] From ec43a24da665f800d95c0d8f24c9b3ba7aca11c2 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 18:58:36 +0200 Subject: [PATCH 06/12] Move to using relative imports + micro refactoring --- aioalice/types/alice_request.py | 12 +++++++----- aioalice/types/alice_response.py | 3 ++- aioalice/types/card.py | 6 +++--- aioalice/types/card_footer.py | 2 +- aioalice/types/entity.py | 4 ++-- aioalice/types/image.py | 2 +- aioalice/types/interfaces.py | 3 --- aioalice/types/markup.py | 3 +++ aioalice/types/meta.py | 3 +-- aioalice/types/natural_language_understanding.py | 8 ++++++-- aioalice/types/request.py | 5 ++--- aioalice/types/response.py | 3 ++- aioalice/types/session.py | 2 +- aioalice/utils/safe_kwargs.py | 4 +++- 14 files changed, 34 insertions(+), 26 deletions(-) diff --git a/aioalice/types/alice_request.py b/aioalice/types/alice_request.py index f8bf337..ad4de78 100644 --- a/aioalice/types/alice_request.py +++ b/aioalice/types/alice_request.py @@ -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 @@ -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): @@ -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, ) ) @@ -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, ) ) diff --git a/aioalice/types/alice_response.py b/aioalice/types/alice_response.py index e4e28aa..e8bb1a4 100644 --- a/aioalice/types/alice_response.py +++ b/aioalice/types/alice_response.py @@ -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 diff --git a/aioalice/types/card.py b/aioalice/types/card.py index e086699..854a0aa 100644 --- a/aioalice/types/card.py +++ b/aioalice/types/card.py @@ -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 @@ -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): diff --git a/aioalice/types/card_footer.py b/aioalice/types/card_footer.py index 8d16066..bd65ddb 100644 --- a/aioalice/types/card_footer.py +++ b/aioalice/types/card_footer.py @@ -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 diff --git a/aioalice/types/entity.py b/aioalice/types/entity.py index 925d847..f589dc8 100644 --- a/aioalice/types/entity.py +++ b/aioalice/types/entity.py @@ -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__) diff --git a/aioalice/types/image.py b/aioalice/types/image.py index fa4a0da..c0e5b7f 100644 --- a/aioalice/types/image.py +++ b/aioalice/types/image.py @@ -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 diff --git a/aioalice/types/interfaces.py b/aioalice/types/interfaces.py index 6bc0e7c..3dcda64 100644 --- a/aioalice/types/interfaces.py +++ b/aioalice/types/interfaces.py @@ -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""" diff --git a/aioalice/types/markup.py b/aioalice/types/markup.py index 5feda8d..3deb0ca 100644 --- a/aioalice/types/markup.py +++ b/aioalice/types/markup.py @@ -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""" diff --git a/aioalice/types/meta.py b/aioalice/types/meta.py index d5b5202..a32dfff 100644 --- a/aioalice/types/meta.py +++ b/aioalice/types/meta.py @@ -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""" diff --git a/aioalice/types/natural_language_understanding.py b/aioalice/types/natural_language_understanding.py index 274d972..e917628 100644 --- a/aioalice/types/natural_language_understanding.py +++ b/aioalice/types/natural_language_understanding.py @@ -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 diff --git a/aioalice/types/request.py b/aioalice/types/request.py index dd092fd..02d461c 100644 --- a/aioalice/types/request.py +++ b/aioalice/types/request.py @@ -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""" diff --git a/aioalice/types/response.py b/aioalice/types/response.py index ff59ba1..f91849a 100644 --- a/aioalice/types/response.py +++ b/aioalice/types/response.py @@ -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 diff --git a/aioalice/types/session.py b/aioalice/types/session.py index ecef5af..7462a7b 100644 --- a/aioalice/types/session.py +++ b/aioalice/types/session.py @@ -21,5 +21,5 @@ def base(self): return BaseSession( self.session_id, self.message_id, - self.user_id + self.user_id, ) diff --git a/aioalice/utils/safe_kwargs.py b/aioalice/utils/safe_kwargs.py index 42d46c4..ae348ee 100644 --- a/aioalice/utils/safe_kwargs.py +++ b/aioalice/utils/safe_kwargs.py @@ -1,4 +1,6 @@ -# https://gist.github.com/surik00/a6c2804a2d18a2ab75630bb5d93693c8 +""" +https://gist.github.com/mahenzon/a6c2804a2d18a2ab75630bb5d93693c8 +""" import inspect import functools From 9e4aef0229dd75d8b9902807bda4cdd180173446 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 19:10:22 +0200 Subject: [PATCH 07/12] safe_kwargs for AliceObject in ensure_cls --- aioalice/utils/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aioalice/utils/__init__.py b/aioalice/utils/__init__.py index 4191436..aff4943 100644 --- a/aioalice/utils/__init__.py +++ b/aioalice/utils/__init__.py @@ -5,11 +5,14 @@ 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): From 499b863bb197142bd9431c9b02b85aebfbe38eb4 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 19:23:00 +0200 Subject: [PATCH 08/12] Add _raw_kwargs field to AliceObject --- aioalice/types/base.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/aioalice/types/base.py b/aioalice/types/base.py index efad3a0..c64e9af 100644 --- a/aioalice/types/base.py +++ b/aioalice/types/base.py @@ -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' From 5eebe8d54727428d0e287b73cd056b48c9467255 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 19:23:31 +0200 Subject: [PATCH 09/12] save raw kwargs for AliceObject in safe_kwargs --- aioalice/utils/safe_kwargs.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/aioalice/utils/safe_kwargs.py b/aioalice/utils/safe_kwargs.py index ae348ee..b491f22 100644 --- a/aioalice/utils/safe_kwargs.py +++ b/aioalice/utils/safe_kwargs.py @@ -2,17 +2,27 @@ 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 From a1dde0dc9f53529b05ad2cb4fba250b927a64c72 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 19:25:46 +0200 Subject: [PATCH 10/12] create unit test for extra kwargs --- tests/_dataset.py | 31 +++++++++++++++++++++++++++++++ tests/test_types.py | 12 +++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/tests/_dataset.py b/tests/_dataset.py index cec9056..c898152 100644 --- a/tests/_dataset.py +++ b/tests/_dataset.py @@ -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', +} diff --git a/tests/test_types.py b/tests/test_types.py index 5e5faae..9c3bb53 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -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/ @@ -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']) @@ -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'] From f837dc255b8a4ed35a1bffbf5067a31e21af0754 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 19:34:56 +0200 Subject: [PATCH 11/12] Rename tests job name --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 047d7c1..423f808 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -3,7 +3,7 @@ name: Python tests on: [push] jobs: - build: + unit-tests: runs-on: ubuntu-latest From 0ce359453e1fe39d59382eb6ab2b352f84d514b3 Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Tue, 18 Feb 2020 19:40:32 +0200 Subject: [PATCH 12/12] Upgrade version to 1.3.0 --- aioalice/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aioalice/__init__.py b/aioalice/__init__.py index 0b0c3c1..eb9f971 100644 --- a/aioalice/__init__.py +++ b/aioalice/__init__.py @@ -11,4 +11,4 @@ asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) -__version__ = '1.2.5' +__version__ = '1.3.0' diff --git a/setup.py b/setup.py index 460db47..7ad3e60 100644 --- a/setup.py +++ b/setup.py @@ -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():