From ea0e9ae0cda2beb53d7faac43a59c00d520f13a1 Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Sun, 5 Mar 2023 13:31:52 +0200 Subject: [PATCH] Fixed some bugs and added new tests New test_multi_03 can be used to test multiple variables at once. Based on new collect function that can be used to collect n variables from parsed Ziffers. --- tests/test_multi_03.py | 13 ++++++++++ tests/{test_scale.py => test_scale_01.py} | 2 -- tests/{test_parser.py => test_singular_02.py} | 26 +++++++++---------- ziffers/__init__.py | 1 - ziffers/classes/items.py | 8 +++--- ziffers/classes/root.py | 22 ++++++++++++++++ ziffers/classes/sequences.py | 6 +++-- ziffers/parser.py | 5 ++-- 8 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 tests/test_multi_03.py rename tests/{test_scale.py => test_scale_01.py} (99%) rename tests/{test_parser.py => test_singular_02.py} (86%) diff --git a/tests/test_multi_03.py b/tests/test_multi_03.py new file mode 100644 index 0000000..fd6bda1 --- /dev/null +++ b/tests/test_multi_03.py @@ -0,0 +1,13 @@ +""" Test cases for the parser """ +import pytest +from ziffers import zparse + +@pytest.mark.parametrize( + "pattern,expected", + [ + ("1 2 3", [[1, 2, 3], [0.25,0.25,0.25]]), + ("q2 eq3 e.4", [[2, 3, 4], [0.25,0.375,0.1875]]), + ], +) +def test_multi_var(pattern: str, expected: list): + assert zparse(pattern).collect(6, keys=["pitch_class", "duration"]) == [item*2 for item in expected] \ No newline at end of file diff --git a/tests/test_scale.py b/tests/test_scale_01.py similarity index 99% rename from tests/test_scale.py rename to tests/test_scale_01.py index bc2c7af..7c434c8 100644 --- a/tests/test_scale.py +++ b/tests/test_scale_01.py @@ -2,8 +2,6 @@ import pytest from ziffers import scale - - @pytest.mark.parametrize( "name,expected", [ diff --git a/tests/test_parser.py b/tests/test_singular_02.py similarity index 86% rename from tests/test_parser.py rename to tests/test_singular_02.py index 06deb3e..aaadca1 100644 --- a/tests/test_parser.py +++ b/tests/test_singular_02.py @@ -1,6 +1,6 @@ """ Test cases for the parser """ import pytest -from ziffers import zparse, collect +from ziffers import zparse, get_items # pylint: disable=missing-function-docstring, line-too-long, invalid-name @@ -52,7 +52,7 @@ def test_parsing_text(pattern: str): ], ) def test_pitch_classes(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"pitch_class") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"pitch_class") == expected*2 @pytest.mark.parametrize( @@ -64,7 +64,7 @@ def test_pitch_classes(pattern: str, expected: list): ] ) def test_pitch_octaves(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"octave") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"octave") == expected*2 @pytest.mark.parametrize( @@ -77,7 +77,7 @@ def test_pitch_octaves(pattern: str, expected: list): ] ) def test_subdivisions(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"duration") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"duration") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -89,7 +89,7 @@ def test_subdivisions(pattern: str, expected: list): ] ) def test_repeats(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"note") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"note") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -104,7 +104,7 @@ def test_repeats(pattern: str, expected: list): ] ) def test_repeat_durations(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"duration") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"duration") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -130,7 +130,7 @@ def test_looping_durations(pattern: str, expected: list): ] ) def test_measure_durations(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"duration") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"duration") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -139,7 +139,7 @@ def test_measure_durations(pattern: str, expected: list): ] ) def test_measure_octaves(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"octave") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"octave") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -148,7 +148,7 @@ def test_measure_octaves(pattern: str, expected: list): ] ) def test_rest(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"duration") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"duration") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -157,7 +157,7 @@ def test_rest(pattern: str, expected: list): ] ) def test_ranges(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"note") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"note") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -166,7 +166,7 @@ def test_ranges(pattern: str, expected: list): ] ) def test_romans(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"note") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"note") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -177,7 +177,7 @@ def test_romans(pattern: str, expected: list): ] ) def test_romans_pcs(pattern: str, expected: list): - assert collect(zparse(pattern),len(expected)*2,"pitches") == expected*2 + assert get_items(zparse(pattern),len(expected)*2,"pitches") == expected*2 @pytest.mark.parametrize( "pattern,expected", @@ -188,4 +188,4 @@ def test_romans_pcs(pattern: str, expected: list): ) def test_cycles(pattern: str, expected: list): zparse.cache_clear() # Clear cache for cycles - assert collect(zparse(pattern),4,"note") == expected + assert get_items(zparse(pattern),4,"note") == expected diff --git a/ziffers/__init__.py b/ziffers/__init__.py index b8243f4..1fa0ca1 100644 --- a/ziffers/__init__.py +++ b/ziffers/__init__.py @@ -1,6 +1,5 @@ from .parser import * from .mapper import * -from .classes import * from .common import * from .defaults import * from .scale import * diff --git a/ziffers/classes/items.py b/ziffers/classes/items.py index 72d7730..4c6c595 100644 --- a/ziffers/classes/items.py +++ b/ziffers/classes/items.py @@ -133,6 +133,10 @@ class Event(Item): duration: float = field(default=None) + def get_duration(self): + """Getter for duration""" + return self.duration + @dataclass class Rest(Event): @@ -202,10 +206,6 @@ class Pitch(Event): """Getter for pitche""" return self.pitch_class - def get_duration(self): - """Getter for duration""" - return self.duration - def update_note(self, force: bool = False): """Update note if Key, Scale and Pitch-class are present""" if ( diff --git a/ziffers/classes/root.py b/ziffers/classes/root.py index a2fef0f..5522699 100644 --- a/ziffers/classes/root.py +++ b/ziffers/classes/root.py @@ -155,3 +155,25 @@ class Ziffers(Sequence): for val in self.evaluated_values if isinstance(val, (Pitch, Chord)) ] + + def collect(self, num: int = None, keys: str | list = None) -> list: + """Collect n items from parsed Ziffers""" + if num is None: + num = len(self.evaluated_values) + if keys is None or isinstance(keys, str): + keys = [keys] + all_items = [] + values = [] + for key in keys: + for i in range(num): + if key is not None: + values.append(getattr(self[i], key, None)) + else: + values.append(self[i]) + all_items.append(values) + values = [] + if len(all_items) > 1: + return all_items + if len(all_items) == 1: + return all_items[0] + return None diff --git a/ziffers/classes/sequences.py b/ziffers/classes/sequences.py index 13d24a3..de5324d 100644 --- a/ziffers/classes/sequences.py +++ b/ziffers/classes/sequences.py @@ -357,8 +357,10 @@ class ListOperation(Sequence): """Vertical arpeggio operation, eg. (135)@(q 1 2 021)""" left = _filter_operation(left, options) right = _filter_operation(right, options) - left = list(left.evaluate_tree(options)) - right = list(right.evaluate_tree(options)) + if not isinstance(left,list): + left = list(left.evaluate_tree(options)) + if not isinstance(right,list): + right = list(right.evaluate_tree(options)) arp_items = [] for item in left: diff --git a/ziffers/parser.py b/ziffers/parser.py index b9568f0..d646604 100644 --- a/ziffers/parser.py +++ b/ziffers/parser.py @@ -53,7 +53,6 @@ def z(expr: str, **opts) -> Ziffers: """Shortened method name for zparse""" return zparse(expr, **opts) - def yield_items(gen: Ziffers, num: int, key: str = None) -> list: """Yield n items from parsed Ziffers""" for i in range(num): @@ -62,7 +61,7 @@ def yield_items(gen: Ziffers, num: int, key: str = None) -> list: else: yield gen[i] -def collect(gen: Ziffers, num: int, key: str = None) -> list: - """Collect n-item from parsed Ziffers""" +def get_items(gen: Ziffers, num: int, key: str = None) -> list: + """Get n-item from parsed Ziffers. Functional alternative to Ziffers-object collect method.""" return list(yield_items(gen,num,key)) \ No newline at end of file