Skip to content

Commit

Permalink
feat(*): Support multiple casting for variables and allows "None" as …
Browse files Browse the repository at this point in the history
…None for default values
  • Loading branch information
Denis Navarro authored and ticdenis committed Feb 20, 2022
1 parent 61155b3 commit b305228
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 11 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pip install aiodi
name = "%env(str:APP_NAME, 'sample')%"
version = "%env(int:APP_VERSION, '1')%"
log_level = "%env(APP_LEVEL, 'INFO')%"
debug = "%env(bool:int:APP_DEBUG, '0')%"
text = "Hello World"

[tool.aiodi.services."_defaults"]
project_dir = "../../.."
Expand Down
2 changes: 1 addition & 1 deletion aiodi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .builder import ContainerBuilder
from .container import Container, ContainerKey

__version__ = '1.1.1'
__version__ = '1.1.2'

__all__ = (
# di
Expand Down
31 changes: 25 additions & 6 deletions aiodi/resolver/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from ..helpers import raise_, re_finditer
from . import Resolver, ValueNotFound, ValueResolutionPostponed

REGEX = r"%(static|env|var)\((str:|int:|float:|bool:)?([\w]+)(,\s{1}'.*?')?\)%"
REGEX = r"%(static|env|var)\(([str:int:float:bool:]*?)([\w]+)(,\s{1}'.*?')?\)%"
STATIC_TEMPLATE: str = "%static({0}:{1}, '{2}')%"
_VAR_DEFAULTS = ...


class VariableMetadata(NamedTuple):
Expand All @@ -15,18 +16,24 @@ class VariableMetadata(NamedTuple):

class MatchMetadata(NamedTuple): # type: ignore
source_kind: str
type: Type[Any]
types: List[Type[Any]]
source_name: str
default: Any
match: Match

@classmethod
def from_match(cls, match: Match) -> 'VariableMetadata.MatchMetadata':
raw_types = (
['str']
if match.groups()[1] is None or len(str(match.groups()[1])) == 0
else [s for s in str(match.groups()[1]).split(':') if s.strip() != '']
)
raw_default = _VAR_DEFAULTS if match.groups()[3] is None else str(match.groups()[3])[3:-1]
return cls(
source_kind=str(match.groups()[0]),
type=str if match.groups()[1] is None else globals()['__builtins__'][str(match.groups()[1])[:-1]],
types=[globals()['__builtins__'][raw_type] for raw_type in raw_types],
source_name=str(match.groups()[2]),
default=None if match.groups()[3] is None else str(match.groups()[3])[3:-1],
default=None if isinstance(raw_default, str) and raw_default == 'None' else raw_default,
match=match,
)

Expand All @@ -36,6 +43,11 @@ def __init__(self, name: str) -> None:
super().__init__(kind='Variable', name=name)


class EnvironmentVariableNotFound(ValueNotFound):
def __init__(self, name: str) -> None:
super().__init__(kind='EnvironmentVariable', name=name)


class VariableResolutionPostponed(ValueResolutionPostponed[VariableMetadata]):
pass

Expand Down Expand Up @@ -77,7 +89,12 @@ def parse_value(
if metadata_.source_kind == 'static':
typ_val = metadata_.default
elif metadata_.source_kind == 'env':
typ_val = getenv(key=metadata_.source_name, default=metadata_.default or '')
typ_val = getenv(key=metadata_.source_name, default=metadata_.default)
if typ_val is None:
# can only concatenate str to str
return typ_val
if metadata_.default is _VAR_DEFAULTS and typ_val == metadata_.default:
raise EnvironmentVariableNotFound(name=metadata_.source_name)
elif metadata_.source_kind == 'var':
if metadata_.source_name not in _variables:
if retries != -1:
Expand All @@ -94,7 +111,9 @@ def parse_value(
values += metadata.value[metadata_.match.end() :]
value: Any = ''.join(values)
if len(metadata.matches) == 1:
value = metadata.matches[0].type(value)
# multiple casting
for type_ in reversed(metadata.matches[0].types):
value = type_(value)
return value


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = "aiodi"
version = "1.1.1"
version = "1.1.2"
description = "Container for the Dependency Injection in Python."
license = "MIT"
authors = ["ticdenis <denisnavarroalcaide@outlook.es>"]
Expand Down
6 changes: 3 additions & 3 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ autoflake==1.4
# fixed due to Python 3.6 support
bandit==1.7.1
black==22.1.0
commitizen==2.20.5
commitizen==2.21.0
isort==5.10.1
liccheck==0.6.5
mkdocs==1.2.3
mkdocs-material==8.1.10
mkdocs-material==8.2.1
mypy==0.931
pre-commit==2.17.0
psutil==5.9.0
Expand All @@ -23,5 +23,5 @@ setuptools==59.6.0
# fixed due to Python 3.6 support
shellcheck-py==0.8.0.3
twine==3.8.0
types-toml==0.10.3
types-toml==0.10.4
wheel==0.37.1
2 changes: 2 additions & 0 deletions sample/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
name = "%env(str:APP_NAME, 'sample')%"
version = "%env(int:APP_VERSION, '1')%"
log_level = "%env(APP_LEVEL, 'INFO')%"
debug = "%env(bool:int:APP_DEBUG, '0')%"
text = "Hello World"

[tool.aiodi.services."_defaults"]
project_dir = "../../.."
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/aiodi/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def test_container() -> None:
assert 'env.log_level' in di and di.get('env.log_level', typ=str) == 'INFO'
assert 'env.name' in di and di.get('env.name', typ=str) == 'sample'
assert 'env.version' in di and di.get('env.version', typ=int) == 1
assert 'env.debug' in di and di.get('env.debug', typ=bool) is False
assert 'env.text' in di and di.get('env.text', typ=str) == 'Hello World'

assert 'UserLogger' in di and di.get('UserLogger', typ=InMemoryUserLogger)
assert 'logging.Logger' in di and di.get(Logger)
Expand Down

0 comments on commit b305228

Please sign in to comment.