{ "info": { "author": "Francesco Zoccheddu", "author_email": "", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7" ], "description": "# swjas\n\n\n**Simple WSGI JSON API Server**\n\n### Installation\n`pip install swjas`\n\n\n## Core\n\n### Usage\nCall `core.makeApplication` to create your WSGI application.\n- **`makeApplication`**\n - **Parameters**\n - `route` : *Iterable* \n Iterable of `(path, handler)` tuples or lists\n - `path` : `str` \n Mapped URL path. \n Do not include scheme, authority, query or fragment.\n - `handler` : *Callable* \n Object (or function) to call to handle incoming POST requests to `path`.\n - **Parameters**\n - `data` : `dict`, `list`, `int`, `float`, `string`, `bool` or `None` \n Deserialied JSON request body if any, otherwhise `None`\n - **Returns** \n JSON serializable object or `None`. \n (Note: `datetime.datetime` objects have built-in serialization support) \n (Note: Custom objects can be serialized if they provide a `_json` property that returns a JSON serializable object)\n - `allowEmptyRequestBody` : `bool` (`True` by default) \n In the case of empty or space request body, if `True` handler argument will be `None`, otherwise a `BadRequestException` will be raised. \n (Note: if `True` empty or space request body will not be distinguishable from `null` body)\n - **Returns** \n WSGI application.\n\n### Examples\nServe using `wsgi_ref`\n```python\nfrom wsgiref.simple_server import make_server\nfrom swjas.core import makeApplication\n\n# Services\n\ndef authentication(data):\n authenticated = data[\"username\"] == \"Francesco\" and data[\"password\"] == \"12345\"\n return {\n \"authenticated\": authenticated\n }\n\ndef intDivision(data):\n if data[\"b\"] == 0:\n return {\n \"error\": \"Division by zero\"\n }\n return {\n \"quotient\": data[\"a\"] // data[\"b\"],\n \"remainder\": data[\"a\"] % data[\"b\"]\n }\n\n# Server\n\nif __name__ == \"__main__\":\n routes = [\n ('auth', authentication),\n ('idiv', intDivision)\n ]\n server = make_server(\"localhost\", 8000, makeApplication(routes))\n server.serve_forever()\n```\nServe using `waitress`\n```python\nimport waitress\nfrom swjas.core import makeApplication\nimport random, string\n\n# Services\n\ndef generateRandomID(data):\n id = \"\"\n for _ in range(data[\"length\"]):\n id += random.choice(string.ascii_letters)\n return {\n \"id\": id\n }\n\n# Server\n\nif __name__ == \"__main__\":\n routes = [\n ('genid', generateRandomID)\n ]\n waitress.serve(makeApplication(routes), listen='*:8000')\n```\nDefine custom JSON serializable objects\n```python\nclass Point:\n \n def __init__(x, y):\n self.x = x\n self.y = y\n\n @property\n def _json(self):\n return [self.x, self.y]\n```\n\n\n## Clean\nValidate and clean the request body.\n\n### Usage\nDecorate your handlers with the `clean.clean` decorator.\n- **`clean`**\n - **Parameters**\n - `field` : `Field` \n Expected request body scheme.\n - **Returns** \n Validated and cleaned request body.\n - **Raises**\n - `exceptions.BadRequestException` \n If the request body does not match the provided scheme.\n- **`Field`**\n - **Constructor**\n - **Parameters**\n - `missing` : `Do` or `Default` \n What to do when this field is missing.\n - `error` : `Do` or `Default` \n What to do when this field is invalid.\n - **Methods**\n - `clean` *(abstract)* \n Validate and clean the field. \n Derived fields must implement it.\n - **Parameters**\n - `value` \n Deserialized request body value to clean.\n - **Returns** \n Cleaned value.\n - **Raises**\n - `FieldException` \n If the value is invalid.\n - `cleanAndAdd` \n Validate and clean the field. \n - **Parameters**\n - `present` : `bool` \n Whether the field is present or not.\n - `value` \n Deserialized request body value to clean.\n - `add` : *Callable* \n Object (or function) to call if the field is cleaned.\n - **Parameters**\n - `value` \n Cleaned value. \n### Examples\nSimple cleaning\n```python\nfrom swjas.clean import clean, Default, IntField, StringField, FloatField, DictField, ListField\n\n@clean(DictField({\n \"a\": IntField(min=1),\n \"b\": IntField(min=1)\n}))\ndef sumPositiveIntegers(data):\n return {\n \"sum\": data[\"a\"] + data[\"b\"]\n }\n\n@clean(ListField(minLength=1, fields=FloatField()))\ndef floatAverage(data):\n return sum(data) / len(data)\n\n@clean(DictField({\n \"username\": StringField(minLength=5, maxLength=20, regex=r\"[A-Za-z0-9]+\"),\n \"password\": StringField(minLength=8, maxLength=16, regex=r\"[A-Za-z0-9]+\"),\n \"age\": IntField(min=18, max=150, missing=Default(None)),\n \"fullName\": StringField(minLength=1, maxLength=128, missing=Default(None))\n}))\ndef printMe(data):\n print(data)\n return {\n \"success\": True\n }\n```\nCustom field\n```python\nfrom swjas.clean import TypeField, FieldException, Do\n\nclass EvenNumberField(TypeField):\n\n def __init__(self, missing=Do.RAISE, error=Do.RAISE):\n super().__init__(int, missing=missing, error=error)\n\n def clean(self, value):\n value = super().clean(value)\n if value % 2 != 0:\n raise FieldException(\"Odd number\")\n return value\n```\n\n\n## Exceptions\n\n### Usage\nAny uncatched `exceptions.HttpException` will be serialized and added to the response body and will set the corresponding Http response status. \nAny `exception.PrintableException` cause set with the `raise ... from` syntax will be added to the response body too.\n\n### Examples\nRaise an `HttpException`\n```python\nfrom swjas.exceptions import AuthorizationException, NotFoundException\nif not authenticated:\n raise AuthorizationException(message=\"Requested object requires authentication\")\nelif not exists:\n raise NotFoundException(message=\"Requested object does not exist\")\n```\nRaise an `HttpException` with cause\n```python\ntry:\n validateRequest(data)\nexcept Exception as e:\n from swjas.exceptions import BadRequestException\n raise BadRequestException(message=\"Error while validating the request\") from e\n```\nCreate an `HttpException` on the fly\n```python\nfrom swjas.exceptions import HttpException\nraise HttpException.build(410)\n```\nDefine an `HttpException`\n```python\nfrom swjas.exceptions import HttpException\n\nclass GoneException(HttpException):\n statusCode = 410\n```\n\n\n## Client\nSend requests.\n\n### Usage\nRun `python -m swjas.client -h` from command line or call `client.request` or `client.service` functions.\n\n### Examples\nSend a request providing JSON body via command line and save result to JSON file\n```\n>>> python -m swjas.client //localhost:8080/signup --outputstatus --indent 4 > response.json\n{\n \"username\": \"Francesco\",\n \"password\": \"12345\" \n}\n^Z\n```\nSend a request providing JSON body via file and print result to console\n```\n>>> python -m swjas.client //localhost:8080/signup --if request.json\n{\n \"result\": \"success\"\n}\n```\nSend a request via script\n```python\nfrom swjas.client import HttpException, RequestException, request\n\ndata = {\n \"username\": \"Francesco\",\n \"password\": \"12345\"\n}\n\ntry:\n res = request(\"//localhost:8080/signup\", data)\nexcept HttpException as e:\n print(f\"{e.statusCode}: {e.statusMessage}\\n{e.responseBody}\")\nexcept RequestException as e:\n print(f\"Request failed: {e}\")\n```", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/francescozoccheddu/swjas", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "swjas", "package_url": "https://pypi.org/project/swjas/", "platform": "", "project_url": "https://pypi.org/project/swjas/", "project_urls": { "Homepage": "https://github.com/francescozoccheddu/swjas" }, "release_url": "https://pypi.org/project/swjas/0.1.0/", "requires_dist": null, "requires_python": "", "summary": "Simple WSGI JSON API Server", "version": "0.1.0" }, "last_serial": 5693003, "releases": { "0.0.4": [ { "comment_text": "", "digests": { "md5": "788c237ea6b5f8c18937e8046575a227", "sha256": "ec9dc7c1993ae6d474ede15fd645a8a9fa603b71ee14a56ec2b8db4b5fbf671b" }, "downloads": -1, "filename": "swjas-0.0.4.tar.gz", "has_sig": false, "md5_digest": "788c237ea6b5f8c18937e8046575a227", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12917, "upload_time": "2019-07-09T12:09:26", "url": "https://files.pythonhosted.org/packages/c5/19/82bfdab789f0352c4f4714ec9460e43639020faafcf8b3ea7688f19eb13a/swjas-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "0cded47cbcbb213eb56e25b42c9a2cac", "sha256": "01ff6d97f77fc80b131a40184c2c2ec4c8f2471867de50997c9d99300990d9ce" }, "downloads": -1, "filename": "swjas-0.0.5.tar.gz", "has_sig": false, "md5_digest": "0cded47cbcbb213eb56e25b42c9a2cac", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13097, "upload_time": "2019-07-09T15:22:41", "url": "https://files.pythonhosted.org/packages/9e/90/9147704cef2074b8df73dd7dcfc0875c0fb163f70b87d4e9956be5bc0e50/swjas-0.0.5.tar.gz" } ], "0.0.6": [ { "comment_text": "", "digests": { "md5": "a1726e7703e0b85673cff375f8ec987e", "sha256": "a1b8bb498a446a0d75d9b19c0d74af0a6c41fe7a908d9be6569d4ac9b8bafb28" }, "downloads": -1, "filename": "swjas-0.0.6.tar.gz", "has_sig": false, "md5_digest": "a1726e7703e0b85673cff375f8ec987e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14045, "upload_time": "2019-07-09T19:42:53", "url": "https://files.pythonhosted.org/packages/97/6e/423c45dfd04468e7d79bb702b1f48eb036993ada0f57b9ec6b17c530e0bc/swjas-0.0.6.tar.gz" } ], "0.0.7": [ { "comment_text": "", "digests": { "md5": "aa9d2190244e6acc2bd93bc546c76d51", "sha256": "4eaf8a56644ae433645b13a25d37e68fea3dfc0288ecdbe8e2e7d4e0cb0b590f" }, "downloads": -1, "filename": "swjas-0.0.7.tar.gz", "has_sig": false, "md5_digest": "aa9d2190244e6acc2bd93bc546c76d51", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15620, "upload_time": "2019-07-13T08:08:27", "url": "https://files.pythonhosted.org/packages/c5/d9/08cbdf70482849ebf2f74a56ffcbf7df74fba7a241737e330c890686fe24/swjas-0.0.7.tar.gz" } ], "0.0.8": [ { "comment_text": "", "digests": { "md5": "89c30347980f8bad62c19401123af9c0", "sha256": "767a31a59545514863d39d1c2bfeb8a4a3c0e78cee7af5b5e7c2483fb14bdc6e" }, "downloads": -1, "filename": "swjas-0.0.8.tar.gz", "has_sig": false, "md5_digest": "89c30347980f8bad62c19401123af9c0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15701, "upload_time": "2019-07-21T16:46:31", "url": "https://files.pythonhosted.org/packages/6e/f0/cbb96e7680002ea60434a8c999d68c8189d295083a476a7206470cba782b/swjas-0.0.8.tar.gz" } ], "0.0.9": [ { "comment_text": "", "digests": { "md5": "47090c0cc9e5916afa1410b90e8d03f4", "sha256": "b315f0eb4dde533789650953ceea10cdd9a5b65d6afbbc9f54c03f7684e882e7" }, "downloads": -1, "filename": "swjas-0.0.9.tar.gz", "has_sig": false, "md5_digest": "47090c0cc9e5916afa1410b90e8d03f4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15688, "upload_time": "2019-08-06T17:46:20", "url": "https://files.pythonhosted.org/packages/67/be/aad331abc43147dfbc9b3ee9818fa4bafff7f49db52a1ee5d4c0010ec565/swjas-0.0.9.tar.gz" } ], "0.1.0": [ { "comment_text": "", "digests": { "md5": "5d9dc5bd4e28d356bba556705ae5466e", "sha256": "f3a9ae56fe4d25c2cb0cabe0069e9345916f2a8d21a5e74c175b25340593bb32" }, "downloads": -1, "filename": "swjas-0.1.0.tar.gz", "has_sig": false, "md5_digest": "5d9dc5bd4e28d356bba556705ae5466e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15754, "upload_time": "2019-08-17T23:35:49", "url": "https://files.pythonhosted.org/packages/7c/3d/b8a19dc55c956e720f953f7fd6877dd6e1b59174959024caef1d61ea03b3/swjas-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "5d9dc5bd4e28d356bba556705ae5466e", "sha256": "f3a9ae56fe4d25c2cb0cabe0069e9345916f2a8d21a5e74c175b25340593bb32" }, "downloads": -1, "filename": "swjas-0.1.0.tar.gz", "has_sig": false, "md5_digest": "5d9dc5bd4e28d356bba556705ae5466e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15754, "upload_time": "2019-08-17T23:35:49", "url": "https://files.pythonhosted.org/packages/7c/3d/b8a19dc55c956e720f953f7fd6877dd6e1b59174959024caef1d61ea03b3/swjas-0.1.0.tar.gz" } ] }