Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use runtime module protocol checks #11

Merged
merged 1 commit into from
Nov 29, 2024
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
1 change: 0 additions & 1 deletion dummio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from dummio import json as json
from dummio import text as text
from dummio.constants import ModuleProtocol as ModuleProtocol

try:
from dummio import yaml as yaml
Expand Down
20 changes: 1 addition & 19 deletions dummio/constants.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
"""Constants for dummio."""

import typing
from pathlib import Path
from typing import Any, Protocol, TypeAlias
from typing import Any, TypeAlias

PathType: TypeAlias = str | Path
AnyDict: TypeAlias = dict[Any, Any]

# pyright expect a type var to be used at least twice within a single method. It's having
# trouble respecting how it's use *accross* methods of a class.
T = typing.TypeVar("T") # pyright: ignore

DEFAULT_ENCODING = "utf-8"
DEFAULT_WRITE_MODE = "w"


@typing.runtime_checkable
class ModuleProtocol(Protocol):
"""Protocol for dummio's IO modules."""

def save(self, data: T, *, filepath: PathType) -> None: # pyright: ignore[reportInvalidTypeVarUse]
"""Declares the signature of an IO module save method."""
...

def load(self, filepath: PathType) -> T: # pyright: ignore[reportInvalidTypeVarUse]
"""Declares the signature of an IO module load method."""
...
30 changes: 27 additions & 3 deletions tests/test_assert_module_protocol.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Assert that every IO module implements the ModuleProtocol."""
"""Assert that every IO module implements save and load in a consistent way."""

import importlib
from typing import Callable, get_type_hints

from dummio import ModuleProtocol
from dummio.constants import PathType

IO_MODULES = [
"dummio.json",
Expand All @@ -17,4 +18,27 @@
def test_assert_module_protocol() -> None:
for module_name in IO_MODULES:
module = importlib.import_module(module_name)
assert isinstance(module, ModuleProtocol)
assert hasattr(module, "save")
assert hasattr(module, "load")

# make the following assertions about the save attribute:
# - it is a function
# - the first argument is named "data"
# - all subsequent arguments are keyword-only
# - the second argument is "filepath" of type dummio.constants.PathType
assert isinstance(module.save, Callable)
signature = get_type_hints(module.save)
first_two_args = list(signature.keys())[:2]
assert first_two_args == ["data", "filepath"]
assert signature["filepath"] == PathType

# make the following assertions about the load attribute:
# - it is a function
# - the first argument is named "filepath", of type dummio.constants.PathType
# - the return type is the same as the "data" argument of the save function
assert isinstance(module.load, Callable)
signature = get_type_hints(module.load)
first_arg = list(signature.keys())[0]
assert first_arg == "filepath"
assert signature["filepath"] == PathType
assert signature["return"] == get_type_hints(module.save)["data"]