{"id":15142143,"url":"https://github.com/deckerego/ampule","last_synced_at":"2025-10-23T19:31:09.490Z","repository":{"id":43349216,"uuid":"404153795","full_name":"deckerego/ampule","owner":"deckerego","description":"A tiny HTTP server made for CircuitPython WiFi devices (like the ESP32)","archived":false,"fork":false,"pushed_at":"2023-01-14T22:13:37.000Z","size":28,"stargazers_count":60,"open_issues_count":2,"forks_count":10,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-01-30T20:40:20.451Z","etag":null,"topics":["circuitpython","circuitpython-library","esp32-s2","feather-huzzah","featherwing","http-server","pixelwing"],"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/deckerego.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}},"created_at":"2021-09-07T23:50:18.000Z","updated_at":"2024-09-10T01:28:32.000Z","dependencies_parsed_at":"2023-02-09T20:45:59.768Z","dependency_job_id":null,"html_url":"https://github.com/deckerego/ampule","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deckerego%2Fampule","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deckerego%2Fampule/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deckerego%2Fampule/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deckerego%2Fampule/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deckerego","download_url":"https://codeload.github.com/deckerego/ampule/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237877021,"owners_count":19380344,"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":["circuitpython","circuitpython-library","esp32-s2","feather-huzzah","featherwing","http-server","pixelwing"],"created_at":"2024-09-26T09:23:39.039Z","updated_at":"2025-10-23T19:31:09.159Z","avatar_url":"https://github.com/deckerego.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ampule\n\nA tiny HTTP server made for CircuitPython WiFi devices (like the ESP32).\n\nNote that ampule is in alpha and right now for use by\n[tally_circuitpy](https://github.com/deckerego/tally_circuitpy). Feel free to\nuse it, but know that there are tons of things not yet implemented.\n\nampule gathers inspiration from\n[Bottle: Python Web Framework](https://bottlepy.org/docs/dev/index.html),\nAdafruit's [CircuitPython WSGI](https://github.com/adafruit/Adafruit_CircuitPython_WSGI)\nlibrary, Adafruit's [ESP32 SPI WSGI Server](https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/main/adafruit_esp32spi/adafruit_esp32spi_wsgiserver.py),\nand Adafruit's [CircuitPython Requests library](https://github.com/adafruit/Adafruit_CircuitPython_Requests).\n\n## Usage\n\nRoute definitions in ampule are expressed through annotations that define the\npath and optionally the method to be matched against for incoming HTTP requests.\nampule will pass along the request and any path elements that operate as arguments,\nand expects the HTTP status code, headers, and body to be returned as a tuple.\n\n### \"Hello World\" Example\n\nThe following example is a simple, working HTTP server that accepts an\nHTTP GET request at `/hello/world` and responds with \"Hi There!\".\n\n```python\nimport ampule, socketpool, wifi\n\n@ampule.route(\"/hello/world\")\ndef light_set(request):\n  return (200, {}, 'Hi There!'})\n\ntry:\n  from secrets import secrets\nexcept ImportError:\n  print(\"WiFi secrets not found in secrets.py\")\n  raise\n\ntry:\n  print(\"Connecting to %s...\" % secrets[\"ssid\"])\n  print(\"MAC: \", [hex(i) for i in wifi.radio.mac_address])\n  wifi.radio.connect(secrets[\"ssid\"], secrets[\"password\"])\nexcept:\n  print(\"Error connecting to WiFi\")\n  raise\n\npool = socketpool.SocketPool(wifi.radio)\nsocket = pool.socket()\nsocket.bind(['0.0.0.0', 80])\nsocket.listen(1)\nsocket.setblocking(True)\nprint(\"Connected to %s, IPv4 Addr: \" % secrets[\"ssid\"], wifi.radio.ipv4_address)\n\nwhile True:\n  ampule.listen(socket)\n```\n\nThe majority of this code is CircuitPython boilerplate that connects to a WiFi\nnetwork, listens on port 80 in blocking mode, and connects ampule to the open socket.\n\nThe line `@ampule.route(\"/hello/world\")` registers the following function for\nthe path specified, and responds with HTTP 200, no headers, and a response body\nof \"Hi There!\"\n\n### Variables in the URL Path\n\nRoute paths can also contain variables, as in:\n\n```python\n@ampule.route(\"/hello/\u003cname\u003e\")\ndef light_set(request, name):\n  return (200, {}, \"Hi there %s!\" % name)\n```\n\n### Query Parameters\n\nQuery parameters are passed along with the request. If a URL ends with\n`/hello/world?name=Bob` then the following route will return\n\"Hi there Bob!\":\n\n```python\n@ampule.route(\"/hello/world\")\ndef light_set(request):\n  name = request.params[\"name\"]\n  return (200, {}, \"Hi there %s!\" % name)\n```\n\n### Specifying HTTP Method on Routes\n\nYou can explicitly specify the HTTP method on a route. If it is omitted,\nthe match defaults to 'GET'.\n\n```python\n@ampule.route(\"/hello/world\", method='POST')\ndef light_set(request):\n  name = request.body\n  return (200, {}, \"Hi there %s!\" % name)\n```\n\n### Specifying Headers\n\nHeaders are returned as part of the returned tuple in the route handler.\nAs an example, returning JSON content and permitting cross-origin access\nwould be:\n\n```python\nheaders = {\n    \"Content-Type\": \"application/json; charset=UTF-8\",\n    \"Access-Control-Allow-Origin\": '*',\n    \"Access-Control-Allow-Methods\": 'GET, POST',\n    \"Access-Control-Allow-Headers\": 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'\n}\n\n@ampule.route(\"/hello/world\")\ndef light_set(request):\n  return (200, headers, '{\"hello\": true}')\n```\n\n## Building ampule\n\nampule can be added as a .py file into CIRCUITPY projects, or it can be\nused as a MicroPython compiled library and added to the `lib/` folder\nof a CIRCUITPY project.\n\n### Building MicroPython libraries\n\nAfter installing the `mpy-cross` compiler from\nhttps://learn.adafruit.com/creating-and-sharing-a-circuitpython-library?view=all#mpy-2982472-11\nyou can execute:\n\n```sh\nmpy-cross ampule.py\n```\n\nTo build a MicroPython compiled library.\n\n### Running tests\n\nTests require `pytest` to be installed along with a local version of ampule, as in:\n\n```sh\npip3 install -e .\n```\n\nOnce installed pytest can be invoked with default arguments, as in:\n\n```sh\npytest\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeckerego%2Fampule","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeckerego%2Fampule","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeckerego%2Fampule/lists"}