diff --git a/docs/changes.rst b/docs/changes.rst index 42af571d..03889439 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -1,6 +1,12 @@ Changes ======= +Version 1.7.8 +------------- + +* Fix sorting of rows with different length + By :user:`arturponinski`, :issue:`385`. + Version 1.7.7 ------------- diff --git a/petl/comparison.py b/petl/comparison.py index fc4d991d..d0ae3064 100644 --- a/petl/comparison.py +++ b/petl/comparison.py @@ -2,7 +2,7 @@ import operator - +from functools import partial from petl.compat import text_type, binary_type, numeric_types @@ -104,6 +104,26 @@ def _typestr(x): def comparable_itemgetter(*args): - f = operator.itemgetter(*args) + getter = operator.itemgetter(*args) + getter_with_default = _itemgetter_with_default(*args) + + def f(x): + try: + return getter(x) + except (IndexError, KeyError): + return getter_with_default(x) g = lambda x: Comparable(f(x)) return g + + +def _itemgetter_with_default(*args): + """ itemgetter compatible with `operator.itemgetter` behavior, filling missing + values with default instead of raising IndexError or KeyError """ + def get_default(obj, item, default): + try: + return obj[item] + except (IndexError, KeyError): + return default + if len(args) == 1: + return partial(get_default, item=args[0], default=None) + return lambda obj: tuple(get_default(obj, item=item, default=None) for item in args) diff --git a/petl/test/transform/test_sorts.py b/petl/test/transform/test_sorts.py index 9728d044..bb003aca 100644 --- a/petl/test/transform/test_sorts.py +++ b/petl/test/transform/test_sorts.py @@ -9,6 +9,7 @@ import pytest +import petl from petl.compat import next @@ -522,3 +523,21 @@ def test_issorted(): assert not issorted(table5, key='foo') assert issorted(table5, key='foo', reverse=True) assert not issorted(table5, key='foo', reverse=True, strict=True) + + +def test_sort_missing_cell_numeric(): + """ Sorting table with missing values raises IndexError #385 """ + tbl = (('a', 'b'), ('4',), ('2', '1'), ('1',)) + expect = (('a', 'b'), ('1',), ('2', '1'), ('4',)) + + sorted = sort(tbl) + ieq(expect, sorted) + + +def test_sort_missing_cell_text(): + """ Sorting table with missing values raises IndexError #385 """ + tbl = (('a', 'b', 'c'), ('C',), ('A', '4', '5')) + expect = (('a', 'b', 'c'), ('A', '4', '5'), ('C',)) + + sorted = sort(tbl) + ieq(expect, sorted) diff --git a/petl/transform/sorts.py b/petl/transform/sorts.py index 521d0cec..d591132e 100755 --- a/petl/transform/sorts.py +++ b/petl/transform/sorts.py @@ -295,7 +295,6 @@ def _iternocache(self, source, key, reverse): else: indices = range(len(hdr)) # now use field indices to construct a _getkey function - # TODO check if this raises an exception on short rows getkey = comparable_itemgetter(*indices) # TODO support native comparison