Skip to content

Commit

Permalink
Merge pull request #4 from AlTosterino/feature/minor-stuff
Browse files Browse the repository at this point in the history
Small refactor of existing code
  • Loading branch information
AlTosterino authored Apr 5, 2024
2 parents cdda81a + 0ffd949 commit bfa672e
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 81 deletions.
98 changes: 49 additions & 49 deletions poetry.lock

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

23 changes: 15 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "lidipy"
version = "0.1.1"
version = "0.2.0"
description = "Lidi is a lightweight dependency injector designed to simplify dependency management in your Python projects."
authors = ["AlTosterino <[email protected]>"]
readme = "README.md"
Expand All @@ -11,10 +11,10 @@ python = "^3.11"


[tool.poetry.group.dev.dependencies]
ruff = "^0.0.261"
black = "^23.3.0"
pytest = "^7.3.0"
mypy = "^1.2.0"
ruff = "~0"
black = "~24"
pytest = "~8"
mypy = "~1"

[build-system]
requires = ["poetry-core"]
Expand All @@ -39,6 +39,13 @@ check_untyped_defs = true
ignore_missing_imports = true

[tool.ruff]
select = ["E", "F", "I", "PL"]
line-length = 100
target-version = "py311"
line-length = 110
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "PL", "N", "S", "B", "A", "TD", "FIX", "PERF", "PT"]
ignore = ["D107", "D203", "D211"]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["E402", "D104"]
"**/{tests,docs,tools}/**" = ["D100", "D101", "D103", "S101"]
81 changes: 76 additions & 5 deletions src/lidipy/container.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
"""Lightweight Dependency Injection (Lidi) framework.
This module provides a simple dependency injection framework for managing
object dependencies within an application.
"""

from typing import Any, Callable, Hashable, Type, TypeVar, Union, cast

from lidipy.exceptions import BindingMissing
Expand All @@ -11,9 +17,7 @@


class Lidi:
"""
Lidi - LIghtweight Dependency Injector
"""
"""Lidi - Lightweight Dependency Injector."""

__slots__ = ("__bindings",)

Expand All @@ -26,6 +30,29 @@ def bind(
instance_or_callable: Injectable | Constructor,
singleton: bool = False,
) -> None:
"""Binds a class or interface to an instance or a callable.
Args:
cls (Binding): The class or interface to bind.
instance_or_callable (Injectable | Constructor):
The instance or callable to bind to the class or interface.
singleton (bool, optional):
Indicates whether the binding should be a singleton.
Defaults to False.
Returns:
None
Raises:
None
Example:
>>> lidi = Lidi()
>>> lidi.bind(IService, ConcreteService)
>>> lidi.bind(ILogger, lambda: Logger())
>>> lidi.bind(IDatabase, DatabaseConnection(), singleton=True)
"""
is_callable = callable(instance_or_callable)
if singleton and is_callable:
self.__bindings[cls] = instance_or_callable()
Expand All @@ -35,17 +62,61 @@ def bind(
self.__bindings[cls] = lambda: instance_or_callable

def resolve(self, cls: Type[T]) -> T:
"""Resolve a class or interface to its bound instance or callable.
Args:
cls (Type[T]): The class or interface to resolve.
Returns:
T: The instance or callable bound to the class or interface.
Raises:
BindingMissing: If no binding is found for the specified class or interface.
Example:
>>> lidi = Lid()
>>> service = lidi.bind(IService, ConcreteService)
>>> service = lidi.resolve(IService)
"""
binding = self.__get_binding(cls=cls)
if callable(binding):
return cast(T, binding())
return binding

def resolve_defer(self, cls: Type[T]) -> Callable[[], T]:
"""Resolve a class or interface to a deferred callable.
Args:
cls (Type[T]): The class or interface to resolve.
Returns:
Callable[[], T]: A callable that, when invoked, resolves the class or interface.
Example:
>>> lidi = Lid()
>>> service = lidi.bind(IService, ConcreteService)
>>> deferred_service = lidi.resolve_defer(IService) # Not resolved
>>> service = deferred_service() # Resolved
"""
return lambda: self.resolve(cls=cls)

def __get_binding(self, cls: Type[T]) -> T:
"""Retrieve the binding for a given class or interface.
Args:
cls (Type[T]): The class or interface to retrieve the binding for.
Returns:
T: The bound instance or callable.
Raises:
BindingMissing: If no binding is found for the specified class or interface.
"""
try:
return cast(T, self.__bindings[cls])
except KeyError:
except KeyError as e:
msg = f"Binding missing for type: {cls.__name__}"
raise BindingMissing(msg)
raise BindingMissing(msg) from e
12 changes: 11 additions & 1 deletion src/lidipy/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
class BaseLidiException(Exception):
"""Exceptions module for the Lidi framework.
Defines custom exceptions used within the Lidi framework.
"""


class BaseLidiException(Exception): # noqa: N818
"""Base class for all Lidi exceptions."""

...


class BindingMissing(BaseLidiException):
"""Raise when binding is not found within Lidi container."""

pass
27 changes: 9 additions & 18 deletions tests/shared.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,31 @@
from dataclasses import dataclass


class SimpleClassA:
...
class SimpleClassA: ...


class SimpleClassB:
...
class SimpleClassB: ...


class SimpleClassC:
...
class SimpleClassC: ...


class SimpleBaseClass:
...
class SimpleBaseClass: ...


class SimpleInheritClassA(SimpleBaseClass):
...
class SimpleInheritClassA(SimpleBaseClass): ...


class SimpleInheritClassB(SimpleBaseClass):
...
class SimpleInheritClassB(SimpleBaseClass): ...


@dataclass
class SimpleDataclassA:
...
class SimpleDataclassA: ...


@dataclass
class SimpleDataclassB:
...
class SimpleDataclassB: ...


@dataclass
class SimpleDataclassC:
...
class SimpleDataclassC: ...

0 comments on commit bfa672e

Please sign in to comment.