Conditions and Pipes¶
Warning
Please, make sure you've covered Reference / Basics first.
Conditions¶
There are 2 conversions which allow to define conditions:
c.if_(condition, if_true, if_false)
for a single conditionc.if_multiple(*condition_to_value_pairs, else_)
obviously for multiple conditions
from convtools import conversion as c
# No. 1
converter = (
c.iter(c.if_(c.this < 0, c.this * 2, c.this / 2))
.as_type(list)
.gen_converter(debug=True)
)
assert converter([-1, 0, 1]) == [-2, 0, 0.5]
# No. 2
converter = (
c.iter(
c.if_multiple(
(c.this < 0, c.this * 2),
(c.this == 0, 100),
(c.this < 10, c.this / 2),
else_=c.this / 10,
)
)
.as_type(list)
.gen_converter(debug=True)
)
assert converter([-1, 0, 1, 20]) == [-2, 100, 0.5, 2]
def converter(data_):
try:
return [(((i * 2) if (i < 0) else (i / 2))) for i in data_]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
def if_multiple_(data_):
if data_ < 0:
return data_ * 2
if data_ == 0:
return 100
if data_ < 10:
return data_ / 2
return data_ / 10
def converter(data_):
try:
return [if_multiple_(i) for i in data_]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
Check and raise¶
There's a convenient helper, which checks whether a condition is met and
returns input as is or raises c.ExpectException
otherwise:
c.expect(condition, error_msg=None)
- also as a method:
c.attr("amount").expect(c.this < 10, "too big")
from convtools import conversion as c
# expect doesn't change input
converter = (
c.iter(c.expect(c.this < 3, "too big") ** 10)
.as_type(list)
.gen_converter(debug=True)
)
assert converter(range(3)) == [0, 1, 1024]
# error_msg can be conversion itself
converter = (
c.item("a")
.expect(
condition=c.this.len() > 3,
error_msg=c.call_func("{} is too short".format, c.this),
)
.gen_converter(debug=True)
)
try:
converter({"a": "val"})
except c.ExpectException as e:
assert str(e) == "val is too short"
def expect(data_):
if data_ < 3:
return data_
raise ExpectException("too big")
def converter(data_):
try:
return [(expect(i) ** 10) for i in data_]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
def expect(data_, *, __format=__naive_values__["__format"]):
if len(data_) > 3:
return data_
raise ExpectException(__format(data_))
def converter(data_):
try:
return expect(data_["a"])
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
Pipes¶
Pipe is the most important conversion which allows to pass results of one
conversion to another. The syntax is simple: first.pipe(second)
.
from convtools import conversion as c
converter = (
c.iter(
(c.item("value") + 1).pipe(
c.if_(c.this < 0, c.this * c.this, c.this * 2)
)
)
.as_type(list)
.gen_converter(debug=True)
)
assert converter([{"value": -4}, {"value": 2}]) == [9, 6]
def pipe_(input_):
return (input_ * input_) if (input_ < 0) else (input_ * 2)
def converter(data_):
try:
return [pipe_((i["value"] + 1)) for i in data_]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
You can also pipe to callables, have a look at its signature:
pipe(next_conversion, *args, label_input=None, label_output=None, **kwargs)
.
It accepts *args
and **kwargs
, which are passed to a callable after pipe
input:
from datetime import datetime
from convtools import conversion as c
assert c.this.pipe(int).execute("123", debug=True) == 123
converter = (
c.item("dt").pipe(datetime.strptime, "%m/%d/%Y").gen_converter(debug=True)
)
assert converter({"dt": "12/25/2000"}) == datetime(2000, 12, 25)
def converter(data_):
try:
return int(data_)
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
def converter(data_, *, __strptime=__naive_values__["__strptime"], __v=__naive_values__["__v"]):
try:
return __strptime(data_["dt"], __v)
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
and_then¶
and_then(conversion, condition=bool)
method applies provided conversion if
condition is true (by default condition is standard python's truth check).
condition
accepts both conversions and callables.
from convtools import conversion as c
# DEFAULT CONDITION
converter = (
c.iter(c.this.and_then(c.this.as_type(int)))
.as_type(list)
.gen_converter(debug=True)
)
assert converter(["1", None, 2.0]) == [1, None, 2]
# CUSTOM CONDITION
converter = (
c.iter(c.this.and_then(c.this + 10, condition=c.this != 1))
.as_type(list)
.gen_converter(debug=True)
)
assert converter(range(3)) == [10, 1, 12]
def converter(data_):
try:
return [(i and int(i)) for i in data_]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
def converter(data_):
try:
return [(((i + 10) if (i != 1) else i)) for i in data_]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
Labels¶
Warning
Despite the fact that convtools
encourages a functional approach and
working with immutable data, sometimes it's inevitable to use global
variables. Anyway avoid using labels if possible.
There are two ways to label data for further use:
pipe
method acceptslabel_input
(applies to pipe's input data) andlabel_output
(applies to the end result) keyword arguments, each of them is either:str
- label namedict
- label names to conversion map. Labels are put on results of conversions.
add_label
- shortcut topipe(This, label_input=label_name)
To reference previously labeled data use c.label("label_name")
.
from datetime import datetime
from convtools import conversion as c
converter = (
c.this.add_label({"a": c.item("a")})
.item("b")
.iter({"a": c.label("a"), "b": c.this})
.as_type(list)
.gen_converter(debug=True)
)
# SAME
converter_2 = (
c.this.pipe(c.item("b"), label_input={"a": c.item("a")})
.iter({"a": c.label("a"), "b": c.this})
.as_type(list)
.gen_converter(debug=True)
)
input_data = {
"a": 1,
"b": [2, 3, 4],
}
expected_output = [{"a": 1, "b": 2}, {"a": 1, "b": 3}, {"a": 1, "b": 4}]
assert (
converter(input_data) == expected_output
and converter_2(input_data) == expected_output
)
# BETTER WITHOUT LABELS (HERE IT'S POSSIBLE)
converter_3 = (
c.zip(a=c.repeat(c.item("a")), b=c.item("b"))
.as_type(list)
.gen_converter(debug=True)
)
assert converter_3(input_data) == expected_output
def pipe_(_labels, input_):
_labels["a"] = input_["a"]
return input_
def converter(data_):
_labels = {}
try:
return [{"a": _labels["a"], "b": i} for i in pipe_(_labels, data_)["b"]]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
def pipe_(_labels, input_):
_labels["a"] = input_["a"]
return [{"a": _labels["a"], "b": i} for i in input_["b"]]
def converter(data_):
_labels = {}
try:
return pipe_(_labels, data_)
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise
def converter(data_, *, __repeat=__naive_values__["__repeat"]):
try:
return [{"a": i[0], "b": i[1]} for i in zip(__repeat(data_["a"]), data_["b"])]
except __exceptions_to_dump_sources:
__convtools__code_storage.dump_sources()
raise