Skip to content

Commit 806dbd4

Browse files
author
Steve Canny
committed
acpt: add _Column.cells access scenario
Along the way: * rename and resequence _CellCollection to _RowCellCollection since there will need to be a _ColumnCellCollection now
1 parent 050e00b commit 806dbd4

File tree

4 files changed

+160
-94
lines changed

4 files changed

+160
-94
lines changed

docx/table.py

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,32 +63,6 @@ def __init__(self, tc):
6363
self._tc = tc
6464

6565

66-
class _CellCollection(object):
67-
"""
68-
Sequence of |_Cell| instances corresponding to the cells in a table row.
69-
"""
70-
def __init__(self, tr):
71-
super(_CellCollection, self).__init__()
72-
self._tr = tr
73-
74-
def __getitem__(self, idx):
75-
"""
76-
Provide indexed access, (e.g. 'cells[0]')
77-
"""
78-
try:
79-
tc = self._tr.tc_lst[idx]
80-
except IndexError:
81-
msg = "cell index [%d] is out of range" % idx
82-
raise IndexError(msg)
83-
return _Cell(tc)
84-
85-
def __iter__(self):
86-
return (_Cell(tc) for tc in self._tr.tc_lst)
87-
88-
def __len__(self):
89-
return len(self._tr.tc_lst)
90-
91-
9266
class _Column(object):
9367
"""
9468
Table column
@@ -98,6 +72,13 @@ def __init__(self, gridCol):
9872
self._gridCol = gridCol
9973

10074

75+
class _ColumnCellCollection(object):
76+
"""
77+
Sequence of |_Cell| instances corresponding to the cells in a table
78+
column.
79+
"""
80+
81+
10182
class _ColumnCollection(object):
10283
"""
10384
Sequence of |_Column| instances corresponding to the columns in a table.
@@ -146,7 +127,33 @@ def cells(self):
146127
"""
147128
Sequence of |_Cell| instances corresponding to the cells in this row.
148129
"""
149-
return _CellCollection(self._tr)
130+
return _RowCellCollection(self._tr)
131+
132+
133+
class _RowCellCollection(object):
134+
"""
135+
Sequence of |_Cell| instances corresponding to the cells in a table row.
136+
"""
137+
def __init__(self, tr):
138+
super(_RowCellCollection, self).__init__()
139+
self._tr = tr
140+
141+
def __getitem__(self, idx):
142+
"""
143+
Provide indexed access, (e.g. 'cells[0]')
144+
"""
145+
try:
146+
tc = self._tr.tc_lst[idx]
147+
except IndexError:
148+
msg = "cell index [%d] is out of range" % idx
149+
raise IndexError(msg)
150+
return _Cell(tc)
151+
152+
def __iter__(self):
153+
return (_Cell(tc) for tc in self._tr.tc_lst)
154+
155+
def __len__(self):
156+
return len(self._tr.tc_lst)
150157

151158

152159
class _RowCollection(object):

features/steps/table.py

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@
1010

1111
from docx import Document
1212
from docx.table import (
13-
_Cell, _CellCollection, _Column, _ColumnCollection, _Row, _RowCollection
13+
_Cell, _Column, _ColumnCellCollection, _ColumnCollection, _Row,
14+
_RowCellCollection, _RowCollection
1415
)
1516

1617
from .helpers import test_docx
1718

1819

1920
# given ===================================================
2021

21-
@given('a cell collection having two cells')
22-
def given_a_cell_collection_having_two_cells(context):
22+
@given('a column cell collection having two cells')
23+
def given_a_column_cell_collection_having_two_cells(context):
2324
docx_path = test_docx('blk-containing-table')
2425
document = Document(docx_path)
25-
context.cells = document.body.tables[0].rows[0].cells
26+
context.cells = document.body.tables[0].columns[0].cells
2627

2728

2829
@given('a column collection having two columns')
@@ -32,6 +33,13 @@ def given_a_column_collection_having_two_columns(context):
3233
context.columns = document.body.tables[0].columns
3334

3435

36+
@given('a row cell collection having two cells')
37+
def given_a_row_cell_collection_having_two_cells(context):
38+
docx_path = test_docx('blk-containing-table')
39+
document = Document(docx_path)
40+
context.cells = document.body.tables[0].rows[0].cells
41+
42+
3543
@given('a row collection having two rows')
3644
def given_a_row_collection_having_two_rows(context):
3745
docx_path = test_docx('blk-containing-table')
@@ -55,6 +63,13 @@ def given_a_table_having_two_rows(context):
5563
context.table_ = document.body.tables[0]
5664

5765

66+
@given('a table column having two cells')
67+
def given_a_table_column_having_two_cells(context):
68+
docx_path = test_docx('blk-containing-table')
69+
document = Document(docx_path)
70+
context.column = document.body.tables[0].columns[0]
71+
72+
5873
@given('a table row having two cells')
5974
def given_a_table_row_having_two_cells(context):
6075
docx_path = test_docx('blk-containing-table')
@@ -73,14 +88,6 @@ def then_can_access_cell_using_its_row_and_col_indices(context):
7388
assert isinstance(cell, _Cell)
7489

7590

76-
@then('I can access a collection cell by index')
77-
def then_can_access_collection_cell_by_index(context):
78-
cells = context.cells
79-
for idx in range(2):
80-
cell = cells[idx]
81-
assert isinstance(cell, _Cell)
82-
83-
8491
@then('I can access a collection column by index')
8592
def then_can_access_collection_column_by_index(context):
8693
columns = context.columns
@@ -97,11 +104,34 @@ def then_can_access_collection_row_by_index(context):
97104
assert isinstance(row, _Row)
98105

99106

107+
@then('I can access a column cell by index')
108+
def then_can_access_column_cell_by_index(context):
109+
cells = context.cells
110+
for idx in range(2):
111+
cell = cells[idx]
112+
assert isinstance(cell, _Cell)
113+
114+
115+
@then('I can access a row cell by index')
116+
def then_can_access_row_cell_by_index(context):
117+
cells = context.cells
118+
for idx in range(2):
119+
cell = cells[idx]
120+
assert isinstance(cell, _Cell)
121+
122+
123+
@then('I can access the cell collection of the column')
124+
def then_can_access_cell_collection_of_column(context):
125+
column = context.column
126+
cells = column.cells
127+
assert isinstance(cells, _ColumnCellCollection)
128+
129+
100130
@then('I can access the cell collection of the row')
101131
def then_can_access_cell_collection_of_row(context):
102132
row = context.row
103133
cells = row.cells
104-
assert isinstance(cells, _CellCollection)
134+
assert isinstance(cells, _RowCellCollection)
105135

106136

107137
@then('I can access the column collection of the table')
@@ -118,15 +148,22 @@ def then_can_access_row_collection_of_table(context):
118148
assert isinstance(rows, _RowCollection)
119149

120150

121-
@then('I can get the length of the cell collection')
122-
def then_can_get_length_of_cell_collection(context):
151+
@then('I can get the length of the column cell collection')
152+
def then_can_get_length_of_column_cell_collection(context):
153+
column = context.column
154+
cells = column.cells
155+
assert len(cells) == 2
156+
157+
158+
@then('I can get the length of the row cell collection')
159+
def then_can_get_length_of_row_cell_collection(context):
123160
row = context.row
124161
cells = row.cells
125162
assert len(cells) == 2
126163

127164

128-
@then('I can iterate over the cell collection')
129-
def then_can_iterate_over_cell_collection(context):
165+
@then('I can iterate over the column cells')
166+
def then_can_iterate_over_the_column_cells(context):
130167
cells = context.cells
131168
actual_count = 0
132169
for cell in cells:
@@ -145,6 +182,16 @@ def then_can_iterate_over_column_collection(context):
145182
assert actual_count == 2
146183

147184

185+
@then('I can iterate over the row cells')
186+
def then_can_iterate_over_the_row_cells(context):
187+
cells = context.cells
188+
actual_count = 0
189+
for cell in cells:
190+
actual_count += 1
191+
assert isinstance(cell, _Cell)
192+
assert actual_count == 2
193+
194+
148195
@then('I can iterate over the row collection')
149196
def then_can_iterate_over_row_collection(context):
150197
rows = context.rows

features/tbl-item-access.feature

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,27 @@ Feature: Access table rows, columns, and cells
2323
Then I can iterate over the column collection
2424
And I can access a collection column by index
2525

26+
@wip
27+
Scenario: Access cell collection of table column
28+
Given a table column having two cells
29+
Then I can access the cell collection of the column
30+
And I can get the length of the column cell collection
31+
2632
Scenario: Access cell collection of table row
2733
Given a table row having two cells
2834
Then I can access the cell collection of the row
29-
And I can get the length of the cell collection
35+
And I can get the length of the row cell collection
36+
37+
@wip
38+
Scenario: Access cell in column cell collection
39+
Given a column cell collection having two cells
40+
Then I can iterate over the column cells
41+
And I can access a column cell by index
3042

31-
Scenario: Access cell in cell collection
32-
Given a cell collection having two cells
33-
Then I can iterate over the cell collection
34-
And I can access a collection cell by index
43+
Scenario: Access cell in row cell collection
44+
Given a row cell collection having two cells
45+
Then I can iterate over the row cells
46+
And I can access a row cell by index
3547

3648
Scenario: Access cell in table
3749
Given a table having two rows

tests/test_table.py

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import pytest
1010

1111
from docx.table import (
12-
_Cell, _CellCollection, _Column, _ColumnCollection, _Row, _RowCollection,
13-
Table
12+
_Cell, _Column, _ColumnCollection, _Row, _RowCellCollection,
13+
_RowCollection, Table
1414
)
1515

1616
from .oxml.unitdata.table import a_gridCol, a_tbl, a_tblGrid, a_tc, a_tr
@@ -71,48 +71,6 @@ def table(self):
7171
return table
7272

7373

74-
class Describe_CellCollection(object):
75-
76-
def it_can_iterate_over_its__Cell_instances(self, cell_count_fixture):
77-
cells, cell_count = cell_count_fixture
78-
actual_count = 0
79-
for cell in cells:
80-
assert isinstance(cell, _Cell)
81-
actual_count += 1
82-
assert actual_count == cell_count
83-
84-
def it_knows_how_many_cells_it_contains(self, cell_count_fixture):
85-
cells, cell_count = cell_count_fixture
86-
assert len(cells) == cell_count
87-
88-
def it_provides_indexed_access_to_cells(self, cell_count_fixture):
89-
cells, cell_count = cell_count_fixture
90-
for idx in range(-cell_count, cell_count):
91-
cell = cells[idx]
92-
assert isinstance(cell, _Cell)
93-
94-
def it_raises_on_indexed_access_out_of_range(self, cell_count_fixture):
95-
cells, cell_count = cell_count_fixture
96-
too_low = -1 - cell_count
97-
too_high = cell_count
98-
with pytest.raises(IndexError):
99-
cells[too_low]
100-
with pytest.raises(IndexError):
101-
cells[too_high]
102-
103-
# fixtures -------------------------------------------------------
104-
105-
@pytest.fixture
106-
def cell_count_fixture(self):
107-
cell_count = 2
108-
tr_bldr = a_tr().with_nsdecls()
109-
for idx in range(cell_count):
110-
tr_bldr.with_child(a_tc())
111-
tr = tr_bldr.element
112-
cells = _CellCollection(tr)
113-
return cells, cell_count
114-
115-
11674
class Describe_ColumnCollection(object):
11775

11876
def it_knows_how_many_columns_it_contains(self, columns_fixture):
@@ -157,7 +115,7 @@ class Describe_Row(object):
157115
def it_provides_access_to_the_row_cells(self, cells_access_fixture):
158116
row = cells_access_fixture
159117
cells = row.cells
160-
assert isinstance(cells, _CellCollection)
118+
assert isinstance(cells, _RowCellCollection)
161119

162120
# fixtures -------------------------------------------------------
163121

@@ -168,6 +126,48 @@ def cells_access_fixture(self):
168126
return row
169127

170128

129+
class Describe_RowCellCollection(object):
130+
131+
def it_can_iterate_over_its__Cell_instances(self, cell_count_fixture):
132+
cells, cell_count = cell_count_fixture
133+
actual_count = 0
134+
for cell in cells:
135+
assert isinstance(cell, _Cell)
136+
actual_count += 1
137+
assert actual_count == cell_count
138+
139+
def it_knows_how_many_cells_it_contains(self, cell_count_fixture):
140+
cells, cell_count = cell_count_fixture
141+
assert len(cells) == cell_count
142+
143+
def it_provides_indexed_access_to_cells(self, cell_count_fixture):
144+
cells, cell_count = cell_count_fixture
145+
for idx in range(-cell_count, cell_count):
146+
cell = cells[idx]
147+
assert isinstance(cell, _Cell)
148+
149+
def it_raises_on_indexed_access_out_of_range(self, cell_count_fixture):
150+
cells, cell_count = cell_count_fixture
151+
too_low = -1 - cell_count
152+
too_high = cell_count
153+
with pytest.raises(IndexError):
154+
cells[too_low]
155+
with pytest.raises(IndexError):
156+
cells[too_high]
157+
158+
# fixtures -------------------------------------------------------
159+
160+
@pytest.fixture
161+
def cell_count_fixture(self):
162+
cell_count = 2
163+
tr_bldr = a_tr().with_nsdecls()
164+
for idx in range(cell_count):
165+
tr_bldr.with_child(a_tc())
166+
tr = tr_bldr.element
167+
cells = _RowCellCollection(tr)
168+
return cells, cell_count
169+
170+
171171
class Describe_RowCollection(object):
172172

173173
def it_knows_how_many_rows_it_contains(self, rows_fixture):

0 commit comments

Comments
 (0)