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
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# External
.DS_Store
.idea/
.vscode/
*.iml

# Internal
*.egg-info/
.coverage
.env.local
.gitignore
.python-version
*.pyc
__pycache__/
build/
dist/
var/
4 changes: 2 additions & 2 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.6.12'
python-version: '3.6.15'
- name: deps
timeout-minutes: 1
run: sh pyscript.sh install
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.6.12'
python-version: '3.6.15'
- name: deps
timeout-minutes: 1
run: sh pyscript.sh install
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# External
.DS_Store/
.DS_Store
.idea/
.vscode/
*.iml
Expand All @@ -14,4 +14,4 @@
__pycache__/
build/
dist/
var/
var/
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
FROM python:3.6.12 as development
FROM docker.io/library/python:3.6.15-slim as development

WORKDIR /app

RUN apt update && apt install -y gcc

COPY .pre-commit-config.yaml LICENSE pyproject.toml pyscript.sh README.md requirements.txt requirements-dev.txt setup.py ./
COPY aioddd ./aioddd/
COPY tests ./tests/
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ aioddd is an async Python DDD utilities library.

## Installation

Use the package manager [pip](https://pip.pypa.io/en/stable/) to install aiocli.
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install aioddd.

```bash
pip install aioddd
```

## Documentation

- Visit [aioddd docs](https://ticdenis.github.io/python-aioddd/).

## Usage

```python
Expand Down
4 changes: 3 additions & 1 deletion aioddd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
find_event_mapper_by_name,
find_event_mapper_by_type,
)
from .utils import get_env, get_simple_logger
from .utils import env, env_resolver, get_env, get_simple_logger
from .value_objects import Id, StrDateTime, Timestamp

__all__ = (
Expand Down Expand Up @@ -94,6 +94,8 @@
# utils
'get_env',
'get_simple_logger',
'env_resolver',
'env',
# value_objects
'Id',
'Timestamp',
Expand Down
35 changes: 21 additions & 14 deletions aioddd/container.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""
This file will be promoted as a package (aiodi) in the next major according to the semver.
"""

from inspect import signature
from logging import getLogger
from typing import (
AbstractSet,
Any,
Expand All @@ -15,13 +20,15 @@

from .helpers import typing_get_args, typing_get_origin

_logger = getLogger('aiodi')

_T = TypeVar('_T')

_primitives = (bytes, bytearray, bool, int, float, str, dict, tuple, list, set, slice, map, zip)


def _is_primitive(val: Any) -> bool:
return isinstance(val, _primitives) or val in _primitives # type: ignore
return isinstance(val, _primitives) or val in _primitives


def _is_object(val: Any) -> bool:
Expand Down Expand Up @@ -71,24 +78,24 @@ def resolve(self, items: List[Union[ContainerKey, Tuple[ContainerKey, _T, Dict[s
# Check if already exist
if item[0] in self or item[1] in self:
if self.debug:
print('Ignoring {} - {}'.format(item[0], item[1]))
_logger.debug('Ignoring {0} - {1}'.format(item[0], item[1]))
del items_[index]
continue
# Resolve 2nd arg if is a primitive or instance
if not isinstance(item[1], type) and len(item[2].keys()) == 0:
if self.debug:
print('Adding {} - {}'.format(item[0], item[1]))
_logger.debug('Adding {0} - {1}'.format(item[0], item[1]))
self.set(item[0], item[1])
del items_[index]
continue
# Resolve or Postpone 2nd arg if is a type
kwargs = self._resolve_or_postpone_item(item, items_)
if kwargs is not None:
if self.debug:
print('Resolving {}'.format(item[1]))
_logger.debug('Resolving {0}'.format(item[1]))
inst = item[1](**kwargs) # type: ignore
if self.debug:
print('Adding {} - {}'.format(item[0], item[1]))
_logger.debug('Adding {0} - {1}'.format(item[0], item[1]))
self.set(item[0], inst)
del items_[index]

Expand Down Expand Up @@ -133,12 +140,12 @@ def get(self, key: ContainerKey, typ: Optional[Type[_T]] = None, instance_of: bo
return self._get_instance_of(here, key) # type: ignore
if isinstance(key, type):
typ = None
key = '{}.{}'.format(key.__module__, key.__name__)
key = '{0}.{1}'.format(key.__module__, key.__name__)
if _is_object(key):
typ = None
key = '{}.{}'.format(key.__class__.__module__, key.__class__.__name__)
key = '{0}.{1}'.format(key.__class__.__module__, key.__class__.__name__)
if not isinstance(key, str):
raise KeyError('<{}> does not exist in container'.format(key))
raise KeyError('<{0}> does not exist in container'.format(key))
keys = key.split('.')
original_key = key
for key in keys[:-1]:
Expand All @@ -147,10 +154,10 @@ def get(self, key: ContainerKey, typ: Optional[Type[_T]] = None, instance_of: bo
try:
val = here[keys[-1]]
if typ and not isinstance(val, (typ,)):
raise TypeError('<{}: {}> does not exist in container'.format(original_key, typ.__name__))
raise TypeError('<{0}: {1}> does not exist in container'.format(original_key, typ.__name__))
return val
except KeyError:
raise KeyError('<{}> does not exist in container'.format(original_key))
raise KeyError('<{0}> does not exist in container'.format(original_key))

def __contains__(self, *o) -> bool: # type: ignore
"""
Expand Down Expand Up @@ -200,7 +207,7 @@ def _resolve_or_postpone_item(
kwargs = {}
break
if not isinstance(val, typ):
raise TypeError('<{}: {}> wrong type <{}> given'.format(name, typ.__name__, type(val).__name__))
raise TypeError('<{0}: {1}> wrong type <{2}> given'.format(name, typ.__name__, type(val).__name__))
kwargs.update({name: val})
continue
if typ in self:
Expand All @@ -217,7 +224,7 @@ def _resolve_or_postpone_item(
continue
if typ not in [i[0] for i in items]:
if self.debug:
print('Postponing {}'.format(typ))
_logger.debug('Postponing {0}'.format(typ))
items.append((typ, typ, {})) # type: ignore
kwargs = {}
break
Expand Down Expand Up @@ -250,14 +257,14 @@ def _resolve_or_postpone_item_parameter(
if isinstance(val, tuple) and len(val) == 2 and callable(val[1]):
try:
if self.debug:
print('Trying resolve parameter "{}" from {}'.format(name, item[1]))
_logger.debug('Trying resolve parameter "{0}" from {1}'.format(name, item[1]))
index = val[0]
item[2][name] = val[1](self)
self._parameter_resolvers = self._parameter_resolvers[:index] + self._parameter_resolvers[index + 1 :]
return item[2][name]
except (KeyError, ValueError):
if self.debug:
print('Postponing parameter resolver {}'.format(typ))
_logger.debug('Postponing parameter resolver {0}'.format(typ))
return None
return val

Expand Down
10 changes: 2 additions & 8 deletions aioddd/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,8 @@ def encode(self, msg: Event) -> Dict[str, Any]:

def decode(self, data: Dict[str, Any]) -> Event:
attributes = self.event_type.Attributes(**data['attributes']) # type: ignore
return self.event_type(
attributes=attributes,
meta=self.event_type.Meta(
id=data['id'],
type=data['type'],
occurred_on=data['occurred_on'],
),
)
meta = self.event_type.Meta(id=data['id'], type=data['type'], occurred_on=data['occurred_on'])
return self.event_type(attributes=attributes, meta=meta)

@staticmethod
def map_attributes(attributes: Event.Attributes) -> Dict[str, Any]:
Expand Down
29 changes: 28 additions & 1 deletion aioddd/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from logging import NOTSET, Formatter, Logger, StreamHandler, getLogger
from os import getenv
from typing import Optional, Union
from typing import Any, Dict, Optional, Type, TypeVar, Union


def get_env(key: str, default: Optional[str] = None) -> str: # pragma: no cover
Expand All @@ -22,3 +22,30 @@ def get_simple_logger(
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger


_T = TypeVar('_T')
_ENV: Optional[Dict[str, Any]] = None


def env_resolver() -> Optional[Dict[str, Any]]:
"""Override this method to use below env method to automatically cache and get type validations magically."""
return {}


def env(key: Optional[str] = None, typ: Optional[Type[_T]] = None) -> _T:
global _ENV
if not _ENV:
_ENV = env_resolver() or {}
if not key:
return _ENV # type: ignore
if key not in _ENV:
raise KeyError(
'<{0}{1}> does not exist as environment variable'.format(key, ': {0}'.format(typ.__name__) if typ else '')
)
val = _ENV[key]
if typ and not isinstance(val, (typ,)):
raise TypeError(
'<{0}{1}> does not exist as environment variable'.format(key, ': {0}'.format(typ.__name__) if typ else '')
)
return val
Loading