Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
run: uv sync --dev

- name: Run tests
run: uv run pytest -vv --cov=src --cov-report term-missing:skip-covered --cov-fail-under=95
run: uv run pytest -vv --cov=src --cov-report term-missing:skip-covered --cov-fail-under=95
24 changes: 12 additions & 12 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Build & maybe upload PyPI package

on:
push:
branches: [main]
Expand All @@ -11,59 +11,59 @@
types:
- published
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
# Always build & lint package.
build-package:
name: Build & verify package
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: hynek/build-and-inspect-python-package@v2

# Upload to Test PyPI on every commit on main.
release-test-pypi:
name: Publish in-dev package to test.pypi.org
environment: release-test-pypi
if: github.repository_owner == 'second-ed' && github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: build-package

steps:
- name: Download packages built by build-and-inspect-python-package
uses: actions/download-artifact@v4
with:
name: Packages
path: dist

- name: Upload package to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

# Upload to real PyPI on GitHub Releases.
release-pypi:
name: Publish released package to pypi.org
environment: release-pypi
if: github.repository_owner == 'second-ed' && github.event.action == 'published'
runs-on: ubuntu-latest
needs: build-package

steps:
- name: Download packages built by build-and-inspect-python-package
uses: actions/download-artifact@v4
with:
name: Packages
path: dist

- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

Expand Down Expand Up @@ -94,4 +94,4 @@
publish_branch: gh-pages
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/docs-build/html
force_orphan: true
force_orphan: true
15 changes: 13 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- id: check-yaml
- id: pretty-format-json
args: ["--autofix", "--no-sort-keys"]
- id: name-tests-test
args: ["--pytest-test-first"]
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.5.26
hooks:
Expand All @@ -18,7 +29,7 @@ repos:
types_or: [ python, pyi, jupyter ]
- id: pytest
name: pytest
entry: uv run pytest -vv --cov=src --cov-report term-missing:skip-covered --cov-fail-under=95
entry: uv run pytest -vv --cov=src --cov-report term-missing:skip-covered --cov-fail-under=95
language: python
pass_filenames: false
always_run: true
Expand All @@ -38,4 +49,4 @@ repos:
- py,md,yaml,toml,lock
- --ignore-dirs
- .venv,target,.git,mock_data
# - --ignore-hidden
# - --ignore-hidden
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ 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.
SOFTWARE.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ orders_adapter: RealAdapter = get_real_adapter("orders")
some_usecase(orders_adapter, "some/path/to/file.json")


# in testing inject the fake which has all the same funcitonality as the
# in testing inject the fake which has all the same funcitonality as the
# `RealAdapter` and assert that the fakes end state is as expected
fake = get_fake_adapter("orders")
some_usecase(fake, "some/path/to/file.json")
Expand Down Expand Up @@ -135,4 +135,4 @@ assert fake.files["some/path/to/file.json"] == {"a": 1}
├── ruff.toml
└── uv.lock
::
```
```
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ local:
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@uv run $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@uv run $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
4 changes: 2 additions & 2 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Example
some_usecase(orders_adapter, "some/path/to/file.json")


# in testing inject the fake which has all the same funcitonality as the
# in testing inject the fake which has all the same funcitonality as the
# `RealAdapter` and assert that the fakes end state is as expected
fake_adapter = get_fake_adapter("orders")
some_usecase(fake_adapter, "some/path/to/file.json")
Expand All @@ -114,4 +114,4 @@ Indices and tables

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
* :ref:`search`
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "io-adapters"
version = "0.2.1"
version = "0.2.2"
description = "Dependency Injection Adapters"
readme = "README.md"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ ignore = [
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
line-ending = "auto"
6 changes: 3 additions & 3 deletions src/io_adapters/_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ def __attrs_post_init__(self) -> None:
self.guid_fn = self.guid_fn or fake_guid
self.datetime_fn = self.datetime_fn or fake_datetime

def _read_fn(self, path: str) -> Data:
def _read_fn(self, path: str | Path) -> Data:
try:
return self.files[path]
return self.files[str(path)]
except KeyError as e:
raise FileNotFoundError(f"{path = } {self.files = }") from e

def _write_fn(self, data: Data, path: str) -> None:
def _write_fn(self, data: Data, path: str | Path) -> None:
self.files[str(path)] = data

def get_guid(self) -> str:
Expand Down
10 changes: 8 additions & 2 deletions src/io_adapters/_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
from collections.abc import Callable, Hashable, Iterable
from enum import Enum, auto
from typing import TypeAlias

import attrs
from attrs.validators import deep_iterable, instance_of
Expand All @@ -18,6 +19,9 @@ class _FnType(Enum):
WRITE = auto()


DomainFns: TypeAlias = dict[Hashable, dict[Hashable, Callable]]


@attrs.define
class Container:
"""Registry and factory for domain-scoped I/O adapters.
Expand Down Expand Up @@ -92,10 +96,12 @@ class Container:
"""

domains: Iterable = attrs.field(validator=deep_iterable(member_validator=instance_of(Hashable)))
domain_fns: dict[Hashable, dict[Hashable, Callable]] = attrs.field(init=False)
domain_fns: DomainFns = attrs.field(init=False)

def __attrs_post_init__(self) -> None:
self.domain_fns = {domain: {_FnType.READ: {}, _FnType.WRITE: {}} for domain in self.domains}
self.domain_fns: DomainFns = {
domain: {_FnType.READ: {}, _FnType.WRITE: {}} for domain in self.domains
}

def add_domain(self, domain: Hashable) -> None:
"""Add a domain to a ``Container``
Expand Down
16 changes: 8 additions & 8 deletions tests/test_adapters_apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,31 @@ def method_a(self, a: int) -> int:
("real", "fake"),
[
pytest.param(
SanityCheck(),
FakeSanityCheck(),
SanityCheck,
FakeSanityCheck,
id="ensure matching public methods pass",
),
pytest.param(
SanityCheck(),
FakeMissingMethod(),
SanityCheck,
FakeMissingMethod,
id="ensure fails if fake missing method",
marks=pytest.mark.xfail(
reason="ensure fails if fake missing method",
strict=True,
),
),
pytest.param(
SanityCheck(),
FakeMismatchingSignature(),
SanityCheck,
FakeMismatchingSignature,
id="ensure fails if fake not matching signature",
marks=pytest.mark.xfail(
reason="ensure fails if fake not matching signature",
strict=True,
),
),
pytest.param(
RealAdapter(),
FakeAdapter(),
RealAdapter,
FakeAdapter,
id="ensure IO wrapper matches fake",
),
pytest.param(
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.