xlutils documentation¶
This package provides a collection of utilities for working with Excel
files. Since these utilities may require either or both of the xlrd
and xlwt
packages, they are collected together here, separate from either
package. The utilities are grouped into several modules within the package, each
of them is documented below:
- xlutils.copy
- Tools for copying
xlrd.Book
objects toxlwt.Workbook
objects. - xlutils.display
- Utility functions for displaying information about
xlrd
-related objects in a user-friendly and safe fashion. - xlutils.filter
- A mini framework for splitting and filtering existing Excel files into new Excel files.
- xlutils.margins
- Tools for finding how much of an Excel file contains useful data.
- xlutils.save
- Tools for serializing
xlrd.Book
objects back to Excel files. - xlutils.styles
- Tools for working with formatting information expressed the styles found in Excel files.
- xlutils.view
- Easy to use views of the data contained in a workbook’s sheets.
xlutils.copy¶
The function in this module copies xlrd.Book
objects into
xlwt.Workbook
objects so they can be manipulated.
You may wish to do this, for example, if you have an existing excel
file where you want to change some cells.
You would start by opening the file with xlrd
:
>>> from os.path import join
>>> from xlrd import open_workbook
>>> rb = open_workbook(join(test_files,'testall.xls'), formatting_info=True, on_demand=True)
>>> rb.sheet_by_index(0).cell(0,0).value
u'R0C0'
>>> rb.sheet_by_index(0).cell(0,1).value
u'R0C1'
You would then use xlutils.copy
to copy the xlrd.Book
object into an xlwt.Workbook
object:
>>> from xlutils.copy import copy
>>> wb = copy(rb)
Now that you have an xlwt.Workbook
, you can modify cells and
then save the changed workbook back to a file:
>>> wb.get_sheet(0).write(0,0,'changed!')
>>> wb.save(join(temp_dir.path,'output.xls'))
>>> temp_dir.listdir()
output.xls
This file can now be loaded using xlrd
to see the changes:
>>> rb = open_workbook(join(temp_dir.path,'output.xls'))
>>> rb.sheet_by_index(0).cell(0,0).value
u'changed!'
>>> rb.sheet_by_index(0).cell(0,1).value
u'R0C1'
Note
You should always pass on_demand=True to open_workbook()
as this
uses much less memory!
xlutils.display¶
This module contains the quoted_sheet_name()
and
cell_display()
functions that allow easy and safe display of
information returned by xlrd
.
The API Reference contains examples of their use.
xlutils.filter¶
This framework is designed to filter and split Excel files using a
series of modular readers, filters, and writers all tied together by
the process()
function. These components are
described below, followed by documentation for the currently available
readers, filters and writers. Right at the bottom is an example that
puts all the various pieces together.
Readers¶
Readers are components that get data from a source and parse it into a
series of xlrd.Book
objects before making calls to the appropriate
methods on the first filter in the chain.
Because it is usually only the source of the data to be processed that changes, a handy base class is provided for readers.
Here’s an example reader that uses this base class to process the file it is initialised with:
>>> import os
>>> from xlrd import open_workbook
>>> from xlutils.filter import BaseReader
>>> class MyReader(BaseReader):
...
... def __init__(self, filename):
... self.filename = filename
...
... def get_filepaths(self):
... return (os.path.join(test_files, self.filename),)
If you need to create a more unorthodox reader, such as one that reads
its data from a network socket or that needs to pass special
parameters to xlrd.open_workbook()
, then read the implementation of
BaseReader
.
Filters¶
Filters are the important bits of this framework. They are built up in chains to achieve the results required for a particular task. A filter must define certain methods, the full set of these is shown in the example below. The implementation of these methods can do whatever the filter requires, but generally they end up calling the appropriate methods on the next filter.
Here’s an example filter that does nothing but print messages when its methods are called and then call the next filter in the chain:
>>> from __future__ import print_function
>>> class MyFilter:
...
... def __init__(self,name):
... self.name = name
...
... def start(self):
... print(self.name, 'start')
... self.next.start()
...
... def workbook(self,rdbook,wtbook_name):
... print(self.name, 'workbook', rdbook, wtbook_name)
... self.next.workbook(rdbook, wtbook_name)
...
... def sheet(self,rdsheet,wtsheet_name):
... print(self.name, 'sheet', rdsheet, wtsheet_name)
... self.next.sheet(rdsheet, wtsheet_name)
...
... def set_rdsheet(self,rdsheet):
... print(self.name, 'set_rdsheet', rdsheet)
... self.next.sheet(rdsheet, wtsheet_name)
...
... def row(self,rdrowx,wtrowx):
... print(self.name, 'row', rdrowx,wtrowx)
... self.next.row(rdrowx, wtrowx)
...
... def cell(self,rdrowx,rdcolx,wtrowx,wtcolx):
... print(self.name, 'cell', rdrowx, rdcolx, wtrowx, wtcolx)
... self.next.cell(rdrowx, rdcolx, wtrowx, wtcolx)
...
... def finish(self):
... print(self.name, 'finish')
... self.next.finish()
For full details of when each of these methods are called, see the
AP documentation for BaseFilterInterface
.
Writers¶
Writers are components that get handle calls from the appropriate methods on the last filter in the chain. It is the writer that actually does the work of copying all the information from the appropriate sources and writing them to the output files.
Because there is a lot of work involved in this and it is only usually the method of writing the binary data to its destination that differs, a handy base class is provided for writers.
Here’s an example writer that just writes the data to a temporary directory:
>>> from xlutils.filter import BaseWriter
>>> our_temp_dir = TempDirectory().path
>>> class MyWriter(BaseWriter):
...
... def get_stream(self,filename):
... return open(os.path.join(our_temp_dir, filename), 'wb')
Available Readers¶
Several reader implementations are included that cover many common use cases:
GlobReader¶
If you’re processing files that are on disk, then this is probably the reader for you. It returns all files matching the path specification it’s created with. Here’s an example:
>>> from xlutils.filter import GlobReader
>>> r = GlobReader(os.path.join(test_files,'test*.xls'))
>>> sorted([p[len(test_files)+1:] for p in r.get_filepaths()])
['test.xls', 'testall.xls', 'testnoformatting.xls']
All the other functionality is provided by BaseReader
:
>>> isinstance(r,BaseReader)
True
XLRDReader¶
If you want to “save” an xlrd.Book
object which you’ve already
created in some other way:
>>> from xlrd import open_workbook
>>> wb = open_workbook(os.path.join(test_files, 'testall.xls'))
Then the XLRDReader
is likely what you’re after:
>>> from xlutils.filter import XLRDReader
>>> r = XLRDReader(wb, 'foo.xls')
Note
You must supply a filename as shown above as the original
filename is not stored in the xlrd.Book
object
Most functionality is provided by BaseReader
:
>>> isinstance(r, BaseReader)
True
However, its get_workbooks()
method just returns the
values it was instantiated with:
>>> tuple(r.get_workbooks())
((<xlrd...Book object at ...>, 'foo.xls'),)
>>> tuple(r.get_workbooks())[0][0] is wb
True
To show it working, here we send the book straight to a writer:
>>> from xlutils.filter import DirectoryWriter, process
>>> os.listdir(temp_dir)
[]
>>> process(r,DirectoryWriter(temp_dir))
>>> os.listdir(temp_dir)
['foo.xls']
TestReader¶
This reader is specifically designed for testing filter implementations with known sets of cells. This example should give a good idea of how to use it:
>>> from mock import Mock
>>> from pprint import pprint
>>> from xlutils.tests.test_filter import TestReader
>>> from xlrd import XL_CELL_NUMBER
>>> r = TestReader(
... ('Sheet1',(('R0C0','R0C1'),
... ('R1C0','R1C1'))),
... ('Sheet2',(((XL_CELL_NUMBER,0.0),),),)
... )
>>> c = Mock()
>>> r(c)
>>> pprint(c.method_calls)
[call.start(),
call.workbook(<xlutils.tests.fixtures.DummyBook...>, 'test.xls'),
call.sheet(<xlrd.sheet.Sheet...>, 'Sheet1'),
call.row(0, 0),
call.cell(0, 0, 0, 0),
call.cell(0, 1, 0, 1),
call.row(1, 1),
call.cell(1, 0, 1, 0),
call.cell(1, 1, 1, 1),
call.sheet(<xlrd.sheet.Sheet...>, 'Sheet2'),
call.row(0, 0),
call.cell(0, 0, 0, 0),
call.finish()]
Available Writers¶
Several writer implementations are included that cover many common use cases:
DirectoryWriter¶
If you’re processing files that are on disk, then this is probably the writer for you. It stores files in the directory passed to it during creation. Here’s an example:
>>> from xlutils.filter import DirectoryWriter
>>> temp_dir = TempDirectory().path
>>> w = DirectoryWriter(temp_dir)
Most of the functionality is provided by BaseWriter
:
>>> isinstance(w, BaseWriter)
True
The get_stream()
method makes sure the files end up in the
directory specified:
>>> os.listdir(temp_dir)
[]
>>> f = w.get_stream('test.xls')
>>> _ = f.write(b'some \r\n data')
>>> f.close()
>>> os.listdir(temp_dir)
['test.xls']
>>> open(os.path.join(temp_dir,'test.xls'),'rb').read()
b'some \r\n data'
StreamWriter¶
If you want to write exactly one workbook to a stream, then this is the writer for you:
>>> from tempfile import TemporaryFile
>>> from xlutils.filter import StreamWriter
>>> tf = TemporaryFile()
>>> w = StreamWriter(tf)
Most of the functionality is provided by BaseWriter
:
>>> isinstance(w, BaseWriter)
True
The get_stream()
method makes sure the excel data is written to
the stream provided:
>>> f = w.get_stream('test.xls')
>>> _ = f.write(b'xls data')
>>> _ = tf.seek(0)
>>> tf.read()
b'xls data'
Note
Only one file may be written to a StreamWriter
,
further attempts will result in an exception being raised:
>>> w.get_stream('test2.xls')
Traceback (most recent call last):
...
Exception: Attempt to write more than one workbook
StreamWriter
also doesn’t close any streams passed to it:
>>> tf = TemporaryFile()
>>> process(TestReader(('Sheet1',[['R0C0']])),StreamWriter(tf))
>>> _ = tf.seek(0)
>>> len(tf.read())
5632
XLWTWriter¶
If you want to change cells after the filtering process is complete
then an XLWTWriter
can be used to obtain the xlwt.Workbook
objects that result:
>>> from xlutils.filter import XLWTWriter
>>> w = XLWTWriter()
>>> process(TestReader(('Sheet1',[['R0C0']])),w)
The objects can then be manipulated and saved as required:
>>> w.output
[('test.xls', <xlwt.Workbook.Workbook object at ...>)]
>>> book = w.output[0][1]
>>> book.get_sheet(0).write(0,1,'R0C1')
>>> temp_dir = TempDirectory()
>>> temp_dir.listdir()
No files or directories found.
>>> book.save(os.path.join(temp_dir.path,w.output[0][0]))
>>> temp_dir.listdir()
test.xls
As with previous writers, most of the functionality is provided by
BaseWriter
:
>>> isinstance(w, BaseWriter)
True
Available Filters¶
A selection of filters are included as described below:
BaseFilter¶
This is a “do nothing” filter that makes a great base class for your own filters. All the required methods are implemented such that they just call the same method on the next filter in the chain.
ColumnTrimmer¶
This filter will strip columns containing no useful data from the end of sheets. For example:
>>> from xlutils.filter import process, ColumnTrimmer
>>> r = TestReader(
... ('Sheet1',(('','',''),
... ('R1C0','',''),
... ('R2C0','R2C1',' \t\r\n'))),
... ('Sheet2',(('R0C0',),),),
... ('Sheet3',(('R0C0','',(XL_CELL_NUMBER,0.0)),)),
... )
>>> c = Mock()
>>> process(r,ColumnTrimmer(),c)
>>> pprint(c.method_calls)
[call.start(),
call.workbook(<xlutils.tests.fixtures.DummyBook...>, 'test.xls'),
call.sheet(<xlrd.sheet.Sheet...>, 'Sheet1'),
call.row(0, 0),
call.row(1, 1),
call.row(2, 2),
call.cell(0, 0, 0, 0),
call.cell(0, 1, 0, 1),
call.cell(1, 0, 1, 0),
call.cell(1, 1, 1, 1),
call.cell(2, 0, 2, 0),
call.cell(2, 1, 2, 1),
call.sheet(<xlrd.sheet.Sheet...>, 'Sheet2'),
call.row(0, 0),
call.cell(0, 0, 0, 0),
call.sheet(<xlrd.sheet.Sheet...>, 'Sheet3'),
call.row(0, 0),
call.cell(0, 0, 0, 0),
call.finish()]
When sheets are trimmed, a message is also logged to aid debugging:
>>> from testfixtures import LogCapture
>>> l = LogCapture()
>>> process(r, ColumnTrimmer(), c)
>>> print(l)
xlutils.filter DEBUG
Number of columns trimmed from 3 to 2 for sheet b'Sheet1'
xlutils.filter DEBUG
Number of columns trimmed from 3 to 1 for sheet b'Sheet3'
The definition of ‘no useful data’ can also be controlled by passing
in a function that returns True
or False
for each value:
>>> def not_useful(cell):
... if not cell.value or cell.value=='junk': return True
>>> r = TestReader(
... ('Sheet1',(('R1C0','','junk'),)),
... )
>>> c = Mock()
>>> process(r, ColumnTrimmer(not_useful), c)
>>> pprint(c.method_calls)
[call.start(),
call.workbook(<xlutils.tests.fixtures.DummyBook...>, 'test.xls'),
call.sheet(<xlrd.sheet.Sheet...>, 'Sheet1'),
call.row(0, 0),
call.cell(0, 0, 0, 0),
call.finish()]
ErrorFilter¶
This filter caches all method calls in a file on disk and will only
pass them on to filters further down in the chain when its
finish
method has been called and no
error messages have been logged to the python logging framework.
Note
To be effective, this filter must be the last in the chain before the writer!
Here’s an example of how to set one up. We need to be able to see
what messages are logged, so we use a LogCapture
:
>>> h = LogCapture()
Now, we install the filter:
>>> from xlutils.filter import process,ErrorFilter
>>> f = ErrorFilter()
To show the filter in action, we need a little helper:
>>> import logging
>>> from xlutils.filter import BaseFilter
>>> class Log(BaseFilter):
... def __init__(self,level):
... self.level = level
... def workbook(self,rdbook,wtbook_name):
... if wtbook_name=='test.xls':
... logging.getLogger('theLogger').log(self.level,'a message')
... self.next.workbook(rdbook,wtbook_name)
So, when we have errors logged, no methods other than finish are passed on to the next filter:
>>> c = Mock()
>>> process(MyReader('test.xls'), Log(logging.ERROR), f, c)
>>> len(c.method_calls)
0
As well as the error message logged, we can also see the ErrorFilter
logs an error to that that the method calls have not been passed on:
>>> print(h)
theLogger ERROR
a message
xlutils.filter ERROR
No output as errors have occurred.
>>> h.clear()
This error message can be controlled when the ErrorFilter
is
instantiated:
>>> f = ErrorFilter(message='wingnuts! errors have occurred!')
>>> process(MyReader('test.xls'), Log(logging.ERROR), f, c)
>>> print(h)
theLogger ERROR
a message
xlutils.filter ERROR
wingnuts! errors have occurred!
However, when no errors are logged, all method calls are passed:
>>> c = Mock()
>>> process(MyReader('test.xls'), Log(logging.WARNING), f, c)
>>> len(c.method_calls)
17
In addition to the logging of error messages, error cells will also cause all methods to be filtered:
>>> from xlrd import XL_CELL_ERROR
>>> r = TestReader(
... (u'Price(\xa3)',(((XL_CELL_ERROR,0),),)),
... )
>>> c = Mock()
>>> h.clear()
>>> process(r,ErrorFilter(),c)
>>> len(c.method_calls)
0
>>> print(h)
xlutils.filter ERROR
Cell A1 of sheet b'Price(?)' contains a bad value: error (#NULL!)
xlutils.filter ERROR
No output as errors have occurred.
You can also configure the log level at which messages prevent the
ErrorFilter
from passing its method calls on to the next
filter in the chain:
>>> f = ErrorFilter(logging.WARNING)
Now, warnings will cause methods to not be passed on:
>>> c = Mock()
>>> process(MyReader('test.xls'),Log(logging.WARNING),f,c)
>>> len(c.method_calls)
0
But if only debug messages are logged, the method calls will still be passed on:
>>> c = Mock()
>>> process(MyReader('test.xls'),Log(logging.DEBUG),f,c)
>>> len(c.method_calls)
17
An example which may prove useful is how to set up a filter such that if any errors are logged while processing one workbook, that workbook is filtered out but other subsequent workbooks are not filtered out.
This is done by inserting a filter such as the following earlier in the chain:
>>> class BatchByWorkbook(BaseFilter):
... started = False
... def start(self): pass
... def workbook(self,rdbook,wtbook_name):
... if self.started:
... self.next.finish()
... self.next.start()
... self.next.workbook(rdbook,wtbook_name)
... self.started = True
Here it is at work, starting with an empty output directory:
>>> temp_dir = TempDirectory().path
>>> os.listdir(temp_dir)
[]
Now test.xls
, testall.xls
and testnoformatting.xls
are processed,
but errors are only logged while processing test.xls
:
>>> process(
... GlobReader(os.path.join(test_files,'test*.xls')),
... BatchByWorkbook(),
... Log(logging.ERROR),
... ErrorFilter(),
... DirectoryWriter(temp_dir)
... )
So, the output directory contains testall.xls
, but no test.xls
:
>>> sorted(os.listdir(temp_dir))
['testall.xls', 'testnoformatting.xls']
Echo¶
This filter will print calls to the methods configured when the filter is created along with the arguments passed.
>>> from xlutils.filter import Echo, process
>>> r = TestReader(
... ('Sheet1',(('R0C0','R0C1'),
... ('R1C0','R1C1'))),
... )
>>> process(r, Echo(methods=('workbook',)), Mock())
workbook:(<...DummyBook...>, 'test.xls')
If True
is passed instead of a list of method names, then all
methods called will be printed:
>>> process(r, Echo(methods=True), Mock())
start:()
workbook:(<...DummyBook...>, 'test.xls')
sheet:(<xlrd.sheet.Sheet...>, 'Sheet1')
row:(0, 0)
cell:(0, 0, 0, 0)
cell:(0, 1, 0, 1)
row:(1, 1)
cell:(1, 0, 1, 0)
cell:(1, 1, 1, 1)
finish:()
If you need to see what’s happening at various points in a chain,
you can also give an Echo
a name:
>>> process(r,Echo('first'), Echo('second'), Mock())
'first' start:()
'second' start:()
'first' workbook:(<...DummyBook...>, 'test.xls')
'second' workbook:(<...DummyBook...>, 'test.xls')
'first' sheet:(<xlrd.sheet.Sheet...>, 'Sheet1')
'second' sheet:(<xlrd.sheet.Sheet...>, 'Sheet1')
'first' row:(0, 0)
'second' row:(0, 0)
'first' cell:(0, 0, 0, 0)
'second' cell:(0, 0, 0, 0)
'first' cell:(0, 1, 0, 1)
'second' cell:(0, 1, 0, 1)
'first' row:(1, 1)
'second' row:(1, 1)
'first' cell:(1, 0, 1, 0)
'second' cell:(1, 0, 1, 0)
'first' cell:(1, 1, 1, 1)
'second' cell:(1, 1, 1, 1)
'first' finish:()
'second' finish:()
MemoryLogger¶
This filter will dump stats to the path it was configured with using the heapy package if it is available. If it is not available, no operations are performed.
For example, with a MemoryLogger
configured as follows:
>>> from xlutils.filter import MemoryLogger
>>> m = MemoryLogger('/some/path', methods=('sheet','cell'))
The equivalent of the following call:
from guppy import hpy; hpy().heap().stat.dump('/some/path')
will be performed whenever the MemoryLogger
’s
sheet()
and cell()
methods are
called.
A MemoryLogger
configured as followed will log memory usage
whenever any of the MemoryLogger
’s methods are called:
>>> m = MemoryLogger('/some/path',True)
For more information on heapy, please see:
Example Usage¶
Here’s an example that makes use of all the types of components described above to filter out odd numbered rows from an original workbook’s sheets. To do this we need one more filter:
>>> from xlutils.filter import BaseFilter
>>> class EvenFilter(BaseFilter):
...
... def row(self,rdrowx,wtrowx):
... if not rdrowx%2:
... self.next.row(rdrowx,wtrowx)
...
... def cell(self,rdrowx,rdcolx,wtrowx,wtcolx):
... if not rdrowx%2:
... self.next.cell(rdrowx,rdcolx,wtrowx,wtcolx)
Now we can put it all together with a call to the process()
function:
>>> from xlutils.filter import process
>>> process(
... MyReader('test.xls'),
... MyFilter('before'),
... EvenFilter(),
... MyFilter('after'),
... MyWriter()
... )
before start
after start
before workbook <xlrd...Book object at ...> test.xls
after workbook <xlrd...Book object at ...> test.xls
before sheet <xlrd.sheet.Sheet object at ...> Sheet1
after sheet <xlrd.sheet.Sheet object at ...> Sheet1
before row 0 0
after row 0 0
before cell 0 0 0 0
after cell 0 0 0 0
before cell 0 1 0 1
after cell 0 1 0 1
before row 1 1
before cell 1 0 1 0
before cell 1 1 1 1
before sheet <xlrd.sheet.Sheet object at ...> Sheet2
after sheet <xlrd.sheet.Sheet object at ...> Sheet2
before row 0 0
after row 0 0
before cell 0 0 0 0
after cell 0 0 0 0
before cell 0 1 0 1
after cell 0 1 0 1
before row 1 1
before cell 1 0 1 0
before cell 1 1 1 1
before finish
after finish
As you can see if you’ve read this far, there’s quite a lot of output, but it’s certainly informative! However, just to be on the safe side, we can see that the output file was actually written:
>>> os.listdir(our_temp_dir)
['test.xls']
xlutils.margins¶
This combined module and script provide information on how much space
is taken up in an Excel file by cells containing no meaningful data.
If xlutils
is installed using easy_install
, pip
or
zc.buildout
, a console script called margins
will be created.
The following example shows how it is used as a script:
python -m xlutils.margins [options] *.xls
To get a list of the available options, do the following:
python -m xlutils.margins --help
The module also provides the tools that do the work for the above
script as the helpful ispunc()
, cells_all_junk()
,
number_of_good_rows()
and number_of_good_cols()
functions.
See the API Reference for more information.
xlutils.save¶
This allows serialisation of xlrd.Book
objects back into binary Excel files.
Here’s a simple example:
>>> import os
>>> from xlrd import open_workbook
>>> from xlutils.save import save
>>> wb = open_workbook(os.path.join(test_files,'testall.xls'))
>>> os.listdir(temp_dir)
[]
>>> save(wb,os.path.join(temp_dir,'saved.xls'))
>>> os.listdir(temp_dir)
['saved.xls']
You can also save the data to a stream that you provide:
>>> from xlutils.compat import BytesIO
>>> s = BytesIO()
>>> save(wb,s)
>>> len(s.getvalue())
5632
xlutils.styles¶
This module provides tools for working with formatting information
provided by xlrd
relating and expressed in the Excel file as styles.
To use these tools, you need to open the workbook with xlrd
and make
sure formatting is enabled:
>>> import os
>>> from xlrd import open_workbook
>>> book = open_workbook(os.path.join(test_files,'testall.xls'), formatting_info=1)
Once you have a Book
object, you can extract the relevent style
information from it as follows:
>>> from xlutils.styles import Styles
>>> s = Styles(book)
You can now look up style information about any cell:
>>> sheet = book.sheet_by_name('Sheet1')
>>> s[sheet.cell(0,0)]
<xlutils.styles.NamedStyle ...>
Note
This is not a suitable object for copying styles to a new
spreadsheet using xlwt
. If that is your intention, you’re
recommended to look at xlutils.save or xlutils.filter.
If you open up testall.xls
in Microsoft’s Excel Viewer or other
suitable software, you’ll see that the following information is
correct for cell A1:
>>> A1_style = s[sheet.cell(0,0)]
>>> A1_style.name
u'Style1'
While that may be interesting, the actual style information is locked
away in an XF
record. Thankfully, a NamedStyle
provides
easy access to this:
>>> A1_xf = A1_style.xf
>>> A1_xf
<xlrd.formatting.XF ...>
Once we have the XF record, for this particular cell, most of the interesting information is in the font definition for the style:
>>> A1_font = book.font_list[A1_xf.font_index]
Using the book’s colour map, you can get the RGB colour for this style, which is blue in this case:
>>> book.colour_map[A1_font.colour_index]
(0, 0, 128)
You can also see that this style specifies text should be underlined with a single line:
>>> A1_font.underline_type
1
Finally, the style specifies that text is not displayed with a “strike through” line:
>>> A1_font.struck_out
0
For completeness, here’s the same information but for cell B1:
>>> B1_style = s[sheet.cell(0,1)]
>>> B1_style.name
u'Style2'
>>> B1_font = book.font_list[B1_style.xf.font_index]
In this case, though, the style’s colour is green:
>>> book.colour_map[B1_font.colour_index]
(0, 128, 0)
The style specifies that text should not be underlined:
>>> B1_font.underline_type
0
And finally, it specifies that text should be displayed with a “strike through” line:
>>> B1_font.struck_out
1
xlutils.view¶
Iterating over the cells in a Sheet
can be
tricky, especially if you want to exclude headers and the like.
This module is designed to make things easier.
For example, to iterate over the cells in the first sheet of a workbook:
>>> def print_data(rows):
... for row in rows:
... for value in row:
... print(value, end=' ')
... print()
>>> from os.path import join
>>> from xlutils.view import View
>>> view = View(join(test_files,'testall.xls'))
>>> print_data(view[0])
R0C0 R0C1
R1C0 R1C1
A merged cell
More merged cells
You can also get a sheet by name:
>>> print_data(view['Sheet2'])
R0C0 R0C1
R1C0 R1C1
One helpful feature is that dates are converted to
datetime
objects rather than being left as numbers:
>>> for row in View(join(test_files,'datetime.xls'))[0]:
... for value in row:
... print(repr(value))
datetime.datetime(2012, 4, 13, 0, 0)
datetime.time(12, 54, 37)
datetime.datetime(2014, 2, 14, 4, 56, 23)
Now, things get really interesting when you start slicing the view of a sheet:
>>> print_data(view['Sheet1'][:2, :1])
R0C0
R1C0
As you can see, these behave exactly as slices into lists would, with the first slice being on rows and the second slice being on columns.
Since looking at a sheet and working with the row and column labels
shown is much easier, Row
and Col
helpers are
provided. When these are used for the stop
part of a slice, they
are inclusive. For example:
>>> from xlutils.view import Row, Col
>>> print_data(view['Sheet1'][Row(1):Row(2), Col('A'):Col('B')])
R0C0 R0C1
R1C0 R1C1
Finally, to aid with automated tests, there is a CheckerView
subclass of View
that provides CheckSheet
views onto
sheets in a workbook. These have a compare()
method
that produces informative AssertionError
exceptions when the
data in the view of the sheet is not as expected:
>>> from xlutils.view import CheckerView
>>> sheet_view = CheckerView(join(test_files,'testall.xls'))[0]
>>> sheet_view[:, Col('A'):Col('A')].compare(
... (u'R0C0', ),
... (u'R0C1', ),
... )
Traceback (most recent call last):
...
AssertionError: sequence not as expected:
same:
((u'R0C0',),)
expected:
((u'R0C1',),)
actual:
((u'R1C0',), (u'A merged cell',), (u'',), (u'',), (u'More merged cells',))
While comparing [1]: sequence not as expected:
same:
()
expected:
(u'R0C1',)
actual:
(u'R1C0',)
While comparing [1][0]: u'R0C1' (expected) != u'R1C0' (actual)
Use of the compare()
method requires
testfixtures to be installed.
Looking at the implementation of CheckerView
will also show
you how you can wire in SheetView
subclasses to provide any
extra methods you may require.
Working with xlutils¶
The following sections describe how to install the package, contribute to its development and the usual boilerplate:
Installation Instructions¶
If you want to experiment with xlutils, the easiest way to install it is:
pip install xlutils
Or, if you’re using zc.buildout, just specify xlutils
as a
required egg.
If you do not install using easy_install or zc.buildout, you will also need to make sure the following python packages are available on your PYTHONPATH:
xlrd
You’ll need version 0.7.2 or later. Latest versions can be found here:
xlwt
You’ll need version 0.7.3 or later. Latest versions can be found here:
If you’re installing with pip, easy_install or buildout, these dependencies will automatically be installed for you.
Additionally, if you want to use an
ErrorFilter
, you should make sure the
following package is installed:
errorhandler
This can be found here:
Since this is a soft dependency, it will not be installed by automatically by pip, easy_install or buildout.
Development¶
This package is developed using continuous integration which can be found here:
https://travis-ci.org/python-excel/xlutils
If you wish to contribute to this project, then you should fork the repository found here:
https://github.com/python-excel/xlutils
Development of this package also requires local clones of both
xlrd
and xlwt
. The following example will set up local
clones as required, but you should use your own forks so that you may
push back to them:
git clone git://github.com/python-excel/xlutils.git
git clone git://github.com/python-excel/xlrd.git
git clone git://github.com/python-excel/xlwt.git
cd xlutils
Once you have an appropriate set of local repositories, you can follow these instructions to perform various development tasks:
Setting up a virtualenv¶
The recommended way to set up a development environment is to turn your checkout into a virtualenv and then install the package in editable form as follows:
$ virtualenv .
$ bin/pip install -U -e .[test,build]
You will now also need to install xlrd and xlwt into the virtualenv:
$ source bin/activate
$ cd ../xlrd
$ pip install -e .
$ cd ../xlwt
$ pip install -e .
Running the tests¶
Once you’ve set up a virtualenv, the tests can be run as follows:
$ bin/nosetests
Building the documentation¶
The Sphinx documentation is built by doing the following from the directory containg setup.py:
cd docs
make html
Making a release¶
To make a release, just update xlutils/version.txt
, update the change log,
tag it and push to https://github.com/python-excel/xlutils
and Travis CI should take care of the rest.
Once the above is done, make sure to go to https://readthedocs.org/projects/xlutils/versions/ and make sure the new release is marked as an Active Version.
API Reference¶
-
xlutils.copy.
copy
(wb)¶ Copy an
xlrd.Book
into anxlwt.Workbook
preserving as much information from the source object as possible.See the xlutils.copy documentation for an example.
-
xlutils.display.
quoted_sheet_name
(sheet_name, encoding='ascii')¶ This returns a string version of the supplied sheet name that is safe to display, including encoding the unicode sheet name into a string:
>>> from xlutils.display import quoted_sheet_name >>> quoted_sheet_name(u'Price(\xa3)','utf-8') b'Price(\xc2\xa3)'
It also quotes the sheet name if it contains spaces:
>>> quoted_sheet_name(u'My Sheet') b"'My Sheet'"
Single quotes are replaced with double quotes:
>>> quoted_sheet_name(u"John's Sheet") b"'John''s Sheet'"
-
xlutils.display.
cell_display
(cell, datemode=0, encoding='ascii')¶ This returns a string representation of the supplied cell, no matter what type of cell it is. Here’s some example output:
>>> import xlrd >>> from xlrd.sheet import Cell >>> from xlutils.display import cell_display >>> from xlutils.compat import PY3
>>> cell_display(Cell(xlrd.XL_CELL_EMPTY, '')) 'undefined'
>>> cell_display(Cell(xlrd.XL_CELL_BLANK, '')) 'blank'
>>> cell_display(Cell(xlrd.XL_CELL_NUMBER, 1.2)) 'number (1.2000)'
>>> cell_display(Cell(xlrd.XL_CELL_BOOLEAN, 0)) 'logical (FALSE)'
>>> cell_display(Cell(xlrd.XL_CELL_DATE, 36892.0)) 'date (2001-01-01 00:00:00)'
Erroneous date values will be displayed like this:
>>> cell_display(Cell(xlrd.XL_CELL_DATE, 1.5)) 'date? (1.500000)'
Note
To display dates correctly, make sure that datemode is passed and is taken from the datemode attribute of the
xlrd.Book
from which the cell originated as shown below:>>> wb = open_workbook(join(test_files,'date.xls')) >>> cell = wb.sheet_by_index(0).cell(0, 0) >>> cell_display(cell, wb.datemode) 'date (2012-04-13 00:00:00)'
If non-unicode characters are to be displayed, they will be masked out:
>>> cd = cell_display(Cell(xlrd.XL_CELL_TEXT,u'Price (\xa3)')) >>> if PY3: ... str(cd) == "text (b'Price (?)')" ... else: ... str(cd) == 'text (Price (?))' True
If you want to see these characters, specify an encoding for the output string:
>>> cd = cell_display(Cell(xlrd.XL_CELL_TEXT,u'Price (\xa3)'), encoding='utf-8') >>> if PY3: ... str(cd) == "text (b'Price (\\xc2\\xa3)')" ... else: ... str(cd) == 'text (Price (\xc2\xa3))' True
Error cells will have their textual description displayed:
>>> cell_display(Cell(xlrd.XL_CELL_ERROR, 0)) 'error (#NULL!)'
>>> cell_display(Cell(xlrd.XL_CELL_ERROR, 2000)) 'unknown error code (2000)'
If you manage to pass a cell with an unknown cell type, an exception will be raised:
>>> cell_display(Cell(69, 0)) Traceback (most recent call last): ... Exception: Unknown Cell.ctype: 69
-
class
xlutils.filter.
BaseFilter
¶ A concrete filter that implements pass-through behaviour for
BaseFilterInterface
.This often makes a great base class for your own filters.
-
class
xlutils.filter.
BaseFilterInterface
¶ This is the filter interface that shows the correct way to call the next filter in the chain. The next attribute is set up by the
process()
function. It can make a good base class for a new filter, but subclassingBaseFilter
is often a better idea!-
cell
(rdrowx, rdcolx, wtrowx, wtcolx)¶ This is called for every cell in the sheet being processed. This is the most common method in which filtering and queuing of onward calls to the next filter takes place.
Parameters: - rdrowx – the index of the row to be read from in the current sheet.
- rdcolx – the index of the column to be read from in the current sheet.
- wtrowx – the index of the row to be written to in the current output sheet.
- wtcolx – the index of the column to be written to in the current output sheet.
-
finish
()¶ This method is called once processing of all workbooks has been completed.
A filter should call this method on the next filter in the chain as an indication that no further calls will be made to any methods or that, if they are, any new calls should be treated as new batch of workbooks with no information retained from the previous batch.
-
row
(rdrowx, wtrowx)¶ This is called every time processing of a new row in the current sheet starts. It is primarily for copying row-based formatting from the source row to the target row.
Parameters: - rdrowx – the index of the row in the current sheet from which information for the row to be written should be copied.
- wtrowx – the index of the row in sheet to be written to which information should be written for the row being read.
-
set_rdsheet
(rdsheet)¶ This is only ever called by a filter that wishes to change the source of cells mid-way through writing a sheet.
Parameters: rdsheet – the Sheet
object from which cells from this point forward should be read from.
-
sheet
(rdsheet, wtsheet_name)¶ This method is called every time processing of a new sheet in the current workbook starts.
Parameters: - rdsheet – the
Sheet
object from which the new sheet should be created. - wtsheet_name – the name of the sheet into which content should be written.
- rdsheet – the
-
start
()¶ This method is called before processing of a batch of input. This allows the filter to initialise any required data structures and dispose of any existing state from previous batches.
It is called once before the processing of any workbooks by the included reader implementations.
This method can be called at any time. One common use is to reset all the filters in a chain in the event of an error during the processing of a rdbook.
Implementations of this method should be extremely robust and must ensure that they call the
start()
method of the next filter in the chain regardless of any work they do.
-
workbook
(rdbook, wtbook_name)¶ This method is called every time processing of a new workbook starts.
Parameters: - rdbook – the
Book
object from which the new workbook should be created. - wtbook_name – the name of the workbook into which content should be written.
- rdbook – the
-
-
class
xlutils.filter.
BaseReader
¶ A base reader good for subclassing.
-
__call__
(filter)¶ Once instantiated, a reader will be called and have the first filter in the chain passed to its
__call__()
method. The implementation of this method should call the appropriate methods on the filter based on the cells found in theBook
objects returned from theget_workbooks()
method.
-
get_filepaths
()¶ This is the most common method to implement. It must return an interable sequence of paths to excel files.
-
get_workbooks
()¶ If the data to be processed is not stored in files or if special parameters need to be passed to
xlrd.open_workbook()
then this method must be overriden. Any implementation must return an iterable sequence of tuples. The first element of which must be anxlrd.Book
object and the second must be the filename of the file from which the book object came.
-
-
class
xlutils.filter.
BaseWriter
¶ This is the base writer that copies all data and formatting from the specified sources. It is designed for sequential use so when, for example, writing two workbooks, the calls must be ordered as follows:
workbook()
call for first workbooksheet()
call for first sheetrow()
call for first rowcell()
call for left-most cell of first rowcell()
call for second-left-most cell of first row- …
row()
call for second row- …
sheet()
call for second sheet- …
workbook()
call for second workbook- …
finish()
call
Usually, only the
get_stream()
method needs to be implemented in subclasses.-
cell
(rdrowx, rdcolx, wtrowx, wtcolx)¶ This should be called for every cell in the sheet being processed.
Parameters: - rdrowx – the index of the row to be read from in the current sheet.
- rdcolx – the index of the column to be read from in the current sheet.
- wtrowx – the index of the row to be written to in the current output sheet.
- wtcolx – the index of the column to be written to in the current output sheet.
-
finish
()¶ This method should be called once processing of all workbooks has been completed.
-
get_stream
(filename)¶ This method is called once for each file written. The filename of the file to be created is passed and something with
write()
andclose()
methods that behave like afile
object’s must be returned.
-
row
(rdrowx, wtrowx)¶ This should be called every time processing of a new row in the current sheet starts.
Parameters: - rdrowx – the index of the row in the current sheet from which information for the row to be written will be copied.
- wtrowx – the index of the row in sheet to be written to which information will be written for the row being read.
-
set_rdsheet
(rdsheet)¶ This should only ever called by a filter that wishes to change the source of cells mid-way through writing a sheet.
Parameters: rdsheet – the Sheet
object from which cells from this point forward will be read.
-
sheet
(rdsheet, wtsheet_name)¶ This method should be called every time processing of a new sheet in the current workbook starts.
Parameters: - rdsheet – the
Sheet
object from which the new sheet will be created. - wtsheet_name – the name of the sheet into which content will be written.
- rdsheet – the
-
start
()¶ This method should be called before processing of a batch of input. This allows the filter to initialise any required data structures and dispose of any existing state from previous batches.
-
workbook
(rdbook, wtbook_name)¶ This method should be called every time processing of a new workbook starts.
Parameters: - rdbook – the
Book
object from which the new workbook will be created. - wtbook_name – the name of the workbook into which content will be written.
- rdbook – the
-
class
xlutils.filter.
ColumnTrimmer
(is_junk=None)¶ This filter will strip columns containing no useful data from the end of sheets.
See the ColumnTrimmer documentation for an example.
-
class
xlutils.filter.
DirectoryWriter
(path)¶ A writer that stores files in a filesystem directory
-
get_stream
(filename)¶ Returns a stream for the file in the configured directory with the specified name.
-
-
class
xlutils.filter.
Echo
(name=None, methods=True)¶ This filter will print calls to the methods configured when the filter is created along with the arguments passed.
For more details, see the documentation.
-
class
xlutils.filter.
ErrorFilter
(level=40, message='No output as errors have occurred.')¶ A filter that gates downstream writers or filters on whether or not any errors have occurred.
See ErrorFilter for details.
-
finish
()¶ The method that triggers downstream filters and writers if no errors have occurred.
-
-
class
xlutils.filter.
GlobReader
(spec)¶ A reader that emits events for all files that match the glob in the spec.
-
class
xlutils.filter.
MemoryLogger
(path, methods=True)¶ This filter will dump stats to the path it was configured with using the heapy package if it is available.
-
class
xlutils.filter.
MethodFilter
(methods=True)¶ This is a base class that implements functionality for filters that want to do a common task such as logging, printing or memory usage recording on certain calls configured at filter instantiation time.
Echo is an example of this.
-
method
(name, *args)¶ This is the method that needs to be implemented. It is called with the name of the method that was called on the MethodFilter and the arguments that were passed to that method.
-
-
class
xlutils.filter.
StreamWriter
(stream)¶ A writer for writing exactly one workbook to the supplied stream
-
get_stream
(filename)¶ Returns the stream passed during instantiation.
-
-
class
xlutils.filter.
XLRDReader
(wb, filename)¶ A reader that uses an in-memory
xlrd.Book
object as its source of events.-
get_workbooks
()¶ Yield the workbook passed during instantiation.
-
-
class
xlutils.filter.
XLWTWriter
¶ A writer that writes to a sequence of in-memory
xlwt.Workbook
objects.
-
xlutils.filter.
process
(reader, *chain)¶ The driver function for the
xlutils.filter
module.It takes a chain of one reader, followed by zero or more filters and ending with one writer.
All the components are chained together by the
process()
function setting theirnext
attributes appropriately. The reader is then called with the first filter in the chain.
-
xlutils.margins.
ispunc
(character)¶ This little helper function returns
True
if called with a punctuation character andFalse
with any other:>>> from xlutils.margins import ispunc >>> ispunc('u') False >>> ispunc(',') True
It also works fine with unicode characters:
>>> ispunc(u',') True >>> ispunc(u'w') False
It does not, however, return sensible answers if called with more than one character:
>>> ispunc(',,,') False
-
xlutils.margins.
cells_all_junk
(cells, is_rubbish=None)¶ Return True if all cells in the sequence are junk. What qualifies as junk: – empty cell – blank cell – zero-length text – text is all whitespace – number cell and is 0.0 – text cell and is_rubbish(cell.value) returns True.
This function returns
True
if all the cells supplied are junk:>>> from xlutils.margins import cells_all_junk >>> from xlrd.sheet import Cell,empty_cell >>> cells_all_junk([empty_cell,empty_cell,empty_cell]) True
But it returns
False
as soon as any of the cells supplied are not junk:>>> from xlrd import XL_CELL_NUMBER >>> cells_all_junk([empty_cell,Cell(XL_CELL_NUMBER,1),empty_cell]) False
The definition of ‘junk’ is as follows:
Empty cells are junk:
>>> from xlrd import XL_CELL_EMPTY >>> cells_all_junk([Cell(XL_CELL_EMPTY,'')]) True
Blank cells are junk:
>>> from xlrd import XL_CELL_BLANK >>> cells_all_junk([Cell(XL_CELL_BLANK,'')]) True
Number cells containing zero are considered junk:
>>> from xlrd import XL_CELL_NUMBER >>> cells_all_junk([Cell(XL_CELL_NUMBER,0)]) True
However, if a number cell contains anything else, it’s not junk:
>>> cells_all_junk([Cell(XL_CELL_NUMBER,1)]) False
Text cells are junk if they don’t contain anything:
>>> from xlrd import XL_CELL_TEXT >>> cells_all_junk([Cell(XL_CELL_TEXT,'')]) True
or if they contain only space characters:
>>> cells_all_junk([Cell(XL_CELL_TEXT,' \t\n\r')]) True
otherwise they aren’t considered junk:
>>> cells_all_junk([Cell(XL_CELL_TEXT,'not junk')]) False
However, you can also pass a checker function such as this one:
>>> def isrubbish(cell): return cell.value=='rubbish'
Which can then be used to check for junk conditions of your own choice:
>>> cells_all_junk([Cell(XL_CELL_TEXT,'rubbish')],isrubbish) True >>> cells_all_junk([Cell(XL_CELL_TEXT,'not rubbish')],isrubbish) False
Passing a function like this isn’t only limited to text cells:
>>> def isnegative(cell): return isinstance(cell.value,float) and cell.value<0 or False
>>> cells_all_junk([Cell(XL_CELL_NUMBER,-1.0)],isnegative) True >>> cells_all_junk([Cell(XL_CELL_NUMBER,1.0)],isnegative) False
Date, boolean, and error fields are all not considered to be junk:
>>> from xlrd import XL_CELL_DATE, XL_CELL_BOOLEAN, XL_CELL_ERROR >>> cells_all_junk([Cell(XL_CELL_DATE,'')]) False >>> cells_all_junk([Cell(XL_CELL_BOOLEAN,'')]) False >>> cells_all_junk([Cell(XL_CELL_ERROR,'')]) False
Be careful, though, as if you call
cells_all_junk()
with an empty sequence of cells, you’ll get True:>>> cells_all_junk([]) True
-
xlutils.margins.
number_of_good_rows
(sheet, checker=None, nrows=None, ncols=None)¶ Return 1 + the index of the last row with meaningful data in it.
This function returns the number of rows in a sheet that contain anything other than junk, as defined by the
cells_all_junk()
function.For example:
>>> from xlutils.tests.fixtures import make_sheet >>> sheet = make_sheet(( ... ('X',' ',' ',' ',' '), ... (' ',' ',' ','X',' '), ... (' ',' ',' ',' ',' '), ... ('X',' ',' ',' ',' '), ... (' ',' ','X',' ',' '), ... (' ',' ',' ',' ',' '), ... )) >>> from xlutils.margins import number_of_good_rows >>> number_of_good_rows(sheet) 5
You can limit the area searched using the nrows and ncols parameters:
>>> number_of_good_rows(sheet,nrows=3) 2 >>> number_of_good_rows(sheet,ncols=2) 4 >>> number_of_good_rows(sheet,ncols=3,nrows=3) 1
You can also pass a checking function through to the
cells_all_junk()
calls:>>> number_of_good_rows(sheet,checker=lambda c:c.value=='X') 0
-
xlutils.margins.
number_of_good_cols
(sheet, checker=None, nrows=None, ncols=None)¶ Return 1 + the index of the last column with meaningful data in it.
This function returns the number of columns in a sheet that contain anything other than junk, as defined by the
cells_all_junk()
function.For example:
>>> sheet = make_sheet(( ... ('X',' ',' ','X',' ',' '), ... (' ',' ',' ',' ',' ',' '), ... (' ',' ',' ',' ','X',' '), ... (' ','X',' ',' ',' ',' '), ... (' ',' ',' ',' ',' ',' '), ... (' ',' ',' ',' ',' ',' '), ... )) >>> from xlutils.margins import number_of_good_cols >>> number_of_good_cols(sheet) 5
You can limit the area searched using the nrows and ncols parameters:
>>> number_of_good_cols(sheet,nrows=2) 4 >>> number_of_good_cols(sheet,ncols=2) 2 >>> number_of_good_cols(sheet,ncols=3,nrows=3) 1
You can also pass a checking function through to the
cells_all_junk()
calls:>>> number_of_good_cols(sheet,checker=lambda c:c.value=='X') 0
-
xlutils.save.
save
(wb, filename_or_stream)¶ Save the supplied
xlrd.Book
to the supplied stream or filename.
-
class
xlutils.styles.
NamedStyle
(name, xf)¶ An object with
name
andxf
attributes representing a particular style in a workbook.
-
class
xlutils.styles.
Styles
(book)¶ A mapping-like object that will return a
NamedStyle
instance for the cell passed to the__getitem__()
method.
-
class
xlutils.view.
CheckSheet
(book, sheet, row_slice=None, col_slice=None)¶ A special sheet view for use in automated tests.
-
compare
(*expected)¶ Call to check whether this view contains the expected data. If it does not, a descriptive
AssertionError
will be raised. Requires testfixtures.Parameters: expected – tuples containing the data that should be present in this view.
-
-
class
xlutils.view.
CheckerView
(path, class_=None)¶ A special subclass of
View
for use in automated tests when you want to check the contents of a generated spreadsheet.Views of sheets are returned as
CheckSheet
instances which have a handycompare()
method.-
class_
¶ alias of
CheckSheet
-
-
class
xlutils.view.
Col
(name)¶ An end-inclusive column label index for use in slices, eg:
[:, Col('A'), Col('B')]
-
class
xlutils.view.
Row
(name)¶ A one-based, end-inclusive row index for use in slices, eg::
[Row(1):Row(2), :]
-
class
xlutils.view.
SheetView
(book, sheet, row_slice=None, col_slice=None)¶ A view on a sheet in a workbook. Should be created by indexing a
View
.These can be sliced to create smaller views.
Views can be iterated over to return a set of iterables, one for each row in the view. Data is returned as in the cell values with the exception of dates and times which are converted into
datetime
instances.-
__weakref__
¶ list of weak references to the object (if defined)
-
book
= None¶ The workbook used by this view.
-
sheet
= None¶ The sheet in the workbook used by this view.
-
-
class
xlutils.view.
View
(path, class_=None)¶ A view wrapper around a
Book
that allows for easy iteration over the data in a group of cells.Parameters: - path – The path of the .xls from which to create views.
- class – An class to use instead of
SheetView
for views of sheets.
-
__getitem__
(item)¶ Returns of a view of a sheet in the workbook this view is created for.
Parameters: item – either zero-based integer index or a sheet name.
-
__weakref__
¶ list of weak references to the object (if defined)
Changes¶
2.0.0 (9 June 2016)¶
- Updated documentation.
- Move to virtualenv/pip based development.
- Move to Read The Docs for documentation.
- Use Travis CI for testing and releases.
- Use features of newer
testfixtures
inCheckerView
. - Python 3 compatibility.
1.7.1 (25 April 2014)¶
1.7.0 (29 October 2013)¶
- Added the
xlutils.view
module.
1.6.0 (5 April 2013)¶
1.5.2 (13 April 2012)¶
- When using
xlutils.copy
, thedatemode
is now copied across from the source solving date problems with certain files. - The
errorhandler
package is no longer a hard dependency. - As a minimum,
xlrd
0.7.2 andxlwt
0.7.4 are now required.
1.5.1 (5 March 2012)¶
- Fix packaging problem caused by the move to git
1.5.0 (5 March 2012)¶
- Take advantage of “ragged rows” optimisation in xlrd 0.7.3
- Add support for PANE records to
xlutils.copy
, which means that zoom factors are now copied.
1.4.1 (6 September 2009)¶
- Removal of references in the finish methods of several filters, easing memory usage in large filtering runs
- Speed optimisations for
BaseFilter
, bringing those benefits to all subclasses. - Memory usage reduction when using
MemoryLogger
1.4.0 (18 August 2009)¶
- Add sheet density information and onesheet option to
xlutils.margins
. - Reduced the memory footprint of
ColumnTrimmer
at the expense of speed. - Fixed incorrect warnings about boolean cells in
ErrorFilter
.xlwt
has always supported boolean cells. BaseReader
now opens workbooks withon_demand = True
- Added support for
xlrd
Books opened withon_demand
asTrue
passed toopen_workbook()
. - Fixed bug when copying error cells.
- Requires the latest versions of
xlrd
(0.7.1) andxlwt
(0.7.2).
1.3.2 (18 June 2009)¶
- Made installation work when setuptools isn’t present.
- Made errorhandler an optional dependency.
1.3.1 (22 May 2009)¶
- In xlutils.styles, handle there case where two names were mapped to the same xfi, but the first one was empty.
1.3.0 (18 Mar 2009)¶
- fix bug that cause BaseWriter to raise exceptions when handling source workbooks opened by xlrd 0.7.0 and above where formatting_info had been passed as False
- add xlutils.copy
1.2.1 (19 Dec 2008)¶
- add extremely limited formatting_info support to DummyBook and TestReader
- move to testfixtures 1.5.3 for tests
1.2.0 (10 Dec 2008)¶
- add and implement start method to components in xlutils.filter.
- fixed bug when using set_rdsheet with ColumnTrimmer.
- improved installation documentation.
- renamed xlutils.styles.CellStyle to more appropriate xlutils.styles.NamedStyle.
- improved documentation for xlutils.styles.
- moved to using TestFixtures and Mock for tests.
- moved to using ErrorHandler rather than duplicating code.
1.1.1 (20 Nov 2008)¶
- prevented generation of excessively long sheet names that cause Excel to complain.
- added test that will fail if the filesystem used doesn’t support filenames with +’s in them.
1.1.0 (14 Nov 2008)¶
- link to the documentation for xlutils.display
- tighten up version requirements for xlrd and xlwt
- use style compression in xlutils.filter.BaseWriter
- prevent generation of bogus sheet names in xlutils.filter.BaseWriter
- xlutils.filter.BaseFilter now keeps track of rdbook, simplifying the implementation of filters.
- add another example for xlutils.filter
- add xlutils.filter.XLRDReader
- add xlutils.filter.StreamWriter
- add xlutils.styles
- add xlutils.save
1.0.0 (8 Nov 2008)¶
- initial public release
License¶
Copyright (c) 2008-2013 Simplistix Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.