powernap


Namepowernap JSON
Version 2.0.0 PyPI version JSON
download
home_pageNone
SummaryA small REST client that refills your batteries
upload_time2025-07-12 08:28:36
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT License Copyright (c) 2022-, Joachim Jablon Based on original work by Michael Liao (askxuefeng@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords httpx rest
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # PowerNap

[![Deployed to PyPI](https://img.shields.io/pypi/pyversions/powernap?logo=pypi&logoColor=white)](https://pypi.org/pypi/powernap)
[![GitHub Repository](https://img.shields.io/github/stars/ewjoachim/powernap?logo=github)](https://github.com/ewjoachim/powernap/)
[![Continuous Integration](https://img.shields.io/github/actions/workflow/status/ewjoachim/powernap/ci.yml?logo=github)](https://github.com/ewjoachim/powernap/actions?workflow=CI)
[![Coverage](https://raw.githubusercontent.com/ewjoachim/powernap/python-coverage-comment-action-data/badge.svg)](https://github.com/ewjoachim/powernap/tree/python-coverage-comment-action-data)
[![MIT License](https://img.shields.io/github/license/ewjoachim/powernap?logo=open-source-initiative&logoColor=white)](https://github.com/ewjoachim/powernap/blob/main/LICENSE.md)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](https://github.com/ewjoachim/powernap/blob/main/LICENSE/CODE_OF_CONDUCT.md)

_A small REST client that recharges your batteries_

PowerNap is a simplistic JSON REST API client, a small wrapper around httpx that makes your code
read slightly better.

## Installation

```console
$ pip install powernap
```

## Usage

```python
# You will need an httpx Client. Let's do a GitHub client!
httpx_client = httpx.Client(
    base_url="https://api.github.com/v3",
    headers={"Authorization": f"token {token}"}
)

# Instantiate the PowerNap client
github_client = powernap.PowerNap(httpx_client=httpx_client)

# PowerNap will help you build complex URLs in a pythonic-looking way:
repo = github_client.repos("ewjoachim/powernap").get()

# And access the json responses like objects
count_stars = repo.stargazers_count

# You can also easily send POST requests.
github_client.repos("ewjoachim/powernap").issues(42).comments.post(
    body=f"Wow look! This repo has {count_stars} stars!"
)
```

### Build complex URLs

You can use `client(something)` or `client.something`, and chain calls.
With the form `client(something)`, you can pass multiple parameters, and they
will be joined together

```python

# The next calls are all identical and target:
#    {base_url}/repos/ewjoachim/powernap/stargazers
github_client.repos("ewjoachim/powernap").stargazers.get()
github_client.repos("ewjoachim").powernap("stargazers").get()
github_client.repos("ewjoachim", "powernap", "stargazers").get()
github_client.repos("ewjoachim", "powernap")("stargazers").get()
github_client("repos/ewjoachim/powernap/stargazers").get()

# The recommended way is to use client.something for static parts of the url and
# client(something) with a variable for dynamic parts.

# You can also target the base url directly
# {base_url}
github_client().get()
```

### Access the json responses like objects

On the json responses, all objects (even nested) are configured so that you can
get keys with the `object.key` syntax in addition to the classic
`object["key"]`.

```python
# GET /nested_json -> {"a": {"b":{"c": "d"}}}
response = some_api_client.nested_json.get()

assert response == {"a": {"b":{"c": "d"}}}
assert response["a"]["b"]["c"] == "d"
# But also the magic form:
assert response.a.b.c == "d"
```

### Arguments

The arguments in the `.get/delete()` calls are used as query parameters on
the call.

The arguments in the `.post/put/patch()` calls are put together, and passed
as the json payload for the call.


### Response types

If the response comes with `Content-Type: application/json`, then you'll get
the "magic" json response as described above. Otherwise, if the content type
is `text/*`, you'll get a `string`, and otherwise, you'll get `bytes`.

### Exceptions

If you want to avoid `httpx` exceptions to reach your code, so as to maintain
a good abstraction layer, you may want to subclass Powernap and implement
`handle_exception(self, exc)`. You'll receive an `httpx.HttpError` and it's
your responsibility to raise whatever exception you see fit. Not raising an
exception in this context is considered as an error, though.

```python
from typing import NoReturn

class ApiError(MyProjectError):
    pass

class ForbiddenError(ApiError):
    pass

class ApiClient(PowerNap):
    def handle_exception(self, exc: httpx.HttpError) -> NoReturn:
        if exc.response.status_code == 403:
            raise ForbiddenError
        raise ApiError
```

### More control over input and output

This magic is nice and all, but sometimes, you may want more control.
If you want to send additional headers or a non-JSON-dict payload, or
if you want to read the headers on the response, it possible too.

Instead of calling `.get()`, use either `.get.i()`, `.get.o()` or `.get.io()`
(it works with any method: `get/post/put/patch/delete`):

- If you call with `.get.i(...)` (or `.get.io(...)`), you control the input.
  The method keyword arguments will be passed to the underlying
  `httpx.Client().get(...)` as-is.
- If you call with `.get.o(...)` (or `.get.io(...)`), you get the original
  output. The function will return a `httpx.Response` object. (Note that in
  this case, we will still have called `.raise_for_status()`)

If you regularly use `get.io()`, it's probably that PowerNap is probably not
the project you need. Use `httpx.Client` directly, build something to help you
craft URLs (you can ~~steal~~ copy the relevant code, don't forget to copy the
license too)

## Name

While looking for a name for this lib, I looked at all the synonyms for "small
rest". It's amazing how many other projects have gone the same route. To name a
few:
- [`nap`](https://pypi.org/project/nap/) looks awesome! Unrelated lib but same
  goals as this one. Seems unmaintained but I'm not sure a lib like this needs
  a lot of maintenance.
- [`catnap`](https://pypi.org/project/Catnap/)
- [`respite`](https://pypi.org/project/respite/)
- `snooze` is not taken but
  [`snooze-server`](https://pypi.org/project/snooze-server/) is and I didn't
  want to create confusion.

Funnily enough, a consequent number of those projects have the same goals as
this one, yet don't have the exact look and feel I'm trying to achieve.

## Credits where due

This lib is heavily inspired from githubpy, which is under Apache license.

- [The version that was used for inspiration](https://github.com/michaelliao/githubpy/blob/96d0c3e729c0b3e3c043a604547ccff17782ac2b/github.py)
- Author: Michael Liao (askxuefeng@gmail.com)
- [Original license](https://github.com/michaelliao/githubpy/blob/96d0c3e729c0b3e3c043a604547ccff17782ac2b/LICENSE.txt)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "powernap",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "httpx, rest",
    "author": null,
    "author_email": "Joachim Jablon <ewjoachim@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/e8/9f/a59df9ba47d48924fdf3d495be7e7ca60ad4a305674d697941a9a0b20694/powernap-2.0.0.tar.gz",
    "platform": null,
    "description": "# PowerNap\n\n[![Deployed to PyPI](https://img.shields.io/pypi/pyversions/powernap?logo=pypi&logoColor=white)](https://pypi.org/pypi/powernap)\n[![GitHub Repository](https://img.shields.io/github/stars/ewjoachim/powernap?logo=github)](https://github.com/ewjoachim/powernap/)\n[![Continuous Integration](https://img.shields.io/github/actions/workflow/status/ewjoachim/powernap/ci.yml?logo=github)](https://github.com/ewjoachim/powernap/actions?workflow=CI)\n[![Coverage](https://raw.githubusercontent.com/ewjoachim/powernap/python-coverage-comment-action-data/badge.svg)](https://github.com/ewjoachim/powernap/tree/python-coverage-comment-action-data)\n[![MIT License](https://img.shields.io/github/license/ewjoachim/powernap?logo=open-source-initiative&logoColor=white)](https://github.com/ewjoachim/powernap/blob/main/LICENSE.md)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](https://github.com/ewjoachim/powernap/blob/main/LICENSE/CODE_OF_CONDUCT.md)\n\n_A small REST client that recharges your batteries_\n\nPowerNap is a simplistic JSON REST API client, a small wrapper around httpx that makes your code\nread slightly better.\n\n## Installation\n\n```console\n$ pip install powernap\n```\n\n## Usage\n\n```python\n# You will need an httpx Client. Let's do a GitHub client!\nhttpx_client = httpx.Client(\n    base_url=\"https://api.github.com/v3\",\n    headers={\"Authorization\": f\"token {token}\"}\n)\n\n# Instantiate the PowerNap client\ngithub_client = powernap.PowerNap(httpx_client=httpx_client)\n\n# PowerNap will help you build complex URLs in a pythonic-looking way:\nrepo = github_client.repos(\"ewjoachim/powernap\").get()\n\n# And access the json responses like objects\ncount_stars = repo.stargazers_count\n\n# You can also easily send POST requests.\ngithub_client.repos(\"ewjoachim/powernap\").issues(42).comments.post(\n    body=f\"Wow look! This repo has {count_stars} stars!\"\n)\n```\n\n### Build complex URLs\n\nYou can use `client(something)` or `client.something`, and chain calls.\nWith the form `client(something)`, you can pass multiple parameters, and they\nwill be joined together\n\n```python\n\n# The next calls are all identical and target:\n#    {base_url}/repos/ewjoachim/powernap/stargazers\ngithub_client.repos(\"ewjoachim/powernap\").stargazers.get()\ngithub_client.repos(\"ewjoachim\").powernap(\"stargazers\").get()\ngithub_client.repos(\"ewjoachim\", \"powernap\", \"stargazers\").get()\ngithub_client.repos(\"ewjoachim\", \"powernap\")(\"stargazers\").get()\ngithub_client(\"repos/ewjoachim/powernap/stargazers\").get()\n\n# The recommended way is to use client.something for static parts of the url and\n# client(something) with a variable for dynamic parts.\n\n# You can also target the base url directly\n# {base_url}\ngithub_client().get()\n```\n\n### Access the json responses like objects\n\nOn the json responses, all objects (even nested) are configured so that you can\nget keys with the `object.key` syntax in addition to the classic\n`object[\"key\"]`.\n\n```python\n# GET /nested_json -> {\"a\": {\"b\":{\"c\": \"d\"}}}\nresponse = some_api_client.nested_json.get()\n\nassert response == {\"a\": {\"b\":{\"c\": \"d\"}}}\nassert response[\"a\"][\"b\"][\"c\"] == \"d\"\n# But also the magic form:\nassert response.a.b.c == \"d\"\n```\n\n### Arguments\n\nThe arguments in the `.get/delete()` calls are used as query parameters on\nthe call.\n\nThe arguments in the `.post/put/patch()` calls are put together, and passed\nas the json payload for the call.\n\n\n### Response types\n\nIf the response comes with `Content-Type: application/json`, then you'll get\nthe \"magic\" json response as described above. Otherwise, if the content type\nis `text/*`, you'll get a `string`, and otherwise, you'll get `bytes`.\n\n### Exceptions\n\nIf you want to avoid `httpx` exceptions to reach your code, so as to maintain\na good abstraction layer, you may want to subclass Powernap and implement\n`handle_exception(self, exc)`. You'll receive an `httpx.HttpError` and it's\nyour responsibility to raise whatever exception you see fit. Not raising an\nexception in this context is considered as an error, though.\n\n```python\nfrom typing import NoReturn\n\nclass ApiError(MyProjectError):\n    pass\n\nclass ForbiddenError(ApiError):\n    pass\n\nclass ApiClient(PowerNap):\n    def handle_exception(self, exc: httpx.HttpError) -> NoReturn:\n        if exc.response.status_code == 403:\n            raise ForbiddenError\n        raise ApiError\n```\n\n### More control over input and output\n\nThis magic is nice and all, but sometimes, you may want more control.\nIf you want to send additional headers or a non-JSON-dict payload, or\nif you want to read the headers on the response, it possible too.\n\nInstead of calling `.get()`, use either `.get.i()`, `.get.o()` or `.get.io()`\n(it works with any method: `get/post/put/patch/delete`):\n\n- If you call with `.get.i(...)` (or `.get.io(...)`), you control the input.\n  The method keyword arguments will be passed to the underlying\n  `httpx.Client().get(...)` as-is.\n- If you call with `.get.o(...)` (or `.get.io(...)`), you get the original\n  output. The function will return a `httpx.Response` object. (Note that in\n  this case, we will still have called `.raise_for_status()`)\n\nIf you regularly use `get.io()`, it's probably that PowerNap is probably not\nthe project you need. Use `httpx.Client` directly, build something to help you\ncraft URLs (you can ~~steal~~ copy the relevant code, don't forget to copy the\nlicense too)\n\n## Name\n\nWhile looking for a name for this lib, I looked at all the synonyms for \"small\nrest\". It's amazing how many other projects have gone the same route. To name a\nfew:\n- [`nap`](https://pypi.org/project/nap/) looks awesome! Unrelated lib but same\n  goals as this one. Seems unmaintained but I'm not sure a lib like this needs\n  a lot of maintenance.\n- [`catnap`](https://pypi.org/project/Catnap/)\n- [`respite`](https://pypi.org/project/respite/)\n- `snooze` is not taken but\n  [`snooze-server`](https://pypi.org/project/snooze-server/) is and I didn't\n  want to create confusion.\n\nFunnily enough, a consequent number of those projects have the same goals as\nthis one, yet don't have the exact look and feel I'm trying to achieve.\n\n## Credits where due\n\nThis lib is heavily inspired from githubpy, which is under Apache license.\n\n- [The version that was used for inspiration](https://github.com/michaelliao/githubpy/blob/96d0c3e729c0b3e3c043a604547ccff17782ac2b/github.py)\n- Author: Michael Liao (askxuefeng@gmail.com)\n- [Original license](https://github.com/michaelliao/githubpy/blob/96d0c3e729c0b3e3c043a604547ccff17782ac2b/LICENSE.txt)\n",
    "bugtrack_url": null,
    "license": "MIT License\n        \n        Copyright (c) 2022-, Joachim Jablon\n        Based on original work by Michael Liao (askxuefeng@gmail.com)\n        \n        Permission is hereby granted, free of charge, to any person obtaining a copy of this\n        software and associated documentation files (the \"Software\"), to deal in the Software\n        without restriction, including without limitation the rights to use, copy, modify,\n        merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n        permit persons to whom the Software is furnished to do so, subject to the following\n        conditions:\n        \n        The above copyright notice and this permission notice shall be included in all copies or\n        substantial portions of the Software.\n        \n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n        INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n        PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n        LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT\n        OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n        OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "A small REST client that refills your batteries",
    "version": "2.0.0",
    "project_urls": {
        "Repository": "https://github.com/ewjoachim/powernap"
    },
    "split_keywords": [
        "httpx",
        " rest"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4884f1085e316818fb8e5fd95643b15b449eda242a8a123808c142fd2ce21f9c",
                "md5": "a3d5ad05b279b827cbcc37b7139f4b12",
                "sha256": "1edcd298ba70ac0dbd4e8141a4cb73d01cb468ff681d55365b50e7098d16a02a"
            },
            "downloads": -1,
            "filename": "powernap-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a3d5ad05b279b827cbcc37b7139f4b12",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 6901,
            "upload_time": "2025-07-12T08:28:35",
            "upload_time_iso_8601": "2025-07-12T08:28:35.580571Z",
            "url": "https://files.pythonhosted.org/packages/48/84/f1085e316818fb8e5fd95643b15b449eda242a8a123808c142fd2ce21f9c/powernap-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e89fa59df9ba47d48924fdf3d495be7e7ca60ad4a305674d697941a9a0b20694",
                "md5": "b15a0f21edb1e5444bb32f358a5d9249",
                "sha256": "1879bf74e68c2b821edf871a489322fc45c56c8caa766c421dea4a68d32abf59"
            },
            "downloads": -1,
            "filename": "powernap-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "b15a0f21edb1e5444bb32f358a5d9249",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 6185,
            "upload_time": "2025-07-12T08:28:36",
            "upload_time_iso_8601": "2025-07-12T08:28:36.628009Z",
            "url": "https://files.pythonhosted.org/packages/e8/9f/a59df9ba47d48924fdf3d495be7e7ca60ad4a305674d697941a9a0b20694/powernap-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-12 08:28:36",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ewjoachim",
    "github_project": "powernap",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "powernap"
}
        
Elapsed time: 2.08446s