Skip to content

Commit

Permalink
Merge pull request #27 from con2/typeop
Browse files Browse the repository at this point in the history
Type check operations
  • Loading branch information
japsu committed Feb 20, 2019
2 parents d7f7b7b + 74a62c8 commit 4736f0f
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,6 @@ venv.bak/

# Visual Studio Code
.vscode/

# Pip metadata
pip-wheel-metadata
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ Python 3.5+ required. Python 2 is not and will not be supported.
| `!IncludeBinary` | Path to a binary file | `!IncludeBinary ../foo.pdf` | Loads the given binary file and returns the contents as bytes. This is practically only useful for hashing. |
| `!IncludeText` | Path to an UTF-8 text file | `!IncludeText ../foo.toml` | Loads the given UTF-8 text file and returns the contents as a string. |
| `!Index` | Accepts the same arguments as `!Loop`, except `template` is optional (default identity), plus the following: <br> `by`: (required) An expression used to determine the key for the current value <br> `result_as`: (optional, string) When evaluating `by`, the enriched `template` is available under this name. <br> `duplicates`: (optional, default `error`) `error`, `warn(ing)` or `ignore` duplicate values. | TBD | Makes a dict out of a list. Keys are determined by `by`. |
| `!IsBoolean` | Data to typecheck. | `!IsBoolean ...` | Returns True if the value enriched is of the given type, False otherwise. |
| `!IsDict` | Data to typecheck. | `!IsDict ...` | Returns True if the value enriched is of the given type, False otherwise. |
| `!IsInteger` | Data to typecheck. | `!IsInteger ...` | Returns True if the value enriched is of the given type, False otherwise. |
| `!IsList` | Data to typecheck. | `!IsList ...` | Returns True if the value enriched is of the given type, False otherwise. |
| `!IsNone` | Data to typecheck. | `!IsNone ...` | Returns True if the value enriched is None (null) or Void, False otherwise. |
| `!IsNumber` | Data to typecheck. | `!IsNumber ...` | Returns True if the value enriched is of the given type, False otherwise. |
| `!IsString` | Data to typecheck. | `!IsString ...` | Returns True if the value enriched is of the given type, False otherwise. |
| `!Join` | `items`: (required) A list of items to be joined together. <br> `separator`: (optional, default space) The separator to place between the items. <br> **OR** <br> a list of items to be joined together with a space as the separator. | `!Join [foo, bar]` <br> `!Join { items: [foo, bar], separator: ', ' }` | Joins a list of items together with a separator. The result is always a string. |
| `!Lookup` | JSONPath expression | `!Lookup people[0].first_name` | Performs a JSONPath lookup returning the first match. If there is no match, an error is raised. |
| `!LookupAll` | JSONPath expression | `!LookupAll people[*].first_name` | Performs a JSONPath lookup returning all matches as a list. If no matches are found, the empty list `[]` is returned. |
Expand Down
8 changes: 8 additions & 0 deletions emrichen/tags/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .merge import Merge
from .not_ import Not
from .op import Op
from .typeop import IsBoolean, IsDict, IsInteger, IsList, IsNone, IsNumber, IsString
from .urlencode import URLEncode
from .var import Var
from .void import Void
Expand All @@ -45,6 +46,13 @@
'IncludeBinary',
'IncludeText',
'Index',
'IsBoolean',
'IsDict',
'IsInteger',
'IsList',
'IsNone',
'IsNumber',
'IsString',
'Join',
'Lookup',
'LookupAll',
Expand Down
68 changes: 68 additions & 0 deletions emrichen/tags/typeop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from numbers import Number

from emrichen.void import Void
from .base import BaseTag


class _BaseIsType(BaseTag):
"""
arguments: Data to typecheck.
example: "`!{name} ...`"
description: Returns True if the value enriched is of the given type, False otherwise.
"""
requisite_type = None
value_types = (object,)

def enrich(self, context):
return self.check(context.enrich(self.data))

def check(self, value):
return isinstance(value, self.requisite_type)


class IsBoolean(_BaseIsType):
__doc__ = _BaseIsType.__doc__
requisite_type = bool


class IsDict(_BaseIsType):
__doc__ = _BaseIsType.__doc__
requisite_type = dict


class IsInteger(_BaseIsType):
__doc__ = _BaseIsType.__doc__
requisite_type = int

def check(self, value):
# Special case: True and False are integers as far as
# Python is concerned.
if value is True or value is False:
return False
return super().check(value)


class IsList(_BaseIsType):
__doc__ = _BaseIsType.__doc__
requisite_type = list


class IsNumber(IsInteger):
__doc__ = _BaseIsType.__doc__
requisite_type = Number


class IsString(_BaseIsType):
__doc__ = _BaseIsType.__doc__
requisite_type = str


class IsNone(_BaseIsType):
"""
arguments: Data to typecheck.
example: "`!{name} ...`"
description: Returns True if the value enriched is None (null) or Void, False otherwise.
"""

def check(self, value):
return (value is None or value is Void)
46 changes: 46 additions & 0 deletions tests/test_flavortown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from emrichen import Template, Context

FLAVORTOWN_YAML = """
flavours: !Loop
as: a
template: !Merge
- flavour_name: !Void
available: true
- !If
test: !IsString,Lookup a
then:
flavour_name: !Lookup a
else:
!Lookup a
over:
- peasoup
- hard liquor
- flavour_name: manifold
available: false
- John
"""

FLAVORTOWN_RESULT = {
"flavours": [
{
"available": True,
"flavour_name": "peasoup"
},
{
"available": True,
"flavour_name": "hard liquor"
},
{
"available": False,
"flavour_name": "manifold"
},
{
"available": True,
"flavour_name": "John"
}
]
}


def test_flavortown():
assert Template.parse(FLAVORTOWN_YAML).enrich(Context()) == [FLAVORTOWN_RESULT]
37 changes: 37 additions & 0 deletions tests/test_typeop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest

from emrichen import Template, Context
from emrichen.void import Void


@pytest.mark.parametrize('tag, val, result', [
('IsBoolean', 0, False),
('IsBoolean', False, True),
('IsBoolean', True, True),
('IsDict', [], False),
('IsDict', {}, True),
('IsInteger', 8, True),
('IsInteger', 8.2, False),
('IsInteger', False, False), # Ylläri!
('IsInteger', True, False), # Ylläri!
('IsList', 8, False),
('IsList', [], True),
('IsNone', '', False),
('IsNone', None, True),
('IsNone', Void, True),
('IsNumber', 8, True),
('IsNumber', 8.2, True),
('IsNumber', False, False), # Ylläri!
('IsNumber', True, False), # Ylläri!
('IsString', 'yes', True),
('IsString', 8, False),
('IsString', Void, False),
])
def test_typeop(tag, val, result):
resolved = Template.parse("!%s,Lookup 'a'" % tag).enrich(Context({'a': val}))[0]
assert resolved == result, '{tag}({val!r}) returned {resolved}, expected {result}'.format(
tag=tag,
val=val,
resolved=resolved,
result=result,
)

0 comments on commit 4736f0f

Please sign in to comment.