Initial commit.
This commit is contained in:
commit
1b47458efa
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@ -0,0 +1,11 @@
|
||||
root=true
|
||||
|
||||
[*.py]
|
||||
tab_width = 4
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
|
||||
[{*.yml, *.yaml}]
|
||||
tab_width = 2
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Others
|
||||
~$*
|
||||
*~
|
||||
*.pfx
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
venv/
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Local dev files I don't want checked in.
|
||||
run.py
|
||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
189
README.md
Normal file
189
README.md
Normal file
@ -0,0 +1,189 @@
|
||||
# EzSequence
|
||||
A quick and dirty sequence generator for Python.
|
||||
|
||||
## About
|
||||
- EzSequence uses a fluent interface to strive for a self-documenting code approach. It works like the _Builder_ pattern.
|
||||
- EzSequence produces iterators that you can use in your FOR loops, your list comprehensions or to be converted to a list all at once.
|
||||
- EzSequence supports infinite sequences but has safety built in.
|
||||
|
||||
## Basic Usage
|
||||
```
|
||||
from ezsequence import *
|
||||
|
||||
seq = sequence_from(1).to(5)
|
||||
print(seq)
|
||||
```
|
||||
|
||||
## Full Usage
|
||||
|
||||
### Static Methods
|
||||
|
||||
There is only one!
|
||||
|
||||
`sequence_from()`
|
||||
|
||||
This function starts your sequence with an initial value and some default behavior. From there, you can customize it with any of the fluent methods listed below.
|
||||
|
||||
### Fluent Methods
|
||||
|Method| Description| Example| Output|
|
||||
|---- |---- |---- |---- |
|
||||
|.by(increment: int) |Optional. Specifies a gap between values. Default is 1. Cannot be used with `.with_increment()`.|`sequence_from(1) .to(5) .by(2)`|[1, 3, 5]|
|
||||
|.with_increment(increment_func: Callable[[int, int], int])| Optional. Specifies the translation of the value on each iteration. By default, value is incremented by 1. |`sequence .from(1) .to(5) .with_increment(lambda index, value: value + 2)` | [1, 3, 5] |
|
||||
|.until(condition: Callable[[int, int], int]) |Optional. Specifies a terminating condition in the sequence. Value is inclusive. In other words, once the condition is met, the next iteration will end the sequence. Multiple .until() calls are permitted. Otherwise, it is the same as `.to()`. |`sequence_from(1) .until(lambda index, value: value == 5)`| [1, 2, 3, 4, 5]|
|
||||
|.as_long_as(condition: Callable[[int, int], int]) |Optional. Specifies a terminating condition in the sequence. Value is exclusive. In other words, once the condition is violated, the current iteration will end the sequence. Multiple .as_long_as() calls are permitted. |`sequence_from(1) .as_long_as(lambda index, value: value <= 5)`| [1, 2, 3, 4, 5]|
|
||||
|.when(condition: Callable[[int, int], int]) |Optional. Specifies a condition for including this value in the sequence output. By default, all values are included. Multiple .when() calls are supported. Evluations do not terminate the sequence. |`sequence_from(1) .to(5) .when(lambda index, value: value % 2 == 1)`| [1, 3, 5]|
|
||||
|.not_when(condition: Callable[[int, int], int]) |Optional. Specifies a condition for excluding values, overriding any .when() condition that included them. By default, there are no exclusions. Multiple .not_when() calls are supported. Evaluations do not terminate the sequence.|`sequence_from(1) .to(5) .not_when(lambda index, value: value % 2 == 0)`| [1, 3, 5]
|
||||
|.safely(max_attempts: int)|Optional. Prevents unintended infinite loops caused by .when() and .not_when() filters which will hang in the _while_ loop that searches for the next unfiltered vlaue in the sequence. Takes an optional parameter for the max number of attempts to find an unfiltered value. Default max attempts is 10000. |`sequence_from(1) .to(5) .when(lambda index, value: value < 0) .safely(100)`|Exception: Safe iteration limit exceeded. 100 attempts exceeded.|
|
||||
|.unsafely()|Optional. Allows unintended infinite loops caused by `.when()` and `.not_when()` filters which may hang in the _while_loop that searchs for the next unfiltered value in the sequence. |`sequence_from(1) .with_increment(lambda index, value: random.Random() .randint(1, 1000)) .as_long_as(lambda index, value: value != 50) .unsafely()` |(No exception but may hang indefinitely)|
|
||||
|.translate_as(projection: Callable[[int, int], any]) |Optional. Specifies a function to convert each element that is returned by the iterator. | `sequence_from(5) .to(9) .translate_as(lambda index, value: value - 4)`| [1, 2, 3, 4, 5]|
|
||||
|.reset() |Rewinds the sequence to the beginning so it can be reused. |`seq.reset()` | N/A|
|
||||
|
||||
|
||||
## More Examples
|
||||
|
||||
Odds
|
||||
```
|
||||
seq = sequence_from(1).to(5) \
|
||||
.when(lambda index, value: value % 2 == 1)
|
||||
```
|
||||
|
||||
Evens
|
||||
```
|
||||
seq = sequence_from(1).to(5) \
|
||||
.when(lambda index, value: value % 2 == 0)
|
||||
```
|
||||
|
||||
Backward
|
||||
```
|
||||
seq = sequence_from(1) \
|
||||
.by(-1) \
|
||||
.until(lambda index, value: value == -5)
|
||||
```
|
||||
|
||||
Random Bytes
|
||||
```
|
||||
import random
|
||||
rnd = random.Random()
|
||||
seq = sequence_from(1).to(8) \
|
||||
.translate_as(lambda index, value: rnd.randint(0, 255))
|
||||
data = list(seq)
|
||||
```
|
||||
|
||||
Random AlphaNumeric Text
|
||||
```
|
||||
import random
|
||||
rnd = random.Random()
|
||||
seq = sequence_from(1).to(8) \
|
||||
.translate_as(lambda index, value: chr(rnd.randint(1, 26) + 64))
|
||||
data = "".join(seq)
|
||||
```
|
||||
|
||||
Reset Example
|
||||
```
|
||||
import random
|
||||
rnd = random.Random()
|
||||
seq = sequence_from(1).to(8) \
|
||||
.translate_as(lambda index, value: chr(rnd.randint(1, 26) + 64))
|
||||
|
||||
data1 = "".join(seq)
|
||||
seq.reset()
|
||||
data2 = "".join(seq)
|
||||
```
|
||||
|
||||
|
||||
Random CSV values
|
||||
```
|
||||
import random
|
||||
rnd = random.Random()
|
||||
|
||||
field_count = 5
|
||||
row_count = 3
|
||||
|
||||
def create_field_data(data_seq):
|
||||
data_seq.reset()
|
||||
return "".join(data_seq)
|
||||
|
||||
def create_row(field_seq):
|
||||
field_seq.reset()
|
||||
return ",".join(field_seq)
|
||||
|
||||
data_seq = sequence_from(1).to(8) \
|
||||
.translate_as(lambda index, value: chr(rnd.randint(1, 26) + 64))
|
||||
|
||||
field_seq = sequence_from(1).to(field_count) \
|
||||
.translate_as(lambda index, value: "\"" + create_field_data(data_seq) + "\"")
|
||||
|
||||
row_seq = sequence_from(1).to(row_count)
|
||||
|
||||
for row in row_seq:
|
||||
print(create_row(field_seq))
|
||||
```
|
||||
|
||||
Guid
|
||||
```
|
||||
import random
|
||||
rnd = random.Random()
|
||||
section_generators = \
|
||||
[
|
||||
sequence_from(1).to(8).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
|
||||
sequence_from(1).to(4).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
|
||||
sequence_from(1).to(4).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
|
||||
sequence_from(1).to(4).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
|
||||
sequence_from(1).to(12).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3])
|
||||
]
|
||||
|
||||
sections = ["".join(generator) for generator in section_generators]
|
||||
guid = "-".join(sections)
|
||||
print(guid)
|
||||
```
|
||||
|
||||
Factorials
|
||||
```
|
||||
from functools import reduce
|
||||
|
||||
factorial_of = 5
|
||||
factors = sequence_from(factorial_of) \
|
||||
.with_increment(lambda index, value: value - 1) \
|
||||
.until(lambda index, value: value == 1)
|
||||
factorial = reduce(lambda running, value: running * value, factors)
|
||||
|
||||
print(list(factors.reset()))
|
||||
print(factorial)
|
||||
```
|
||||
|
||||
Fibonacci
|
||||
```
|
||||
import itertools
|
||||
|
||||
class Fibonacci:
|
||||
def __init__(self):
|
||||
self.previous2 = 0
|
||||
self.previous1 = 1
|
||||
def calculate_fibonacci(self, value):
|
||||
next_value = self.previous1 + self.previous2
|
||||
self.previous2 = self.previous1
|
||||
self.previous1 = next_value
|
||||
return next_value
|
||||
def sequence(self):
|
||||
seq = sequence_from(1).to(20) \
|
||||
.translate_as(lambda index, value: self.calculate_fibonacci(value)) \
|
||||
.as_long_as(lambda index, value: value < 500000000)
|
||||
return itertools.chain([1], seq)
|
||||
|
||||
fib = list(Fibonacci().sequence())
|
||||
print(fib)
|
||||
```
|
||||
|
||||
Multiple _when_, _not_when_, _until_ and _as_long_as_
|
||||
```
|
||||
seq = sequence_from(1).to(50) \
|
||||
.by(3) \
|
||||
.when(lambda index, value: value >= 10) \
|
||||
.when(lambda index, value: value % 2 == 1) \
|
||||
.when_not(lambda index, value: value == 25) \
|
||||
.as_long_as(lambda index, value: value <= 40) \
|
||||
.as_long_as(lambda index, value: value != 38) \
|
||||
.safely(10)
|
||||
|
||||
print(list(seq))
|
||||
```
|
||||
1
ezsequence/__init__.py
Normal file
1
ezsequence/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .ezsequence import *
|
||||
139
ezsequence/ezsequence.py
Normal file
139
ezsequence/ezsequence.py
Normal file
@ -0,0 +1,139 @@
|
||||
from typing import Callable
|
||||
|
||||
class EzSequence:
|
||||
def __init__(self, start_value):
|
||||
self.start_value = start_value
|
||||
self.value = start_value
|
||||
self.index = 0
|
||||
self.when_funcs = []
|
||||
self.not_when_funcs = []
|
||||
self.until_funcs = []
|
||||
self.as_long_as_funcs = []
|
||||
self.is_last = False
|
||||
self.by(1)
|
||||
self.consecutive_attempts = 0
|
||||
self.safely(100)
|
||||
self.is_first_element = True
|
||||
self.translate_as(lambda index, value: value)
|
||||
def reset(self):
|
||||
self.value = self.start_value
|
||||
self.index = 0
|
||||
self.is_first_element = True
|
||||
self.is_last = False
|
||||
return self
|
||||
def __iter__(self):
|
||||
return self
|
||||
def __next__(self) -> int:
|
||||
if self.is_first_element:
|
||||
self.is_first_element = False
|
||||
|
||||
if self.until_funcs != None and len(self.until_funcs) > 0:
|
||||
if any(f(self.index, self.value) for f in self.until_funcs):
|
||||
self.is_last = True
|
||||
|
||||
if ((self.when_funcs != None and len(self.when_funcs) > 0) or (self.not_when_funcs != None and len(self.not_when_funcs) > 0)):
|
||||
if (all(f(self.index, self.value) for f in self.when_funcs)) and (not any(f(self.index, self.value) for f in self.not_when_funcs)):
|
||||
return self.translate_as_func(self.index, self.value)
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
return self.translate_as_func(self.index, self.value)
|
||||
|
||||
self.index -= 1 # Because it will be incremented below and we are still in the first element.
|
||||
|
||||
#current_index = self.index
|
||||
current_value = self.value
|
||||
|
||||
# Handle UNTIL
|
||||
if self.is_last:
|
||||
raise StopIteration
|
||||
|
||||
# Find next value.
|
||||
|
||||
self.consecutive_attempts = 1
|
||||
next_value = self.increment_func(self.index + 1, current_value)
|
||||
next_index = self.index + 1
|
||||
|
||||
# Keep looking for a new value until we get a value that can be output or until we violate the safety limit.
|
||||
if ((self.when_funcs != None and len(self.when_funcs) > 0) or (self.not_when_funcs != None and len(self.not_when_funcs) > 0)):
|
||||
while True:
|
||||
if (not all(f(next_index, next_value) for f in self.when_funcs)) or (any(f(next_index, next_value) for f in self.not_when_funcs)):
|
||||
if self.is_last:
|
||||
raise StopIteration
|
||||
|
||||
# This value will not be output. Find a new one that will.
|
||||
self.consecutive_attempts += 1
|
||||
if self.use_safety and (self.consecutive_attempts > self.max_consective_attempts):
|
||||
raise Exception(f"Safe iteration limit exceeded. {self.max_consective_attempts} attempts exceeded.")
|
||||
|
||||
next_value = self.increment_func(next_index, next_value)
|
||||
|
||||
# Stop if we encountered the end of the sequence.
|
||||
if self.as_long_as_funcs != None and len(self.as_long_as_funcs) > 0:
|
||||
if not all(f(next_index, next_value) for f in self.as_long_as_funcs):
|
||||
raise StopIteration
|
||||
|
||||
if self.until_funcs != None and len(self.until_funcs) > 0:
|
||||
if any(f(next_index, next_value) for f in self.until_funcs):
|
||||
self.is_last = True
|
||||
else:
|
||||
break
|
||||
|
||||
# Stop if we encountered the end of the sequence.
|
||||
if self.as_long_as_funcs != None and len(self.as_long_as_funcs) > 0:
|
||||
if not all(f(next_index, next_value) for f in self.as_long_as_funcs):
|
||||
raise StopIteration
|
||||
|
||||
# Return the value THIS time, but prepare to stop NEXT time if UNTIL is encountered here.
|
||||
if self.until_funcs != None and len(self.until_funcs) > 0:
|
||||
if any(f(next_index, next_value) for f in self.until_funcs):
|
||||
self.is_last = True
|
||||
|
||||
self.index + next_index
|
||||
self.value = next_value
|
||||
|
||||
return self.translate_as_func(self.index, self.value)
|
||||
|
||||
def by(self, increment: Callable[[int, int], int]):
|
||||
self.with_increment(lambda index, value: value + increment)
|
||||
return self
|
||||
|
||||
def with_increment(self, increment_func: Callable[[int, int], int] ):
|
||||
self.increment_func = increment_func
|
||||
return self
|
||||
|
||||
def until(self, until_func: Callable[[int, int], bool]):
|
||||
self.until_funcs.append(until_func)
|
||||
return self
|
||||
|
||||
def as_long_as(self, as_long_as_func: Callable[[int, int], bool]):
|
||||
self.as_long_as_funcs.append(as_long_as_func)
|
||||
return self
|
||||
|
||||
def to(self, last_value):
|
||||
self.until(lambda index, value: value == last_value)
|
||||
return self
|
||||
|
||||
def when(self, when_func: Callable[[int, int], bool]):
|
||||
self.when_funcs.append(when_func)
|
||||
return self
|
||||
|
||||
def not_when(self, not_when_func: Callable[[int, int], bool]):
|
||||
self.not_when_funcs.append(not_when_func)
|
||||
return self
|
||||
|
||||
def translate_as(self, translate_as_func: Callable[[int, int], int]):
|
||||
self.translate_as_func = translate_as_func
|
||||
return self
|
||||
|
||||
def safely(self, max_attempts: int = 10000):
|
||||
self.use_safety = True
|
||||
self.max_consective_attempts = max_attempts
|
||||
return self
|
||||
|
||||
def unsafely(self):
|
||||
self.use_safety = False
|
||||
return self
|
||||
|
||||
def sequence_from(start_value: int):
|
||||
return EzSequence(start_value)
|
||||
22
pyproject.toml
Normal file
22
pyproject.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "EzSequence"
|
||||
version = "0.0.1"
|
||||
authors = [{ name = "Phil Gilmore", email="spgilmore+pypi@gmail.com"}]
|
||||
description = "A simple iterable sequence generator."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.6"
|
||||
classifiers = [
|
||||
"Natural Language :: English",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Operating System :: OS Independent",
|
||||
"License :: OSI Approved :: MIT License"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://git.pillidar.com/PillidarPublic/EzSequencePy"
|
||||
Repository = "https://git.pillidar.com/PillidarPublic/EzSequencePy"
|
||||
Issues = "https://git.pillidar.com/PillidarPublic/EzSequencePy/issues"
|
||||
5
runtests.py
Normal file
5
runtests.py
Normal file
@ -0,0 +1,5 @@
|
||||
import unittest
|
||||
from tests.test_ezsequence import *
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
135
tests/test_ezsequence.py
Normal file
135
tests/test_ezsequence.py
Normal file
@ -0,0 +1,135 @@
|
||||
import unittest
|
||||
from ezsequence import *
|
||||
|
||||
class TestAll(unittest.TestCase):
|
||||
def number_names(self):
|
||||
return \
|
||||
{
|
||||
1: "One",
|
||||
2: "Two",
|
||||
3: "Three",
|
||||
4: "Four",
|
||||
5: "Five"
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_contains_test(self):
|
||||
full_list = [1, 2, 3]
|
||||
sub_list = [1, 2]
|
||||
list_intersection = list(set(full_list) & set(sub_list))
|
||||
self.assertListEqual(sub_list, list_intersection)
|
||||
|
||||
def test_from_to(self):
|
||||
expected = [1, 2, 3, 4, 5]
|
||||
actual = list(sequence_from(1).to(5))
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_until(self):
|
||||
expected = [1, 2, 3, 4, 5]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.until(lambda index, value: value == 5)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_as_long_as(self):
|
||||
expected = [1, 2, 3, 4, 5]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.as_long_as(lambda index, value: value < 6)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_when(self):
|
||||
expected = [1, 3, 5, 7, 9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.when(lambda index, value: value % 2 == 1) \
|
||||
.as_long_as(lambda index, value: value <= 10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_with_increment(self):
|
||||
expected = [1, 3, 5, 7, 9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.with_increment(lambda index, value: value + 2) \
|
||||
.as_long_as(lambda index, value: value <= 10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_with_increment_backward(self):
|
||||
expected = [1, -1, -3, -5, -7, -9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.with_increment(lambda index, value: value - 2) \
|
||||
.as_long_as(lambda index, value: value >= -10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_multiple_when(self):
|
||||
expected = [5, 7, 9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.when(lambda index, value: value % 2 == 1) \
|
||||
.when(lambda index, value: value >= 5) \
|
||||
.as_long_as(lambda index, value: value <= 10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_not_when(self):
|
||||
expected = [1, 3, 5, 7, 9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.not_when(lambda index, value: value % 2 == 0) \
|
||||
.as_long_as(lambda index, value: value <= 10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_multiple_not_when(self):
|
||||
expected = [5, 7, 9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.not_when(lambda index, value: value % 2 == 0) \
|
||||
.not_when(lambda index, value: value < 5) \
|
||||
.as_long_as(lambda index, value: value <= 10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_mixed_when_and_not_when(self):
|
||||
expected = [3, 9]
|
||||
sequence = \
|
||||
sequence_from(1) \
|
||||
.when(lambda index, value: value % 2 == 1) \
|
||||
.when(lambda index, value: value > 1) \
|
||||
.not_when(lambda index, value: value == 7) \
|
||||
.not_when(lambda index, value: value == 5) \
|
||||
.as_long_as(lambda index, value: value <= 10)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_translate_on_one_item(self):
|
||||
expected = [4]
|
||||
sequence = sequence_from(1).to(1) \
|
||||
.translate_as(lambda index, value: value * 3 + 1)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_translate_on_multiple_items(self):
|
||||
expected = [4, 7, 10, 13, 16]
|
||||
sequence = sequence_from(1).to(5) \
|
||||
.translate_as(lambda index, value: value * 3 + 1)
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
def test_translate_on_disparate_types(self):
|
||||
expected = ["ONE", "TWO", "THREE", "FOUR", "FIVE"]
|
||||
sequence = sequence_from(1).to(5) \
|
||||
.translate_as(lambda index, value: self.number_names()[value].upper())
|
||||
actual = list(sequence)
|
||||
self.assertListEqual(expected, actual)
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# unittest.main()
|
||||
Loading…
Reference in New Issue
Block a user