{"id":13813834,"url":"https://github.com/bagrat/calm","last_synced_at":"2025-04-09T17:14:27.101Z","repository":{"id":89355517,"uuid":"57916400","full_name":"bagrat/calm","owner":"bagrat","description":"It is always Calm before a Tornado!","archived":false,"fork":false,"pushed_at":"2017-08-18T14:12:57.000Z","size":774,"stargazers_count":48,"open_issues_count":10,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-09T17:14:21.621Z","etag":null,"topics":["python","python-3","python-3-6","rest","rest-api","restapi","restful","restful-api","tornado"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bagrat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-05-02T19:38:57.000Z","updated_at":"2023-07-07T15:56:03.000Z","dependencies_parsed_at":"2024-01-20T15:59:54.688Z","dependency_job_id":"0c80723f-3ad3-4f8a-94a8-fce657515359","html_url":"https://github.com/bagrat/calm","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bagrat%2Fcalm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bagrat%2Fcalm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bagrat%2Fcalm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bagrat%2Fcalm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bagrat","download_url":"https://codeload.github.com/bagrat/calm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248074925,"owners_count":21043490,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","python-3","python-3-6","rest","rest-api","restapi","restful","restful-api","tornado"],"created_at":"2024-08-04T04:01:32.149Z","updated_at":"2025-04-09T17:14:27.077Z","avatar_url":"https://github.com/bagrat.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# Calm\n\n\u003ca href=\"http://calm.n9co.de/\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/n9code/calm/master/docs/logo/calm-logo.png\"\n         alt=\"Calm Logo\"\n         align=\"right\"\n         width=70\n         height=70 /\u003e\n\u003c/a\u003e\n    \n[![PyPI](https://img.shields.io/pypi/v/calm.svg)](https://pypi.python.org/pypi/calm)\n[![Build Status](https://travis-ci.org/bagrat/calm.svg?branch=master)](https://travis-ci.org/bagrat/calm)\n[![Coverage Status](https://coveralls.io/repos/github/bagrat/calm/badge.svg?branch=master)](https://coveralls.io/github/bagrat/calm?branch=master)\n[![Code Health](https://landscape.io/github/bagrat/calm/master/landscape.svg?style=flat)](https://landscape.io/github/bagrat/calm/master)\n[![Gitter](https://badges.gitter.im/bagrat/calm.svg)](https://gitter.im/bagrat/calm?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/bagrat/calm/master/LICENSE)\n\n## Introduction\n\nCalm is an extension to Tornado Framework that provides decorators and other\ntools to easily implement RESTful APIs. The purpose of Calm is to ease the\nprocess of defining your API, parsing argument values in the request handlers,\netc.\n\n### Installation\n\nCalm installation process is dead simple with `pip`:\n\n```\n$ pip install calm\n```\n\n*Note: Calm works only with Python 3.5*\n\n## Let's code!\n\nHere is a basic usage example of Calm:\n\n```python\nimport tornado.ioloop\nfrom calm import Application\n\n\napp = Application()\n\n\n@app.get('/hello/:your_name')\nasync def hello_world(request, your_name):\n    return {'hello': your_name}\n\n\ntornado_app = app.make_app()\ntornado_app.listen(8888)\ntornado.ioloop.IOLoop.current().start()\n```\n\nNow go ahead and try your new application! Navigate to\n`http://localhost:8888/hello/YOUR_NAME_HERE` and see what you get.\n\nNow that you built your first Calm RESTful API, let us dive deeper and see more\nfeatures of Calm. Go ahead and add the following code to your first application.\n\n```python\n# Calm has the notion of a Service. A Service is nothing more than a URL prefix for\n# a group of endpoints.\nmy_service = app.service('/my_service')\n\n\n# So when usually you would define your handler with `@app.get`\n# (or `post`, `put`, `delete`), with Service you use the same named methods of\n# the Service instance\n@my_service.post('/body_demo')\nasync def body_demo(request):\n    \"\"\"\n    The request body is automatically parsed to a dict.\n\n    If the request body is not a valid JSON, `400` HTTP error is returned.\n\n    When the handler returns not a `dict` object, the return value is nested\n    into a JSON, e.g.:\n\n        {\"result\": YOUR_RETURN_VALUE}\n\n    \"\"\"\n    return request.body['key']\n\n\n@my_service.get('/args_demo/:number')\nasync def args_demo(request, number: int, arg1: int, arg2='arg2_default'):\n    \"\"\"\n    You can specify types for your request arguments.\n\n    When specified, Calm will parse the arguments to the appropriate type. When\n    there is an error parsing the value, `400` HTTP error is returned.\n\n    Any function parameters that do not appear as path arguments, are\n    considered query arguments. If a default value is assigned for a query\n    argument it is considered optional. And finally if not all required query\n    arguments are passed, `400` HTTP error is returned.\n    \"\"\"\n    return {\n        'type(number)': str(type(number)),\n        'type(arg1)': str(type(arg1)),\n        'arg2': arg2\n    }\n```\n\nIf you followed the comments in the example, then we are ready to play with it!\n\nFirst let us see how Calm treats request and response bodies:\n\n```\n$ curl -X POST --data '{\"key\": \"value\"}' 'localhost:8888/my_service/body_demo'\n{\"result\": \"value\"}\n\n$ curl -X POST --data '{\"another_key\": \"value\"}' 'localhost:8888/my_service/body_demo'\n{\"error\": \"Oops our bad. We are working to fix this!\"}\n\n$ curl -X POST --data 'This is not JSON' 'localhost:8888/my_service/body_demo'\n{\"error\": \"Malformed request body. JSON is expected.\"}\n```\n\nNow it's time to observe some request argument magic!\n\n```\n$ curl 'localhost:8888/my_service/args_demo/0'\n{\"error\": \"Missing required query param 'arg1'\"}\n\n$ curl 'localhost:8888/my_service/args_demo/0?arg1=12'\n{\"type(arg1)\": \"\u003cclass 'int'\u003e\", \"type(number)\": \"\u003cclass 'int'\u003e\", \"arg2\": \"arg2_default\"}\n\n$ curl 'localhost:8888/my_service/args_demo/0?arg1=not_a_number'\n{\"error\": \"Bad value for integer: not_a_number\"}\n\n$ curl 'localhost:8888/my_service/args_demo/0?arg1=12\u0026arg2=hello'\n{\"type(arg1)\": \"\u003cclass 'int'\u003e\", \"type(number)\": \"\u003cclass 'int'\u003e\", \"arg2\": \"hello\"}\n```\n\n### Adding custom `RequestHandler` implementations\n\nIf you have a custom Tornado `RequestHandler` implementation, you can easily add\nthem to your Calm application in one of the two ways:\n\n* using the `Application.add_handler` method\n* using the `Application.custom_handler` decorator\n\nFor the first option, you can just define the custom handler and manually add it\nto the Calm application, just like you would define a Tornado application:\n\n```python\nclass MyHandler(RequestHandler):\n    def get(self):\n        self.write('Hello Custom Handler!')\n\napp.add_handler('/custom_handler', MyHandler)\n```\n\nThe second option might look more consistent with other Calm-style definitions:\n\n```python\n@app.custom_handler('/custom_handler')\nclass MyHandler(RequestHandler):\n    def get(self):\n        self.write('Hello Custom Handler!')\n```\n\nYou can also use the `custom_handler` decorator of services, e.g.:\n\n\n```python\ncustom_service = app.service('/custom')\n\n@custom_service.custom_handler('/custom_handler')\nclass MyHandler(RequestHandler):\n    def get(self):\n        self.write('Hello Custom Handler!')\n```\n\n## Contributions\n\nCalm loves Pull Requests and welcomes any contribution be it an issue,\ndocumentation or code. A good start for a contribution can be reviewing existing\nopen issues and trying to fix one of them.\n\nIf you find nothing to work on but cannot kill the urge, jump into the [gitter\nchannel](https://gitter.im/n9code/calm) and ask \"what can I do?\".\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbagrat%2Fcalm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbagrat%2Fcalm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbagrat%2Fcalm/lists"}