'NoneType' object has no attribute 'parent' accessing the Fill property of an empty cell

Issue #625 resolved
Julian Trevino Mondragon
created an issue

Preconditions

Create an empty workbook and add only content to cell B2.

Steps to reproduce

  1. Open the workbook from precondition
  2. Attempt to get the fill property of a cell obtained by iterating over rows from workbook.rows.

Sample snippet showing the failure,

from openpyxl import load_workbook

wb = load_workbook("test_file.xlsx", read_only=True)
ws = wb.get_active_sheet()

for ri, row in enumerate(ws.rows):
    for rc, cell in enumerate(row):
        col = chr(rc + ord('A'))
        try:
            print "%s, %d - %s" % (col, ri + 1, cell.fill)
        except Exception as err:
            print "%s, %d - ERROR %s" % (col, ri + 1, err)

Observed results

Attemps to obtain the fill properties of cells A1, A2, B1 faill with error:

  File "C:\Python27\lib\site-packages\openpyxl\cell\read_only.py", line 82, in fill
    _id = self.style_array.fillId
  File "C:\Python27\lib\site-packages\openpyxl\cell\read_only.py", line 65, in style_array
    return self.parent.parent._cell_styles[self._style_id]
AttributeError: 'NoneType' object has no attribute 'parent'

Expected results

I presume that such cells should, at least, return a None value instead of raising an exception.

Comments (10)

  1. Charlie Clark

    You can avoid this by checking whether a cell is styled at all: cell.has_style which I think is reasonable for this kind of optimisation.

    PS. why are you calculating column names manually?

  2. Charlie Clark

    Actually, the issue here is with cells that do not physically exist in the worksheet source but are created by openpyxl to fit any gaps. These are currently implemented as orphan ReadOnlyCells with no parent worksheet. You would need to check whether cell.parent is None to identify these. I'm not sure whether the API should be any richer here.

  3. godmar

    I am doing:

        for idx, row in enumerate(ws.rows):
            # skip rows in which all cells are empty
            if all(x == EMPTY_CELL for x in row):
                continue
    

    and in the eq method I get:

      File "/var/www/services/albatross/scripts/consolidated_lib.py", line 81, in <genexpr>
        if all(x == EMPTY_CELL for x in row):
      File "/var/www/services/albatross/venv/local/lib/python2.7/site-packages/openpyxl/cell/read_only.py", line 41, in __eq__
        if getattr(self, a) != getattr(other, a):
    AttributeError: 'EmptyCell' object has no attribute 'parent'
    

    Adding 'parent = None' to the patch in cset 3fe257cf1304 corrects the problem for me.

  4. godmar

    Ok. You mean id(x) == id(EMPTY_CELL)? That works.

    Will have to change 2 dozens occurrences though since somehow this must have worked in some older version of openpyxl (I just pip installed the latest and saw it break my existing code).

  5. Charlie Clark

    Well, EMPTY_CELL is basically a singleton so x is EMPTY_CELL should work. But your approach is focussed on implementation. It's always better to check the value, which is why EMPTY_CELL has the attributes it does.

  6. Log in to comment