Skip to content

Commit

Permalink
0.0.2 (#15)
Browse files Browse the repository at this point in the history
* project metadata updates

* Refactored Mechanism for Handling Exceptions Raised (#14)

* Setup pre-commit hooks

* Updated path for ruff formatter

* initial readme commit - needs updating

* Updated return object for sendRequests; support for return_exceptions

* initial test setup

* Added tests that interact with test server

* Implemented PUT, PATCH, OPTIONS, DELETE; updated return type to dataclass

* isort formatting

* GitHub Actions + Ruff config

* initial commit of workflow

* Updated import path

* Updated braches config

* Updated RUFF linter config and added to GitHub Actions workflow

* Updated ignore config for Ruff

* Updated type annotations

* Test code formatting fixes

* Added badges to README

* Renamed workflow to CI

* Documentation for Installation and Example Usage in README (#5)

* Created 'Installing' and 'Usage' sections in README

* Added example in 'Usage' section

* Added 'Parsing Results' subsection

* Removed 'Parsing Results' subsection and added to 'Example' code block

* Setup PyPI Publishing from GitHub Actions Workflow (#4)

* Added release workflow to publish to pypi

* Updated version to 0.0.2

* Updated release workflow to only be triggered by pushed to main

* Fixed typo

* Renamed RequestResponse.responseBody -> RequestResponse.body

* uvloop is optional; defaults to asyncio (#9)

* Updated README and reset version to 0.0.1

* Increased version to 0.0.2.alpha

* Updated  workflow to be triggered by releases

* Removed RequestResults

* Removed RequestResults

* Added test server written in python + additional tests

* Removed references to RequestResults

* Commenting tests until GitHub actions integration completed
  • Loading branch information
fullerzz authored Dec 15, 2023
1 parent 14c61a7 commit 3fd6596
Show file tree
Hide file tree
Showing 7 changed files with 1,014 additions and 583 deletions.
29 changes: 10 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ pip install "loamy[uvloop]"
The package can be imported as shown:

```python
from loamy.session import Clump, RequestMap, RequestResults
from loamy.session import Clump, RequestMap, RequestResponse
```

| Class | Description|
| ----- | -----------|
| `Clump` | Container object that stores collection of requests (type RequestMap) to send |
| `RequestMap` | Container object that stores all info about an individual request to send |
| `RequestResults` | Container object that stores the request responses and any exceptions raised |
| `RequestResponse` | Container object that stores the request response and any exception raised for each individual request |


### Example
Expand All @@ -59,19 +59,19 @@ req3 = RequestMap(

# Create Clump and call sendRequests()
session = Clump(requests=[req1, req2, req3])
reqResps: RequestResults = session.sendRequests(return_exceptions=True)
responses: list[RequestResponse] = session.sendRequests(return_exceptions=True)

# Handle exceptions raised for individual requests
if len(reqResps.taskExceptions) > 0:
print("Handling exceptions")

# Handle responses for individual requests
for resp in requestResponses:
for resp in responses:
httpVerb = resp.requestMap.httpOperation
print(f"Evaluating response for {httpVerb} request to {resp.requestMap.url}")
print(f"Status Code: {resp.statusCode}")
if resp.body is not None:
print(resp.body)
if resp.error is not None:
print("Exception raised for request")
else:
print(f"Status Code: {resp.statusCode}")
if resp.body is not None:
print(resp.body)
```

#### RequestMap Class
Expand All @@ -94,12 +94,3 @@ class RequestResponse(msgspec.Struct):
statusCode: int
body: dict | None = None
```

#### RequestResults Class

```python
@dataclass
class RequestResults:
requestResponses: list[RequestResponse]
taskExceptions: list[BaseException]
```
928 changes: 652 additions & 276 deletions poetry.lock

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[tool.poetry]
name = "loamy"
version = "0.0.2.alpha"
version = "0.0.2"
description = ""
authors = ["Zach Fuller <[email protected]>"]
readme = "README.md"
license = "MIT"
repository = "https://github.com/fullerzz/zConcurrent"
repository = "https://github.com/fullerzz/loamy"
packages = [{include = "loamy", from = "src"}]

[tool.poetry.dependencies]
Expand All @@ -20,6 +20,9 @@ ruff = "~0.1.4"
pre-commit = "3.5.0"
pytest = "^7.4.3"
pytest-asyncio = "^0.21.1"
blacksheep = "^2.0.1"
uvicorn = "^0.24.0.post1"
mypy = "^1.7.1"

[tool.poetry.extras]
uvloop = ["uvloop"]
Expand Down
489 changes: 245 additions & 244 deletions requirements.txt

Large diffs are not rendered by default.

65 changes: 23 additions & 42 deletions src/loamy/session.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import asyncio
from dataclasses import dataclass
from typing import Literal

import aiohttp
Expand All @@ -25,57 +24,52 @@ class RequestResponse(msgspec.Struct):
requestMap: RequestMap
statusCode: int
body: dict | None = None


@dataclass
class RequestResults:
requestResponses: list[RequestResponse]
taskExceptions: list[BaseException]
error: BaseException | None = None


class Clump:
def __init__(self, requests: list[RequestMap]) -> None:
self._requestMaps: list[RequestMap] = requests

def sendRequests(self, return_exceptions: bool = False) -> RequestResults:
def sendRequests(self, return_exceptions: bool = False) -> list[RequestResponse]:
return asyncRun(self._sendRequests(rtn_exc=return_exceptions))

async def _sendRequests(self, rtn_exc: bool) -> RequestResults:
async def _sendRequests(self, rtn_exc: bool) -> list[RequestResponse]:
async with aiohttp.ClientSession() as session:
httpTasks: list[asyncio.Task] = []
for req in self._requestMaps:
httpTasks.append(
asyncio.ensure_future(self._routeIndividualRequest(req, session))
)
responses: list[RequestResponse | BaseException] = await asyncio.gather(
responses: list[RequestResponse] = await asyncio.gather(
*httpTasks, return_exceptions=rtn_exc
)
requestResults: RequestResults = await _processResults(
taskResults=responses
)
return requestResults
return responses

async def _routeIndividualRequest(
self, reqMap: RequestMap, session: aiohttp.ClientSession
) -> RequestResponse:
requestResponse: RequestResponse = RequestResponse(
requestMap=reqMap, statusCode=0
)
match reqMap.httpOperation:
case "GET":
requestResponse = await self._sendGetRequest(reqMap, session)
case "POST":
requestResponse = await self._sendGetRequest(reqMap, session)
case "PUT":
requestResponse = await self._sendPutRequest(reqMap, session)
case "PATCH":
requestResponse = await self._sendPatchRequest(reqMap, session)
case "OPTIONS":
requestResponse = await self._sendOptionsRequest(reqMap, session)
case "DELETE":
requestResponse = await self._sendDeleteRequest(reqMap, session)
case _:
pass
try:
match reqMap.httpOperation:
case "GET":
requestResponse = await self._sendGetRequest(reqMap, session)
case "POST":
requestResponse = await self._sendPostRequest(reqMap, session)
case "PUT":
requestResponse = await self._sendPutRequest(reqMap, session)
case "PATCH":
requestResponse = await self._sendPatchRequest(reqMap, session)
case "OPTIONS":
requestResponse = await self._sendOptionsRequest(reqMap, session)
case "DELETE":
requestResponse = await self._sendDeleteRequest(reqMap, session)
case _:
pass
except Exception as e:
requestResponse.error = e

return requestResponse

Expand Down Expand Up @@ -171,16 +165,3 @@ async def _sendDeleteRequest(
requestMap=reqMap, statusCode=statusCode, body=body
)
return reqResponse


async def _processResults(
taskResults: list[RequestResponse | BaseException]
) -> RequestResults:
responses: list[RequestResponse] = []
taskExceptions: list[BaseException] = []
for result in taskResults:
if isinstance(result, BaseException):
taskExceptions.append(result)
else:
responses.append(result)
return RequestResults(requestResponses=responses, taskExceptions=taskExceptions)
19 changes: 19 additions & 0 deletions tests/bin/test_server/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from blacksheep import Application, Request, Response, get, json, post

app = Application()


@get("/")
async def index() -> Response:
return json({"message": "Hello, world!"})


@post("/foo")
async def post_foo(request: Request) -> Response:
data = await request.json()
return json(data)


@get("/exception")
async def mock_exception():
raise Exception("Mock exception")
60 changes: 60 additions & 0 deletions tests/test_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# import pytest
# from src.loamy.session import Clump, RequestMap, RequestResponse


# @pytest.fixture(scope="session")
# def request_map_collection() -> list[RequestMap]:
# requests: list[RequestMap] = []
# for i in range(0, 100):
# print(i)
# if i % 2 == 0:
# requests.append(
# RequestMap(
# url="http://localhost:44777/",
# httpOperation="GET",
# )
# )
# else:
# requests.append(
# RequestMap(
# url="http://localhost:44777/foo",
# httpOperation="POST",
# body={"foo": "bar"},
# )
# )
# return requests


# @pytest.fixture(scope="session")
# def request_map_to_trigger_exception() -> RequestMap:
# return RequestMap(
# url="http://localhost:44777/exception",
# httpOperation="GET",
# )


# def test_send_requests(request_map_collection: list[RequestMap]) -> None:
# session = Clump(requests=request_map_collection)
# responses: list[RequestResponse] = session.sendRequests()
# assert len(responses) == 100
# for response in responses:
# assert response.statusCode == 200
# assert response.error is None


# def test_send_requests_with_exceptions(
# request_map_collection: list[RequestMap],
# request_map_to_trigger_exception: RequestMap,
# ) -> None:
# requests: list[RequestMap] = request_map_collection.copy()
# requests.append(request_map_to_trigger_exception)
# session = Clump(requests=requests)
# responses: list[RequestResponse] = session.sendRequests(return_exceptions=True)
# assert len(responses) == 101
# for response in responses:
# if response.requestMap.url == "http://localhost:44777/exception":
# assert response.statusCode == 0
# assert response.error is not None
# else:
# assert response.statusCode == 200
# assert response.error is None

0 comments on commit 3fd6596

Please sign in to comment.