Skip to content

Commit

Permalink
[PPA-2] InMemoryRepository Generation System (#1)
Browse files Browse the repository at this point in the history
* impl(InMemoryRepository): metaclass (WIP)

* impl(InMemoryRepository): metaclass's _make_retrieve_method

* impl(InMemoryRepository): metaclass's _make_update_method

* impl(InMemoryRepository): decorator and tests

* impl(InMemoryRepository): decorator and tests

* impl(InMemoryRepository): decorator and tests

* fix(InMemoryRepository): types and syntax

* add(CI): ci

* version(toml): 0.2.0
  • Loading branch information
Saucisse à roulettes committed Jun 10, 2023
1 parent b84185e commit 3e461c4
Show file tree
Hide file tree
Showing 8 changed files with 972 additions and 0 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Checks

on:
workflow_dispatch:
push:

jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up python
id: setup-python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Load cached Poetry installation
id: cached-poetry
uses: actions/cache@v3
with:
path: ~/.local
key: check-poetry

- name: Install and configure Poetry
if: steps.cached-poetry.outputs.cache-hit != 'true'
uses: snok/install-poetry@v1

- name: Load cached venv
id: cached-venv
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-

- name: Install dependencies
if: steps.cached-venv.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root --with=dev

- name: Check syntax
run: poetry run black src --check

- name: Check typing
run : poetry run mypy

- name: Run pytest
run: poetry run pytest
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,12 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
/.idea/.gitignore
/.idea/aws.xml
/.idea/misc.xml
/.idea/modules.xml
/.idea/ppa.iml
/.idea/inspectionProfiles/profiles_settings.xml
/.idea/inspectionProfiles/Project_Default.xml
/.idea/copyright/Saucisses_A_Roulettes.xml
/.idea/vcs.xml
802 changes: 802 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[tool.poetry]
name = "ppa"
version = "0.2.0"
description = ""
authors = ["Saucisse À Roulette <gael.monachon.dev@gmail.com>"]
readme = "README.md"
packages = [{include = "src"}]

[tool.poetry.dependencies]
python = "^3.11"
pydantic = {version = "^1.10.9"}

[tool.poetry.group.dev.dependencies]
pydantic = {extras = ["mypy"], version = "^1.10.9"}
pytest = "^7.3.2"
mypy = "^1.3.0"
black = {extras = ["d"], version = "^23.3.0"}

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.black]
line-length = 120
target-version = ['py311']
include = '\.pyi?$'

[tool.mypy]
python_version = 3.11
files = "src/"
Empty file added src/__init__.py
Empty file.
42 changes: 42 additions & 0 deletions src/in_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from typing import Any, Type


def in_memory_behavior(cls) -> Type:
class InMemoryRepository:
find_by_fields: list[str] = ["id"]
entity_already_exists_exception: Type[Exception] = ValueError
entity_not_found_exception: Type[Exception] = ValueError

def __init__(self) -> None:
self._store: dict[str, Any] = {}
for key in self.find_by_fields:
setattr(self, f"find_by_{key}", self._make_find_by_method(key))

def add(self, entity: Any) -> Any:
if entity.id in self._store:
raise self.entity_already_exists_exception()
self._store[entity.id] = entity

def retrieve(self, id_: Any) -> Any:
try:
return self._store[id_]
except KeyError as err:
raise self.entity_not_found_exception() from err

def update(self, entity: Any) -> None:
if entity.id not in self._store:
raise self.entity_not_found_exception()
self._store[entity.id] = entity

def delete(self, id_: Any) -> None:
if id_ not in self._store:
raise self.entity_not_found_exception(id_)
del self._store[id_]

def _make_find_by_method(self, field_name: str) -> Any:
def find_by_method(value: Any) -> list[Any]:
return [entity for id_, entity in self._store.items() if getattr(entity, field_name) == value]

return find_by_method

return InMemoryRepository
Empty file added src/test/__init__.py
Empty file.
39 changes: 39 additions & 0 deletions src/test/test_in_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pytest

from src.in_memory import in_memory_behavior


@pytest.fixture
def User():
class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name

return User


@pytest.fixture
def UserRepository(User):
@in_memory_behavior
class UserRepository:
pass

return UserRepository()


@pytest.mark.parametrize("entity_id, entity_name", [(1, "John"), (2, "Alice"), (3, "Bob")])
def test_in_memory_behavior(UserRepository, User, entity_id, entity_name):
user = User(id=entity_id, name=entity_name)

UserRepository.add(user)
retrieved_user = UserRepository.retrieve(entity_id)
assert retrieved_user == user

updated_user = User(id=entity_id, name="Updated Name")
UserRepository.update(updated_user)
assert UserRepository.retrieve(entity_id) == updated_user

UserRepository.delete(entity_id)
with pytest.raises(ValueError):
UserRepository.retrieve(entity_id)

0 comments on commit 3e461c4

Please sign in to comment.