Repository: frictionlessdata/tabulator-py (python)

Issue:
Remove x_class arguments from topen API
# Overview

As a part of road to the v1 (https://github.com/frictionlessdata/tabulator-py/milestone/5) and technically breaking v0.5 release (https://github.com/frictionlessdata/tabulator-py/milestone/6) we need to remove `table_class`, `iterator_class` and `row_class` (replace with `iter(keyed=True)`) from `topen` signature. This is bloating the API without a reason.

Pull request:
Clean API
It fixes #62 

Patch:
diff --git a/README.md b/README.md
index 9e6908e..da9315a 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,6 @@ to use encoding of choice to open the table.
 Function `topen` returns `Table` instance. We use context manager
 to call `table.open()` on enter and `table.close()` when we exit:
 - table can be iterated like file-like object returning row by row
-- table can be read row by bow using `readrow` method (it returns row tuple)
 - table can be read into memory using `read` function (return list or row tuples)
 with `limit` of output rows as parameter.
 - headers can be accessed via `headers` property
diff --git a/tabulator/iterator.py b/tabulator/iterator.py
index 68f2a82..864cf53 100644
--- a/tabulator/iterator.py
+++ b/tabulator/iterator.py
@@ -18,8 +18,8 @@ class Iterator(object):
 
     # Public
 
-    def __init__(self, items, processors):
-        self.__items = items
+    def __init__(self, extended_rows, processors):
+        self.__extended_rows = extended_rows
         self.__processors = processors
         self.__index = None
         self.__count = 0
@@ -57,9 +57,9 @@ class Iterator(object):
         self.__is_stop = False
         self.__is_skip = False
 
-        # Get next keys, values from items
+        # Get next keys, values from extended_rows
         try:
-            self.__keys, self.__values = next(self.__items)
+            _, self.__keys, self.__values = next(self.__extended_rows)
         except StopIteration:
             raise
         except Exception as exception:
diff --git a/tabulator/parsers/api.py b/tabulator/parsers/api.py
index 83790b3..55dcbd0 100644
--- a/tabulator/parsers/api.py
+++ b/tabulator/parsers/api.py
@@ -65,14 +65,13 @@ class Parser(object):
 
     @property
     @abstractmethod
-    def items(self):
-        """Items generator.
+    def extended_rows(self):
+        """iterator: Extended rows.
 
-        Emit from parsed underlaying stream
-        keys, values pairs.
+        Extended rows iterator from parsed underlaying stream.
 
         Yields:
-            (keys, values) (tuple): keys, values pairs
+            (index, headers, values) (tuple): extended row
 
         """
         pass  # pragma: no cover
diff --git a/tabulator/parsers/csv.py b/tabulator/parsers/csv.py
index 838a9d2..b35a5bd 100644
--- a/tabulator/parsers/csv.py
+++ b/tabulator/parsers/csv.py
@@ -21,10 +21,10 @@ class CSVParser(api.Parser):
     # Public
 
     def __init__(self, **options):
+        self.__extended_rows = None
         self.__options = options
         self.__loader = None
         self.__chars = None
-        self.__items = None
 
     @property
     def closed(self):
@@ -42,30 +42,30 @@ class CSVParser(api.Parser):
 
     def reset(self):
         helpers.reset_stream(self.__chars)
-        self.__items = self.__emit_items()
+        self.__extended_rows = self.__iter_extended_rows()
 
     @property
-    def items(self):
-        return self.__items
+    def extended_rows(self):
+        return self.__extended_rows
 
     # Private
 
-    def __emit_items(self):
+    def __iter_extended_rows(self):
 
         # For PY2 encode/decode
         if six.PY2:
             # Reader requires utf-8 encoded stream
             bytes = iterencode(self.__chars, 'utf-8')
             items = csv.reader(bytes, **self.__options)
-            for item in items:
+            for index, item in enumerate(items):
                 values = []
                 for value in item:
                     value = value.decode('utf-8')
                     values.append(value)
-                yield (None, tuple(values))
+                yield (index, None, tuple(values))
 
         # For PY3 use chars
         else:
             items = csv.reader(self.__chars, **self.__options)
-            for item in items:
-                yield (None, tuple(item))
+            for index, item in enumerate(items):
+                yield (index, None, tuple(item))
diff --git a/tabulator/parsers/excel.py b/tabulator/parsers/excel.py
index d716974..7fc8fd1 100644
--- a/tabulator/parsers/excel.py
+++ b/tabulator/parsers/excel.py
@@ -21,7 +21,7 @@ class ExcelParser(api.Parser):
     def __init__(self, sheet_index=0):
         self.__sheet_index = sheet_index
         self.__bytes = None
-        self.__items = None
+        self.__extended_rows = None
 
     @property
     def closed(self):
@@ -43,14 +43,14 @@ class ExcelParser(api.Parser):
 
     def reset(self):
         helpers.reset_stream(self.__bytes)
-        self.__items = self.__emit_items()
+        self.__extended_rows = self.__iter_extended_rows()
 
     @property
-    def items(self):
-        return self.__items
+    def extended_rows(self):
+        return self.__extended_rows
 
     # Private
 
-    def __emit_items(self):
-        for rownum in range(self.__sheet.nrows):
-            yield (None, tuple(self.__sheet.row_values(rownum)))
+    def __iter_extended_rows(self):
+        for index in range(self.__sheet.nrows):
+            yield (index, None, tuple(self.__sheet.row_values(index)))
diff --git a/tabulator/parsers/excelx.py b/tabulator/parsers/excelx.py
index c4ea7cb..ef224a5 100644
--- a/tabulator/parsers/excelx.py
+++ b/tabulator/parsers/excelx.py
@@ -21,7 +21,7 @@ class ExcelxParser(api.Parser):
     def __init__(self, sheet_index=0):
         self.__sheet_index = sheet_index
         self.__bytes = None
-        self.__items = None
+        self.__extended_rows = None
 
     @property
     def closed(self):
@@ -41,14 +41,14 @@ class ExcelxParser(api.Parser):
 
     def reset(self):
         helpers.reset_stream(self.__bytes)
-        self.__items = self.__emit_items()
+        self.__extended_rows = self.__iter_extended_rows()
 
     @property
-    def items(self):
-        return self.__items
+    def extended_rows(self):
+        return self.__extended_rows
 
     # Private
 
-    def __emit_items(self):
-        for row in self.__sheet.rows:
-            yield (None, tuple(cell.value for cell in row))
+    def __iter_extended_rows(self):
+        for index, row in enumerate(self.__sheet.rows):
+            yield (index, None, tuple(cell.value for cell in row))
diff --git a/tabulator/parsers/json.py b/tabulator/parsers/json.py
index 60ef470..593d48c 100644
--- a/tabulator/parsers/json.py
+++ b/tabulator/parsers/json.py
@@ -19,9 +19,9 @@ class JSONParser(api.Parser):
     # Public
 
     def __init__(self, path=None):
+        self.__extended_rows = None
         self.__path = path
         self.__chars = None
-        self.__items = None
 
     @property
     def closed(self):
@@ -39,29 +39,29 @@ class JSONParser(api.Parser):
 
     def reset(self):
         helpers.reset_stream(self.__chars)
-        self.__items = self.__emit_items()
+        self.__extended_rows = self.__iter_extended_rows()
 
     @property
-    def items(self):
-        return self.__items
+    def extended_rows(self):
+        return self.__extended_rows
 
     # Private
 
-    def __emit_items(self):
+    def __iter_extended_rows(self):
         prefix = 'item'
         if self.__path is not None:
             prefix = '%s.item' % self.__path
         items = ijson.items(self.__chars, prefix)
-        for item in items:
+        for index, item in enumerate(items):
             if isinstance(item, (tuple, list)):
-                yield (None, tuple(item))
+                yield (index, None, tuple(item))
             elif isinstance(item, dict):
                 keys = []
                 values = []
                 for key in sorted(item.keys()):
                     keys.append(key)
                     values.append(item[key])
-                yield (tuple(keys), tuple(values))
+                yield (index, tuple(keys), tuple(values))
             else:
                 message = 'JSON item has to be list or dict'
                 raise errors.Error(message)
diff --git a/tabulator/parsers/native.py b/tabulator/parsers/native.py
index 87cf1de..c26b160 100644
--- a/tabulator/parsers/native.py
+++ b/tabulator/parsers/native.py
@@ -17,9 +17,9 @@ class NativeParser(api.Parser):
     # Public
 
     def __init__(self, **options):
+        self.__extended_rows = None
         self.__options = options
         self.__source = None
-        self.__items = None
 
     @property
     def closed(self):
@@ -34,25 +34,25 @@ class NativeParser(api.Parser):
         pass
 
     def reset(self):
-        self.__items = self.__emit_items()
+        self.__extended_rows = self.__iter_extended_rows()
 
     @property
-    def items(self):
-        return self.__items
+    def extended_rows(self):
+        return self.__extended_rows
 
     # Private
 
-    def __emit_items(self):
+    def __iter_extended_rows(self):
         items = self.__source
-        for item in items:
+        for index, item in enumerate(items):
             if isinstance(item, (tuple, list)):
-                yield (None, tuple(item))
+                yield (index, None, tuple(item))
             elif isinstance(item, dict):
                 keys = []
                 values = []
                 for key in sorted(item.keys()):
                     keys.append(key)
                     values.append(item[key])
-                yield (tuple(keys), tuple(values))
+                yield (index, tuple(keys), tuple(values))
             else:
                 raise errors.Error('Native item has to be tuple, list or dict')
diff --git a/tabulator/parsers/tsv.py b/tabulator/parsers/tsv.py
index 7a2a9f7..7e1dfdc 100644
--- a/tabulator/parsers/tsv.py
+++ b/tabulator/parsers/tsv.py
@@ -22,10 +22,10 @@ class TSVParser(api.Parser):
     # Public
 
     def __init__(self, **options):
+        self.__extended_rows = None
         self.__options = options
         self.__loader = None
         self.__chars = None
-        self.__items = None
 
     @property
     def closed(self):
@@ -43,15 +43,15 @@ class TSVParser(api.Parser):
 
     def reset(self):
         helpers.reset_stream(self.__chars)
-        self.__items = self.__emit_items()
+        self.__extended_rows = self.__iter_extended_rows()
 
     @property
-    def items(self):
-        return self.__items
+    def extended_rows(self):
+        return self.__extended_rows
 
     # Private
 
-    def __emit_items(self):
+    def __iter_extended_rows(self):
         items = tsv.un(self.__chars)
-        for item in items:
-            yield (None, tuple(item))
+        for index, item in enumerate(items):
+            yield (index, None, tuple(item))
diff --git a/tabulator/table.py b/tabulator/table.py
index 91f2809..b9af010 100644
--- a/tabulator/table.py
+++ b/tabulator/table.py
@@ -78,7 +78,8 @@ class Table(object):
         # Open parser, create iterator
         if self.closed:
             self.__parser.open(self.__source, self.__encoding, self.__loader)
-            self.__iterator = Iterator(self.__parser.items, self.__processors)
+            self.__iterator = Iterator(
+                self.__parser.extended_rows, self.__processors)
 
         return self
 
@@ -100,7 +101,8 @@ class Table(object):
 
         # Reset parser, recreate iterator
         self.__parser.reset()
-        self.__iterator = Iterator(self.__parser.items, self.__processors)
+        self.__iterator = Iterator(
+            self.__parser.extended_rows, self.__processors)
 
     @property
     def headers(self):
@@ -125,11 +127,6 @@ class Table(object):
         self.__keyed = keyed
         return self
 
-    def readrow(self):
-        """Return the next row from the table.
-        """
-        return next(self)
-
     def read(self, limit=None):
         """Return full table with row limit.
         """


Test patch:
diff --git a/tests/parsers/test_csv.py b/tests/parsers/test_csv.py
index 8c2410a..c7e6274 100644
--- a/tests/parsers/test_csv.py
+++ b/tests/parsers/test_csv.py
@@ -23,14 +23,14 @@ def test_csv_parser():
     parser.open(source, encoding, loader)
     assert not parser.closed
 
-    assert list(parser.items) == [
-        (None, ('id', 'name')),
-        (None, ('1', 'english')),
-        (None, ('2', '中国人'))]
+    assert list(parser.extended_rows) == [
+        (0, None, ('id', 'name')),
+        (1, None, ('1', 'english')),
+        (2, None, ('2', '中国人'))]
 
-    assert len(list(parser.items)) == 0
+    assert len(list(parser.extended_rows)) == 0
     parser.reset()
-    assert len(list(parser.items)) == 3
+    assert len(list(parser.extended_rows)) == 3
 
     parser.close()
     assert parser.closed
diff --git a/tests/parsers/test_excel.py b/tests/parsers/test_excel.py
index 42f1ae8..b7d8891 100644
--- a/tests/parsers/test_excel.py
+++ b/tests/parsers/test_excel.py
@@ -23,14 +23,14 @@ def test_excel_parser():
     parser.open(source, encoding, loader)
     assert not parser.closed
 
-    assert list(parser.items) == [
-        (None, ('id', 'name')),
-        (None, (1.0, 'english')),
-        (None, (2.0, '中国人'))]
+    assert list(parser.extended_rows) == [
+        (0, None, ('id', 'name')),
+        (1, None, (1.0, 'english')),
+        (2, None, (2.0, '中国人'))]
 
-    assert len(list(parser.items)) == 0
+    assert len(list(parser.extended_rows)) == 0
     parser.reset()
-    assert len(list(parser.items)) == 3
+    assert len(list(parser.extended_rows)) == 3
 
     parser.close()
     assert parser.closed
diff --git a/tests/parsers/test_excelx.py b/tests/parsers/test_excelx.py
index f284089..ca8c29e 100644
--- a/tests/parsers/test_excelx.py
+++ b/tests/parsers/test_excelx.py
@@ -23,14 +23,14 @@ def test_excelx_parser():
     parser.open(source, encoding, loader)
     assert not parser.closed
 
-    assert list(parser.items) == [
-        (None, ('id', 'name')),
-        (None, (1.0, 'english')),
-        (None, (2.0, '中国人'))]
+    assert list(parser.extended_rows) == [
+        (0, None, ('id', 'name')),
+        (1, None, (1.0, 'english')),
+        (2, None, (2.0, '中国人'))]
 
-    assert len(list(parser.items)) == 0
+    assert len(list(parser.extended_rows)) == 0
     parser.reset()
-    assert len(list(parser.items)) == 3
+    assert len(list(parser.extended_rows)) == 3
 
     parser.close()
     assert parser.closed
diff --git a/tests/parsers/test_json.py b/tests/parsers/test_json.py
index 42901d9..4c31df7 100644
--- a/tests/parsers/test_json.py
+++ b/tests/parsers/test_json.py
@@ -23,13 +23,13 @@ def test_json_parser():
     parser.open(source, encoding, loader)
     assert not parser.closed
 
-    assert list(parser.items) == [
-        (('id', 'name'), (1, 'english')),
-        (('id', 'name'), (2, '中国人'))]
+    assert list(parser.extended_rows) == [
+        (0, ('id', 'name'), (1, 'english')),
+        (1, ('id', 'name'), (2, '中国人'))]
 
-    assert len(list(parser.items)) == 0
+    assert len(list(parser.extended_rows)) == 0
     parser.reset()
-    assert len(list(parser.items)) == 2
+    assert len(list(parser.extended_rows)) == 2
 
     parser.close()
     assert parser.closed
diff --git a/tests/parsers/test_tsv.py b/tests/parsers/test_tsv.py
index 697c800..bcbdb81 100644
--- a/tests/parsers/test_tsv.py
+++ b/tests/parsers/test_tsv.py
@@ -23,15 +23,15 @@ def test_tsv_parser():
     parser.open(source, encoding, loader)
     assert not parser.closed
 
-    assert list(parser.items) == [
-        (None, ('id', 'name')),
-        (None, ('1', 'english')),
-        (None, ('2', '中国人')),
-        (None, ('3', None))]
+    assert list(parser.extended_rows) == [
+        (0, None, ('id', 'name')),
+        (1, None, ('1', 'english')),
+        (2, None, ('2', '中国人')),
+        (3, None, ('3', None))]
 
-    assert len(list(parser.items)) == 0
+    assert len(list(parser.extended_rows)) == 0
     parser.reset()
-    assert len(list(parser.items)) == 4
+    assert len(list(parser.extended_rows)) == 4
 
     parser.close()
     assert parser.closed