Tuning ValueHolder as I could not find better option to generate the double-underscore methods.
This commit is contained in:
@ -74,9 +74,11 @@ class Configurable(metaclass=ConfigurableMeta):
|
|||||||
# transform positional arguments in keyword arguments if possible.
|
# transform positional arguments in keyword arguments if possible.
|
||||||
position = 0
|
position = 0
|
||||||
for positional_option in self.__positional_options__:
|
for positional_option in self.__positional_options__:
|
||||||
if positional_option in missing:
|
if len(args) <= position:
|
||||||
|
break
|
||||||
kwargs[positional_option] = args[position]
|
kwargs[positional_option] = args[position]
|
||||||
position += 1
|
position += 1
|
||||||
|
if positional_option in missing:
|
||||||
missing.remove(positional_option)
|
missing.remove(positional_option)
|
||||||
|
|
||||||
# complain if there are still missing options.
|
# complain if there are still missing options.
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import functools
|
|
||||||
|
|
||||||
import types
|
import types
|
||||||
from collections import Iterable
|
from collections import Iterable
|
||||||
|
from contextlib import contextmanager
|
||||||
from bonobo.util.compat import deprecated_alias, deprecated
|
|
||||||
|
|
||||||
from bonobo.config.options import Option
|
from bonobo.config.options import Option
|
||||||
|
from bonobo.util.compat import deprecated_alias
|
||||||
from bonobo.util.iterators import ensure_tuple
|
from bonobo.util.iterators import ensure_tuple
|
||||||
|
|
||||||
_CONTEXT_PROCESSORS_ATTR = '__processors__'
|
_CONTEXT_PROCESSORS_ATTR = '__processors__'
|
||||||
@ -52,6 +50,14 @@ class ContextCurrifier:
|
|||||||
self._stack = []
|
self._stack = []
|
||||||
self._stack_values = []
|
self._stack_values = []
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield from self.wrapped
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
if not callable(self.wrapped) and isinstance(self.wrapped, Iterable):
|
||||||
|
return self.__iter__()
|
||||||
|
return self.wrapped(*self.context, *args, **kwargs)
|
||||||
|
|
||||||
def setup(self, *context):
|
def setup(self, *context):
|
||||||
if len(self._stack):
|
if len(self._stack):
|
||||||
raise RuntimeError('Cannot setup context currification twice.')
|
raise RuntimeError('Cannot setup context currification twice.')
|
||||||
@ -63,14 +69,6 @@ class ContextCurrifier:
|
|||||||
self.context += ensure_tuple(_append_to_context)
|
self.context += ensure_tuple(_append_to_context)
|
||||||
self._stack.append(_processed)
|
self._stack.append(_processed)
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
yield from self.wrapped
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
if not callable(self.wrapped) and isinstance(self.wrapped, Iterable):
|
|
||||||
return self.__iter__()
|
|
||||||
return self.wrapped(*self.context, *args, **kwargs)
|
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
while len(self._stack):
|
while len(self._stack):
|
||||||
processor = self._stack.pop()
|
processor = self._stack.pop()
|
||||||
@ -84,6 +82,23 @@ class ContextCurrifier:
|
|||||||
# No error ? We should have had StopIteration ...
|
# No error ? We should have had StopIteration ...
|
||||||
raise RuntimeError('Context processors should not yield more than once.')
|
raise RuntimeError('Context processors should not yield more than once.')
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def as_contextmanager(self, *context):
|
||||||
|
"""
|
||||||
|
Convenience method to use it as a contextmanager, mostly for test purposes.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> with ContextCurrifier(node).as_contextmanager(context) as stack:
|
||||||
|
... stack()
|
||||||
|
|
||||||
|
:param context:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.setup(*context)
|
||||||
|
yield self
|
||||||
|
self.teardown()
|
||||||
|
|
||||||
|
|
||||||
def resolve_processors(mixed):
|
def resolve_processors(mixed):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -46,7 +46,7 @@ class OpenDataSoftAPI(Configurable):
|
|||||||
for row in records:
|
for row in records:
|
||||||
yield {**row.get('fields', {}), 'geometry': row.get('geometry', {})}
|
yield {**row.get('fields', {}), 'geometry': row.get('geometry', {})}
|
||||||
|
|
||||||
start.value += self.rows
|
start += self.rows
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@ -3,10 +3,12 @@ from pprint import pprint as _pprint
|
|||||||
|
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
|
|
||||||
|
from bonobo.config import Configurable, Option
|
||||||
from bonobo.config.processors import ContextProcessor
|
from bonobo.config.processors import ContextProcessor
|
||||||
from bonobo.structs.bags import Bag
|
from bonobo.structs.bags import Bag
|
||||||
from bonobo.util.objects import ValueHolder
|
from bonobo.util.objects import ValueHolder
|
||||||
from bonobo.util.term import CLEAR_EOL
|
from bonobo.util.term import CLEAR_EOL
|
||||||
|
from bonobo.constants import NOT_MODIFIED
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'identity',
|
'identity',
|
||||||
@ -23,19 +25,26 @@ def identity(x):
|
|||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def Limit(n=10):
|
class Limit(Configurable):
|
||||||
from bonobo.constants import NOT_MODIFIED
|
"""
|
||||||
i = 0
|
Creates a Limit() node, that will only let go through the first n rows (defined by the `limit` option), unmodified.
|
||||||
|
|
||||||
def _limit(*args, **kwargs):
|
.. attribute:: limit
|
||||||
nonlocal i, n
|
|
||||||
i += 1
|
Number of rows to let go through.
|
||||||
if i <= n:
|
|
||||||
|
"""
|
||||||
|
limit = Option(positional=True, default=10)
|
||||||
|
|
||||||
|
@ContextProcessor
|
||||||
|
def counter(self, context):
|
||||||
|
yield ValueHolder(0)
|
||||||
|
|
||||||
|
def call(self, counter, *args, **kwargs):
|
||||||
|
counter += 1
|
||||||
|
if counter <= self.limit:
|
||||||
yield NOT_MODIFIED
|
yield NOT_MODIFIED
|
||||||
|
|
||||||
_limit.__name__ = 'Limit({})'.format(n)
|
|
||||||
return _limit
|
|
||||||
|
|
||||||
|
|
||||||
def Tee(f):
|
def Tee(f):
|
||||||
from bonobo.constants import NOT_MODIFIED
|
from bonobo.constants import NOT_MODIFIED
|
||||||
@ -57,7 +66,7 @@ def count(counter, *args, **kwargs):
|
|||||||
def _count_counter(self, context):
|
def _count_counter(self, context):
|
||||||
counter = ValueHolder(0)
|
counter = ValueHolder(0)
|
||||||
yield counter
|
yield counter
|
||||||
context.send(Bag(counter.value))
|
context.send(Bag(counter._value))
|
||||||
|
|
||||||
|
|
||||||
pprint = Tee(_pprint)
|
pprint = Tee(_pprint)
|
||||||
|
|||||||
@ -46,8 +46,11 @@ class CsvReader(CsvHandler, FileReader):
|
|||||||
|
|
||||||
def read(self, fs, file, headers):
|
def read(self, fs, file, headers):
|
||||||
reader = csv.reader(file, delimiter=self.delimiter, quotechar=self.quotechar)
|
reader = csv.reader(file, delimiter=self.delimiter, quotechar=self.quotechar)
|
||||||
headers.value = headers.value or next(reader)
|
|
||||||
field_count = len(headers.value)
|
if not headers.get():
|
||||||
|
headers.set(next(reader))
|
||||||
|
|
||||||
|
field_count = len(headers)
|
||||||
|
|
||||||
if self.skip and self.skip > 0:
|
if self.skip and self.skip > 0:
|
||||||
for _ in range(0, self.skip):
|
for _ in range(0, self.skip):
|
||||||
@ -68,9 +71,9 @@ class CsvWriter(CsvHandler, FileWriter):
|
|||||||
yield writer, headers
|
yield writer, headers
|
||||||
|
|
||||||
def write(self, fs, file, lineno, writer, headers, row):
|
def write(self, fs, file, lineno, writer, headers, row):
|
||||||
if not lineno.value:
|
if not lineno:
|
||||||
headers.value = headers.value or row.keys()
|
headers.set(headers.value or row.keys())
|
||||||
writer.writerow(headers.value)
|
writer.writerow(headers.get())
|
||||||
writer.writerow(row[header] for header in headers.value)
|
writer.writerow(row[header] for header in headers.get())
|
||||||
lineno.value += 1
|
lineno += 1
|
||||||
return NOT_MODIFIED
|
return NOT_MODIFIED
|
||||||
|
|||||||
@ -86,7 +86,7 @@ class FileWriter(Writer):
|
|||||||
|
|
||||||
@ContextProcessor
|
@ContextProcessor
|
||||||
def lineno(self, context, fs, file):
|
def lineno(self, context, fs, file):
|
||||||
lineno = ValueHolder(0, type=int)
|
lineno = ValueHolder(0)
|
||||||
yield lineno
|
yield lineno
|
||||||
|
|
||||||
def write(self, fs, file, lineno, row):
|
def write(self, fs, file, lineno, row):
|
||||||
@ -94,7 +94,7 @@ class FileWriter(Writer):
|
|||||||
Write a row on the next line of opened file in context.
|
Write a row on the next line of opened file in context.
|
||||||
"""
|
"""
|
||||||
self._write_line(file, (self.eol if lineno.value else '') + row)
|
self._write_line(file, (self.eol if lineno.value else '') + row)
|
||||||
lineno.value += 1
|
lineno += 1
|
||||||
return NOT_MODIFIED
|
return NOT_MODIFIED
|
||||||
|
|
||||||
def _write_line(self, file, line):
|
def _write_line(self, file, line):
|
||||||
|
|||||||
@ -65,8 +65,10 @@ class Bag:
|
|||||||
if len(args) == 0 and len(kwargs) == 0:
|
if len(args) == 0 and len(kwargs) == 0:
|
||||||
try:
|
try:
|
||||||
iter(func_or_iter)
|
iter(func_or_iter)
|
||||||
|
|
||||||
def generator():
|
def generator():
|
||||||
yield from func_or_iter
|
yield from func_or_iter
|
||||||
|
|
||||||
return generator()
|
return generator()
|
||||||
except TypeError as exc:
|
except TypeError as exc:
|
||||||
raise TypeError('Could not apply bag to {}.'.format(func_or_iter)) from exc
|
raise TypeError('Could not apply bag to {}.'.format(func_or_iter)) from exc
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import functools
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
|
||||||
def get_name(mixed):
|
def get_name(mixed):
|
||||||
try:
|
try:
|
||||||
return mixed.__name__
|
return mixed.__name__
|
||||||
@ -27,181 +31,194 @@ class ValueHolder:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, value, *, type=None):
|
def __init__(self, value):
|
||||||
self.value = value
|
self._value = value
|
||||||
self.type = type
|
|
||||||
|
|
||||||
def __repr__(self):
|
@property
|
||||||
return repr(self.value)
|
def value(self):
|
||||||
|
# XXX deprecated
|
||||||
|
return self._value
|
||||||
|
|
||||||
def __lt__(self, other):
|
def get(self):
|
||||||
return self.value < other
|
return self._value
|
||||||
|
|
||||||
def __le__(self, other):
|
def set(self, new_value):
|
||||||
return self.value <= other
|
self._value = new_value
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self._value)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.value == other
|
return self._value == other
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return self.value != other
|
return self._value != other
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self._value)
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self._value < other
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self._value <= other
|
||||||
|
|
||||||
def __gt__(self, other):
|
def __gt__(self, other):
|
||||||
return self.value > other
|
return self._value > other
|
||||||
|
|
||||||
def __ge__(self, other):
|
def __ge__(self, other):
|
||||||
return self.value >= other
|
return self._value >= other
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return self.value + other
|
return self._value + other
|
||||||
|
|
||||||
def __radd__(self, other):
|
def __radd__(self, other):
|
||||||
return other + self.value
|
return other + self._value
|
||||||
|
|
||||||
def __iadd__(self, other):
|
def __iadd__(self, other):
|
||||||
self.value += other
|
self._value += other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
return self.value - other
|
return self._value - other
|
||||||
|
|
||||||
def __rsub__(self, other):
|
def __rsub__(self, other):
|
||||||
return other - self.value
|
return other - self._value
|
||||||
|
|
||||||
def __isub__(self, other):
|
def __isub__(self, other):
|
||||||
self.value -= other
|
self._value -= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return self.value * other
|
return self._value * other
|
||||||
|
|
||||||
def __rmul__(self, other):
|
def __rmul__(self, other):
|
||||||
return other * self.value
|
return other * self._value
|
||||||
|
|
||||||
def __imul__(self, other):
|
def __imul__(self, other):
|
||||||
self.value *= other
|
self._value *= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __matmul__(self, other):
|
def __matmul__(self, other):
|
||||||
return self.value @ other
|
return self._value @ other
|
||||||
|
|
||||||
def __rmatmul__(self, other):
|
def __rmatmul__(self, other):
|
||||||
return other @ self.value
|
return other @ self._value
|
||||||
|
|
||||||
def __imatmul__(self, other):
|
def __imatmul__(self, other):
|
||||||
self.value @= other
|
self._value @= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __truediv__(self, other):
|
def __truediv__(self, other):
|
||||||
return self.value / other
|
return self._value / other
|
||||||
|
|
||||||
def __rtruediv__(self, other):
|
def __rtruediv__(self, other):
|
||||||
return other / self.value
|
return other / self._value
|
||||||
|
|
||||||
def __itruediv__(self, other):
|
def __itruediv__(self, other):
|
||||||
self.value /= other
|
self._value /= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __floordiv__(self, other):
|
def __floordiv__(self, other):
|
||||||
return self.value // other
|
return self._value // other
|
||||||
|
|
||||||
def __rfloordiv__(self, other):
|
def __rfloordiv__(self, other):
|
||||||
return other // self.value
|
return other // self._value
|
||||||
|
|
||||||
def __ifloordiv__(self, other):
|
def __ifloordiv__(self, other):
|
||||||
self.value //= other
|
self._value //= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __mod__(self, other):
|
def __mod__(self, other):
|
||||||
return self.value % other
|
return self._value % other
|
||||||
|
|
||||||
def __rmod__(self, other):
|
def __rmod__(self, other):
|
||||||
return other % self.value
|
return other % self._value
|
||||||
|
|
||||||
def __imod__(self, other):
|
def __imod__(self, other):
|
||||||
self.value %= other
|
self._value %= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __divmod__(self, other):
|
def __divmod__(self, other):
|
||||||
return divmod(self.value, other)
|
return divmod(self._value, other)
|
||||||
|
|
||||||
def __rdivmod__(self, other):
|
def __rdivmod__(self, other):
|
||||||
return divmod(other, self.value)
|
return divmod(other, self._value)
|
||||||
|
|
||||||
def __pow__(self, other):
|
def __pow__(self, other):
|
||||||
return self.value**other
|
return self._value**other
|
||||||
|
|
||||||
def __rpow__(self, other):
|
def __rpow__(self, other):
|
||||||
return other**self.value
|
return other**self._value
|
||||||
|
|
||||||
def __ipow__(self, other):
|
def __ipow__(self, other):
|
||||||
self.value **= other
|
self._value **= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __lshift__(self, other):
|
def __lshift__(self, other):
|
||||||
return self.value << other
|
return self._value << other
|
||||||
|
|
||||||
def __rlshift__(self, other):
|
def __rlshift__(self, other):
|
||||||
return other << self.value
|
return other << self._value
|
||||||
|
|
||||||
def __ilshift__(self, other):
|
def __ilshift__(self, other):
|
||||||
self.value <<= other
|
self._value <<= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __rshift__(self, other):
|
def __rshift__(self, other):
|
||||||
return self.value >> other
|
return self._value >> other
|
||||||
|
|
||||||
def __rrshift__(self, other):
|
def __rrshift__(self, other):
|
||||||
return other >> self.value
|
return other >> self._value
|
||||||
|
|
||||||
def __irshift__(self, other):
|
def __irshift__(self, other):
|
||||||
self.value >>= other
|
self._value >>= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __and__(self, other):
|
def __and__(self, other):
|
||||||
return self.value & other
|
return self._value & other
|
||||||
|
|
||||||
def __rand__(self, other):
|
def __rand__(self, other):
|
||||||
return other & self.value
|
return other & self._value
|
||||||
|
|
||||||
def __iand__(self, other):
|
def __iand__(self, other):
|
||||||
self.value &= other
|
self._value &= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __xor__(self, other):
|
def __xor__(self, other):
|
||||||
return self.value ^ other
|
return self._value ^ other
|
||||||
|
|
||||||
def __rxor__(self, other):
|
def __rxor__(self, other):
|
||||||
return other ^ self.value
|
return other ^ self._value
|
||||||
|
|
||||||
def __ixor__(self, other):
|
def __ixor__(self, other):
|
||||||
self.value ^= other
|
self._value ^= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __or__(self, other):
|
def __or__(self, other):
|
||||||
return self.value | other
|
return self._value | other
|
||||||
|
|
||||||
def __ror__(self, other):
|
def __ror__(self, other):
|
||||||
return other | self.value
|
return other | self._value
|
||||||
|
|
||||||
def __ior__(self, other):
|
def __ior__(self, other):
|
||||||
self.value |= other
|
self._value |= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __neg__(self):
|
def __neg__(self):
|
||||||
return -self.value
|
return -self._value
|
||||||
|
|
||||||
def __pos__(self):
|
def __pos__(self):
|
||||||
return +self.value
|
return +self._value
|
||||||
|
|
||||||
def __abs__(self):
|
def __abs__(self):
|
||||||
return abs(self.value)
|
return abs(self._value)
|
||||||
|
|
||||||
def __invert__(self):
|
def __invert__(self):
|
||||||
return ~self.value
|
return ~self._value
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.value)
|
return len(self._value)
|
||||||
|
|
||||||
|
|
||||||
def get_attribute_or_create(obj, attr, default):
|
def get_attribute_or_create(obj, attr, default):
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import bonobo
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import bonobo
|
||||||
from bonobo.config.processors import ContextCurrifier
|
from bonobo.config.processors import ContextCurrifier
|
||||||
from bonobo.constants import NOT_MODIFIED
|
from bonobo.constants import NOT_MODIFIED
|
||||||
|
|
||||||
@ -10,14 +11,12 @@ def test_count():
|
|||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
bonobo.count()
|
bonobo.count()
|
||||||
|
|
||||||
|
|
||||||
context = MagicMock()
|
context = MagicMock()
|
||||||
|
|
||||||
currified = ContextCurrifier(bonobo.count)
|
with ContextCurrifier(bonobo.count).as_contextmanager(context) as stack:
|
||||||
currified.setup(context)
|
|
||||||
|
|
||||||
for i in range(42):
|
for i in range(42):
|
||||||
currified()
|
stack()
|
||||||
currified.teardown()
|
|
||||||
|
|
||||||
assert len(context.method_calls) == 1
|
assert len(context.method_calls) == 1
|
||||||
bag = context.send.call_args[0][0]
|
bag = context.send.call_args[0][0]
|
||||||
@ -32,18 +31,31 @@ def test_identity():
|
|||||||
|
|
||||||
|
|
||||||
def test_limit():
|
def test_limit():
|
||||||
limit = bonobo.Limit(2)
|
context, results = MagicMock(), []
|
||||||
results = []
|
|
||||||
|
with ContextCurrifier(bonobo.Limit(2)).as_contextmanager(context) as stack:
|
||||||
for i in range(42):
|
for i in range(42):
|
||||||
results += list(limit())
|
results += list(stack())
|
||||||
|
|
||||||
assert results == [NOT_MODIFIED] * 2
|
assert results == [NOT_MODIFIED] * 2
|
||||||
|
|
||||||
|
|
||||||
def test_limit_not_there():
|
def test_limit_not_there():
|
||||||
limit = bonobo.Limit(42)
|
context, results = MagicMock(), []
|
||||||
results = []
|
|
||||||
|
with ContextCurrifier(bonobo.Limit(42)).as_contextmanager(context) as stack:
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
results += list(limit())
|
results += list(stack())
|
||||||
|
|
||||||
|
assert results == [NOT_MODIFIED] * 10
|
||||||
|
|
||||||
|
def test_limit_default():
|
||||||
|
context, results = MagicMock(), []
|
||||||
|
|
||||||
|
with ContextCurrifier(bonobo.Limit()).as_contextmanager(context) as stack:
|
||||||
|
for i in range(20):
|
||||||
|
results += list(stack())
|
||||||
|
|
||||||
assert results == [NOT_MODIFIED] * 10
|
assert results == [NOT_MODIFIED] * 10
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -21,4 +21,3 @@ def test_deprecated_alias():
|
|||||||
|
|
||||||
with pytest.warns(DeprecationWarning):
|
with pytest.warns(DeprecationWarning):
|
||||||
foo()
|
foo()
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ def test_wrapper_name():
|
|||||||
|
|
||||||
def test_valueholder():
|
def test_valueholder():
|
||||||
x = ValueHolder(42)
|
x = ValueHolder(42)
|
||||||
|
|
||||||
assert x == 42
|
assert x == 42
|
||||||
x += 1
|
x += 1
|
||||||
assert x == 43
|
assert x == 43
|
||||||
|
|||||||
Reference in New Issue
Block a user