Skip to content

Commit

Permalink
merging
Browse files Browse the repository at this point in the history
  • Loading branch information
lubaskinc0de committed Jul 28, 2024
2 parents 98b2540 + 92c7203 commit faffe2a
Show file tree
Hide file tree
Showing 25 changed files with 426 additions and 111 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: CI

on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]

jobs:
cpython:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
python-version:
- "3.10"
- "3.11"
- "3.12"

steps:
- uses: actions/checkout@v4
- name: Set up ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install '.' -r requirements_dev.txt
- name: Run ruff
run: |
ruff check .
- name: Run tests
run: |
pytest
42 changes: 42 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
line-length = 79
target-version="py38"
src = ["src"]

include = ["src/**.py", "tests/**.py"]
exclude = ["src/dishka/_adaptix/**"]

lint.select = [
"ALL"
]
lint.ignore = [
"ARG",
"ANN",
"D",
"EM101",
"EM102",
"PT001",
"PT023",
"SIM108",
"SIM114",
"TRY003",
"PLW2901",
"RET505",
"RET506",
"PLR0913",
"UP038",
"TCH001",
"FA100",
# tempraty disabled
"PGH005",
"PLR2004",
"N818", # compatibility issue
]

[lint.per-file-ignores]
"tests/**" = ["TID252", "PLR2004", "S101", "A002"]

[lint.isort]
no-lines-before = ["local-folder"]

[lint.flake8-tidy-imports]
ban-relative-imports = "parents"
10 changes: 0 additions & 10 deletions .travis.yml

This file was deleted.

37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,40 @@ To set same behavior for all methods inherit from BoundMethod class, override `_
### Other params

You can use different body argument name if you want. Just pass `body_name` to the decorator.


### Special cases

#### `None` in query params

By default, AioHTTP doesn't skip query params, you can customize that overriding `_pre_process_request` in Method class

```python
class NoneAwareAiohttpMethod(AiohttpMethod):
async def _pre_process_request(self, request: HttpRequest) -> HttpRequest:
request.query_params = {
k: v for k, v in request.query_params.items() if v is not None
}
return request


class Client(AiohttpClient):
method_class = NoneAwareAiohttpMethod
```

#### Handling `No content`

By default, en each method json response is expected. Sometime you expect no content from server. Especially for 204.
You can handle it by overriding `_response_body` method, e.g.:

```python
class NoneAwareRequestsMethod(RequestsMethod):
def _response_body(self, response: Response) -> Any:
if response.status_code == http.HTTPStatus.NO_CONTENT:
return None
return super()._response_body(response)


class Client(RequestsClient):
method_class = NoneAwareRequestsMethod
```
4 changes: 2 additions & 2 deletions examples/async_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ async def delete_todo(self, id: int):

@post("todos")
async def create_todo(self, body: Todo) -> Todo:
"""Созадем Todo"""
"""Создаем Todo"""

@get("https://httpbin.org/get")
def get_httpbin(self) -> Any:
"""Используемый другой base_url"""
"""Используем другой base_url"""

@post("https://httpbin.org/post")
def upload_image(self, file: File):
Expand Down
38 changes: 38 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[build-system]
requires = ["setuptools>=66.0"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = ["src"]

[project]
name = "dataclass_rest"
version = "0.4"
readme = "README.md"
authors = [
{ name = "Andrey Tikhonov", email = "[email protected]" },
]
license = { text = "Apache-2.0" }
description = "An utility for writing simple clients for REST like APIs"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Python :: 3",
"Topic :: Software Development :: Libraries",
"Typing :: Typed",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
]
dependencies = [
"adaptix",
]

[project.urls]
"Source" = "https://github.com/reagento/dataclass-rest"
"Homepage" = "https://github.com/reagento/dataclass-rest"
"Bug Tracker" = "https://github.com/reagento/dataclass-rest/issues"


6 changes: 0 additions & 6 deletions requirements.txt

This file was deleted.

10 changes: 10 additions & 0 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
adaptix
typing_extensions
aiohttp
requests
requests-mock
mypy
pytest
pytest-asyncio

ruff==0.5.*
37 changes: 0 additions & 37 deletions setup.py

This file was deleted.

2 changes: 1 addition & 1 deletion src/dataclass_rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
]

from .http_request import File
from .rest import rest, get, put, post, patch, delete
from .rest import delete, get, patch, post, put, rest
3 changes: 2 additions & 1 deletion src/dataclass_rest/base_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from adaptix import Retort

from .client_protocol import (
ClientProtocol, FactoryProtocol,
ClientProtocol,
FactoryProtocol,
)


Expand Down
18 changes: 8 additions & 10 deletions src/dataclass_rest/boundmethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from abc import ABC, abstractmethod
from inspect import getcallargs
from logging import getLogger
from typing import Dict, Any, Callable, Optional, NoReturn, Type
from typing import Any, Callable, Dict, NoReturn, Optional, Type

from .client_protocol import ClientProtocol, ClientMethodProtocol
from .exceptions import MalformedResponse
from .http_request import HttpRequest, File
from .client_protocol import ClientMethodProtocol, ClientProtocol
from .exceptions import ClientLibraryError, MalformedResponse
from .http_request import File, HttpRequest
from .methodspec import MethodSpec

logger = getLogger(__name__)
Expand Down Expand Up @@ -84,7 +84,7 @@ def __call__(self, *args, **kwargs):
raise NotImplementedError

def _on_error_default(self, response: Any) -> Any:
raise RuntimeError # TODO exceptions
raise ClientLibraryError


class SyncMethod(BoundMethod):
Expand All @@ -100,8 +100,7 @@ def __call__(self, *args, **kwargs):
request = self._pre_process_request(request)
raw_response = self.client.do_request(request)
response = self._pre_process_response(raw_response)
response = self._post_process_response(response)
return response
return self._post_process_response(response)

def _pre_process_request(self, request: HttpRequest) -> HttpRequest:
return request
Expand Down Expand Up @@ -145,8 +144,7 @@ async def __call__(self, *args, **kwargs):
raw_response = await self.client.do_request(request)
response = await self._pre_process_response(raw_response)
await self._release_raw_response(raw_response)
response = await self._post_process_response(response)
return response
return await self._post_process_response(response)

async def _pre_process_request(self, request: HttpRequest) -> HttpRequest:
return request
Expand All @@ -172,7 +170,7 @@ async def _pre_process_response(self, response: Any) -> Any:
raise MalformedResponse from e

async def _on_error_default(self, response: Any) -> NoReturn:
raise RuntimeError # TODO exceptions
raise ClientLibraryError

@abstractmethod
async def _response_body(self, response: Any) -> Any:
Expand Down
12 changes: 10 additions & 2 deletions src/dataclass_rest/client_protocol.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from typing import (
Protocol, Any, Optional, Callable, Type, runtime_checkable, TypeVar,
Any,
Callable,
Optional,
Protocol,
Type,
TypeVar,
runtime_checkable,
)

from .http_request import HttpRequest
Expand All @@ -18,7 +24,9 @@ class FactoryProtocol(Protocol):
def load(self, data: Any, class_: Type[TypeT]) -> TypeT:
raise NotImplementedError

def dump(self, data: TypeT, class_: Type[TypeT] = None) -> Any:
def dump(
self, data: TypeT, class_: Optional[Type[TypeT]] = None,
) -> Any:
raise NotImplementedError


Expand Down
19 changes: 13 additions & 6 deletions src/dataclass_rest/http/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@

from aiohttp import FormData
from aiohttp.client import (
ClientResponse, ClientSession, ClientError as AioHttpClientError,
ClientError as AioHttpClientError,
)
from aiohttp.client import (
ClientResponse,
ClientSession,
)

from ..base_client import BaseClient
from ..boundmethod import AsyncMethod
from ..exceptions import (
ClientError, ClientLibraryError, ServerError, MalformedResponse,
from dataclass_rest.base_client import BaseClient
from dataclass_rest.boundmethod import AsyncMethod
from dataclass_rest.exceptions import (
ClientError,
ClientLibraryError,
MalformedResponse,
ServerError,
)
from ..http_request import HttpRequest
from dataclass_rest.http_request import HttpRequest


class AiohttpMethod(AsyncMethod):
Expand Down
15 changes: 9 additions & 6 deletions src/dataclass_rest/http/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
from json import JSONDecodeError
from typing import Any, Optional, Tuple

from requests import Session, Response, RequestException
from requests import RequestException, Response, Session

from ..base_client import BaseClient
from ..boundmethod import SyncMethod
from ..exceptions import (
ClientLibraryError, ClientError, ServerError, MalformedResponse,
from dataclass_rest.base_client import BaseClient
from dataclass_rest.boundmethod import SyncMethod
from dataclass_rest.exceptions import (
ClientError,
ClientLibraryError,
MalformedResponse,
ServerError,
)
from ..http_request import HttpRequest, File
from dataclass_rest.http_request import File, HttpRequest


class RequestsMethod(SyncMethod):
Expand Down
Loading

0 comments on commit faffe2a

Please sign in to comment.