From ae50f6ed3c64c7269732c50fcb4506aa02048602 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 2 Oct 2015 22:13:43 -0400 Subject: [PATCH 1/6] ENH: add concat Adds a top level function `concat` and a `Cycler` method `concat` which will concatenate two cyclers. The method can be chained. closes #1 --- cycler.py | 35 +++++++++++++++++++++++++++++++++++ test_cycler.py | 20 ++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/cycler.py b/cycler.py index 48a2b3f..9e603ba 100644 --- a/cycler.py +++ b/cycler.py @@ -389,6 +389,41 @@ def simplify(self): trans = self._transpose() return reduce(add, (_cycler(k, v) for k, v in six.iteritems(trans))) + def concat(self, other): + return concat(self, other) + + +def concat(left, right): + """Concatenate two cyclers. + + The keys must match exactly. + + This returns a single Cycler which is equivalent to + `itertools.chain(left, right)` + + Parameters + ---------- + left, right : `Cycler` + The two `Cycler` instances to concatenate + + Returns + ------- + ret : `Cycler` + The concatenated `Cycler` + """ + if left.keys != right.keys: + msg = '\n\t'.join(["Keys do not match:", + "Intersection: {both!r}", + "Disjoint: {just_one!r}" + ]).format( + both=left.keys&right.keys, + just_one=left.keys^right.keys) + + raise ValueError(msg) + + _l = left._transpose() + _r = right._transpose() + return reduce(add, (_cycler(k, _l[k] + _r[k]) for k in left.keys)) def cycler(*args, **kwargs): """ diff --git a/test_cycler.py b/test_cycler.py index 6d93566..c8c2f8c 100644 --- a/test_cycler.py +++ b/test_cycler.py @@ -2,10 +2,10 @@ import six from six.moves import zip, range -from cycler import cycler, Cycler +from cycler import cycler, Cycler, concat from nose.tools import (assert_equal, assert_not_equal, assert_raises, assert_true) -from itertools import product, cycle +from itertools import product, cycle, chain from operator import add, iadd, mul, imul @@ -279,3 +279,19 @@ def test_starange_init(): c2 = cycler('lw', range(3)) cy = Cycler(list(c), list(c2), zip) assert_equal(cy, c + c2) + + +def test_concat(): + a = cycler('a', range(3)) + for con, chn in zip(a.concat(a), chain(a, a)): + assert_equal(con, chn) + + for con, chn in zip(concat(a, a), chain(a, a)): + assert_equal(con, chn) + + +def test_concat_fail(): + a = cycler('a', range(3)) + b = cycler('b', range(3)) + assert_raises(ValueError, concat, a, b) + assert_raises(ValueError, a.concat, b) From f978df02134f57c510968a630e7e288aa92d5661 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 2 Oct 2015 22:51:39 -0400 Subject: [PATCH 2/6] DOC: add concat to docs --- cycler.py | 14 ++++++++++++++ doc/source/index.rst | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/cycler.py b/cycler.py index 9e603ba..60afbe6 100644 --- a/cycler.py +++ b/cycler.py @@ -390,6 +390,20 @@ def simplify(self): return reduce(add, (_cycler(k, v) for k, v in six.iteritems(trans))) def concat(self, other): + """Concatenate two cyclers. + + The keys must match exactly. + + Parameters + ---------- + other : `Cycler` + The `Cycler` instances to concatenate to this one + + Returns + ------- + ret : `Cycler` + The concatenated `Cycler` + """ return concat(self, other) diff --git a/doc/source/index.rst b/doc/source/index.rst index e1a1f6f..ddf21a7 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -180,6 +180,22 @@ Integer Multiplication 2 * color_cycle +Concatenation +~~~~~~~~~~~~~ + +`Cycler` objects can be concatenated either via the :py:meth:`Cycler.concat` method + +.. ipython:: python + + color_cycle.concat(color_cycle) + +or the top-level :py:func:`concat` function + +.. ipython:: python + + from cycler import concat + concat(color_cycle, color_cycle) + Slicing ------- From c3913ba2d3ef309373058abe4860d647694f48e3 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Oct 2015 11:21:58 -0400 Subject: [PATCH 3/6] TST: make tests a less gameable --- test_cycler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test_cycler.py b/test_cycler.py index c8c2f8c..d1dc11e 100644 --- a/test_cycler.py +++ b/test_cycler.py @@ -283,10 +283,11 @@ def test_starange_init(): def test_concat(): a = cycler('a', range(3)) - for con, chn in zip(a.concat(a), chain(a, a)): + b = cycler('a', 'abc') + for con, chn in zip(a.concat(b), chain(a, b)): assert_equal(con, chn) - for con, chn in zip(concat(a, a), chain(a, a)): + for con, chn in zip(concat(a, b), chain(a, b)): assert_equal(con, chn) From b7df4b767dc8ef5fb09883e28fff13fe50edb9f4 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Oct 2015 11:22:23 -0400 Subject: [PATCH 4/6] TST: update travis test matrix --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bfc5179..c354030 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,9 @@ language: python matrix: include: - - python: 2.6 - python: 2.7 - - python: 3.3 - python: 3.4 + - python: 3.5 - python: "nightly" env: PRE=--pre allow_failures: From 1e119199cbf255714babd3e8ce38cebf97c27fe6 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Oct 2015 11:27:15 -0400 Subject: [PATCH 5/6] DOC: expand docstring --- cycler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cycler.py b/cycler.py index 60afbe6..5836f64 100644 --- a/cycler.py +++ b/cycler.py @@ -415,6 +415,14 @@ def concat(left, right): This returns a single Cycler which is equivalent to `itertools.chain(left, right)` + Examples + -------- + + >>> num = cycler('a', range(3)) + >>> let = cycler('a', 'abc') + >>> num.concat(let) + cycler('a', [0, 1, 2, 'a', 'b', 'c']) + Parameters ---------- left, right : `Cycler` From 495d0f41b8ea400274ca685dcc7b7c06832d2bed Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Oct 2015 11:35:58 -0400 Subject: [PATCH 6/6] DOC: update other concat docstring --- cycler.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cycler.py b/cycler.py index 5836f64..8fd93ef 100644 --- a/cycler.py +++ b/cycler.py @@ -390,14 +390,25 @@ def simplify(self): return reduce(add, (_cycler(k, v) for k, v in six.iteritems(trans))) def concat(self, other): - """Concatenate two cyclers. + """Concatenate this cycler and an other. The keys must match exactly. + This returns a single Cycler which is equivalent to + `itertools.chain(self, other)` + + Examples + -------- + + >>> num = cycler('a', range(3)) + >>> let = cycler('a', 'abc') + >>> num.concat(let) + cycler('a', [0, 1, 2, 'a', 'b', 'c']) + Parameters ---------- other : `Cycler` - The `Cycler` instances to concatenate to this one + The `Cycler` to concatenate to this one. Returns -------