diff options
| author | CoprDistGit <infra@openeuler.org> | 2023-05-10 06:58:44 +0000 |
|---|---|---|
| committer | CoprDistGit <infra@openeuler.org> | 2023-05-10 06:58:44 +0000 |
| commit | 68379aa0f5b4b11b2d53df14c8c9b4033cf009f1 (patch) | |
| tree | 1a1ba95047a452e515ff5fc3cc9153790d5a691c | |
| parent | b5bdae8e444dd53c4e0259fe9cc4fdeb429a4557 (diff) | |
automatic import of python-restresponse
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | python-restresponse.spec | 1128 | ||||
| -rw-r--r-- | sources | 1 |
3 files changed, 1130 insertions, 0 deletions
@@ -0,0 +1 @@ +/RestResponse-3.1.2.tar.gz diff --git a/python-restresponse.spec b/python-restresponse.spec new file mode 100644 index 0000000..35a4960 --- /dev/null +++ b/python-restresponse.spec @@ -0,0 +1,1128 @@ +%global _empty_manifest_terminate_build 0 +Name: python-RestResponse +Version: 3.1.2 +Release: 1 +Summary: A fluent python object for interfacing with RESTful JSON APIs. +License: MIT +URL: https://github.com/tysonholub/RestResponse.git +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/a9/cb/019050f785f44d133908067fc453c9a3d090456f809f6157b68beabc128a/RestResponse-3.1.2.tar.gz +BuildArch: noarch + + +%description +# RestResponse [](https://circleci.com/gh/tysonholub/RestResponse) [](https://pypi.org/project/RestResponse/) [](https://pypistats.org/packages/restresponse) + +Powered by [python-autoviv](https://gitlab.com/tysonholub/python-autoviv) under the hood, RestResponse aims to be a fluent python object for interfacing with RESTful JSON APIs. + +Python2 support has been dropped in v3. If you need legacy support, see the [v2 branch](https://github.com/tysonholub/RestResponse/tree/v2). + +## Installation + +Add this line to your application's requirements.txt: + +```python +RestResponse +``` + +And then execute: + + $ pip install -r requirements.txt + +Or install it yourself as: + + $ pip install RestResponse + +## Usage + +Import RestResponse and call parse on any list, dict, or primitive. You can also call loads on serialized json + +```python +>>> import RestResponse +>>> import requests +>>> r = requests.get('http://jsonplaceholder.typicode.com/users') +>>> users = RestResponse.parse(r.json()) +>>> # or +... users = RestResponse.loads(r.text) +>>> for user in users: +... print(user.name) +... +Leanne Graham +Ervin Howell +Clementine Bauch +Patricia Lebsack +Chelsey Dietrich +Mrs. Dennis Schulist +Kurtis Weissnat +Nicholas Runolfsdottir V +Glenna Reichert +Clementina DuBuque +>>> user = users[0] +>>> print(user.pretty_print(indent=4)) +{ + "username": "Bret", + "website": "hildegard.org", + "name": "Leanne Graham", + "company": { + "bs": "harness real-time e-markets", + "name": "Romaguera-Crona", + "catchPhrase": "Multi-layered client-server neural-net" + }, + "id": 1, + "phone": "1-770-736-8031 x56442", + "address": { + "suite": "Apt. 556", + "street": "Kulas Light", + "geo": { + "lat": "-37.3159", + "lng": "81.1496" + }, + "zipcode": "92998-3874", + "city": "Gwenborough" + }, + "email": "Sincere@april.biz" +} +>>> user.name = 'Rest Response' +>>> r = requests.put('http://jsonplaceholder.typicode.com/users/{0}'.format(user.id), json=user) +>>> response = RestResponse.parse(r.json()) +>>> print(response.name) +Rest Response +>>> new = RestResponse.parse({}) +>>> new.id = 5 +>>> if not new.username: +... new.username = 'New User' +... +>>> new.address.geo.lat = "-42.3433" +>>> new.address.geo.lng = "74.3433" +>>> new.email = 'someone@somewhere.biz' +>>> print(new.pretty_print()) +{ + "username": "New User", + "email": "someone@somewhere.biz", + "id": 5, + "address": { + "geo": { + "lat": "-42.3433", + "lng": "74.3433" + } + } +} +``` +### Callables +Callable properties will be encoded via [cloudpickle](https://github.com/cloudpipe/cloudpickle) and base64 prefixed with `__callable__: `. +```python +>>> import RestResponse +>>> data = RestResponse.parse({'callable': lambda x: x + 1}) +>>> data.callable +<function <lambda> at 0x7f92593de9b0> +>>> data.callable(1) +2 +>>> pretty_str = data.pretty_print() +>>> print(pretty_str) +{ + "callable": "__callable__: gAJjY2xvdWRwaWNrbGUuY2xvdWRwaWNrbGUKX2ZpbGxfZnVuY3Rpb24KcQAoY2Nsb3VkcGlja2xlLmNsb3VkcGlja2xlCl9tYWtlX3NrZWxfZnVuYwpxAWNjbG91ZHBpY2tsZS5jbG91ZHBpY2tsZQpfYnVpbHRpbl90eXBlCnECVQhDb2RlVHlwZXEDhXEEUnEFKEsBSwFLAktDVQh8AABkAQAXU3EGTksBhnEHKVUBeHEIhXEJVQc8c3RkaW4+cQpVCDxsYW1iZGE+cQtLAVUAcQwpKXRxDVJxDkr/////VQhfX21haW5fX3EPh3EQUnERfXESKFUEbmFtZXETaAtVA2RvY3EUTlUGbW9kdWxlcRVoD1UOY2xvc3VyZV92YWx1ZXNxFk5VB2dsb2JhbHNxF31xGFUEZGljdHEZfXEaVQhkZWZhdWx0c3EbTnV0Ui4=" +} +>>> data = RestResponse.loads(pretty_str) +>>> data.callable(1) +2 +``` +### Binary +Properties detected as binary data will be encoded via base64 prefixed with `__binary__: `. +```python +>>> import RestResponse +>>> import requests +>>> data = RestResponse.parse({ +... 'binary': requests.get('https://cataas.com/cat').content +... }) +>>> data.binary +'\xff\xd8\xff\xdb\x00C\x00\x06\x04\x05\x06\x05\x04\x06\x06\x05\x06\x07\x07\x06\x08\n\x10\n\n\t\t\n\x14\x0e\x0f\x0c\x10\x17\x14\x18\x18\x17\x14\x16\x16\x1a\x1d%\x1f\x1a\x1b#\x1c\x16\x16 , #&\')*)\x19\x1f-0-(0%()(\xff\xdb\x00C\x01\x07\x07\x07\n\x08\n\x13\n\n\x13(\x1a\x16\x1a((((((((((((((((((((((((((((((((((((((((((((((((((\xff\xc2\x00\x11\x08\x00\x01\x00\x01\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x90\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x06?\x02\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?!\x7f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\xfb\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x7f\xff\xd9' +>>> pretty_str = data.pretty_print() +>>> print(pretty_str) +{ + "binary": "__binary__: /9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wgARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAT/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGQB//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQ+//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z" +} +>>> data = RestResponse.loads(pretty_str) +>>> data.binary +'\xff\xd8\xff\xdb\x00C\x00\x06\x04\x05\x06\x05\x04\x06\x06\x05\x06\x07\x07\x06\x08\n\x10\n\n\t\t\n\x14\x0e\x0f\x0c\x10\x17\x14\x18\x18\x17\x14\x16\x16\x1a\x1d%\x1f\x1a\x1b#\x1c\x16\x16 , #&\')*)\x19\x1f-0-(0%()(\xff\xdb\x00C\x01\x07\x07\x07\n\x08\n\x13\n\n\x13(\x1a\x16\x1a((((((((((((((((((((((((((((((((((((((((((((((((((\xff\xc2\x00\x11\x08\x00\x01\x00\x01\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x90\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x06?\x02\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?!\x7f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\xfb\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x7f\xff\xd9' +``` +### NoneProp +It should be noted that missing referenced properties, including nested, are gracefully falsey. + +```python +>>> import RestResponse +>>> data = RestResponse.parse({}) +>>> data.property.is_none +None +>>> bool(data.property.is_none) +False +>>> isinstance(data.property.is_none, RestResponse.NoneProp) +True +>>> 'some data' in data.property.is_none +False +>>> [x for x in data.property.is_none] +[] +>>> data.property.is_none = None +>>> isinstance(data.property.is_none, RestResponse.NoneProp) +False +>>> print(data.pretty_print()) +{ + "property": { + "is_none": null + } +} +``` +### SQLAlchemy ORM +RestResponse uses a Mutable mixin provided by SQLAlchemy for interfacing with databases. The following (Flask) snippet should get you started: +```python +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from RestResponse.orm.sqlalchemy import RESTResponse + + +app = Flask(__name__) +db = SQLAlchemy(app) + + +class SomeModel(db.Model): + id = db.Column(db.Integer(), primary_key=True) + data = db.Column(RESTResponse(), nullable=False) + + def __init__(self, data): + self.data = data # data should be json serializable +``` +Data will be saved to the database as a serialized JSON blob. When data is loaded it will be coerced to the underlying RestResponseObj + +## ApiModel + +Inherit RestResponse.ApiModel to facilitate typing out API models. Please note your properties should NOT start with `_`. `ApiModel._data` setter ensures that passed in data conforms to your properties, effectively filtering out underscored properties identified via `dir()`. This behavior can be overriden by adding properties to `__opts__['_overrides']` (**Note**: `__opts__` must be updated before setting `_data`). + +For convenience ApiModel includes helper methods for property integrity. These include: + + 1. `_set_int`, `_get_int` + 2. `_set_string`, `_get_string` + 3. `_set_date`, `_get_date` + 4. `_set_datetime`, `_get_datetime` + 5. `_set_float`, `_get_float` + 6. `_set_bool`, `_get_bool` + +You can initalize ApiCollection with one of these helpers (or define your own), or another object such as `Reference` below. + + +Consider the following snippet: +```python +from RestResponse import ApiModel, ApiCollection + + +class Model(ApiModel): + def __init__(self, data): + self.__opts__['_overrides'] = ['_bar'] + self._data = data + + # this _foo property will be ignored on init + @property + def _foo(self): + return self._get_string(self._data._foo) + + @_foo.setter + def _foo(self, _foo): + self._data._foo = self._set_string(_foo) + + @property + def _bar(self): + return self._get_string(self._data._bar) + + @_bar.setter + def _bar(self, _bar): + self._data._bar = self._set_string(_bar) + + @property + def id(self): + return self._get_int(self._data.id) + + @id.setter + def id(self, id): + self._data.id = self._set_int(id) + + @property + def reference(self): + if not self._data.reference: + self._data.reference = Reference() + return Reference(self._data.reference) + + @reference.setter + def reference(self, reference): + self._data.reference = Reference(reference) + + @property + def int_collection(self): + if not self._data.int_collection: + self._data.int_collection = ApiCollection(self._set_int) + return self._data.int_collection + + @int_collection.setter + def int_collection(self, int_collection): + self._data.int_collection = ApiCollection(self._set_int, int_collection) + + @property + def ref_collection(self): + if not self._data.ref_collection: + self._data.ref_collection = ApiCollection(Reference) + return self._data.ref_collection + + @ref_collection.setter + def ref_collection(self, ref_collection): + self._data.ref_collection = ApiCollection(Reference, ref_collection) + + +class Reference(ApiModel): + def __init__(self, data): + self._data = data + + @property + def id(self): + return self._get_int(self._data.id) + + @id.setter + def id(self, id): + self._data.id = self._set_int(id) +``` +Then initialize with RestObject, dict, ApiModel, or serialized JSON +```python +>>> model = Model({ + 'id': 5, + 'ref': { + 'id': 5, + 'foo': 'bar' + }, + 'int_collection': [1, 2, 3], + 'ref_collection': [ + { + 'id': 1, + }, + { + 'id': 2, + } + ], + 'foo': 'bar', + '_foo': '_bar', + '_bar': 'foo' +}) +>>> print(model._data.pretty_print()) +{ + "int_collection": [ + 1, + 2, + 3 + ], + "ref": { + "id": 5 + }, + "ref_collection": [ + { + "id": 1 + }, + { + "id": 2 + } + ], + "id": 5, + "_bar": "foo" +} +>>> assert '_foo' not in model._data +>>> assert '_bar' in model._data +>>> assert 'foo' not in model._data +>>> assert 'foo' not in model.ref._data +>>> type(model._data) +<class 'RestResponse.objects.RestObject'> +``` +Note: ApiModel will NOT decode/encode binary/callable types by default. You'll need to turn this behavior on by overriding `ApiModel.__opts__`: +```python +Model.__opts__ = { + 'encode_binary': True, + 'encode_callable': True, + 'decode_binary': True, + 'decode_callable': True +} +``` + +## Testing + +Install test dependencies with pipenv. + + $ pip install pipenv + $ pipenv install -d + $ ./test.sh + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/tysonholub/RestResponse.git. This project is +intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the +[Contributor Covenant](http://contributor-covenant.org) code of conduct. + + +## License + +This package is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). + + +%package -n python3-RestResponse +Summary: A fluent python object for interfacing with RESTful JSON APIs. +Provides: python-RestResponse +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-RestResponse +# RestResponse [](https://circleci.com/gh/tysonholub/RestResponse) [](https://pypi.org/project/RestResponse/) [](https://pypistats.org/packages/restresponse) + +Powered by [python-autoviv](https://gitlab.com/tysonholub/python-autoviv) under the hood, RestResponse aims to be a fluent python object for interfacing with RESTful JSON APIs. + +Python2 support has been dropped in v3. If you need legacy support, see the [v2 branch](https://github.com/tysonholub/RestResponse/tree/v2). + +## Installation + +Add this line to your application's requirements.txt: + +```python +RestResponse +``` + +And then execute: + + $ pip install -r requirements.txt + +Or install it yourself as: + + $ pip install RestResponse + +## Usage + +Import RestResponse and call parse on any list, dict, or primitive. You can also call loads on serialized json + +```python +>>> import RestResponse +>>> import requests +>>> r = requests.get('http://jsonplaceholder.typicode.com/users') +>>> users = RestResponse.parse(r.json()) +>>> # or +... users = RestResponse.loads(r.text) +>>> for user in users: +... print(user.name) +... +Leanne Graham +Ervin Howell +Clementine Bauch +Patricia Lebsack +Chelsey Dietrich +Mrs. Dennis Schulist +Kurtis Weissnat +Nicholas Runolfsdottir V +Glenna Reichert +Clementina DuBuque +>>> user = users[0] +>>> print(user.pretty_print(indent=4)) +{ + "username": "Bret", + "website": "hildegard.org", + "name": "Leanne Graham", + "company": { + "bs": "harness real-time e-markets", + "name": "Romaguera-Crona", + "catchPhrase": "Multi-layered client-server neural-net" + }, + "id": 1, + "phone": "1-770-736-8031 x56442", + "address": { + "suite": "Apt. 556", + "street": "Kulas Light", + "geo": { + "lat": "-37.3159", + "lng": "81.1496" + }, + "zipcode": "92998-3874", + "city": "Gwenborough" + }, + "email": "Sincere@april.biz" +} +>>> user.name = 'Rest Response' +>>> r = requests.put('http://jsonplaceholder.typicode.com/users/{0}'.format(user.id), json=user) +>>> response = RestResponse.parse(r.json()) +>>> print(response.name) +Rest Response +>>> new = RestResponse.parse({}) +>>> new.id = 5 +>>> if not new.username: +... new.username = 'New User' +... +>>> new.address.geo.lat = "-42.3433" +>>> new.address.geo.lng = "74.3433" +>>> new.email = 'someone@somewhere.biz' +>>> print(new.pretty_print()) +{ + "username": "New User", + "email": "someone@somewhere.biz", + "id": 5, + "address": { + "geo": { + "lat": "-42.3433", + "lng": "74.3433" + } + } +} +``` +### Callables +Callable properties will be encoded via [cloudpickle](https://github.com/cloudpipe/cloudpickle) and base64 prefixed with `__callable__: `. +```python +>>> import RestResponse +>>> data = RestResponse.parse({'callable': lambda x: x + 1}) +>>> data.callable +<function <lambda> at 0x7f92593de9b0> +>>> data.callable(1) +2 +>>> pretty_str = data.pretty_print() +>>> print(pretty_str) +{ + "callable": "__callable__: gAJjY2xvdWRwaWNrbGUuY2xvdWRwaWNrbGUKX2ZpbGxfZnVuY3Rpb24KcQAoY2Nsb3VkcGlja2xlLmNsb3VkcGlja2xlCl9tYWtlX3NrZWxfZnVuYwpxAWNjbG91ZHBpY2tsZS5jbG91ZHBpY2tsZQpfYnVpbHRpbl90eXBlCnECVQhDb2RlVHlwZXEDhXEEUnEFKEsBSwFLAktDVQh8AABkAQAXU3EGTksBhnEHKVUBeHEIhXEJVQc8c3RkaW4+cQpVCDxsYW1iZGE+cQtLAVUAcQwpKXRxDVJxDkr/////VQhfX21haW5fX3EPh3EQUnERfXESKFUEbmFtZXETaAtVA2RvY3EUTlUGbW9kdWxlcRVoD1UOY2xvc3VyZV92YWx1ZXNxFk5VB2dsb2JhbHNxF31xGFUEZGljdHEZfXEaVQhkZWZhdWx0c3EbTnV0Ui4=" +} +>>> data = RestResponse.loads(pretty_str) +>>> data.callable(1) +2 +``` +### Binary +Properties detected as binary data will be encoded via base64 prefixed with `__binary__: `. +```python +>>> import RestResponse +>>> import requests +>>> data = RestResponse.parse({ +... 'binary': requests.get('https://cataas.com/cat').content +... }) +>>> data.binary +'\xff\xd8\xff\xdb\x00C\x00\x06\x04\x05\x06\x05\x04\x06\x06\x05\x06\x07\x07\x06\x08\n\x10\n\n\t\t\n\x14\x0e\x0f\x0c\x10\x17\x14\x18\x18\x17\x14\x16\x16\x1a\x1d%\x1f\x1a\x1b#\x1c\x16\x16 , #&\')*)\x19\x1f-0-(0%()(\xff\xdb\x00C\x01\x07\x07\x07\n\x08\n\x13\n\n\x13(\x1a\x16\x1a((((((((((((((((((((((((((((((((((((((((((((((((((\xff\xc2\x00\x11\x08\x00\x01\x00\x01\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x90\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x06?\x02\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?!\x7f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\xfb\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x7f\xff\xd9' +>>> pretty_str = data.pretty_print() +>>> print(pretty_str) +{ + "binary": "__binary__: /9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wgARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAT/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGQB//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQ+//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z" +} +>>> data = RestResponse.loads(pretty_str) +>>> data.binary +'\xff\xd8\xff\xdb\x00C\x00\x06\x04\x05\x06\x05\x04\x06\x06\x05\x06\x07\x07\x06\x08\n\x10\n\n\t\t\n\x14\x0e\x0f\x0c\x10\x17\x14\x18\x18\x17\x14\x16\x16\x1a\x1d%\x1f\x1a\x1b#\x1c\x16\x16 , #&\')*)\x19\x1f-0-(0%()(\xff\xdb\x00C\x01\x07\x07\x07\n\x08\n\x13\n\n\x13(\x1a\x16\x1a((((((((((((((((((((((((((((((((((((((((((((((((((\xff\xc2\x00\x11\x08\x00\x01\x00\x01\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x90\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x06?\x02\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?!\x7f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\xfb\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x7f\xff\xd9' +``` +### NoneProp +It should be noted that missing referenced properties, including nested, are gracefully falsey. + +```python +>>> import RestResponse +>>> data = RestResponse.parse({}) +>>> data.property.is_none +None +>>> bool(data.property.is_none) +False +>>> isinstance(data.property.is_none, RestResponse.NoneProp) +True +>>> 'some data' in data.property.is_none +False +>>> [x for x in data.property.is_none] +[] +>>> data.property.is_none = None +>>> isinstance(data.property.is_none, RestResponse.NoneProp) +False +>>> print(data.pretty_print()) +{ + "property": { + "is_none": null + } +} +``` +### SQLAlchemy ORM +RestResponse uses a Mutable mixin provided by SQLAlchemy for interfacing with databases. The following (Flask) snippet should get you started: +```python +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from RestResponse.orm.sqlalchemy import RESTResponse + + +app = Flask(__name__) +db = SQLAlchemy(app) + + +class SomeModel(db.Model): + id = db.Column(db.Integer(), primary_key=True) + data = db.Column(RESTResponse(), nullable=False) + + def __init__(self, data): + self.data = data # data should be json serializable +``` +Data will be saved to the database as a serialized JSON blob. When data is loaded it will be coerced to the underlying RestResponseObj + +## ApiModel + +Inherit RestResponse.ApiModel to facilitate typing out API models. Please note your properties should NOT start with `_`. `ApiModel._data` setter ensures that passed in data conforms to your properties, effectively filtering out underscored properties identified via `dir()`. This behavior can be overriden by adding properties to `__opts__['_overrides']` (**Note**: `__opts__` must be updated before setting `_data`). + +For convenience ApiModel includes helper methods for property integrity. These include: + + 1. `_set_int`, `_get_int` + 2. `_set_string`, `_get_string` + 3. `_set_date`, `_get_date` + 4. `_set_datetime`, `_get_datetime` + 5. `_set_float`, `_get_float` + 6. `_set_bool`, `_get_bool` + +You can initalize ApiCollection with one of these helpers (or define your own), or another object such as `Reference` below. + + +Consider the following snippet: +```python +from RestResponse import ApiModel, ApiCollection + + +class Model(ApiModel): + def __init__(self, data): + self.__opts__['_overrides'] = ['_bar'] + self._data = data + + # this _foo property will be ignored on init + @property + def _foo(self): + return self._get_string(self._data._foo) + + @_foo.setter + def _foo(self, _foo): + self._data._foo = self._set_string(_foo) + + @property + def _bar(self): + return self._get_string(self._data._bar) + + @_bar.setter + def _bar(self, _bar): + self._data._bar = self._set_string(_bar) + + @property + def id(self): + return self._get_int(self._data.id) + + @id.setter + def id(self, id): + self._data.id = self._set_int(id) + + @property + def reference(self): + if not self._data.reference: + self._data.reference = Reference() + return Reference(self._data.reference) + + @reference.setter + def reference(self, reference): + self._data.reference = Reference(reference) + + @property + def int_collection(self): + if not self._data.int_collection: + self._data.int_collection = ApiCollection(self._set_int) + return self._data.int_collection + + @int_collection.setter + def int_collection(self, int_collection): + self._data.int_collection = ApiCollection(self._set_int, int_collection) + + @property + def ref_collection(self): + if not self._data.ref_collection: + self._data.ref_collection = ApiCollection(Reference) + return self._data.ref_collection + + @ref_collection.setter + def ref_collection(self, ref_collection): + self._data.ref_collection = ApiCollection(Reference, ref_collection) + + +class Reference(ApiModel): + def __init__(self, data): + self._data = data + + @property + def id(self): + return self._get_int(self._data.id) + + @id.setter + def id(self, id): + self._data.id = self._set_int(id) +``` +Then initialize with RestObject, dict, ApiModel, or serialized JSON +```python +>>> model = Model({ + 'id': 5, + 'ref': { + 'id': 5, + 'foo': 'bar' + }, + 'int_collection': [1, 2, 3], + 'ref_collection': [ + { + 'id': 1, + }, + { + 'id': 2, + } + ], + 'foo': 'bar', + '_foo': '_bar', + '_bar': 'foo' +}) +>>> print(model._data.pretty_print()) +{ + "int_collection": [ + 1, + 2, + 3 + ], + "ref": { + "id": 5 + }, + "ref_collection": [ + { + "id": 1 + }, + { + "id": 2 + } + ], + "id": 5, + "_bar": "foo" +} +>>> assert '_foo' not in model._data +>>> assert '_bar' in model._data +>>> assert 'foo' not in model._data +>>> assert 'foo' not in model.ref._data +>>> type(model._data) +<class 'RestResponse.objects.RestObject'> +``` +Note: ApiModel will NOT decode/encode binary/callable types by default. You'll need to turn this behavior on by overriding `ApiModel.__opts__`: +```python +Model.__opts__ = { + 'encode_binary': True, + 'encode_callable': True, + 'decode_binary': True, + 'decode_callable': True +} +``` + +## Testing + +Install test dependencies with pipenv. + + $ pip install pipenv + $ pipenv install -d + $ ./test.sh + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/tysonholub/RestResponse.git. This project is +intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the +[Contributor Covenant](http://contributor-covenant.org) code of conduct. + + +## License + +This package is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). + + +%package help +Summary: Development documents and examples for RestResponse +Provides: python3-RestResponse-doc +%description help +# RestResponse [](https://circleci.com/gh/tysonholub/RestResponse) [](https://pypi.org/project/RestResponse/) [](https://pypistats.org/packages/restresponse) + +Powered by [python-autoviv](https://gitlab.com/tysonholub/python-autoviv) under the hood, RestResponse aims to be a fluent python object for interfacing with RESTful JSON APIs. + +Python2 support has been dropped in v3. If you need legacy support, see the [v2 branch](https://github.com/tysonholub/RestResponse/tree/v2). + +## Installation + +Add this line to your application's requirements.txt: + +```python +RestResponse +``` + +And then execute: + + $ pip install -r requirements.txt + +Or install it yourself as: + + $ pip install RestResponse + +## Usage + +Import RestResponse and call parse on any list, dict, or primitive. You can also call loads on serialized json + +```python +>>> import RestResponse +>>> import requests +>>> r = requests.get('http://jsonplaceholder.typicode.com/users') +>>> users = RestResponse.parse(r.json()) +>>> # or +... users = RestResponse.loads(r.text) +>>> for user in users: +... print(user.name) +... +Leanne Graham +Ervin Howell +Clementine Bauch +Patricia Lebsack +Chelsey Dietrich +Mrs. Dennis Schulist +Kurtis Weissnat +Nicholas Runolfsdottir V +Glenna Reichert +Clementina DuBuque +>>> user = users[0] +>>> print(user.pretty_print(indent=4)) +{ + "username": "Bret", + "website": "hildegard.org", + "name": "Leanne Graham", + "company": { + "bs": "harness real-time e-markets", + "name": "Romaguera-Crona", + "catchPhrase": "Multi-layered client-server neural-net" + }, + "id": 1, + "phone": "1-770-736-8031 x56442", + "address": { + "suite": "Apt. 556", + "street": "Kulas Light", + "geo": { + "lat": "-37.3159", + "lng": "81.1496" + }, + "zipcode": "92998-3874", + "city": "Gwenborough" + }, + "email": "Sincere@april.biz" +} +>>> user.name = 'Rest Response' +>>> r = requests.put('http://jsonplaceholder.typicode.com/users/{0}'.format(user.id), json=user) +>>> response = RestResponse.parse(r.json()) +>>> print(response.name) +Rest Response +>>> new = RestResponse.parse({}) +>>> new.id = 5 +>>> if not new.username: +... new.username = 'New User' +... +>>> new.address.geo.lat = "-42.3433" +>>> new.address.geo.lng = "74.3433" +>>> new.email = 'someone@somewhere.biz' +>>> print(new.pretty_print()) +{ + "username": "New User", + "email": "someone@somewhere.biz", + "id": 5, + "address": { + "geo": { + "lat": "-42.3433", + "lng": "74.3433" + } + } +} +``` +### Callables +Callable properties will be encoded via [cloudpickle](https://github.com/cloudpipe/cloudpickle) and base64 prefixed with `__callable__: `. +```python +>>> import RestResponse +>>> data = RestResponse.parse({'callable': lambda x: x + 1}) +>>> data.callable +<function <lambda> at 0x7f92593de9b0> +>>> data.callable(1) +2 +>>> pretty_str = data.pretty_print() +>>> print(pretty_str) +{ + "callable": "__callable__: gAJjY2xvdWRwaWNrbGUuY2xvdWRwaWNrbGUKX2ZpbGxfZnVuY3Rpb24KcQAoY2Nsb3VkcGlja2xlLmNsb3VkcGlja2xlCl9tYWtlX3NrZWxfZnVuYwpxAWNjbG91ZHBpY2tsZS5jbG91ZHBpY2tsZQpfYnVpbHRpbl90eXBlCnECVQhDb2RlVHlwZXEDhXEEUnEFKEsBSwFLAktDVQh8AABkAQAXU3EGTksBhnEHKVUBeHEIhXEJVQc8c3RkaW4+cQpVCDxsYW1iZGE+cQtLAVUAcQwpKXRxDVJxDkr/////VQhfX21haW5fX3EPh3EQUnERfXESKFUEbmFtZXETaAtVA2RvY3EUTlUGbW9kdWxlcRVoD1UOY2xvc3VyZV92YWx1ZXNxFk5VB2dsb2JhbHNxF31xGFUEZGljdHEZfXEaVQhkZWZhdWx0c3EbTnV0Ui4=" +} +>>> data = RestResponse.loads(pretty_str) +>>> data.callable(1) +2 +``` +### Binary +Properties detected as binary data will be encoded via base64 prefixed with `__binary__: `. +```python +>>> import RestResponse +>>> import requests +>>> data = RestResponse.parse({ +... 'binary': requests.get('https://cataas.com/cat').content +... }) +>>> data.binary +'\xff\xd8\xff\xdb\x00C\x00\x06\x04\x05\x06\x05\x04\x06\x06\x05\x06\x07\x07\x06\x08\n\x10\n\n\t\t\n\x14\x0e\x0f\x0c\x10\x17\x14\x18\x18\x17\x14\x16\x16\x1a\x1d%\x1f\x1a\x1b#\x1c\x16\x16 , #&\')*)\x19\x1f-0-(0%()(\xff\xdb\x00C\x01\x07\x07\x07\n\x08\n\x13\n\n\x13(\x1a\x16\x1a((((((((((((((((((((((((((((((((((((((((((((((((((\xff\xc2\x00\x11\x08\x00\x01\x00\x01\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x90\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x06?\x02\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?!\x7f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\xfb\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x7f\xff\xd9' +>>> pretty_str = data.pretty_print() +>>> print(pretty_str) +{ + "binary": "__binary__: /9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wgARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAT/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGQB//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQ+//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z" +} +>>> data = RestResponse.loads(pretty_str) +>>> data.binary +'\xff\xd8\xff\xdb\x00C\x00\x06\x04\x05\x06\x05\x04\x06\x06\x05\x06\x07\x07\x06\x08\n\x10\n\n\t\t\n\x14\x0e\x0f\x0c\x10\x17\x14\x18\x18\x17\x14\x16\x16\x1a\x1d%\x1f\x1a\x1b#\x1c\x16\x16 , #&\')*)\x19\x1f-0-(0%()(\xff\xdb\x00C\x01\x07\x07\x07\n\x08\n\x13\n\n\x13(\x1a\x16\x1a((((((((((((((((((((((((((((((((((((((((((((((((((\xff\xc2\x00\x11\x08\x00\x01\x00\x01\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x90\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x06?\x02\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?!\x7f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\xfb\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x7f\xff\xd9' +``` +### NoneProp +It should be noted that missing referenced properties, including nested, are gracefully falsey. + +```python +>>> import RestResponse +>>> data = RestResponse.parse({}) +>>> data.property.is_none +None +>>> bool(data.property.is_none) +False +>>> isinstance(data.property.is_none, RestResponse.NoneProp) +True +>>> 'some data' in data.property.is_none +False +>>> [x for x in data.property.is_none] +[] +>>> data.property.is_none = None +>>> isinstance(data.property.is_none, RestResponse.NoneProp) +False +>>> print(data.pretty_print()) +{ + "property": { + "is_none": null + } +} +``` +### SQLAlchemy ORM +RestResponse uses a Mutable mixin provided by SQLAlchemy for interfacing with databases. The following (Flask) snippet should get you started: +```python +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from RestResponse.orm.sqlalchemy import RESTResponse + + +app = Flask(__name__) +db = SQLAlchemy(app) + + +class SomeModel(db.Model): + id = db.Column(db.Integer(), primary_key=True) + data = db.Column(RESTResponse(), nullable=False) + + def __init__(self, data): + self.data = data # data should be json serializable +``` +Data will be saved to the database as a serialized JSON blob. When data is loaded it will be coerced to the underlying RestResponseObj + +## ApiModel + +Inherit RestResponse.ApiModel to facilitate typing out API models. Please note your properties should NOT start with `_`. `ApiModel._data` setter ensures that passed in data conforms to your properties, effectively filtering out underscored properties identified via `dir()`. This behavior can be overriden by adding properties to `__opts__['_overrides']` (**Note**: `__opts__` must be updated before setting `_data`). + +For convenience ApiModel includes helper methods for property integrity. These include: + + 1. `_set_int`, `_get_int` + 2. `_set_string`, `_get_string` + 3. `_set_date`, `_get_date` + 4. `_set_datetime`, `_get_datetime` + 5. `_set_float`, `_get_float` + 6. `_set_bool`, `_get_bool` + +You can initalize ApiCollection with one of these helpers (or define your own), or another object such as `Reference` below. + + +Consider the following snippet: +```python +from RestResponse import ApiModel, ApiCollection + + +class Model(ApiModel): + def __init__(self, data): + self.__opts__['_overrides'] = ['_bar'] + self._data = data + + # this _foo property will be ignored on init + @property + def _foo(self): + return self._get_string(self._data._foo) + + @_foo.setter + def _foo(self, _foo): + self._data._foo = self._set_string(_foo) + + @property + def _bar(self): + return self._get_string(self._data._bar) + + @_bar.setter + def _bar(self, _bar): + self._data._bar = self._set_string(_bar) + + @property + def id(self): + return self._get_int(self._data.id) + + @id.setter + def id(self, id): + self._data.id = self._set_int(id) + + @property + def reference(self): + if not self._data.reference: + self._data.reference = Reference() + return Reference(self._data.reference) + + @reference.setter + def reference(self, reference): + self._data.reference = Reference(reference) + + @property + def int_collection(self): + if not self._data.int_collection: + self._data.int_collection = ApiCollection(self._set_int) + return self._data.int_collection + + @int_collection.setter + def int_collection(self, int_collection): + self._data.int_collection = ApiCollection(self._set_int, int_collection) + + @property + def ref_collection(self): + if not self._data.ref_collection: + self._data.ref_collection = ApiCollection(Reference) + return self._data.ref_collection + + @ref_collection.setter + def ref_collection(self, ref_collection): + self._data.ref_collection = ApiCollection(Reference, ref_collection) + + +class Reference(ApiModel): + def __init__(self, data): + self._data = data + + @property + def id(self): + return self._get_int(self._data.id) + + @id.setter + def id(self, id): + self._data.id = self._set_int(id) +``` +Then initialize with RestObject, dict, ApiModel, or serialized JSON +```python +>>> model = Model({ + 'id': 5, + 'ref': { + 'id': 5, + 'foo': 'bar' + }, + 'int_collection': [1, 2, 3], + 'ref_collection': [ + { + 'id': 1, + }, + { + 'id': 2, + } + ], + 'foo': 'bar', + '_foo': '_bar', + '_bar': 'foo' +}) +>>> print(model._data.pretty_print()) +{ + "int_collection": [ + 1, + 2, + 3 + ], + "ref": { + "id": 5 + }, + "ref_collection": [ + { + "id": 1 + }, + { + "id": 2 + } + ], + "id": 5, + "_bar": "foo" +} +>>> assert '_foo' not in model._data +>>> assert '_bar' in model._data +>>> assert 'foo' not in model._data +>>> assert 'foo' not in model.ref._data +>>> type(model._data) +<class 'RestResponse.objects.RestObject'> +``` +Note: ApiModel will NOT decode/encode binary/callable types by default. You'll need to turn this behavior on by overriding `ApiModel.__opts__`: +```python +Model.__opts__ = { + 'encode_binary': True, + 'encode_callable': True, + 'decode_binary': True, + 'decode_callable': True +} +``` + +## Testing + +Install test dependencies with pipenv. + + $ pip install pipenv + $ pipenv install -d + $ ./test.sh + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/tysonholub/RestResponse.git. This project is +intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the +[Contributor Covenant](http://contributor-covenant.org) code of conduct. + + +## License + +This package is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). + + +%prep +%autosetup -n RestResponse-3.1.2 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-RestResponse -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Wed May 10 2023 Python_Bot <Python_Bot@openeuler.org> - 3.1.2-1 +- Package Spec generated @@ -0,0 +1 @@ +3ede928d2b704bb56abaca850b5267dd RestResponse-3.1.2.tar.gz |
