petl v1.7.28 - XLSX Range Rows and Numeric Standardize


petl v1.7.28 was published on 29 September 2026. The change that alters extracted data is the XLSX range fix, and fromxlsx with range_string now returns rows for a single cell, a whole row, or a whole column, including on a read only worksheet. The same tag adds standardize for numeric fields, two substring converters, and a see() fix that keeps blanks on short rows in the right column.

The full release notes and downloads are on the GitHub release page. The commit range from v1.7.27 is the full changelog.

fromxlsx(range_string=...) trusted Worksheet.__getitem__, which does not return one shape. B2 comes back as a cell. 1:1 and A:A come back as flat tuples. A:B comes back as columns. The adapter treated all of those as rows, so the first three raised TypeError and A:B loaded as columns. Read only worksheets have no iter_cols, so a whole column failed there too.

Pull request 724 parses the range with openpyxl.utils.cell.range_boundaries and passes numeric bounds to ws.iter_rows in both modes. range_string still overrides explicit numeric bounds. An empty range is rejected instead of selecting the sheet. A row zero reference is rejected because iter_rows treats zero as an unspecified bound. range_string is not passed into iter_rows. openpyxl stays optional.

A pipeline that transposed A:B to undo the old column layout will now transpose twice.

Pull request 723 adds standardize for numeric fields. Each field becomes a z score, mean 0 and standard deviation 1. Call petl.standardize(...) or Table.standardize(...). Fields are overwritten unless newfields appends scaled values under new names. ddof selects population or sample standard deviation.

petl/transform/standardize.py materializes every source row before it computes the mean and standard deviation. A one shot iterator works, and the upstream extract is not read twice. The table sits in memory. This release does not spool rows to disk.

Decimal is numeric. Large values are not collapsed by an early cast to float. NaN and infinity stay out of the mean and are written back unchanged. A missing cell on a short row does not raise IndexError. A constant finite field does not raise. Other non numeric cells pass through. The code still runs on Python 2.7 and 3.6.

petl/util/misc.py gains substringbefore and substringafter in pull request 722. Both are exported on the public petl API. Each factory calls partition and keeps the text before or after the first delimiter only. Tests cover a missing delimiter, a leading delimiter, a repeated delimiter, and bytes.

Pull request 726 fixes see() on short rows. See.__repr__ in petl/util/vis.py stores present cells under str(i), the column index. The IndexError branch stored the blank under str(f), the field name. For [('foo', 'bar'), ('a',), ('b', 2)] the old text dropped the hole.

foo: 'a', 'b'
bar: 2

The same table now keeps the blank in place.

foo: 'a', 'b'
bar: , 2

Fields named foo and 0 used to put that blank in the wrong column, because an older duplicate header fix left this branch keyed by name. Source rows do not change. A diff of see() text will fail on short rows.

Pull request 725 is documentation. Runtime behavior is the same. docs/config.rst was an automodule page, so it described failonerror and skipped the other 12 assignments in petl/config.py. It now documents all 13 process wide defaults, including inspection layout, limits, formatters, sort buffering, and conversion errors.

Inspection limit=0 inherits configuration, while limit=None shows every row. buffersize=None inherits configuration and does not force an in memory sort. Explicit failonerror=False overrides a global True. Views capture those defaults at construction. HTML rendering reads them at render time. The values are process wide. No new key was added.

Pull request 727 stays in the test suite. Sandbox containers go to docker-compose-shell.yml. Databases and remote file services stay in docker-compose.yml. tox.ini starts Compose with --wait and quotes PETL_TEST_SMB and PETL_TEST_SFTP. SQLite teardown in petl/test/io/test_db.py drops iterators before it closes the connection. Database tests skip when the driver or server is absent. None of this changes the petl API.

No function was removed. Two caller visible behaviors change.

fromxlsx on B2, 1:1, and A:A used to raise TypeError and now yields rows. A:B yields rows rather than columns, so drop a transpose that only undid the old layout. An empty range_string and a row zero reference are rejected instead of selecting the sheet.

see() prints a blank in the missing column of a short row. The table itself is unchanged. standardize is new and holds the full table in memory before the first output row.