diff --git a/.carthorse.yml b/.carthorse.yml
new file mode 100644
index 00000000..7b6ca85b
--- /dev/null
+++ b/.carthorse.yml
@@ -0,0 +1,9 @@
+carthorse:
+ version-from: setup.py
+ tag-format: "{version}"
+ when:
+ - version-not-tagged
+ actions:
+ - run: "sudo pip install -e .[build]"
+ - run: "twine upload -u __token__ -p $PYPI_TOKEN dist/*"
+ - create-tag
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000..73edaec3
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,98 @@
+version: 2.1
+
+orbs:
+ python: cjw296/python-ci@2.1
+
+jobs:
+ coverage:
+ docker:
+ - image: circleci/python:3.8
+ steps:
+ - checkout
+ - attach_workspace:
+ at: coverage_output
+ - run:
+ name: "Check coverage"
+ command: |
+ sudo pip install coverage
+ coverage combine coverage_output/
+ bash <(curl -s https://codecov.io/bash)
+
+ check-package:
+ parameters:
+ image:
+ type: string
+ docker:
+ - image: << parameters.image >>
+ steps:
+ - python/check-package:
+ package: "xlrd"
+ test:
+ - run:
+ name: "Check Import"
+ command: python -c "import xlrd"
+ - run:
+ name: "Check no XLS in wheel"
+ command: "! unzip -l dist/*.whl | egrep '.xlsx?$'"
+ - run:
+ name: "Check no XLS in source dist"
+ command: "! tar tzf dist/*.tar.gz | egrep '.xlsx?$'"
+
+common: &common
+ jobs:
+
+ - python/pip-run-tests:
+ matrix:
+ parameters:
+ image:
+ - circleci/python:2.7
+ - circleci/python:3.6
+ - circleci/python:3.9
+
+ - coverage:
+ name: coverage
+ requires:
+ - python/pip-run-tests
+
+ - python/pip-docs:
+ name: docs
+ requires:
+ - coverage
+
+ - python/pip-setuptools-build-package:
+ name: package
+ requires:
+ - docs
+ filters:
+ branches:
+ only: master
+
+ - check-package:
+ matrix:
+ parameters:
+ image:
+ - circleci/python:2.7
+ - circleci/python:3.9
+ requires:
+ - package
+
+ - python/release:
+ name: release
+ config: .carthorse.yml
+ requires:
+ - check-package
+ filters:
+ branches:
+ only: master
+
+workflows:
+ push:
+ <<: *common
+ periodic:
+ <<: *common
+ triggers:
+ - schedule:
+ cron: "0 0 11 * *"
+ filters:
+ branches:
+ only: master
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 00000000..3fb98a12
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,10 @@
+[run]
+source = xlrd,scripts,tests
+
+[report]
+exclude_lines =
+ # the original exclude
+ pragma: no cover
+
+ # debug stuff
+ if DEBUG:
diff --git a/.gitignore b/.gitignore
index c45445b8..8e46f60d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,15 @@
/build
/dist
-*.egg-info
\ No newline at end of file
+*.egg-info
+build/
+_build/
+*.pyc
+/.coverage
+/.tox
+/*.xml
+/htmlcov
+MANIFEST
+/bin
+.Python
+/include
+/lib
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 00000000..4bc268f0
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,10 @@
+version: 2
+python:
+ version: 3.8
+ install:
+ - method: pip
+ path: .
+ extra_requirements:
+ - docs
+sphinx:
+ fail_on_warning: true
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 00000000..24adbbc2
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,590 @@
+Changes
+=======
+
+2.0.2 (14 June 2025)
+--------------------
+
+- Fix bug reading sheets containing invalid formulae.
+
+Thanks to sanshi42 for the fix!
+
+2.0.1 (11 December 2020)
+------------------------
+
+- Use the README as the long description on PyPI.
+
+2.0.0 (11 December 2020)
+------------------------
+
+- Remove support for anything other than ``.xls`` files.
+- Remove support for ``psyco``.
+- Change the default encoding used when no ``CODEPAGE`` record can be found
+ from ``ascii`` to ``iso-8859-1``.
+- Add support for iterating over :class:`~xlrd.book.Book` objects.
+- Add support for item access from :class:`~xlrd.book.Book` objects,
+ where integer indices and string sheet names are supported.
+- Non-unicode spaces are now stripped from the "last author" information.
+- Workbook corruption errors can now be ignored using the
+ ``ignore_workbook_corruption`` option to :class:`~xlrd.open_workbook`.
+- Handle ``WRITEACCESS`` records with invalid trailing characters.
+- Officially support Python 3.8 and 3.9.
+
+Thanks to the following for their contributions to this release:
+
+- Jon Dufresne
+- Tore Lundqvist
+- nayyarv
+- Michael Davis
+- skonik
+
+1.2.0 (15 December 2018)
+------------------------
+
+- Added support for Python 3.7.
+- Added optional support for defusedxml to help mitigate exploits.
+- Automatically convert ``~`` in file paths to the current user's home
+ directory.
+- Removed ``examples`` directory from the installed package. They are still
+ available in the source distribution.
+- Fixed ``time.clock()`` deprecation warning.
+
+1.1.0 (22 August 2017)
+----------------------
+
+- Fix for parsing of merged cells containing a single cell reference in xlsx
+ files.
+
+- Fix for "invalid literal for int() with base 10: 'true'" when reading some
+ xlsx files.
+
+- Make xldate_as_datetime available to import direct from xlrd.
+
+- Build universal wheels.
+
+- Sphinx documentation.
+
+- Document the problem with XML vulnerabilities in xlsx files and mitigation
+ measures.
+
+- Fix :class:`NameError` on ``has_defaults is not defined``.
+
+- Some whitespace and code style tweaks.
+
+- Make example in README compatible with both Python 2 and 3.
+
+- Add default value for cells containing errors that causeed parsing of some
+ xlsx files to fail.
+
+- Add Python 3.6 to the list of supported Python versions, drop 3.3 and 2.6.
+
+- Use generator expressions to avoid unnecessary lists in memory.
+
+- Document unicode encoding used in Excel files from Excel 97 onwards.
+
+- Report hyperlink errors in R1C1 syntax.
+
+Thanks to the following for their contributions to this release:
+
+- icereval@gmail.com
+- Daniel Rech
+- Ville Skyttä
+- Yegor Yefremov
+- Maxime Lorant
+- Alexandr N Zamaraev
+- Zhaorong Ma
+- Jon Dufresne
+- Chris McIntyre
+- coltleese@gmail.com
+- Ivan Masá
+
+1.0.0 (2 June 2016)
+-------------------
+
+- Official support, such as it is, is now for 2.6, 2.7, 3.3+
+
+- Fixes a bug in looking up non-lowercase sheet filenames by ensuring that the
+ sheet targets are transformed the same way as the component_names dict keys.
+
+- Fixes a bug for ``ragged_rows=False`` when merged cells increases the number
+ of columns in the sheet. This requires all rows to be extended to ensure equal
+ row lengths that match the number of columns in the sheet.
+
+- Fixes to enable reading of SAP-generated .xls files.
+
+- support BIFF4 files with missing FORMAT records.
+
+- support files with missing WINDOW2 record.
+
+- Empty cells are now always unicode strings, they were a bytestring on
+ Python 2 and a unicode string on Python 3.
+
+- Fix for `` Revision : 3782 -- Author: sjmachin -- Date: 2009-02-23 23:00:50
- Revision : 3613 -- Author: chris -- Date: 2008-11-22 04:06:36
- Revision : 3574 -- Author: sjmachin -- Date: 2008-11-04 11:51:20
- Revision : 3480 -- Author: chris -- Date: 2008-09-19 20:43:00
- Revision : 3431 -- Author: sjmachin -- Date: 2008-07-28 10:37:35
- Revision : 3311 -- Author: chris -- Date: 2008-03-14 22:09:01
- Revision : 3287 -- Author: sjmachin -- Date: 2008-02-14 06:33:32
- Revision : 3284 -- Author: sjmachin -- Date: 2008-02-09 05:37:57
- Revision : 3265 -- Author: sjmachin -- Date: 2007-12-25 19:09:45
- Revision : 3263 -- Author: sjmachin -- Date: 2007-12-20 07:04:55
- Revision : 3262 -- Author: sjmachin -- Date: 2007-12-11 07:40:33
- Revision : 3250 -- Author: sjmachin -- Date: 2007-12-04 20:37:14
- Revision : 3234 -- Author: sjmachin -- Date: 2007-11-21 00:55:56
- Revision : 3168 -- Author: sjmachin -- Date: 2007-10-13 09:19:01
- Revision : 2868 -- Author: sjmachin -- Date: 2007-07-11 11:02:55
- Version 0.6.1, 2007-06-10
- Version 0.6.1a5
- Version 0.6.1a4
- Version 0.6.1a3
- Version 0.6.1a2
- Version 0.6.1a1, 2006-12-18
- Version 0.6.0a4, not released
- Version 0.6.0a3, 2006-09-19
- Version 0.6.0a2, 2006-09-13
- Version 0.6.0a1, 2006-09-08
- Version 0.5.3a1, 2006-05-24
- Version 0.5.2, 2006-03-14, public release
- Version 0.5.2a3, 2006-03-13
- Version 0.5.2a2, 2006-03-09
- Version 0.5.2a1, 2006-03-06
- Version 0.5.1, 2006-02-18, released to Journyx
- Version 0.5, 2006-02-07, released to Journyx
- Version 0.4a1, 2005-09-07, released to Laurent T.
- Version 0.3a1, 2005-05-15, first public release
- Purpose: Provide a library for developers to use to extract data
- from Microsoft Excel (tm) spreadsheet files.
- It is not an end-user tool.
- Author: John Machin, Lingfo Pty Ltd (sjmachin@lexicon.net)
- Licence: BSD-style (see licences.py)
- Version of xlrd: 0.7.1 -- 2009-05-31
- Versions of Python supported: 2.6-2.7.
- External modules required:
- Versions of Excel supported:
- 2004, 2003, XP, 2000, 97, 95, 5.0, 4.0, 3.0, 2.1, 2.0.
- Support for Excel 2007 .xlsx files scheduled for version 0.7.1.
- Outside the current scope: xlrd will safely and reliably ignore any of these
-if present in the file:
- Unlikely to be done:
- Particular emphasis (refer docs for details):
- Quick start:
-
- Another quick start: This will show the first, second and last rows of each
- sheet in each file:
- Installation:
- Download URLs:
- Acknowledgements:
-
-# For more information on the PythonDoc tool and the markup format, see
-# the PythonDoc page
-# at effbot.org.
-##
-
-# --------------------------------------------------------------------
-# Software License
-# --------------------------------------------------------------------
-#
-# Copyright (c) 2002-2007 by Fredrik Lundh
-#
-# By obtaining, using, and/or copying this software and/or its
-# associated documentation, you agree that you have read, understood,
-# and will comply with the following terms and conditions:
-#
-# Permission to use, copy, modify, and distribute this software and
-# its associated documentation for any purpose and without fee is
-# hereby granted, provided that the above copyright notice appears in
-# all copies, and that both that copyright notice and this permission
-# notice appear in supporting documentation, and that the name of
-# Secret Labs AB or the author not be used in advertising or publicity
-# pertaining to distribution of the software without specific, written
-# prior permission.
-#
-# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
-# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
-# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
-# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
-# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
-# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
-# OF THIS SOFTWARE.
-#
-# --------------------------------------------------------------------
-
-# to do in later releases:
-#
-# TODO: test this release under 1.5.2 !
-# TODO: better rendering of constructors/package modules
-# TODO: check @param names against @def/define tags
-# TODO: support recursive parsing (-R)
-# TODO: warn for tags that doesn't make sense for a given target type
-# TODO: HTML output localization (the %s module, returns, raises, etc)
-# TODO: make compactHTML generate an element tree instead of raw HTML
-#
-# nice to have, maybe:
-#
-# IDEA: support multiple output handlers (multiple -O statements);
-# make -x an alias for -Oxml
-# IDEA: make pythondoc self-contained (include stub element implementation)
-
-VERSION_DATE = "2.1b7-20070909"
-VERSION = VERSION_DATE.split("-")[0]
-
-COPYRIGHT = "(c) 2002-2007 by Fredrik Lundh"
-
-# explicitly import site (for exemaker etc)
-import site
-
-# stuff we use in this module
-import glob, os, re, string, sys, tokenize
-
-# make sure elementtree is available
-try:
- try:
- import xml.etree.ElementTree as ET
- except ImportError:
- import elementtree.ElementTree as ET
-except ImportError:
- raise RuntimeError(
- "PythonDoc %s requires ElementTree 1.1 or later "
- "(available from http://effbot.org/downloads)." % VERSION
- )
-
-# extension separator (not all systems use a period)
-try:
- EXTSEP = os.extsep
-except AttributeError:
- EXTSEP = "."
-
-##
-# Debug level. The higher the value, the more junk you'll see on
-# standard output.
-#
-# You can use the -V option to pythondoc to increase
-# the debug level.
-
-DEBUG = 0
-
-##
-# Whitespace tokens. These are ignored when the parser is scanning
-# for a subject.
-
-WHITESPACE_TOKEN = (
- tokenize.NL, tokenize.NEWLINE, tokenize.DEDENT, tokenize.INDENT
- )
-
-##
-# Default encoding. To override this for a module, put a "coding"
-# directive in your Python module (see PEP 263 for details).
-
-ENCODING = "iso-8859-1"
-
-##
-# Known tags. The parser generates warnings for tags that are not in
-# this list, but it still copies them to the XML infoset.
-
-TAGS = (
- "def", "defreturn",
- "param", "keyparam",
- "return",
- "throws", "exception",
- # javadoc tags not used by the standard generator
- "author", "deprecated", "see", "since", "version"
- )
-
-##
-# (Helper) Combines filename prefix with extension part.
-#
-# @param prefix Filename prefix.
-# @param ext Extension string, including a leading period. The
-# period is replaced with a platform-specific separator, if
-# necessary.
-# @return The combined name.
-
-def joinext(prefix, ext):
- assert ext[0] == "." # require leading separator, to match os.path.splitext
- return prefix + EXTSEP + ext[1:]
-
-##
-# (Helper) Extracts block tags from a PythonDoc comment.
-#
-# @param comment Comment text.
-# @return A list of (lineno, tag, text) tuples, where the tag is None
-# for the initial description.
-# @defreturn List of tuples.
-
-def gettags(comment):
-
- tags = []
-
- tag = None
- tag_lineno = lineno = 0
- tag_text = []
-
- for line in comment:
- if line[:1] == "@":
- tags.append((tag_lineno, tag, string.join(tag_text, "\n")))
- line = string.split(line, " ", 1)
- tag = line[0][1:]
- if len(line) > 1:
- tag_text = [line[1]]
- else:
- tag_text = []
- tag_lineno = lineno
- else:
- tag_text.append(line)
- lineno = lineno + 1
-
- tags.append((tag_lineno, tag, string.join(tag_text, "\n")))
-
- return tags
-
-##
-# (Helper) Flattens an element tree, returning only the text contents.
-#
-# @param elem An element tree.
-# @return A text string.
-# @defreturn String.
-
-def flatten(elem):
- text = elem.text or ""
- for e in elem:
- text += flatten(e)
- if e.tail:
- text += e.tail
- return text
-
-##
-# (Helper) Extracts summary from a PythonDoc comment. This function
-# gets the first complete sentence from the description string.
-#
-# @param description An element containing the description.
-# @return A summary string.
-# @defreturn String.
-
-def getsummary(description):
-
- description = flatten(description)
-
- # extract the first sentence from the description
- m = re.search("(?s)(.+?\.)\s", description + " ")
- if m:
- return m.group(1)
-
- return description # sorry
-
-##
-# (Helper) Parses HTML descriptor text into an XHTML structure.
-#
-# @param parser Parser instance (provides a warning method).
-# @param text Text fragment.
-# @return An element tree containing XHTML data.
-# @defreturn Element.
-
-def parsehtml(parser, tag, text, lineno):
-
- # transcode
- if parser.encoding != "ascii":
- try:
- text = unicode(text, parser.encoding)
- except NameError:
- pass # 1.5.2
-
- # process inline links (@link, @linkplain)
- # note that links are replaced with %s" % (href, text)
- else:
- return "%s" % (href, text)
- text = re.sub("\{(@link[^}]+)\}", fixlink, text)
-
- if "<" not in text and "&" not in text:
- # plain text
- elem = ET.Element(tag)
- elem.text = string.strip(text)
- return elem
-
- p = HTMLTreeBuilder()
- ix = 0
- try:
- p.feed("<%s>" % tag)
- p.feed(" ") # make sure everything's wrapped in a paragraph tag
- # feed line by line
- for line in string.split(text, "\n"):
- p.feed(line + "\n")
- ix = ix + 1
- p.feed("%s>" % tag)
- tree = p.close()
- except:
- parser.warning(
- (lineno+ix, 0),
- "HTML parser error near this line (%s)",
- sys.exc_value
- )
- return ET.Element("p")
-
- return tree
-
-# --------------------------------------------------------------------
-# copied from ElementTree/HTMLTreeBuilder.py
-
-import htmlentitydefs
-
-AUTOCLOSE = "p", "li", "tr", "th", "td", "head", "body"
-IGNOREEND = "img", "hr", "meta", "link", "br"
-
-if sys.version[:3] == "1.5":
- is_not_ascii = re.compile(r"[\x80-\xff]").search # 1.5.2
-else:
- is_not_ascii = re.compile(eval(r'u"[\u0080-\uffff]"')).search
-
-try:
- from HTMLParser import HTMLParser
-except ImportError:
- from sgmllib import SGMLParser
- # hack to use sgmllib's SGMLParser to emulate 2.2's HTMLParser
- class HTMLParser(SGMLParser):
- # the following only works as long as this class doesn't
- # provide any do, start, or end handlers
- def unknown_starttag(self, tag, attrs):
- self.handle_starttag(tag, attrs)
- def unknown_endtag(self, tag):
- self.handle_endtag(tag)
-
-##
-# ElementTree builder for HTML source code. This builder converts an
-# HTML document or fragment to an ElementTree.
-#
-# The parser is relatively picky, and requires balanced tags for most
-# elements. However, elements belonging to the following group are
-# automatically closed: P, LI, TR, TH, and TD. In addition, the
-# parser automatically inserts end tags immediately after the start
-# tag, and ignores any end tags for the following group: IMG, HR,
-# META, and LINK.
-#
-# @keyparam builder Optional builder object. If omitted, the parser
-# uses the standard elementtree builder.
-# @keyparam encoding Optional character encoding, if known. If omitted,
-# the parser looks for META tags inside the document. If no tags
-# are found, the parser defaults to ISO-8859-1. Note that if your
-# document uses a non-ASCII compatible encoding, you must decode
-# the document before parsing.
-
-class HTMLTreeBuilder(HTMLParser):
-
- def __init__(self, encoding=None):
- self.__stack = []
- self.__builder = ET.TreeBuilder()
- self.encoding = encoding or "iso-8859-1"
- HTMLParser.__init__(self)
-
- ##
- # Flushes parser buffers, and return the root element.
- #
- # @return An Element instance.
-
- def close(self):
- HTMLParser.close(self)
- return self.__builder.close()
-
- ##
- # (Internal) Handles start tags.
-
- def handle_starttag(self, tag, attrs):
- if tag == "meta":
- # look for encoding directives
- http_equiv = content = None
- for k, v in attrs:
- if k == "http-equiv":
- http_equiv = string.lower(v)
- elif k == "content":
- content = v
- if http_equiv == "content-type" and content:
- # use mimetools to parse the http header
- import mimetools, StringIO
- header = mimetools.Message(
- StringIO.StringIO("%s: %s\n\n" % (http_equiv, content))
- )
- encoding = header.getparam("charset")
- if encoding:
- self.encoding = encoding
- if tag in AUTOCLOSE:
- if self.__stack and self.__stack[-1] == tag:
- self.handle_endtag(tag)
- self.__stack.append(tag)
- attrib = {}
- if attrs:
- for k, v in attrs:
- attrib[string.lower(k)] = v
- self.__builder.start(tag, attrib)
- if tag in IGNOREEND:
- self.__stack.pop()
- self.__builder.end(tag)
-
- ##
- # (Internal) Handles end tags.
-
- def handle_endtag(self, tag):
- if tag in IGNOREEND:
- return
- lasttag = self.__stack.pop()
- if tag != lasttag and lasttag in AUTOCLOSE:
- self.handle_endtag(lasttag)
- self.__builder.end(tag)
-
- ##
- # (Internal) Handles character references.
-
- def handle_charref(self, char):
- if char[:1] == "x":
- char = int(char[1:], 16)
- else:
- char = int(char)
- if 0 <= char < 128:
- self.__builder.data(chr(char))
- else:
- self.__builder.data(unichr(char))
-
- ##
- # (Internal) Handles entity references.
-
- def handle_entityref(self, name):
- entity = htmlentitydefs.entitydefs.get(name)
- if entity:
- if len(entity) == 1:
- entity = ord(entity)
- else:
- entity = int(entity[2:-1])
- if 0 <= entity < 128:
- self.__builder.data(chr(entity))
- else:
- self.__builder.data(unichr(entity))
- else:
- self.unknown_entityref(name)
-
- ##
- # (Internal) Handles character data.
-
- def handle_data(self, data):
- if isinstance(data, type('')) and is_not_ascii(data):
- # convert to unicode, but only if necessary
- data = unicode(data, self.encoding, "ignore")
- self.__builder.data(data)
-
- ##
- # (Hook) Handles unknown entity references. The default action
- # is to ignore unknown entities.
-
- def unknown_entityref(self, name):
- pass # ignore by default; override if necessary
-
-# --------------------------------------------------------------------
-
-##
-# (Helper) Parses a PythonDoc comment into an PythonDoc info structure.
-#
-# @param parser Parser instance (provides a warning method).
-# @param lineno Line number where this comment starts.
-# @param comment A list of text line making up the comment.
-# @param dedent If true, strip leading whitespace from all comment
-# lines except the first one.
-# @return An element tree containing XHTML data.
-# @defreturn Element.
-
-def parsecomment(parser, lineno, comment, dedent=0):
-
- subject_info = ET.Element("info")
-
- # untabify
- for ix in range(len(comment)):
- comment[ix] = string.expandtabs(comment[ix])
-
- if dedent:
- margin = None
- for ix in range(1, len(comment)):
- s = string.lstrip(comment[ix])
- if not s:
- continue
- m = len(comment[ix]) - len(s)
- if margin is None:
- margin = m
- else:
- margin = min(m, margin)
- if margin:
- for ix in range(1, len(comment)):
- comment[ix] = comment[ix][margin:]
-
- for ix, tag, text in gettags(comment):
-
- pos = lineno + ix + 1, 0
-
- # check tag name
- if tag is None:
- tag = "description"
- else:
- if tag not in TAGS:
- parser.warning(
- pos,
- "unknown tag in description: @%s", tag
- )
- if tag in ("throws", "exception"):
- tag = "exception" # PythonDoc extension
-
- # deal with "named" tags
- if tag in ("param", "keyparam", "exception"):
- text = string.split(text, " ", 1)
- name = text[0]
- if len(text) > 1:
- text = string.lstrip(text[1])
- else:
- text = ""
- else:
- name = None
-
- tag_elem = parsehtml(parser, tag, text, pos[0])
-
- # generate summaries
- if tag == "description":
- summary = getsummary(tag_elem)
- if summary:
- elem = ET.SubElement(subject_info, "summary")
- elem.text = summary
-
- subject_info.append(tag_elem)
-
- if name:
- tag_elem.set("name", name)
-
- return subject_info
-
-##
-# Module parser.
-#
-# This class implements the PythonDoc source code scanner. It reads
-# source code from a file or a file-like object, and builds an element
-# tree with information about the module.
-#
-# Note that the constructor only sets things up for parsing. Use the
-# {@link ModuleParser.parse} method to parse the file. Or for
-# convenience, use the {@link parse} function to create a parser
-# object and parse a given file.
-#
-# @param file Name of the module source file, or a file object. If a
-# file object is used, it must provide a name attribute and
-# a readline method.
-# @param prefix Optional name prefix. If given, this is prepended to
-# the module name. For example, if the prefix is set to "prefix"
-# and the module filename is "name.py", the module is assumed to
-# contain the "prefix.name" namespace.
-
-class ModuleParser:
-
- ##
- # Module name.
-
- name = None
-
- def __init__(self, file, prefix=None):
- if hasattr(file, "readline"):
- self.file = file
- self.filename = file.name
- else:
- self.file = None
- self.filename = file
- name = os.path.splitext(os.path.basename(self.filename))[0]
- if prefix and prefix != ".":
- name = prefix + "." + name
- self.name = name
- self.stack = [
- ET.Element(
- "module",
- name=name, filename=self.filename
- )
- ]
- self.indent = 0
- self.scope = [] # list of (indent, tag, name, ...) tuples
- self.handler = self.look_for_encoding
- self.encoding = ENCODING
-
- ##
- # Parses the file.
- #
- # @keyparam docstring If true, look for markup in docstrings.
- # @return An element tree containing information about the module.
- # @defreturn Element.
- # @exception IOError If the file could not be opened.
-
- def parse(self, docstring=0):
- if self.file is None:
- file = open(self.filename)
- else:
- file = self.file
- try:
- tokenize.tokenize(file.readline, self.handle_token)
- except tokenize.TokenError, v:
- message, lineno = v
- self.warning(lineno, "exception in tokenizer: %s", message)
- if len(self.stack) != 1:
- pass # FIXME: print warning?
- tree = self.stack[0] # may be incomplete
- # fixup internal links
- # 1) find all named elements
- elems = {}
- for elem in tree.getiterator():
- name = elem.get("name")
- if name:
- elems[name] = elem
- # 2) find all link anchors
- for elem in tree.getiterator("a"):
- href = elem.get("href")
- if href[:5] == "link:":
- # FIXME: add support for external links
- href = href[5:]
- if href[:1] == "#":
- href = href[1:]
- target = elems.get(self.name + "." + href)
- if target:
- href = "#" + target.get("name") + "-" + target.tag
- elem.set("href", href)
- if docstring:
- # look for markup in docstrings
- for info in tree.getiterator("info"):
- docstring = info.findtext("docstring")
- if not docstring:
- continue
- comment = docstring.split("\n")
- newinfo = parsecomment(self, 0, comment, dedent=1)
- for elem in info:
- if newinfo.find(elem.tag) is None:
- newinfo.append(elem)
- info[:] = newinfo
- return tree
-
- ##
- # Prints a warning message to standard output.
- #
- # @param position A (line, column) tuple. The column can be set
- # to None if not known (or not relevant).
- # @param format Message or format string.
- # @param *args Optional arguments.
-
- def warning(self, position, format, *args):
- line, column = position
- message = "%s:%d: WARNING: %s" % (self.filename, line, format % args)
- sys.stderr.write(message)
- sys.stderr.write("\n")
-
- ##
- # Dispatches tokens to the current handler. Each handler should
- # return the handler to call for the next token.
- #
- # This method also handles indentation and dedentation tokens,
- # and manages the scope stack.
-
- def handle_token(self, *args):
- # dispatch incoming tokens to the current handler
- if DEBUG > 1:
- print self.handler.im_func.func_name, self.indent,
- print tokenize.tok_name[args[0]], repr(args[1])
- if args[0] == tokenize.DEDENT:
- self.indent = self.indent - 1
- while self.scope and self.scope[-1][0] >= self.indent:
- del self.scope[-1]
- del self.stack[-1]
- self.handler = apply(self.handler, args)
- if args[0] == tokenize.INDENT:
- self.indent = self.indent + 1
-
- ##
- # (Token handler) Scans for encoding directive.
-
- def look_for_encoding(self, type, token, start, end, line):
- if type == tokenize.COMMENT:
- if string.rstrip(token) == "##":
- return self.look_for_pythondoc(type, token, start, end, line)
- m = re.search("coding[:=]\s*([-_.\w]+)", token)
- if m:
- self.encoding = m.group(1)
- return self.look_for_pythondoc
- if start[0] > 2:
- return self.look_for_pythondoc
- return self.look_for_encoding
-
- ##
- # (Token handler) Scans for PythonDoc comments.
-
- def look_for_pythondoc(self, type, token, start, end, line):
- if type == tokenize.COMMENT and string.rstrip(token) == "##":
- # found a comment: set things up for comment processing
- self.comment_start = start
- self.comment = []
- return self.process_comment_body
- else:
- # deal with "bare" subjects
- if token == "def" or token == "class":
- self.subject_indent = self.indent
- self.subject_parens = 0
- self.subject_start = self.comment_start = None
- self.subject = []
- return self.process_subject(type, token, start, end, line)
- return self.look_for_pythondoc
-
- ##
- # (Token handler) Processes a comment body. This handler adds
- # comment lines to the current comment.
-
- def process_comment_body(self, type, token, start, end, line):
- if type == tokenize.COMMENT:
- if start[1] != self.comment_start[1]:
- self.warning(
- start,
- "comment line should be aligned with marker"
- )
- line = string.rstrip(token)
- if line == "##":
- # handle module comments (experimental)
- # FIXME: add more consistency checks?
- if self.stack[0].find("info") is not None:
- self.warning(
- self.comment_start,
- "multiple module comments are not allowed"
- )
- # FIXME: ignore additional comments?
- self.process_subject_info(None, self.stack[0])
- return self.look_for_pythondoc
- elif line[:2] == "# ":
- line = line[2:]
- elif line[:1] == "#":
- line = line[1:]
- self.comment.append(line)
- else:
- if not self.comment:
- self.warning(
- self.comment_start,
- "found pythondoc marker but no comment body"
- )
- return self.look_for_pythondoc
- self.subject_start = None
- self.subject = []
- if type != tokenize.NL:
- return self.process_subject(type, token, start, end, line)
- return self.process_subject # end of comment
- return self.process_comment_body
-
- ##
- # (Token handler) Processes the comment subject. The subject can
- # be either a plain variable, or a function/method or class
- # definition.
- #
- # This method is also used to process "bare" subjects; that is,
- # functions, methods, and classes that don't have PythonDoc
- # markup. In that case, the comment_start variable is set to
- # None.
-
- def process_subject(self, type, token, start, end, line):
- # got an item; deal with it
- if self.subject:
- # method/function/class definition
- definition = self.subject[0] in ("def", "class")
- if definition:
- if type not in WHITESPACE_TOKEN:
- if token == "(":
- self.subject_parens = self.subject_parens + 1
- elif token == ")":
- self.subject_parens = self.subject_parens - 1
- if self.subject_parens or token != ":":
- self.subject.append(token)
- return self.process_subject
- else:
- # simple assignment
- if token != "=":
- self.warning(
- self.subject_start,
- "bad subject %s; ignoring description",
- repr(self.subject[0])
- )
- # might be a pythondoc marker; pass it to the scanner
- return self.look_for_pythondoc(
- type, token, start, end, line
- )
- # FIXME: keep adding stuff until end of expression
- else:
- if type in WHITESPACE_TOKEN:
- return self.process_subject
- if type == tokenize.COMMENT:
- self.warning(
- start,
- "comment between description and subject; " +
- "ignoring description"
- )
- # might be a pythondoc marker; pass it to the scanner
- return self.look_for_pythondoc(
- type, token, start, end, line
- )
- # FIXME: check token type!
- # the @ token type is currently tokenize.ERRORTOKEN; hopefully
- # this will change before 2.4 final
- if token == "@":
- self.decorator_parens = 0
- return self.skip_decorator
- self.subject_start = start
- self.subject.append(token)
- if token in ("def", "class"):
- # handle single-line subjects
- while self.scope and self.scope[-1][0] >= self.indent:
- self.scope.pop()
- self.stack.pop()
- self.subject_indent = self.indent
- self.subject_parens = 0
- return self.process_subject
-
- # check if this is a method or a function
- method = self.scope and self.scope[-1][1] == "class"
-
- # calculate fully qualified subject name
- name = [self.name]
- for s in self.scope:
- name.append(s[2])
- if definition:
- name.append(self.subject[1])
- else:
- name.append(self.subject[0])
-
- # calculate subject definition statement
- statement = []
- for part in self.subject:
- if part in ("class", "def"):
- continue
- statement.append(part)
- if part == ",":
- statement.append(" ")
- if self.subject[0] == "def" and method:
- # ignore the first argument for methods
- # 'name', '(', 'self', ',', ' ', ...)
- del statement[2:min(5, len(statement)-1)]
- statement = string.join(statement, "")
-
- # create subject element
- if self.subject[0] == "class":
- subject_elem = ET.Element("class")
- elif self.subject[0] == "def":
- if method:
- subject_elem = ET.Element("method")
- else:
- subject_elem = ET.Element("function")
- else:
- subject_elem = ET.Element("variable")
-
- self.stack[-1].append(subject_elem)
-
- # add new subject to the scope and element stacks
- if definition:
- self.scope.append((self.subject_indent,) + tuple(self.subject))
- self.stack.append(subject_elem)
-
- subject_info = self.process_subject_info(name, subject_elem)
-
- # add local name to info
- elem = ET.Element("name")
- elem.text = name[-1]
-
- subject_info.insert(0, elem)
-
- name = string.join(name, ".")
-
- subject_elem.set("name", name)
- subject_elem.set("lineno", str(self.subject_start[0]))
-
- if subject_info.find("def") is None and statement:
- # add subject definition (unless specified in comment)
- elem = ET.Element("def")
- elem.text = statement
- # add to front, to make the XML easier to read
- subject_info.insert(0, elem)
-
- if definition:
- return self.look_for_docstring(type, token, start, end, line)
- else:
- return self.look_for_pythondoc(type, token, start, end, line)
-
- ##
- # (Token handler) Skips a decorator.
-
- def skip_decorator(self, type, token, start, end, line):
- if token == "(":
- self.decorator_parens = self.decorator_parens + 1
- elif token == ")":
- self.decorator_parens = self.decorator_parens - 1
- if self.decorator_parens or type != tokenize.NEWLINE:
- return self.skip_decorator
- return self.process_subject
-
- ##
- # (Token handler helper) Processes a PythonDoc comment. This
- # method creates an "info" element based on the current comment,
- # and attaches it to the current subject element.
- #
- # @param subject_name Subject name (or None if the name is not known).
- # @param subject_elem The current subject element.
- # @return The info element. Note that this element has already
- # been attached to the subject element.
- # @defreturn Element
-
- def process_subject_info(self, subject_name, subject_elem):
-
- # process pythondoc comment (if any)
- if self.comment_start:
- subject_info = parsecomment(
- self, self.comment_start[0], self.comment
- )
- else:
- subject_info = ET.Element("info")
-
- subject_elem.append(subject_info)
-
- if DEBUG:
- if subject_name:
- subject_name = string.join(subject_name, ".")
- else:
- subject_name = "
-# This function creates a {@link #ModuleParser} instance, and uses it
-# to parse the given file. For details, see {@linkplain #ModuleParser
-# the ModuleParser documentation}.
-#
-# @param file Name of the module source file, or a file object.
-# @param prefix Optional name prefix.
-# @keyparam docstring If true, look for markup in docstrings.
-# @return An element tree containing the module description.
-# @defreturn Element.
-# @exception IOError If the file could not be found, or could not
-# be opened for reading.
-
-def parse(file, prefix=None, docstring=0):
- m = ModuleParser(file, prefix)
- return m.parse(docstring=docstring)
-
-# --------------------------------------------------------------------
-# default formatter
-
-if sys.version[:3] == "1.5":
- _escape = re.compile(r"[&<>\"\x80-\xff]") # 1.5.2
-else:
- _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]"'))
-
-_escape_map = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
-}
-
-##
-# Encodes reserved HTML characters and non-ASCII characters as HTML
-# character references.
-#
-# @def html_encode(text)
-# @param text Source text.
-# @return An encoded string.
-
-def html_encode(text, pattern=_escape):
- if not text:
- return ""
- def escape_entities(m, map=_escape_map):
- char = m.group()
- text = map.get(char)
- if text is None:
- text = "%d;" % ord(char)
- return text
- text = pattern.sub(escape_entities, text)
- try:
- return text.encode("ascii")
- except AttributeError:
- return text # 1.5.2
-
-##
-# Compact HTML formatter. This formatter turns a module XML
-# description into a minimal HTML document.
-#
-# This formatter supports the following options:
-# %s %s For more information about this class, see "
- "The %s Class. Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd This script is part of the xlrd package, which is released under a
-# BSD-style licence. Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd This module is part of the xlrd package, which is released under a
-# BSD-style licence. A Python module for extracting data from MS Excel (TM) spreadsheet files.
-#
-# Development of this module would not have been possible without the document
-# "OpenOffice.org's Documentation of the Microsoft Excel File Format"
-# ("OOo docs" for short).
-# The latest version is available from OpenOffice.org in
-# PDF format
-# and
-# ODT format.
-# Small portions of the OOo docs are reproduced in this
-# document. A study of the OOo docs is recommended for those who wish a
-# deeper understanding of the Excel file layout than the xlrd docs can provide.
-# Backporting to Python 2.1 was partially funded by
-#
-# Journyx - provider of timesheet and project accounting solutions.
-#
-# Provision of formatting information in version 0.6.1 was funded by
-#
-# Simplistix Ltd.
-#
-# This module presents all text strings as Python unicode objects.
-# From Excel 97 onwards, text in Excel spreadsheets has been stored as Unicode.
-# Older files (Excel 95 and earlier) don't keep strings in Unicode;
-# a CODEPAGE record provides a codepage number (for example, 1252) which is
-# used by xlrd to derive the encoding (for same example: "cp1252") which is
-# used to translate to Unicode. If the CODEPAGE record is missing (possible if the file was created
-# by third-party software), xlrd will assume that the encoding is ascii, and keep going.
-# If the actual encoding is not ascii, a UnicodeDecodeError exception will be raised and
-# you will need to determine the encoding yourself, and tell xlrd:
-# If the CODEPAGE record exists but is wrong (for example, the codepage
-# number is 1251, but the strings are actually encoded in koi8_r),
-# it can be overridden using the same mechanism.
-# The supplied runxlrd.py has a corresponding command-line argument, which
-# may be used for experimentation:
-#
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Python package "xlrd"
-
-
-
-
-
-
- import xlrd
- book = xlrd.open_workbook("myfile.xls")
- print "The number of worksheets is", book.nsheets
- print "Worksheet name(s):", book.sheet_names()
- sh = book.sheet_by_index(0)
- print sh.name, sh.nrows, sh.ncols
- print "Cell D30 is", sh.cell_value(rowx=29, colx=3)
- for rx in range(sh.nrows):
- print sh.row(rx)
- # Refer to docs for more details.
- # Feedback on API is welcomed.
-
-
- OS-prompt>python PYDIR/scripts/runxlrd.py 3rows *blah*.xls
-
-
-
-
-
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..e3b8077b
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,60 @@
+xlrd
+====
+
+|Build Status|_ |Coverage Status|_ |Documentation|_ |PyPI version|_
+
+.. |Build Status| image:: https://circleci.com/gh/python-excel/xlrd/tree/master.svg?style=shield
+.. _Build Status: https://circleci.com/gh/python-excel/xlrd/tree/master
+
+.. |Coverage Status| image:: https://codecov.io/gh/python-excel/xlrd/branch/master/graph/badge.svg?token=lNSqwBBbvk
+.. _Coverage Status: https://codecov.io/gh/python-excel/xlrd
+
+.. |Documentation| image:: https://readthedocs.org/projects/xlrd/badge/?version=latest
+.. _Documentation: http://xlrd.readthedocs.io/en/latest/?badge=latest
+
+.. |PyPI version| image:: https://badge.fury.io/py/xlrd.svg
+.. _PyPI version: https://badge.fury.io/py/xlrd
+
+
+xlrd is a library for reading data and formatting information from Excel
+files in the historical ``.xls`` format.
+
+.. warning::
+
+ This library will no longer read anything other than ``.xls`` files. For
+ alternatives that read newer file formats, please see http://www.python-excel.org/.
+
+The following are also not supported but will safely and reliably be ignored:
+
+* Charts, Macros, Pictures, any other embedded object, **including** embedded worksheets.
+* VBA modules
+* Formulas, but results of formula calculations are extracted.
+* Comments
+* Hyperlinks
+* Autofilters, advanced filters, pivot tables, conditional formatting, data validation
+
+Password-protected files are not supported and cannot be read by this library.
+
+Quick start:
+
+.. code-block:: bash
+
+ pip install xlrd
+
+.. code-block:: python
+
+ import xlrd
+ book = xlrd.open_workbook("myfile.xls")
+ print("The number of worksheets is {0}".format(book.nsheets))
+ print("Worksheet name(s): {0}".format(book.sheet_names()))
+ sh = book.sheet_by_index(0)
+ print("{0} {1} {2}".format(sh.name, sh.nrows, sh.ncols))
+ print("Cell D30 is {0}".format(sh.cell_value(rowx=29, colx=3)))
+ for rx in range(sh.nrows):
+ print(sh.row(rx))
+
+From the command line, this will show the first, second and last rows of each sheet in each file:
+
+.. code-block:: bash
+
+ python PYDIR/scripts/runxlrd.py 3rows *blah*.xls
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..a4635183
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,135 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make
-#
-#
-# @param options Options dictionary.
-
-class CompactHTML:
-
- def __init__(self, options=None):
- self.options = options or {}
-
- ##
- # Writes an element containing some text (plain or formatted).
- #
- # @param elem Element.
- # @param compact If true, try to minimize the amount of vertical
- # padding.
-
- def writetext(self, elem, compact=0):
- if len(elem):
- if compact and len(elem) == 1 and elem[0].tag == "p":
- elem = elem[0]
- self.file.write(html_encode(elem.text))
- for e in elem:
- ET.ElementTree(e).write(self.file)
- self.file.write(html_encode(elem.tail))
- else:
- for e in elem:
- ET.ElementTree(e).write(self.file)
- elif elem is not None and elem.text:
- if compact:
- self.file.write(html_encode(elem.text))
- else:
- self.file.write("\n")
- for p in param + keyparam:
- name = p.get("name")
- if p.tag == "keyparam":
- name = name + "="
- self.file.write("
\n")
- if object.tag == "class" and summary:
- self.file.write(
- "%s
\n" % title)
-
- # 0) module comments
- info = module.find("info")
- if info is not None:
- self.writetext(info.find("description"))
- self.file.write("Module Contents
\n")
-
- # 1) toplevel subjects (including class overviews)
- objects = []
- for object in module:
- info = object.find("info")
- if info is None or info.find("description") is None:
- continue
- if object.tag in ("variable", "function", "class"):
- objects.append(object)
- objects.sort(lambda a, b: cmp(
- string.lower(string.split(a.get("name"), ".")[-1]),
- string.lower(string.split(b.get("name"), ".")[-1])
- ))
- self.file.write("\n")
- for object in objects:
- self.writeobject(object, object.tag == "class")
- self.file.write("
\n")
- # 2) class descriptions
- for object in objects:
- if object.tag != "class":
- continue
- name = object.get("name")
- localname = string.split(name, ".")[-1]
- anchor = name + "-class"
- self.file.write(
- "The %s Class
\n" % (
- anchor, anchor, localname
- )
- )
- self.file.write("\n")
- self.writeobject(object)
- objects = []
- for object in object:
- info = object.find("info")
- if info is None or info.find("description") is None:
- continue
- if object.tag not in ("method", "variable"):
- continue
- objects.append(object)
- objects.sort(lambda a, b: cmp(
- string.lower(string.split(a.get("name"), ".")[-1]),
- string.lower(string.split(b.get("name"), ".")[-1])
- ))
- for object in objects:
- if object.tag == "variable":
- object.tag = "attribute"
- self.writeobject(object)
- if object.tag == "attribute":
- object.tag = "variable"
- self.file.write("
\n")
-
- if not zone:
- self.file.write("\n")
-
- self.file.close()
- self.file = None
-
- return filename
-
-##
-# Prints a usage message and exits.
-
-def usage():
- print "PythonDoc", VERSION, COPYRIGHT
- print
- print "Usage:"
- print
- print " pythondoc [options] files..."
- print
- print "where the files can be either python modules or package"
- print "directories."
- print
- print "Options:"
- print
- print " -p prefix Prepend given prefix to symbol names."
- print " -f Generate output also for files without descriptions."
- print " -x Generate XML output (pythondoc infosets)."
- print
- print " -s Look for markup in docstrings (experimental)."
- print
- print "Output options:"
- print
- print " -O format Use given output format handler."
- print " -D name Define output variable."
- print " -D name=text Set output variable to given text."
- print
- print "For more information on PythonDoc and the PythonDoc comment syntax,"
- print "see http://effbot.org/zone/pythondoc.htm"
- sys.exit(1)
-
-if __name__ == "__main__":
-
- import getopt
-
- try:
- opts, args = getopt.getopt(sys.argv[1:], "D:fO:p:Vsx")
- except getopt.error:
- usage()
-
- force = 0
- prefix = None
- docstring = 0
- output_xml = 0
- output_handler = CompactHTML
- output_options = {}
-
- for k, v in opts:
- if k == "-f":
- force = 1
- elif k == "-p":
- prefix = v
- elif k == "-s":
- docstring = 1
- elif k == "-x":
- output_xml = 1
- elif k == "-O":
- try:
- m = __import__(v)
- for k in string.split(v, ".")[1:]:
- m = getattr(m, k)
- output_handler = getattr(m, "PythonDocGenerator")
- except (ImportError, AttributeError):
- print "cannot find/load", repr(v), "generator"
- sys.exit(1)
- elif k == "-D":
- try:
- k, v = string.split(v, "=", 1)
- except ValueError:
- k = v; v = None
- output_options[k] = v
- elif k == "-V":
- DEBUG = DEBUG + 1
-
- if not args:
- usage()
-
- # instantiate output handler
- output_handler = output_handler(output_options)
-
- # check if handler supports custom tags
- try:
- TAGS = TAGS + output_handler.tags
- except AttributeError:
- pass
-
- import time
- t0 = time.time()
-
- input = output = 0
-
- for filename in args:
-
- this_prefix = prefix
-
- if os.path.isdir(filename):
- # FIXME: explicitly check if this is a package?
- files = glob.glob(os.path.join(filename, joinext("*", ".py")))
- if not this_prefix:
- this_prefix = os.path.basename(filename)
- else:
- if sys.platform == "win32" and glob.has_magic(filename):
- files = glob.glob(filename)
- else:
- files = [filename]
-
- files.sort()
-
- for file in files:
-
- try:
- module = parse(file, this_prefix, docstring=docstring)
- except IOError, v:
- sys.stderr.write("%s error: %s\n" % (file, v[1]))
- continue
-
- input = input + 1
-
- # check if any toplevel object has a description
- if not force:
- for n in module:
- i = n.find("info")
- if i and i.find("description") is not None:
- break
- else:
- continue # no documented subjects
-
- f = "pythondoc-" + string.replace(module.get("name"), ".", EXTSEP)
-
- if output_xml:
- # generate XML
- filename = joinext(f, ".xml")
- try:
- out = open(filename, "w")
- ET.ElementTree(module).write(out)
- out.close()
- except IOError, v:
- sys.stderr.write("%s error: %s\n" % (filename, v[1]))
- else:
- sys.stderr.write("%s ok\n" % filename)
-
- # generate output
- try:
- out = output_handler.save(module, f)
- except IOError, v:
- sys.stderr.write("%s error: %s\n" % (file, v[1]))
- else:
- if out:
- sys.stderr.write("%s ok\n" % out)
-
- output = output + 1
-
- # flush output handler
- try:
- done = output_handler.done
- except AttributeError:
- pass
- else:
- out = output_handler.done()
- if out:
- sys.stderr.write("%s ok\n" % out)
-
- if DEBUG:
- sys.stderr.write(
- "%d files parsed, %d descriptions generated, in %.2f seconds\n" % (
- input, output, time.time() - t0
- ))
diff --git a/scripts/runxlrd.py b/scripts/runxlrd.py
index 2bc1343c..b284a594 100644
--- a/scripts/runxlrd.py
+++ b/scripts/runxlrd.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
-# -*- coding: ascii -*-
-#
-# Version 0.7.4 -- April 2012
-# General information
-#
-# Acknowledgements
-#
-# Unicode
-#
-#
-# book = xlrd.open_workbook(..., encoding_override="cp1252")
-#
-# runxlrd.py -e koi8_r 3rows myfile.xls
-#
The first place to look for an encoding ("codec name") is -# -# the Python documentation. -#
-# -# -#In reality, there are no such things. What you have are floating point -# numbers and pious hope. -# There are several problems with Excel dates:
-# -#(1) Dates are not stored as a separate data type; they are stored as -# floating point numbers and you have to rely on -# (a) the "number format" applied to them in Excel and/or -# (b) knowing which cells are supposed to have dates in them. -# This module helps with (a) by inspecting the -# format that has been applied to each number cell; -# if it appears to be a date format, the cell -# is classified as a date rather than a number. Feedback on this feature, -# especially from non-English-speaking locales, would be appreciated.
-# -#(2) Excel for Windows stores dates by default as the number of -# days (or fraction thereof) since 1899-12-31T00:00:00. Excel for -# Macintosh uses a default start date of 1904-01-01T00:00:00. The date -# system can be changed in Excel on a per-workbook basis (for example: -# Tools -> Options -> Calculation, tick the "1904 date system" box). -# This is of course a bad idea if there are already dates in the -# workbook. There is no good reason to change it even if there are no -# dates in the workbook. Which date system is in use is recorded in the -# workbook. A workbook transported from Windows to Macintosh (or vice -# versa) will work correctly with the host Excel. When using this -# module's xldate_as_tuple function to convert numbers from a workbook, -# you must use the datemode attribute of the Book object. If you guess, -# or make a judgement depending on where you believe the workbook was -# created, you run the risk of being 1462 days out of kilter.
-# -#Reference: -# http://support.microsoft.com/default.aspx?scid=KB;EN-US;q180162
-# -# -#(3) The Excel implementation of the Windows-default 1900-based date system works on the -# incorrect premise that 1900 was a leap year. It interprets the number 60 as meaning 1900-02-29, -# which is not a valid date. Consequently any number less than 61 is ambiguous. Example: is 59 the -# result of 1900-02-28 entered directly, or is it 1900-03-01 minus 2 days? The OpenOffice.org Calc -# program "corrects" the Microsoft problem; entering 1900-02-27 causes the number 59 to be stored. -# Save as an XLS file, then open the file with Excel -- you'll see 1900-02-28 displayed.
-# -#Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;214326
-# -#(4) The Macintosh-default 1904-based date system counts 1904-01-02 as day 1 and 1904-01-01 as day zero. -# Thus any number such that (0.0 <= number < 1.0) is ambiguous. Is 0.625 a time of day (15:00:00), -# independent of the calendar, -# or should it be interpreted as an instant on a particular day (1904-01-01T15:00:00)? -# The xldate_* functions in this module -# take the view that such a number is a calendar-independent time of day (like Python's datetime.time type) for both -# date systems. This is consistent with more recent Microsoft documentation -# (for example, the help file for Excel 2002 which says that the first day -# in the 1904 date system is 1904-01-02). -# -#
(5) Usage of the Excel DATE() function may leave strange dates in a spreadsheet. Quoting the help file, -# in respect of the 1900 date system: "If year is between 0 (zero) and 1899 (inclusive), -# Excel adds that value to 1900 to calculate the year. For example, DATE(108,1,2) returns January 2, 2008 (1900+108)." -# This gimmick, semi-defensible only for arguments up to 99 and only in the pre-Y2K-awareness era, -# means that DATE(1899, 12, 31) is interpreted as 3799-12-31.
-# -#For further information, please refer to the documentation for the xldate_* functions.
-# -#-# A name is used to refer to a cell, a group of cells, a constant -# value, a formula, or a macro. Usually the scope of a name is global -# across the whole workbook. However it can be local to a worksheet. -# For example, if the sales figures are in different cells in -# different sheets, the user may define the name "Sales" in each -# sheet. There are built-in names, like "Print_Area" and -# "Print_Titles"; these two are naturally local to a sheet. -#
-# To inspect the names with a user interface like MS Excel, OOo Calc, -# or Gnumeric, click on Insert/Names/Define. This will show the global -# names, plus those local to the currently selected sheet. -#
-# A Book object provides two dictionaries (name_map and -# name_and_scope_map) and a list (name_obj_list) which allow various -# ways of accessing the Name objects. There is one Name object for -# each NAME record found in the workbook. Name objects have many -# attributes, several of which are relevant only when obj.macro is 1. -#
-# In the examples directory you will find namesdemo.xls which -# showcases the many different ways that names can be used, and -# xlrdnamesAPIdemo.py which offers 3 different queries for inspecting -# the names in your files, and shows how to extract whatever a name is -# referring to. There is currently one "convenience method", -# Name.cell(), which extracts the value in the case where the name -# refers to a single cell. More convenience methods are planned. The -# source code for Name.cell (in __init__.py) is an extra source of -# information on how the Name attributes hang together. -#
-# -#Name information is not extracted from files older than -# Excel 5.0 (Book.biff_version < 50)
-# -#This collection of features, new in xlrd version 0.6.1, is intended -# to provide the information needed to (1) display/render spreadsheet contents -# (say) on a screen or in a PDF file, and (2) copy spreadsheet data to another -# file without losing the ability to display/render it.
-# -#A colour is represented in Excel as a (red, green, blue) ("RGB") tuple -# with each component in range(256). However it is not possible to access an -# unlimited number of colours; each spreadsheet is limited to a palette of 64 different -# colours (24 in Excel 3.0 and 4.0, 8 in Excel 2.0). Colours are referenced by an index -# ("colour index") into this palette. -# -# Colour indexes 0 to 7 represent 8 fixed built-in colours: black, white, red, green, blue, -# yellow, magenta, and cyan.
-#
-# The remaining colours in the palette (8 to 63 in Excel 5.0 and later)
-# can be changed by the user. In the Excel 2003 UI, Tools/Options/Color presents a palette
-# of 7 rows of 8 colours. The last two rows are reserved for use in charts.
-# The correspondence between this grid and the assigned
-# colour indexes is NOT left-to-right top-to-bottom.
-# Indexes 8 to 15 correspond to changeable
-# parallels of the 8 fixed colours -- for example, index 7 is forever cyan;
-# index 15 starts off being cyan but can be changed by the user.
-#
-# The default colour for each index depends on the file version; tables of the defaults
-# are available in the source code. If the user changes one or more colours,
-# a PALETTE record appears in the XLS file -- it gives the RGB values for *all* changeable
-# indexes.
-# Note that colours can be used in "number formats": "[CYAN]...." and "[COLOR8]...." refer
-# to colour index 7; "[COLOR16]...." will produce cyan
-# unless the user changes colour index 15 to something else.
-#
-#
In addition, there are several "magic" colour indexes used by Excel:
-# 0x18 (BIFF3-BIFF4), 0x40 (BIFF5-BIFF8): System window text colour for border lines
-# (used in XF, CF, and WINDOW2 records)
-# 0x19 (BIFF3-BIFF4), 0x41 (BIFF5-BIFF8): System window background colour for pattern background
-# (used in XF and CF records )
-# 0x43: System face colour (dialogue background colour)
-# 0x4D: System window text colour for chart border lines
-# 0x4E: System window background colour for chart areas
-# 0x4F: Automatic colour for chart border lines (seems to be always Black)
-# 0x50: System ToolTip background colour (used in note objects)
-# 0x51: System ToolTip text colour (used in note objects)
-# 0x7FFF: System window text colour for fonts (used in FONT and CF records)
-# Note 0x7FFF appears to be the *default* colour index. It appears quite often in FONT
-# records.
-#
-#
This feature, new in version 0.7.1, is governed by the on_demand argument -# to the open_workbook() function and allows saving memory and time by loading -# only those sheets that the caller is interested in, and releasing sheets -# when no longer required.
-# -#on_demand=False (default): No change. open_workbook() loads global data -# and all sheets, releases resources no longer required (principally the -# str or mmap object containing the Workbook stream), and returns.
-# -#on_demand=True and BIFF version < 5.0: A warning message is emitted, -# on_demand is recorded as False, and the old process is followed.
-# -#on_demand=True and BIFF version >= 5.0: open_workbook() loads global -# data and returns without releasing resources. At this stage, the only -# information available about sheets is Book.nsheets and Book.sheet_names().
-# -#Book.sheet_by_name() and Book.sheet_by_index() will load the requested -# sheet if it is not already loaded.
-# -#Book.sheets() will load all/any unloaded sheets.
-# -#The caller may save memory by calling -# Book.unload_sheet(sheet_name_or_index) when finished with the sheet. -# This applies irrespective of the state of on_demand.
-# -#The caller may re-load an unloaded sheet by calling Book.sheet_by_xxxx() -# -- except if those required resources have been released (which will -# have happened automatically when on_demand is false). This is the only -# case where an exception will be raised.
-# -#The caller may query the state of a sheet: -# Book.sheet_loaded(sheet_name_or_index) -> a bool
-# -#Book.release_resources() may used to save memory and close -# any memory-mapped file before proceding to examine already-loaded -# sheets. Once resources are released, no further sheets can be loaded.
-# -#When using on-demand, it is advisable to ensure that -# Book.release_resources() is always called even if an exception -# is raised in your own code; otherwise if the input file has been -# memory-mapped, the mmap.mmap object will not be closed and you will -# not be able to access the physical file until your Python process -# terminates. This can be done by calling Book.release_resources() -# explicitly in the finally suite of a try/finally block. -# New in xlrd 0.7.2: the Book object is a "context manager", so if -# using Python 2.5 or later, you can wrap your code in a "with" -# statement.
-## - -import sys, zipfile, pprint -import timemachine -from biffh import ( - XLRDError, - biff_text_from_num, +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +import os +import pprint +import sys +import zipfile + +from . import timemachine +from .biffh import ( + XL_CELL_BLANK, XL_CELL_BOOLEAN, XL_CELL_DATE, XL_CELL_EMPTY, XL_CELL_ERROR, + XL_CELL_NUMBER, XL_CELL_TEXT, XLRDError, biff_text_from_num, error_text_from_code, - XL_CELL_BLANK, - XL_CELL_TEXT, - XL_CELL_BOOLEAN, - XL_CELL_ERROR, - XL_CELL_EMPTY, - XL_CELL_DATE, - XL_CELL_NUMBER - ) -from formula import * # is constrained by __all__ -from book import Book, colname #### TODO #### formula also has `colname` (restricted to 256 cols) -from sheet import empty_cell -from xldate import XLDateError, xldate_as_tuple - -if sys.version.startswith("IronPython"): - # print >> sys.stderr, "...importing encodings" - import encodings - -try: - import mmap - MMAP_AVAILABLE = 1 -except ImportError: - MMAP_AVAILABLE = 0 -USE_MMAP = MMAP_AVAILABLE - -## -# -# Open a spreadsheet file for data extraction. -# -# @param filename The path to the spreadsheet file to be opened. -# -# @param logfile An open file to which messages and diagnostics are written. -# -# @param verbosity Increases the volume of trace material written to the logfile. -# -# @param pickleable Default is true. In Python 2.4 or earlier, setting to false -# will cause use of array.array objects which save some memory but can't be pickled. -# In Python 2.5, array.arrays are used unconditionally. Note: if you have large files that -# you need to read multiple times, it can be much faster to cPickle.dump() the xlrd.Book object -# once, and use cPickle.load() multiple times. -# @param use_mmap Whether to use the mmap module is determined heuristically. -# Use this arg to override the result. Current heuristic: mmap is used if it exists. -# -# @param file_contents ... as a string or an mmap.mmap object or some other behave-alike object. -# If file_contents is supplied, filename will not be used, except (possibly) in messages. -# -# @param encoding_override Used to overcome missing or bad codepage information -# in older-version files. Refer to discussion in the Unicode section above. -#Portions copyright © 2005-2010 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under a BSD-style licence.
-## - -# 2010-03-01 SJM Reading SCL record -# 2010-03-01 SJM Added more record IDs for biff_dump & biff_count -# 2008-02-10 SJM BIFF2 BLANK record -# 2008-02-08 SJM Preparation for Excel 2.0 support -# 2008-02-02 SJM Added suffixes (_B2, _B2_ONLY, etc) on record names for biff_dump & biff_count -# 2007-12-04 SJM Added support for Excel 2.x (BIFF2) files. -# 2007-09-08 SJM Avoid crash when zero-length Unicode string missing options byte. -# 2007-04-22 SJM Remove experimental "trimming" facility. +# -*- coding: utf-8 -*- +# Portions copyright © 2005-2010 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +from __future__ import print_function + +import sys +from struct import unpack + +from .timemachine import * DEBUG = 0 -from struct import unpack -import sys -from timemachine import * + class XLRDError(Exception): - pass + """ + An exception indicating problems reading data from an Excel file. + """ -## -# Parent of almost all other classes in the package. Defines a common "dump" method -# for debugging. class BaseObject(object): + """ + Parent of almost all other classes in the package. Defines a common + :meth:`dump` method for debugging. + """ _repr_these = [] - ## - # @param f open file object, to which the dump is written - # @param header text to write before the dump - # @param footer text to write after the dump - # @param indent number of leading spaces (for recursive calls) def dump(self, f=None, header=None, footer=None, indent=0): + """ + :param f: open file object, to which the dump is written + :param header: text to write before the dump + :param footer: text to write after the dump + :param indent: number of leading spaces (for recursive calls) + """ if f is None: f = sys.stderr if hasattr(self, "__slots__"): @@ -48,9 +43,9 @@ def dump(self, f=None, header=None, footer=None, indent=0): alist.append((attr, getattr(self, attr))) else: alist = self.__dict__.items() - alist.sort() + alist = sorted(alist) pad = " " * indent - if header is not None: print >> f, header + if header is not None: print(header, file=f) list_type = type([]) dict_type = type({}) for attr, value in alist: @@ -58,13 +53,12 @@ def dump(self, f=None, header=None, footer=None, indent=0): value.dump(f, header="%s%s (%s object):" % (pad, attr, value.__class__.__name__), indent=indent+4) - elif attr not in self._repr_these and ( - isinstance(value, list_type) or isinstance(value, dict_type) - ): - print >> f, "%s%s: %s, len = %d" % (pad, attr, type(value), len(value)) + elif (attr not in self._repr_these and + (isinstance(value, list_type) or isinstance(value, dict_type))): + print("%s%s: %s, len = %d" % (pad, attr, type(value), len(value)), file=f) else: - print >> f, "%s%s: %r" % (pad, attr, value) - if footer is not None: print >> f, footer + fprintf(f, "%s%s: %r\n", pad, attr, value) + if footer is not None: print(footer, file=f) FUN, FDT, FNU, FGE, FTX = range(5) # unknown, date, number, general, text DATEFORMAT = FDT @@ -91,21 +85,10 @@ def dump(self, f=None, header=None, footer=None, indent=0): 70: "7", 80: "8", 85: "8X", - } - -## -#This dictionary can be used to produce a text version of the internal codes -# that Excel uses for error cells. Here are its contents: -#
-# 0x00: '#NULL!', # Intersection of two cell ranges is empty -# 0x07: '#DIV/0!', # Division by zero -# 0x0F: '#VALUE!', # Wrong type of operand -# 0x17: '#REF!', # Illegal or deleted cell reference -# 0x1D: '#NAME?', # Wrong function or range name -# 0x24: '#NUM!', # Value range overflow -# 0x2A: '#N/A', # Argument or function not available -#+} +#: This dictionary can be used to produce a text version of the internal codes +#: that Excel uses for error cells. error_text_from_code = { 0x00: '#NULL!', # Intersection of two cell ranges is empty 0x07: '#DIV/0!', # Division by zero @@ -245,19 +228,13 @@ def dump(self, f=None, header=None, footer=None, indent=0): XL_NUMBER, XL_RK, XL_RSTRING, - ] +] _cell_opcode_dict = {} for _cell_opcode in _cell_opcode_list: _cell_opcode_dict[_cell_opcode] = 1 -is_cell_opcode = _cell_opcode_dict.has_key - -# def fprintf(f, fmt, *vargs): f.write(fmt % vargs) -def fprintf(f, fmt, *vargs): - if fmt.endswith('\n'): - print >> f, fmt[:-1] % vargs - else: - print >> f, fmt % vargs, +def is_cell_opcode(c): + return c in _cell_opcode_dict def upkbits(tgt_obj, src, manifest, local_setattr=setattr): for n, mask, attr in manifest: @@ -288,9 +265,9 @@ def unpack_unicode(data, pos, lenlen=2): if not nchars: # Ambiguous whether 0-length string should have an "options" byte. # Avoid crash if missing. - return u"" + return UNICODE_LITERAL("") pos += lenlen - options = ord(data[pos]) + options = BYTES_ORD(data[pos]) pos += 1 # phonetic = options & 0x04 # richtext = options & 0x08 @@ -331,8 +308,8 @@ def unpack_unicode_update_pos(data, pos, lenlen=2, known_len=None): pos += lenlen if not nchars and not data[pos:]: # Zero-length string with no options byte - return (u"", pos) - options = ord(data[pos]) + return (UNICODE_LITERAL(""), pos) + options = BYTES_ORD(data[pos]) pos += 1 phonetic = options & 0x04 richtext = options & 0x08 @@ -356,8 +333,7 @@ def unpack_unicode_update_pos(data, pos, lenlen=2, known_len=None): pos += sz return (strg, pos) -def unpack_cell_range_address_list_update_pos( - output_list, data, pos, biff_version, addr_size=6): +def unpack_cell_range_address_list_update_pos(output_list, data, pos, biff_version, addr_size=6): # output_list is updated in situ assert addr_size in (6, 8) # Used to assert size == 6 if not BIFF8, but pyWLWriter writes @@ -557,9 +533,11 @@ def hex_char_dump(strg, ofs, dlen, base=0, fout=sys.stdout, unnumbered=False): '??? hex_char_dump: ofs=%d dlen=%d base=%d -> endpos=%d pos=%d endsub=%d substrg=%r\n', ofs, dlen, base, endpos, pos, endsub, substrg) break - hexd = ''.join(["%02x " % ord(c) for c in substrg]) + hexd = ''.join("%02x " % BYTES_ORD(c) for c in substrg) + chard = '' for c in substrg: + c = chr(BYTES_ORD(c)) if c == '\0': c = '~' elif not (' ' <= c <= '~'): @@ -567,6 +545,7 @@ def hex_char_dump(strg, ofs, dlen, base=0, fout=sys.stdout, unnumbered=False): chard += c if numbered: num_prefix = "%5d: " % (base+pos-ofs) + fprintf(fout, "%s %-48s %s\n", num_prefix, hexd, chard) pos = endsub @@ -580,7 +559,7 @@ def biff_dump(mem, stream_offset, stream_len, base=0, fout=sys.stdout, unnumbere while stream_end - pos >= 4: rc, length = unpack('
Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under a -# BSD-style licence.
- -from timemachine import * -from biffh import * -import struct; unpack = struct.unpack -import sys -import time -import sheet -import compdoc -from xldate import xldate_as_tuple, XLDateError -from formula import * -import formatting -if sys.version.startswith("IronPython"): - # print >> sys.stderr, "...importing encodings" - import encodings +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. -empty_cell = sheet.empty_cell # for exposure to the world ... - -DEBUG = 0 +from __future__ import print_function -USE_FANCY_CD = 1 +import struct -TOGGLE_GC = 0 -import gc -# gc.set_debug(gc.DEBUG_STATS) +from . import compdoc, formatting, sheet +from .biffh import * +from .formula import * +from .timemachine import * try: - import mmap - MMAP_AVAILABLE = 1 + from time import perf_counter except ImportError: - MMAP_AVAILABLE = 0 -USE_MMAP = MMAP_AVAILABLE + # Python 2.7 + from time import clock as perf_counter + +from struct import unpack + +empty_cell = sheet.empty_cell # for exposure to the world ... + +DEBUG = 0 + +import mmap MY_EOF = 0xF00BAAA # not a 16-bit number @@ -41,49 +31,50 @@ SUPPORTED_VERSIONS = (80, 70, 50, 45, 40, 30, 21, 20) -code_from_builtin_name = { - u"Consolidate_Area": u"\x00", - u"Auto_Open": u"\x01", - u"Auto_Close": u"\x02", - u"Extract": u"\x03", - u"Database": u"\x04", - u"Criteria": u"\x05", - u"Print_Area": u"\x06", - u"Print_Titles": u"\x07", - u"Recorder": u"\x08", - u"Data_Form": u"\x09", - u"Auto_Activate": u"\x0A", - u"Auto_Deactivate": u"\x0B", - u"Sheet_Title": u"\x0C", - u"_FilterDatabase": u"\x0D", - } +_code_from_builtin_name = { + "Consolidate_Area": "\x00", + "Auto_Open": "\x01", + "Auto_Close": "\x02", + "Extract": "\x03", + "Database": "\x04", + "Criteria": "\x05", + "Print_Area": "\x06", + "Print_Titles": "\x07", + "Recorder": "\x08", + "Data_Form": "\x09", + "Auto_Activate": "\x0A", + "Auto_Deactivate": "\x0B", + "Sheet_Title": "\x0C", + "_FilterDatabase": "\x0D", +} builtin_name_from_code = {} -for _bin, _bic in code_from_builtin_name.items(): +code_from_builtin_name = {} +for _bin, _bic in _code_from_builtin_name.items(): + _bin = UNICODE_LITERAL(_bin) + _bic = UNICODE_LITERAL(_bic) + code_from_builtin_name[_bin] = _bic builtin_name_from_code[_bic] = _bin -del _bin, _bic +del _bin, _bic, _code_from_builtin_name def open_workbook_xls(filename=None, - logfile=sys.stdout, verbosity=0, pickleable=True, use_mmap=USE_MMAP, - file_contents=None, - encoding_override=None, - formatting_info=False, on_demand=False, ragged_rows=False, - ): - t0 = time.clock() - if TOGGLE_GC: - orig_gc_enabled = gc.isenabled() - if orig_gc_enabled: - gc.disable() + logfile=sys.stdout, verbosity=0, use_mmap=True, + file_contents=None, + encoding_override=None, + formatting_info=False, on_demand=False, ragged_rows=False, + ignore_workbook_corruption=False): + t0 = perf_counter() bk = Book() try: bk.biff2_8_load( filename=filename, file_contents=file_contents, - logfile=logfile, verbosity=verbosity, pickleable=pickleable, use_mmap=use_mmap, + logfile=logfile, verbosity=verbosity, use_mmap=use_mmap, encoding_override=encoding_override, formatting_info=formatting_info, on_demand=on_demand, ragged_rows=ragged_rows, - ) - t1 = time.clock() + ignore_workbook_corruption=ignore_workbook_corruption + ) + t1 = perf_counter() bk.load_time_stage_1 = t1 - t0 biff_version = bk.getbof(XL_WORKBOOK_GLOBALS) if not biff_version: @@ -92,7 +83,7 @@ def open_workbook_xls(filename=None, raise XLRDError( "BIFF version %s is not supported" % biff_text_from_num[biff_version] - ) + ) bk.biff_version = biff_version if biff_version <= 40: # no workbook globals, only 1 worksheet @@ -116,17 +107,15 @@ def open_workbook_xls(filename=None, bk.get_sheets() bk.nsheets = len(bk._sheet_list) if biff_version == 45 and bk.nsheets > 1: - fprintf(bk.logfile, + fprintf( + bk.logfile, "*** WARNING: Excel 4.0 workbook (.XLW) file contains %d worksheets.\n" "*** Book-level data will be that of the last worksheet.\n", bk.nsheets - ) - if TOGGLE_GC: - if orig_gc_enabled: - gc.enable() - t2 = time.clock() + ) + t2 = perf_counter() bk.load_time_stage_2 = t2 - t1 - except: + except Exception: bk.release_resources() raise # normal exit @@ -134,107 +123,87 @@ def open_workbook_xls(filename=None, bk.release_resources() return bk -## -# For debugging: dump the file's BIFF records in char & hex. -# @param filename The path to the file to be dumped. -# @param outfile An open file, to which the dump is written. -# @param unnumbered If true, omit offsets (for meaningful diffs). - -def dump(filename, outfile=sys.stdout, unnumbered=False): - bk = Book() - bk.biff2_8_load(filename=filename, logfile=outfile, ) - biff_dump(bk.mem, bk.base, bk.stream_len, 0, outfile, unnumbered) - -## -# For debugging and analysis: summarise the file's BIFF records. -# I.e. produce a sorted file of (record_name, count). -# @param filename The path to the file to be summarised. -# @param outfile An open file, to which the summary is written. - -def count_records(filename, outfile=sys.stdout): - bk = Book() - bk.biff2_8_load(filename=filename, logfile=outfile, ) - biff_count_records(bk.mem, bk.base, bk.stream_len, outfile) - -## -# Information relating to a named reference, formula, macro, etc. -#WARNING: You don't call this class yourself. You use the Book object that -# was returned when you called xlrd.open_workbook("myfile.xls").
class Book(BaseObject): + """ + Contents of a "workbook". - ## - # The number of worksheets present in the workbook file. - # This information is available even when no sheets have yet been loaded. - nsheets = 0 + .. warning:: - ## - # Which date system was in force when this file was last saved.Copyright � 2005-2012 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under a BSD-style licence.
-## - -# No part of the content of this file was derived from the works of David Giffin. - -# 2008-11-04 SJM Avoid assertion error when -1 used instead of -2 for first_SID of empty SCSS [Frank Hoffsuemmer] -# 2007-09-08 SJM Warning message if sector sizes are extremely large. -# 2007-05-07 SJM Meaningful exception instead of IndexError if a SAT (sector allocation table) is corrupted. -# 2007-04-22 SJM Missing "<" in a struct.unpack call => can't open files on bigendian platforms. - -from __future__ import nested_scopes +import array import sys from struct import unpack -from timemachine import * -import array -## -# Magic cookie that should appear in the first 8 bytes of the file. -SIGNATURE = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" +from .timemachine import * + +#: Magic cookie that should appear in the first 8 bytes of the file. +SIGNATURE = b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" EOCSID = -2 FREESID = -1 @@ -46,7 +41,7 @@ def __init__(self, DID, dent, DEBUG=0, logfile=sys.stdout): (self.first_SID, self.tot_size) = \ unpack('Implements the minimal functionality required -to extract a "Workbook" or "Book" stream (as one big string) -from an OLE2 Compound Document file. -
Copyright © 2005-2012 Stephen John Machin, Lingfo Pty Ltd
-This module is part of the xlrd package, which is released under a BSD-style licence.
-Compound document handler.
-For more information about this class, see The CompDoc Class.
-Magic cookie that should appear in the first 8 bytes of the file.
-Compound document handler.
-Interrogate the compound document's directory; return the stream as a string if found, otherwise -return None.
-Interrogate the compound document's directory. -If the named stream is not found, (None, 0, 0) will be returned. -If the named stream is found and is contiguous within the original byte sequence ("mem") -used when the document was opened, -then (mem, offset_to_start_of_stream, length_of_stream) is returned. -Otherwise a new string is built from the fragments and (new_string, 0, length_of_stream) is returned.
-A Python module for extracting data from MS Excel (TM) spreadsheet files.
-
-Version 0.7.4 -- April 2012
-
-Development of this module would not have been possible without the document -"OpenOffice.org's Documentation of the Microsoft Excel File Format" -("OOo docs" for short). -The latest version is available from OpenOffice.org in - PDF format -and - ODT format. -Small portions of the OOo docs are reproduced in this -document. A study of the OOo docs is recommended for those who wish a -deeper understanding of the Excel file layout than the xlrd docs can provide. -
- -Backporting to Python 2.1 was partially funded by - - Journyx - provider of timesheet and project accounting solutions. - -
- -Provision of formatting information in version 0.6.1 was funded by - - Simplistix Ltd. - -
- -This module presents all text strings as Python unicode objects. -From Excel 97 onwards, text in Excel spreadsheets has been stored as Unicode. -Older files (Excel 95 and earlier) don't keep strings in Unicode; -a CODEPAGE record provides a codepage number (for example, 1252) which is -used by xlrd to derive the encoding (for same example: "cp1252") which is -used to translate to Unicode.
- -If the CODEPAGE record is missing (possible if the file was created -by third-party software), xlrd will assume that the encoding is ascii, and keep going. -If the actual encoding is not ascii, a UnicodeDecodeError exception will be raised and -you will need to determine the encoding yourself, and tell xlrd: -
- book = xlrd.open_workbook(..., encoding_override="cp1252") --
If the CODEPAGE record exists but is wrong (for example, the codepage -number is 1251, but the strings are actually encoded in koi8_r), -it can be overridden using the same mechanism. -The supplied runxlrd.py has a corresponding command-line argument, which -may be used for experimentation: -
- runxlrd.py -e koi8_r 3rows myfile.xls --
The first place to look for an encoding ("codec name") is - -the Python documentation. -
- - -In reality, there are no such things. What you have are floating point -numbers and pious hope. -There are several problems with Excel dates:
- -(1) Dates are not stored as a separate data type; they are stored as -floating point numbers and you have to rely on -(a) the "number format" applied to them in Excel and/or -(b) knowing which cells are supposed to have dates in them. -This module helps with (a) by inspecting the -format that has been applied to each number cell; -if it appears to be a date format, the cell -is classified as a date rather than a number. Feedback on this feature, -especially from non-English-speaking locales, would be appreciated.
- -(2) Excel for Windows stores dates by default as the number of -days (or fraction thereof) since 1899-12-31T00:00:00. Excel for -Macintosh uses a default start date of 1904-01-01T00:00:00. The date -system can be changed in Excel on a per-workbook basis (for example: -Tools -> Options -> Calculation, tick the "1904 date system" box). -This is of course a bad idea if there are already dates in the -workbook. There is no good reason to change it even if there are no -dates in the workbook. Which date system is in use is recorded in the -workbook. A workbook transported from Windows to Macintosh (or vice -versa) will work correctly with the host Excel. When using this -module's xldate_as_tuple function to convert numbers from a workbook, -you must use the datemode attribute of the Book object. If you guess, -or make a judgement depending on where you believe the workbook was -created, you run the risk of being 1462 days out of kilter.
- -Reference: -http://support.microsoft.com/default.aspx?scid=KB;EN-US;q180162
- - -(3) The Excel implementation of the Windows-default 1900-based date system works on the -incorrect premise that 1900 was a leap year. It interprets the number 60 as meaning 1900-02-29, -which is not a valid date. Consequently any number less than 61 is ambiguous. Example: is 59 the -result of 1900-02-28 entered directly, or is it 1900-03-01 minus 2 days? The OpenOffice.org Calc -program "corrects" the Microsoft problem; entering 1900-02-27 causes the number 59 to be stored. -Save as an XLS file, then open the file with Excel -- you'll see 1900-02-28 displayed.
- -Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;214326
- -(4) The Macintosh-default 1904-based date system counts 1904-01-02 as day 1 and 1904-01-01 as day zero. -Thus any number such that (0.0 <= number < 1.0) is ambiguous. Is 0.625 a time of day (15:00:00), -independent of the calendar, -or should it be interpreted as an instant on a particular day (1904-01-01T15:00:00)? -The xldate_* functions in this module -take the view that such a number is a calendar-independent time of day (like Python's datetime.time type) for both -date systems. This is consistent with more recent Microsoft documentation -(for example, the help file for Excel 2002 which says that the first day -in the 1904 date system is 1904-01-02). - -
(5) Usage of the Excel DATE() function may leave strange dates in a spreadsheet. Quoting the help file, -in respect of the 1900 date system: "If year is between 0 (zero) and 1899 (inclusive), -Excel adds that value to 1900 to calculate the year. For example, DATE(108,1,2) returns January 2, 2008 (1900+108)." -This gimmick, semi-defensible only for arguments up to 99 and only in the pre-Y2K-awareness era, -means that DATE(1899, 12, 31) is interpreted as 3799-12-31.
- -For further information, please refer to the documentation for the xldate_* functions.
- --A name is used to refer to a cell, a group of cells, a constant -value, a formula, or a macro. Usually the scope of a name is global -across the whole workbook. However it can be local to a worksheet. -For example, if the sales figures are in different cells in -different sheets, the user may define the name "Sales" in each -sheet. There are built-in names, like "Print_Area" and -"Print_Titles"; these two are naturally local to a sheet. -
-To inspect the names with a user interface like MS Excel, OOo Calc, -or Gnumeric, click on Insert/Names/Define. This will show the global -names, plus those local to the currently selected sheet. -
-A Book object provides two dictionaries (name_map and -name_and_scope_map) and a list (name_obj_list) which allow various -ways of accessing the Name objects. There is one Name object for -each NAME record found in the workbook. Name objects have many -attributes, several of which are relevant only when obj.macro is 1. -
-In the examples directory you will find namesdemo.xls which -showcases the many different ways that names can be used, and -xlrdnamesAPIdemo.py which offers 3 different queries for inspecting -the names in your files, and shows how to extract whatever a name is -referring to. There is currently one "convenience method", -Name.cell(), which extracts the value in the case where the name -refers to a single cell. More convenience methods are planned. The -source code for Name.cell (in __init__.py) is an extra source of -information on how the Name attributes hang together. -
- -Name information is not extracted from files older than -Excel 5.0 (Book.biff_version < 50)
- -This collection of features, new in xlrd version 0.6.1, is intended -to provide the information needed to (1) display/render spreadsheet contents -(say) on a screen or in a PDF file, and (2) copy spreadsheet data to another -file without losing the ability to display/render it.
- -A colour is represented in Excel as a (red, green, blue) ("RGB") tuple -with each component in range(256). However it is not possible to access an -unlimited number of colours; each spreadsheet is limited to a palette of 64 different -colours (24 in Excel 3.0 and 4.0, 8 in Excel 2.0). Colours are referenced by an index -("colour index") into this palette. - -Colour indexes 0 to 7 represent 8 fixed built-in colours: black, white, red, green, blue, -yellow, magenta, and cyan.
-
-The remaining colours in the palette (8 to 63 in Excel 5.0 and later)
-can be changed by the user. In the Excel 2003 UI, Tools/Options/Color presents a palette
-of 7 rows of 8 colours. The last two rows are reserved for use in charts.
-The correspondence between this grid and the assigned
-colour indexes is NOT left-to-right top-to-bottom.
-Indexes 8 to 15 correspond to changeable
-parallels of the 8 fixed colours -- for example, index 7 is forever cyan;
-index 15 starts off being cyan but can be changed by the user.
-
-The default colour for each index depends on the file version; tables of the defaults
-are available in the source code. If the user changes one or more colours,
-a PALETTE record appears in the XLS file -- it gives the RGB values for *all* changeable
-indexes.
-Note that colours can be used in "number formats": "[CYAN]...." and "[COLOR8]...." refer
-to colour index 7; "[COLOR16]...." will produce cyan
-unless the user changes colour index 15 to something else.
-
-
In addition, there are several "magic" colour indexes used by Excel:
-0x18 (BIFF3-BIFF4), 0x40 (BIFF5-BIFF8): System window text colour for border lines
-(used in XF, CF, and WINDOW2 records)
-0x19 (BIFF3-BIFF4), 0x41 (BIFF5-BIFF8): System window background colour for pattern background
-(used in XF and CF records )
-0x43: System face colour (dialogue background colour)
-0x4D: System window text colour for chart border lines
-0x4E: System window background colour for chart areas
-0x4F: Automatic colour for chart border lines (seems to be always Black)
-0x50: System ToolTip background colour (used in note objects)
-0x51: System ToolTip text colour (used in note objects)
-0x7FFF: System window text colour for fonts (used in FONT and CF records)
-Note 0x7FFF appears to be the *default* colour index. It appears quite often in FONT
-records.
-
-
This feature, new in version 0.7.1, is governed by the on_demand argument -to the open_workbook() function and allows saving memory and time by loading -only those sheets that the caller is interested in, and releasing sheets -when no longer required.
- -on_demand=False (default): No change. open_workbook() loads global data -and all sheets, releases resources no longer required (principally the -str or mmap object containing the Workbook stream), and returns.
- -on_demand=True and BIFF version < 5.0: A warning message is emitted, -on_demand is recorded as False, and the old process is followed.
- -on_demand=True and BIFF version >= 5.0: open_workbook() loads global -data and returns without releasing resources. At this stage, the only -information available about sheets is Book.nsheets and Book.sheet_names().
- -Book.sheet_by_name() and Book.sheet_by_index() will load the requested -sheet if it is not already loaded.
- -Book.sheets() will load all/any unloaded sheets.
- -The caller may save memory by calling -Book.unload_sheet(sheet_name_or_index) when finished with the sheet. -This applies irrespective of the state of on_demand.
- -The caller may re-load an unloaded sheet by calling Book.sheet_by_xxxx() - -- except if those required resources have been released (which will -have happened automatically when on_demand is false). This is the only -case where an exception will be raised.
- -The caller may query the state of a sheet: -Book.sheet_loaded(sheet_name_or_index) -> a bool
- -Book.release_resources() may used to save memory and close -any memory-mapped file before proceding to examine already-loaded -sheets. Once resources are released, no further sheets can be loaded.
- -When using on-demand, it is advisable to ensure that -Book.release_resources() is always called even if an exception -is raised in your own code; otherwise if the input file has been -memory-mapped, the mmap.mmap object will not be closed and you will -not be able to access the physical file until your Python process -terminates. This can be done by calling Book.release_resources() -explicitly in the finally suite of a try/finally block. -New in xlrd 0.7.2: the Book object is a "context manager", so if -using Python 2.5 or later, you can wrap your code in a "with" -statement.
-Parent of almost all other classes in the package.
-For more information about this class, see The BaseObject Class.
-Contents of a "workbook".
-For more information about this class, see The Book Class.
-Contains the data for one cell.
-For more information about this class, see The Cell Class.
-Utility function: (5, 7) => 'H6'
-Utility function: (5, 7) => '$H$6'
-Width and default formatting information that applies to one or -more columns in a sheet.
-For more information about this class, see The Colinfo Class.
-Utility function: 7 => 'H', 27 => 'AB'
-For debugging and analysis: summarise the file's BIFF records. -I.e. produce a sorted file of (record_name, count).
-For debugging: dump the file's BIFF records in char & hex. -
There is one and only one instance of an empty cell -- it's a singleton. This is it. -You may use a test like "acell is empty_cell".
-This mixin class exists solely so that Format, Font, and XF....
-For more information about this class, see The EqNeAttrs Class.
-This dictionary can be used to produce a text version of the internal codes -that Excel uses for error cells. Here are its contents: -
-0x00: '#NULL!', # Intersection of two cell ranges is empty -0x07: '#DIV/0!', # Division by zero -0x0F: '#VALUE!', # Wrong type of operand -0x17: '#REF!', # Illegal or deleted cell reference -0x1D: '#NAME?', # Wrong function or range name -0x24: '#NUM!', # Value range overflow -0x2A: '#N/A', # Argument or function not available --
An Excel "font" contains the details of not only what is normally -considered a font, but also several other display attributes.
-For more information about this class, see The Font Class.
-"Number format" information from a FORMAT record.
-For more information about this class, see The Format Class.
-Contains the attributes of a hyperlink.
-For more information about this class, see The Hyperlink Class.
-Information relating to a named reference, formula, macro, etc.
-For more information about this class, see The Name Class.
-Represents a user "comment" or "note".
-For more information about this class, see The Note Class.
-Open a spreadsheet file for data extraction.
-Used in evaluating formulas.
-For more information about this class, see The Operand Class.
-Utility function:
-
Ref3D((1, 4, 5, 20, 7, 10)) => 'Sheet2:Sheet3!$H$6:$J$20'
-
Utility function:
-
Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))
-R1C1 mode => 'Sheet1!R[-32]C[-13]:R[-23]C[12]'
-A1 mode => depends on base cell (browx, bcolx)
-
Represents an absolute or relative 3-dimensional reference to a box -of one or more cells.
-For more information about this class, see The Ref3D Class.
-Height and default formatting information that applies to a row in a sheet.
-For more information about this class, see The Rowinfo Class.
-Contains the data for one worksheet.
-For more information about this class, see The Sheet Class.
-eXtended Formatting information for cells, rows, columns and styles.
-For more information about this class, see The XF Class.
-A collection of the alignment and similar attributes of an XF record.
-For more information about this class, see The XFAlignment Class.
-A collection of the background-related attributes of an XF record.
-For more information about this class, see The XFBackground Class.
-A collection of the border-related attributes of an XF record.
-For more information about this class, see The XFBorder Class.
-A collection of the protection-related attributes of an XF record.
-For more information about this class, see The XFProtection Class.
-Convert an Excel number (presumed to represent a date, a datetime or a time) into -a tuple suitable for feeding to datetime or mx.DateTime constructors.
-Convert a date tuple (year, month, day) to an Excel date.
-Convert a datetime tuple (year, month, day, hour, minute, second) to an Excel date value. -For more details, refer to other xldate_from_*_tuple functions.
-Convert a time tuple (hour, minute, second) to an Excel "date" value (fraction of a day).
-Parent of almost all other classes in the package. Defines a common "dump" method -for debugging.
-Contents of a "workbook". -
WARNING: You don't call this class yourself. You use the Book object that -was returned when you called xlrd.open_workbook("myfile.xls").
-Version of BIFF (Binary Interchange File Format) used to create the file. -Latest is 8.0 (represented here as 80), introduced with Excel 97. -Earliest supported by this module: 2.0 (represented as 20).
-An integer denoting the character set used for strings in this file. -For BIFF 8 and later, this will be 1200, meaning Unicode; more precisely, UTF_16_LE. -For earlier versions, this is used to derive the appropriate Python encoding -to be used to convert to Unicode. -Examples: 1252 -> 'cp1252', 10000 -> 'mac_roman'
-This provides definitions for colour indexes. Please refer to the
-above section "The Palette; Colour Indexes" for an explanation
-of how colours are represented in Excel.
-Colour indexes into the palette map into (red, green, blue) tuples.
-"Magic" indexes e.g. 0x7FFF map to None.
-colour_map is what you need if you want to render cells on screen or in a PDF
-file. If you are writing an output XLS file, use palette_record.
-
-- New in version 0.6.1. Extracted only if open_workbook(..., formatting_info=True)
-
A tuple containing the (telephone system) country code for:
- [0]: the user-interface setting when the file was created.
- [1]: the regional settings.
-Example: (1, 61) meaning (USA, Australia).
-This information may give a clue to the correct encoding for an unknown codepage.
-For a long list of observed values, refer to the OpenOffice.org documentation for
-the COUNTRY record.
-
Which date system was in force when this file was last saved.
- 0 => 1900 system (the Excel for Windows default).
- 1 => 1904 system (the Excel for Macintosh default).
-
The encoding that was derived from the codepage.
-A list of Font class instances, each corresponding to a FONT record.
-
-- New in version 0.6.1
-
A list of Format objects, each corresponding to a FORMAT record, in
-the order that they appear in the input file.
-It does not contain builtin formats.
-If you are creating an output file using (for example) pyExcelerator,
-use this list.
-The collection to be used for all visual rendering purposes is format_map.
-
-- New in version 0.6.1
-
The mapping from XF.format_key to Format object.
-
-- New in version 0.6.1
-
Time in seconds to extract the XLS image as a contiguous string (or mmap equivalent).
-Time in seconds to parse the data from the contiguous string (or mmap equivalent).
-A mapping from (lower_case_name, scope) to a single Name object.
-
-- New in version 0.6.0
-
A mapping from lower_case_name to a list of Name objects. The list is
-sorted in scope order. Typically there will be one item (of global scope)
-in the list.
-
-- New in version 0.6.0
-
List containing a Name object for each NAME record in the workbook.
-
-- New in version 0.6.0
-
The number of worksheets present in the workbook file. -This information is available even when no sheets have yet been loaded.
-If the user has changed any of the colours in the standard palette, the XLS
-file will contain a PALETTE record with 56 (16 for Excel 4.0 and earlier)
-RGB values in it, and this list will be e.g. [(r0, b0, g0), ..., (r55, b55, g55)].
-Otherwise this list will be empty. This is what you need if you are
-writing an output XLS file. If you want to render cells on screen or in a PDF
-file, use colour_map.
-
-- New in version 0.6.1. Extracted only if open_workbook(..., formatting_info=True)
-
This method has a dual purpose. You can call it to release -memory-consuming objects and (possibly) a memory-mapped file -(mmap.mmap object) when you have finished loading sheets in -on_demand mode, but still require the Book object to examine the -loaded sheets. It is also called automatically (a) when open_workbook -raises an exception and (b) if you are using a "with" statement, when -the "with" block is exited. Calling this method multiple times on the -same object has no ill effect.
-This provides access via name to the extended format information for
-both built-in styles and user-defined styles.
-It maps name to (built_in, xf_index), where:
-name is either the name of a user-defined style,
-or the name of one of the built-in styles. Known built-in names are
-Normal, RowLevel_1 to RowLevel_7,
-ColLevel_1 to ColLevel_7, Comma, Currency, Percent, "Comma [0]",
-"Currency [0]", Hyperlink, and "Followed Hyperlink".
-built_in 1 = built-in style, 0 = user-defined
-xf_index is an index into Book.xf_list.
-References: OOo docs s6.99 (STYLE record); Excel UI Format/Style
-
-- New in version 0.6.1; since 0.7.4, extracted only if
-open_workbook(..., formatting_info=True)
-
What (if anything) is recorded as the name of the last user to save the file.
-A list of XF class instances, each corresponding to an XF record.
-
-- New in version 0.6.1
-
Contains the data for one cell.
- -WARNING: You don't call this class yourself. You access Cell objects -via methods of the Sheet object(s) that you found in the Book object that -was returned when you called xlrd.open_workbook("myfile.xls").
-Cell objects have three attributes: ctype is an int, value -(which depends on ctype) and xf_index. -If "formatting_info" is not enabled when the workbook is opened, xf_index will be None. -The following table describes the types of cells and how their values -are represented in Python.
- -| Type symbol | -Type number | -Python value | -
|---|---|---|
| XL_CELL_EMPTY | -0 | -empty string u'' | -
| XL_CELL_TEXT | -1 | -a Unicode string | -
| XL_CELL_NUMBER | -2 | -float | -
| XL_CELL_DATE | -3 | -float | -
| XL_CELL_BOOLEAN | -4 | -int; 1 means TRUE, 0 means FALSE | -
| XL_CELL_ERROR | -5 | -int representing internal Excel codes; for a text representation, -refer to the supplied dictionary error_text_from_code | -
| XL_CELL_BLANK | -6 | -empty string u''. Note: this type will appear only when -open_workbook(..., formatting_info=True) is used. | -
Width and default formatting information that applies to one or -more columns in a sheet. Derived from COLINFO records. - -
Here is the default hierarchy for width, according to the OOo docs:
-
-
"""In BIFF3, if a COLINFO record is missing for a column,
-the width specified in the record DEFCOLWIDTH is used instead.
-
-
In BIFF4-BIFF7, the width set in this [COLINFO] record is only used,
-if the corresponding bit for this column is cleared in the GCW
-record, otherwise the column width set in the DEFCOLWIDTH record
-is used (the STANDARDWIDTH record is always ignored in this case [see footnote!]).
-
-
In BIFF8, if a COLINFO record is missing for a column,
-the width specified in the record STANDARDWIDTH is used.
-If this [STANDARDWIDTH] record is also missing,
-the column width of the record DEFCOLWIDTH is used instead."""
-
-
-Footnote: The docs on the GCW record say this:
-"""
-If a bit is set, the corresponding column uses the width set in the STANDARDWIDTH
-record. If a bit is cleared, the corresponding column uses the width set in the
-COLINFO record for this column.
-
If a bit is set, and the worksheet does not contain the STANDARDWIDTH record, or if
-the bit is cleared, and the worksheet does not contain the COLINFO record, the DEFCOLWIDTH
-record of the worksheet will be used instead.
-
"""
-At the moment (2007-01-17) xlrd is going with the GCW version of the story.
-Reference to the source may be useful: see the computed_column_width(colx) method
-of the Sheet class.
-
-- New in version 0.6.1
-
Value of a 1-bit flag whose purpose is unknown -but is often seen set to 1
-1 = column is collapsed
-1 = column is hidden
-Outline level of the column, in range(7). -(0 = no outline)
-Width of the column in 1/256 of the width of the zero character, -using default font (first FONT record in the file).
-XF index to be used for formatting empty cells.
-This mixin class exists solely so that Format, Font, and XF.... objects -can be compared by value of their attributes.
-An Excel "font" contains the details of not only what is normally
-considered a font, but also several other display attributes.
-Items correspond to those in the Excel UI's Format/Cells/Font tab.
-
-- New in version 0.6.1
-
1 = Characters are bold. Redundant; see "weight" attribute.
-Values: 0 = ANSI Latin, 1 = System default, 2 = Symbol, -77 = Apple Roman, -128 = ANSI Japanese Shift-JIS, -129 = ANSI Korean (Hangul), -130 = ANSI Korean (Johab), -134 = ANSI Chinese Simplified GBK, -136 = ANSI Chinese Traditional BIG5, -161 = ANSI Greek, -162 = ANSI Turkish, -163 = ANSI Vietnamese, -177 = ANSI Hebrew, -178 = ANSI Arabic, -186 = ANSI Baltic, -204 = ANSI Cyrillic, -222 = ANSI Thai, -238 = ANSI Latin II (Central European), -255 = OEM Latin I
-An explanation of "colour index" is given in the Formatting -section at the start of this document.
-1 = Superscript, 2 = Subscript.
-0 = None (unknown or don't care)
-1 = Roman (variable width, serifed)
-2 = Swiss (variable width, sans-serifed)
-3 = Modern (fixed width, serifed or sans-serifed)
-4 = Script (cursive)
-5 = Decorative (specialised, for example Old English, Fraktur)
-
The 0-based index used to refer to this Font() instance. -Note that index 4 is never used; xlrd supplies a dummy place-holder.
-Height of the font (in twips). A twip = 1/20 of a point.
-1 = Characters are italic.
-The name of the font. Example: u"Arial"
-1 = Font is outline style (Macintosh only)
-1 = Font is shadow style (Macintosh only)
-1 = Characters are struck out.
-0 = None
-1 = Single; 0x21 (33) = Single accounting
-2 = Double; 0x22 (34) = Double accounting
-
1 = Characters are underlined. Redundant; see "underline_type" attribute.
-Font weight (100-1000). Standard values are 400 for normal text -and 700 for bold text.
-"Number format" information from a FORMAT record.
-
-- New in version 0.6.1
-
The key into Book.format_map
-The format string
-A classification that has been inferred from the format string.
-Currently, this is used only to distinguish between numbers and dates.
-
Values:
-
FUN = 0 # unknown
-
FDT = 1 # date
-
FNU = 2 # number
-
FGE = 3 # general
-
FTX = 4 # text
-
Contains the attributes of a hyperlink.
-Hyperlink objects are accessible through Sheet.hyperlink_list
-and Sheet.hyperlink_map.
-
-- New in version 0.7.2
-
Description ... this is displayed in the cell, -and should be identical to the cell value. Unicode string, or None. It seems -impossible NOT to have a description created by the Excel UI.
-Index of first column
-Index of first row
-Index of last column
-Index of last row
-The text of the "quick tip" displayed when the cursor -hovers over the hyperlink.
-Target frame. Unicode string. Note: I have not seen a case of this. -It seems impossible to create one in the Excel UI.
-"Textmark": the piece after the "#" in -"http://docs.python.org/library#struct_module", or the Sheet1!A1:Z99 -part when type is "workbook".
-Type of hyperlink. Unicode string, one of 'url', 'unc', -'local file', 'workbook', 'unknown'
-The URL or file-path, depending in the type. Unicode string, except -in the rare case of a local but non-existent file with non-ASCII -characters in the name, in which case only the "8.3" filename is available, -as a bytes (3.x) or str (2.x) string, with unknown encoding. -
Information relating to a named reference, formula, macro, etc.
-
-- New in version 0.6.0
-
-- Name information is not extracted from files older than
-Excel 5.0 (Book.biff_version < 50)
-
This is a convenience method for the use case where the name -refers to one rectangular area in one worksheet.
-0 = Formula definition; 1 = Binary data
No examples have been sighted.
-
0 = User-defined name; 1 = Built-in name -(common examples: Print_Area, Print_Titles; see OOo docs for full list)
-This is a convenience method for the frequent use case where the name -refers to a single cell.
-0 = Simple formula; 1 = Complex formula (array formula or user defined)
-No examples have been sighted.
-
0 = Command macro; 1 = Function macro. Relevant only if macro == 1
-Function group. Relevant only if macro == 1; see OOo docs for values.
-0 = Visible; 1 = Hidden
-0 = Standard name; 1 = Macro name
-A Unicode string. If builtin, decoded as per OOo docs.
-The index of this object in book.name_obj_list
-An 8-bit string.
-The result of evaluating the formula, if any. -If no formula, or evaluation of the formula encountered problems, -the result is None. Otherwise the result is a single instance of the -Operand class.
--1: The name is global (visible in all calculation sheets).
--2: The name belongs to a macro sheet or VBA sheet.
--3: The name is invalid.
-0 <= scope < book.nsheets: The name is local to the sheet whose index is scope.
-
0 = Sheet macro; 1 = VisualBasic macro. Relevant only if macro == 1
- Represents a user "comment" or "note".
-Note objects are accessible through Sheet.cell_note_map.
-
-- New in version 0.7.2
-
Author of note
-True if the containing column is hidden
-Column index
-List of (offset_in_string, font_index) tuples. -Unlike Sheet.rich_text_runlist_map, the first offset should always be 0. -
True if the containing row is hidden
-Row index
-True if note is always shown
-Text of the note
-Used in evaluating formulas. -The following table describes the kinds and how their values -are represented.
- -| Kind symbol | -Kind number | -Value representation | -
|---|---|---|
| oBOOL | -3 | -integer: 0 => False; 1 => True | -
| oERR | -4 | -None, or an int error code (same as XL_CELL_ERROR in the Cell class). - | -
| oMSNG | -5 | -Used by Excel as a placeholder for a missing (not supplied) function -argument. Should *not* appear as a final formula result. Value is None. | -
| oNUM | -2 | -A float. Note that there is no way of distinguishing dates. | -
| oREF | --1 | -The value is either None or a non-empty list of
-absolute Ref3D instances. - |
-
| oREL | --2 | -The value is None or a non-empty list of -fully or partially relative Ref3D instances. - | -
| oSTRG | -1 | -A Unicode string. | -
| oUNK | -0 | -The kind is unknown or ambiguous. The value is None | -
oUNK means that the kind of operand is not known unambiguously.
-The reconstituted text of the original formula. Function names will be -in English irrespective of the original language, which doesn't seem -to be recorded anywhere. The separator is ",", not ";" or whatever else -might be more appropriate for the end-user's locale; patches welcome.
-None means that the actual value of the operand is a variable -(depends on cell data), not a constant.
-Represents an absolute or relative 3-dimensional reference to a box
-of one or more cells.
--- New in version 0.6.0
-
The coords attribute is a tuple of the form:
-(shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)
-where 0 <= thingxlo <= thingx < thingxhi.
-Note that it is quite possible to have thingx > nthings; for example
-Print_Titles could have colxhi == 256 and/or rowxhi == 65536
-irrespective of how many columns/rows are actually used in the worksheet.
-The caller will need to decide how to handle this situation.
-Keyword: IndexError :-)
-
The components of the coords attribute are also available as individual -attributes: shtxlo, shtxhi, rowxlo, rowxhi, colxlo, and colxhi.
- -The relflags attribute is a 6-tuple of flags which indicate whether
-the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0).
-Note that there is necessarily no information available as to what cell(s)
-the reference could possibly be relative to. The caller must decide what if
-any use to make of oREL operands. Note also that a partially relative
-reference may well be a typo.
-For example, define name A1Z10 as $a$1:$z10 (missing $ after z)
-while the cursor is on cell Sheet3!A27.
-The resulting Ref3D instance will have coords = (2, 3, 0, -16, 0, 26)
-and relflags = (0, 0, 0, 1, 0, 0).
-So far, only one possibility of a sheet-relative component in
-a reference has been noticed: a 2D reference located in the "current sheet".
-
This will appear as coords = (0, 1, ...) and relflags = (1, 1, ...).
-
Height and default formatting information that applies to a row in a sheet.
-Derived from ROW records.
-
-- New in version 0.6.1
height: Height of the row, in twips. One twip == 1/20 of a point.
- -has_default_height: 0 = Row has custom height; 1 = Row has default height.
- -outline_level: Outline level of the row (0 to 7)
- -outline_group_starts_ends: 1 = Outline group starts or ends here (depending on where the -outline buttons are located, see WSBOOL record [TODO ??]), -and is collapsed
- -hidden: 1 = Row is hidden (manually, or by a filter or outline group)
- -height_mismatch: 1 = Row height and default font height do not match
- -has_default_xf_index: 1 = the xf_index attribute is usable; 0 = ignore it
- -xf_index: Index to default XF record for empty cells in this row. -Don't use this if has_default_xf_index == 0.
- -additional_space_above: This flag is set, if the upper border of at least one cell in this row -or if the lower border of at least one cell in the row above is -formatted with a thick line style. Thin and medium line styles are not -taken into account.
- -additional_space_below: This flag is set, if the lower border of at least one cell in this row -or if the upper border of at least one cell in the row below is -formatted with a medium or thick line style. Thin line styles are not -taken into account.
-Contains the data for one worksheet.
- -In the cell access functions, "rowx" is a row index, counting from zero, and "colx" is a -column index, counting from zero. -Negative values for row/column indexes and slice positions are supported in the expected fashion.
- -For information about cell types and cell values, refer to the documentation of the Cell class.
- -WARNING: You don't call this class yourself. You access Sheet objects via the Book object that -was returned when you called xlrd.open_workbook("myfile.xls").
-A reference to the Book object to which this sheet belongs. -Example usage: some_sheet.book.datemode
-Cell object in the given row and column. -
A sparse mapping from (rowx, colx) to a Note object.
-Cells not containing a note ("comment") are not mapped.
-
-- New in version 0.7.2
Type of the cell in the given row and column. -Refer to the documentation of the Cell class. -
Value of the cell in the given row and column.
-XF index of the cell in the given row and column.
-This is an index into Book.xf_list.
-
-- New in version 0.6.1
-
Returns a sequence of the Cell objects in the given column. -
List of address ranges of cells containing column labels.
-These are set up in Excel by Insert > Name > Labels > Columns.
-
-- New in version 0.6.0
-
How to deconstruct the list:
-
-for crange in thesheet.col_label_ranges: - rlo, rhi, clo, chi = crange - for rx in xrange(rlo, rhi): - for cx in xrange(clo, chi): - print "Column label at (rowx=%d, colx=%d) is %r" \ - (rx, cx, thesheet.cell_value(rx, cx)) --
Returns a slice of the Cell objects in the given column. -
Returns a slice of the types of the cells in the given column.
-Returns a slice of the values of the cells in the given column.
-The map from a column index to a Colinfo object. Often there is an entry
-in COLINFO records for all column indexes in range(257).
-Note that xlrd ignores the entry for the non-existent
-257th column. On the other hand, there may be no entry for unused columns.
-
-- New in version 0.6.1. Populated only if open_workbook(formatting_info=True).
-
Determine column display width.
-
-- New in version 0.6.1
-
-
Default value to be used for a row if there is -no ROW record for that row. -From the optional DEFAULTROWHEIGHT record. -
Default value to be used for a row if there is -no ROW record for that row. -From the optional DEFAULTROWHEIGHT record. -
Default value to be used for a row if there is -no ROW record for that row. -From the optional DEFAULTROWHEIGHT record. -
Default value to be used for a row if there is -no ROW record for that row. -From the optional DEFAULTROWHEIGHT record. -
Default value to be used for a row if there is -no ROW record for that row. -From the optional DEFAULTROWHEIGHT record. -
Default column width from DEFCOLWIDTH record, else None.
-From the OOo docs:
-"""Column width in characters, using the width of the zero character
-from default font (first FONT record in the file). Excel adds some
-extra space to the default width, depending on the default font and
-default font size. The algorithm how to exactly calculate the resulting
-column width is not known.
-Example: The default width of 8 set in this record results in a column
-width of 8.43 using Arial font with a size of 10 points."""
-For the default hierarchy, refer to the Colinfo class.
-
-- New in version 0.6.1
-
A 256-element tuple corresponding to the contents of the GCW record for this sheet. -If no such record, treat as all bits zero. -Applies to BIFF4-7 only. See docs of the Colinfo class for discussion. -
Boolean specifying if a PANE record was present, ignore unless you're xlutils.copy
-A list of the horizontal page breaks in this sheet.
-Breaks are tuples in the form (index of row after break, start col index, end col index).
-Populated only if open_workbook(formatting_info=True).
-
-- New in version 0.7.2
-
Index of first visible row in bottom frozen/split pane
-Number of rows in top pane (frozen panes; for split panes, see comments below in code)
-A list of Hyperlink objects corresponding to HLINK records found
-in the worksheet.
-- New in version 0.7.2
A sparse mapping from (rowx, colx) to an item in hyperlink_list.
-Cells not covered by a hyperlink are not mapped.
-It is possible using the Excel UI to set up a hyperlink that
-covers a larger-than-1x1 rectangle of cells.
-Hyperlink rectangles may overlap (Excel doesn't check).
-When a multiply-covered cell is clicked on, the hyperlink that is activated
-(and the one that is mapped here) is the last in hyperlink_list.
-
-- New in version 0.7.2
List of address ranges of cells which have been merged.
-These are set up in Excel by Format > Cells > Alignment, then ticking
-the "Merge cells" box.
-
-- New in version 0.6.1. Extracted only if open_workbook(formatting_info=True).
-
How to deconstruct the list:
-
-for crange in thesheet.merged_cells: - rlo, rhi, clo, chi = crange - for rowx in xrange(rlo, rhi): - for colx in xrange(clo, chi): - # cell (rlo, clo) (the top left one) will carry the data - # and formatting info; the remainder will be recorded as - # blank cells, but a renderer will apply the formatting info - # for the top left cell (e.g. border, pattern) to all cells in - # the range. --
Name of sheet.
-Nominal number of columns in sheet. It is 1 + the maximum column index -found, ignoring trailing empty cells. See also open_workbook(ragged_rows=?) -and Sheet.row_len(row_index). -
Number of rows in sheet. A row index is in range(thesheet.nrows).
-Mapping of (rowx, colx) to list of (offset, font_index) tuples. The offset
-defines where in the string the font begins to be used.
-Offsets are expected to be in ascending order.
-If the first offset is not zero, the meaning is that the cell's XF's font should
-be used from offset 0.
-
This is a sparse mapping. There is no entry for cells that are not formatted with
-rich text.
-
How to use:
-
-runlist = thesheet.rich_text_runlist_map.get((rowx, colx)) -if runlist: - for offset, font_index in runlist: - # do work here. - pass --Populated only if open_workbook(formatting_info=True). -
Returns a sequence of the Cell objects in the given row. -
List of address ranges of cells containing row labels.
-For more details, see col_label_ranges above.
-
-- New in version 0.6.0
-
Returns the effective number of cells in the given row. For use with
-open_workbook(ragged_rows=True) which is likely to produce rows
-with fewer than ncols cells.
-
-- New in version 0.7.2
-
Returns a slice of the Cell objects in the given row. -
Returns a slice of the types -of the cells in the given row.
-Returns a slice of the values -of the cells in the given row.
-The map from a row index to a Rowinfo object. Note that it is possible
-to have missing entries -- at least one source of XLS files doesn't
-bother writing ROW records.
-
-- New in version 0.6.1. Populated only if open_workbook(formatting_info=True).
-
Frozen panes: ignore it. Split panes: explanation and diagrams in OOo docs.
-Default column width from STANDARDWIDTH record, else None.
-From the OOo docs:
-"""Default width of the columns in 1/256 of the width of the zero
-character, using default font (first FONT record in the file)."""
-For the default hierarchy, refer to the Colinfo class.
-
-- New in version 0.6.1
-
Index of first visible column in right frozen/split pane
-Number of columns in left pane (frozen panes; for split panes, see comments below in code)
-A list of the vertical page breaks in this sheet.
-Breaks are tuples in the form (index of col after break, start row index, end row index).
-Populated only if open_workbook(formatting_info=True).
-
-- New in version 0.7.2
-
Visibility of the sheet. 0 = visible, 1 = hidden (can be unhidden -by user -- Format/Sheet/Unhide), 2 = "very hidden" (can be unhidden -only by VBA macro).
-eXtended Formatting information for cells, rows, columns and styles.
-
-- New in version 0.6.1
-
-
Each of the 6 flags below describes the validity of
-a specific group of attributes.
-
-In cell XFs, flag==0 means the attributes of the parent style XF are used,
-(but only if the attributes are valid there); flag==1 means the attributes
-of this XF are used.
-In style XFs, flag==0 means the attribute setting is valid; flag==1 means
-the attribute should be ignored.
-Note that the API
-provides both "raw" XFs and "computed" XFs -- in the latter case, cell XFs
-have had the above inheritance mechanism applied.
-
-
An instance of an XFAlignment object.
-An instance of an XFBackground object.
-An instance of an XFBorder object.
-Index into Book.font_list
-Key into Book.format_map -
-Warning: OOo docs on the XF record call this "Index to FORMAT record". -It is not an index in the Python sense. It is a key to a map. -It is true only for Excel 4.0 and earlier files -that the key into format_map from an XF instance -is the same as the index into format_list, and only -if the index is less than 164. -
-0 = cell XF, 1 = style XF
-cell XF: Index into Book.xf_list
-of this XF's style XF
-style XF: 0xFFF
-
An instance of an XFProtection object.
-Index into Book.xf_list
-A collection of the alignment and similar attributes of an XF record.
-Items correspond to those in the Excel UI's Format/Cells/Alignment tab.
-
-- New in version 0.6.1
-
Values: section 6.115 (p 214) of OOo docs
-A number in range(15).
-Values: section 6.115 (p 215) of OOo docs.
-Note: file versions BIFF7 and earlier use the documented
-"orientation" attribute; this will be mapped (without loss)
-into "rotation".
-
1 = shrink font size to fit text into cell.
-0 = according to context; 1 = left-to-right; 2 = right-to-left
-1 = text is wrapped at right margin
-Values: section 6.115 (p 215) of OOo docs
-A collection of the background-related attributes of an XF record.
-Items correspond to those in the Excel UI's Format/Cells/Patterns tab.
-An explanation of "colour index" is given in the Formatting
-section at the start of this document.
-
-- New in version 0.6.1
-
See section 3.11 of the OOo docs.
-See section 3.11 of the OOo docs.
-See section 3.11 of the OOo docs.
-A collection of the border-related attributes of an XF record. -Items correspond to those in the Excel UI's Format/Cells/Border tab.
-An explanations of "colour index" is given in the Formatting -section at the start of this document. -There are five line style attributes; possible values and the -associated meanings are: -0 = No line, -1 = Thin, -2 = Medium, -3 = Dashed, -4 = Dotted, -5 = Thick, -6 = Double, -7 = Hair, -8 = Medium dashed, -9 = Thin dash-dotted, -10 = Medium dash-dotted, -11 = Thin dash-dot-dotted, -12 = Medium dash-dot-dotted, -13 = Slanted medium dash-dotted. -The line styles 8 to 13 appear in BIFF8 files (Excel 97 and later) only. -For pictures of the line styles, refer to OOo docs s3.10 (p22) -"Line Styles for Cell Borders (BIFF3-BIFF8)".
-The colour index for the cell's bottom line
-The line style for the cell's bottom line
-The colour index for the cell's diagonal lines, if any
-1 = draw a diagonal from top left to bottom right
-The line style for the cell's diagonal lines, if any
-1 = draw a diagonal from bottom left to top right
-The colour index for the cell's left line
-The line style for the cell's left line
-The colour index for the cell's right line
-The line style for the cell's right line
-The colour index for the cell's top line
-The line style for the cell's top line
-A collection of the protection-related attributes of an XF record.
-Items correspond to those in the Excel UI's Format/Cells/Protection tab.
-Note the OOo docs include the "cell or style" bit
-in this bundle of attributes.
-This is incorrect; the bit is used in determining which bundles to use.
-
-- New in version 0.6.1
-
1 = Cell is prevented from being changed, moved, resized, or deleted -(only if the sheet is protected).
-1 = Hide formula so that it doesn't appear in the formula bar when -the cell is selected (only if the sheet is protected).
-Copyright © 2006 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under a BSD-style licence.
-## - -import xlrd -import sys -import glob - -def scope_as_string(book, scope): - if 0 <= scope < book.nsheets: - return "sheet #%d (%r)" % (scope, book.sheet_names()[scope]) - if scope == -1: - return "Global" - if scope == -2: - return "Macro/VBA" - return "Unknown scope value (%r)" % scope - -def do_scope_query(book, scope_strg, show_contents=0, f=sys.stdout): - try: - qscope = int(scope_strg) - except ValueError: - if scope_strg == "*": - qscope = None # means "all' - else: - # so assume it's a sheet name ... - qscope = book.sheet_names().index(scope_strg) - print >> f, "%r => %d" % (scope_strg, qscope) - for nobj in book.name_obj_list: - if qscope is None or nobj.scope == qscope: - show_name_object(book, nobj, show_contents, f) - -def show_name_details(book, name, show_contents=0, f=sys.stdout): - """ - book -- Book object obtained from xlrd.open_workbook(). - name -- The name that's being investigated. - show_contents -- 0: Don't; 1: Non-empty cells only; 2: All cells - f -- Open output file handle. - """ - name_lcase = name.lower() # Excel names are case-insensitive. - nobj_list = book.name_map.get(name_lcase) - if not nobj_list: - print >> f, "%r: unknown name" % name - return - for nobj in nobj_list: - show_name_object(book, nobj, show_contents, f) - -def show_name_details_in_scope( - book, name, scope_strg, show_contents=0, f=sys.stdout, - ): - try: - scope = int(scope_strg) - except ValueError: - # so assume it's a sheet name ... - scope = book.sheet_names().index(scope_strg) - print >> f, "%r => %d" % (scope_strg, scope) - name_lcase = name.lower() # Excel names are case-insensitive. - while 1: - nobj = book.name_and_scope_map.get((name_lcase, scope)) - if nobj: - break - print >> f, "Name %r not found in scope %d" % (name, scope) - if scope == -1: - return - scope = -1 # Try again with global scope - print >> f, "Name %r found in scope %d" % (name, scope) - show_name_object(book, nobj, show_contents, f) - -def showable_cell_value(celltype, cellvalue, datemode): - if celltype == xlrd.XL_CELL_DATE: - try: - showval = xlrd.xldate_as_tuple(cellvalue, datemode) - except xlrd.XLDateError: - e1, e2 = sys.exc_info()[:2] - showval = "%s:%s" % (e1.__name__, e2) - elif celltype == xlrd.XL_CELL_ERROR: - showval = xlrd.error_text_from_code.get( - cellvalue, 'Copyright © 2005-2012 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under -# a BSD-style licence.
-## - -# No part of the content of this file was derived from the works of David Giffin. - -# 2010-10-30 SJM Added space after colon in "# coding" line to work around IBM iSeries Python bug -# 2009-05-31 SJM Fixed problem with non-zero reserved bits in some STYLE records in Mac Excel files -# 2008-08-03 SJM Ignore PALETTE record when Book.formatting_info is false -# 2008-08-03 SJM Tolerate up to 4 bytes trailing junk on PALETTE record -# 2008-05-10 SJM Do some XF checks only when Book.formatting_info is true -# 2008-02-08 SJM Preparation for Excel 2.0 support -# 2008-02-03 SJM Another tweak to is_date_format_string() -# 2007-12-04 SJM Added support for Excel 2.x (BIFF2) files. -# 2007-10-13 SJM Warning: style XF whose parent XF index != 0xFFF -# 2007-09-08 SJM Work around corrupt STYLE record -# 2007-07-11 SJM Allow for BIFF2/3-style FORMAT record in BIFF4/8 file +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +# No part of the content of this file was derived from the works of +# David Giffin. +""" +Module for formatting information. +""" -DEBUG = 0 -import copy, re -from timemachine import * -from biffh import BaseObject, unpack_unicode, unpack_string, \ - upkbits, upkbitsL, fprintf, \ - FUN, FDT, FNU, FGE, FTX, XL_CELL_NUMBER, XL_CELL_DATE, \ - XL_FORMAT, XL_FORMAT2, \ - XLRDError +from __future__ import print_function + +import re from struct import unpack +from .biffh import ( + FDT, FGE, FNU, FTX, FUN, XL_CELL_DATE, XL_CELL_NUMBER, XL_CELL_TEXT, + XL_FORMAT, XL_FORMAT2, BaseObject, XLRDError, fprintf, unpack_string, + unpack_unicode, upkbits, upkbitsL, +) +from .timemachine import * + +DEBUG = 0 + +_cellty_from_fmtty = { + FNU: XL_CELL_NUMBER, + FUN: XL_CELL_NUMBER, + FGE: XL_CELL_NUMBER, + FDT: XL_CELL_DATE, + FTX: XL_CELL_NUMBER, # Yes, a number can be formatted as text. +} + excel_default_palette_b5 = ( ( 0, 0, 0), (255, 255, 255), (255, 0, 0), ( 0, 255, 0), ( 0, 0, 255), (255, 255, 0), (255, 0, 255), ( 0, 255, 255), @@ -47,7 +45,7 @@ (255, 153, 0), (255, 102, 0), (102, 102, 153), (150, 150, 150), ( 0, 51, 102), ( 51, 153, 102), ( 0, 51, 0), ( 51, 51, 0), (153, 51, 0), (153, 51, 102), ( 51, 51, 153), ( 51, 51, 51), - ) +) excel_default_palette_b2 = excel_default_palette_b5[:16] @@ -68,7 +66,7 @@ (255,153, 0), (255,102, 0), (102,102,153), (150,150,150), # 44 ( 0, 51,102), ( 51,153,102), ( 0, 51, 0), ( 51, 51, 0), # 48 (153, 51, 0), (153, 51,102), ( 51, 51,153), ( 51, 51, 51), # 52 - ) +) default_palette = { 80: excel_default_palette_b8, @@ -79,20 +77,18 @@ 30: excel_default_palette_b2, 21: excel_default_palette_b2, 20: excel_default_palette_b2, - } +} -""" -00H = Normal -01H = RowLevel_lv (see next field) -02H = ColLevel_lv (see next field) -03H = Comma -04H = Currency -05H = Percent -06H = Comma [0] (BIFF4-BIFF8) -07H = Currency [0] (BIFF4-BIFF8) -08H = Hyperlink (BIFF8) -09H = Followed Hyperlink (BIFF8) -""" +# 00H = Normal +# 01H = RowLevel_lv (see next field) +# 02H = ColLevel_lv (see next field) +# 03H = Comma +# 04H = Currency +# 05H = Percent +# 06H = Comma [0] (BIFF4-BIFF8) +# 07H = Currency [0] (BIFF4-BIFF8) +# 08H = Hyperlink (BIFF8) +# 09H = Followed Hyperlink (BIFF8) built_in_style_names = [ "Normal", "RowLevel_", @@ -104,7 +100,7 @@ "Currency [0]", "Hyperlink", "Followed Hyperlink", - ] +] def initialise_colour_map(book): book.colour_map = {} @@ -123,18 +119,20 @@ def initialise_colour_map(book): # System window text colour for border lines book.colour_map[ndpal+8] = None # System window background colour for pattern background - book.colour_map[ndpal+8+1] = None # - for ci in ( - 0x51, # System ToolTip text colour (used in note objects) - 0x7FFF, # 32767, system window text colour for fonts - ): - book.colour_map[ci] = None + book.colour_map[ndpal+8+1] = None + # System ToolTip text colour (used in note objects) + book.colour_map[0x51] = None + # 32767, system window text colour for fonts + book.colour_map[0x7FFF] = None + def nearest_colour_index(colour_map, rgb, debug=0): - # General purpose function. Uses Euclidean distance. - # So far used only for pre-BIFF8 WINDOW2 record. - # Doesn't have to be fast. - # Doesn't have to be fancy. + """ + General purpose function. Uses Euclidean distance. + So far used only for pre-BIFF8 ``WINDOW2`` record. + Doesn't have to be fast. + Doesn't have to be fancy. + """ best_metric = 3 * 256 * 256 best_colourx = 0 for colourx, cand_rgb in colour_map.items(): @@ -149,14 +147,15 @@ def nearest_colour_index(colour_map, rgb, debug=0): if metric == 0: break if 0 and debug: - print "nearest_colour_index for %r is %r -> %r; best_metric is %d" \ - % (rgb, best_colourx, colour_map[best_colourx], best_metric) + print("nearest_colour_index for %r is %r -> %r; best_metric is %d" + % (rgb, best_colourx, colour_map[best_colourx], best_metric)) return best_colourx -## -# This mixin class exists solely so that Format, Font, and XF.... objects -# can be compared by value of their attributes. class EqNeAttrs(object): + """ + This mixin class exists solely so that :class:`Format`, :class:`Font`, and + :class:`XF` objects can be compared by value of their attributes. + """ def __eq__(self, other): return self.__dict__ == other.__dict__ @@ -164,85 +163,96 @@ def __eq__(self, other): def __ne__(self, other): return self.__dict__ != other.__dict__ -## -# An Excel "font" contains the details of not only what is normally -# considered a font, but also several other display attributes. -# Items correspond to those in the Excel UI's Format/Cells/Font tab. -#A collection of the border-related attributes of an XF record. -# Items correspond to those in the Excel UI's Format/Cells/Border tab.
-#An explanations of "colour index" is given in the Formatting -# section at the start of this document. -# There are five line style attributes; possible values and the -# associated meanings are: -# 0 = No line, -# 1 = Thin, -# 2 = Medium, -# 3 = Dashed, -# 4 = Dotted, -# 5 = Thick, -# 6 = Double, -# 7 = Hair, -# 8 = Medium dashed, -# 9 = Thin dash-dotted, -# 10 = Medium dash-dotted, -# 11 = Thin dash-dot-dotted, -# 12 = Medium dash-dot-dotted, -# 13 = Slanted medium dash-dotted. -# The line styles 8 to 13 appear in BIFF8 files (Excel 97 and later) only. -# For pictures of the line styles, refer to OOo docs s3.10 (p22) -# "Line Styles for Cell Borders (BIFF3-BIFF8)".
-#Each of the 6 flags below describes the validity of
-# a specific group of attributes.
-#
-# In cell XFs, flag==0 means the attributes of the parent style XF are used,
-# (but only if the attributes are valid there); flag==1 means the attributes
-# of this XF are used.
-# In style XFs, flag==0 means the attribute setting is valid; flag==1 means
-# the attribute should be ignored.
-# Note that the API
-# provides both "raw" XFs and "computed" XFs -- in the latter case, cell XFs
-# have had the above inheritance mechanism applied.
-#
- # Warning: OOo docs on the XF record call this "Index to FORMAT record". - # It is not an index in the Python sense. It is a key to a map. - # It is true only for Excel 4.0 and earlier files - # that the key into format_map from an XF instance - # is the same as the index into format_list, and only - # if the index is less than 164. - #
+ + #: Key into :attr:`~xlrd.book.Book.format_map` + #: + #: .. warning:: + #: OOo docs on the XF record call this "Index to FORMAT record". + #: It is not an index in the Python sense. It is a key to a map. + #: It is true *only* for Excel 4.0 and earlier files + #: that the key into format_map from an XF instance + #: is the same as the index into format_list, and *only* + #: if the index is less than 164. format_key = 0 - ## - # An instance of an XFProtection object. + + #: An instance of an :class:`XFProtection` object. protection = None - ## - # An instance of an XFBackground object. + + #: An instance of an :class:`XFBackground` object. background = None - ## - # An instance of an XFAlignment object. + + #: An instance of an :class:`XFAlignment` object. alignment = None - ## - # An instance of an XFBorder object. + + #: An instance of an :class:`XFBorder` object. border = None diff --git a/xlrd/formula.py b/xlrd/formula.py index 37477348..00e4464a 100644 --- a/xlrd/formula.py +++ b/xlrd/formula.py @@ -1,21 +1,24 @@ -# -*- coding: cp1252 -*- +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +# No part of the content of this file was derived from the works of +# David Giffin. +""" +Module for parsing/evaluating Microsoft Excel formulas. +""" + +from __future__ import print_function -## -# Module for parsing/evaluating Microsoft Excel formulas. -# -#Copyright © 2005-2012 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under -# a BSD-style licence.
-## - -# No part of the content of this file was derived from the works of David Giffin. - -from __future__ import nested_scopes import copy +import operator as opr from struct import unpack -from timemachine import * -from biffh import unpack_unicode_update_pos, unpack_string_update_pos, \ - XLRDError, hex_char_dump, error_text_from_code, BaseObject + +from .biffh import ( + BaseObject, XLRDError, error_text_from_code, hex_char_dump, + unpack_string_update_pos, unpack_unicode_update_pos, +) +from .timemachine import * __all__ = [ 'oBOOL', 'oERR', 'oNUM', 'oREF', 'oREL', 'oSTRG', 'oUNK', @@ -30,7 +33,8 @@ 'FMLA_TYPE_COND_FMT', 'FMLA_TYPE_DATA_VAL', 'FMLA_TYPE_NAME', - ] + 'Operand', 'Ref3D', +] FMLA_TYPE_CELL = 1 FMLA_TYPE_SHARED = 2 @@ -48,7 +52,7 @@ 8 : 'COND-FMT', 16: 'DATA-VAL', 32: 'NAME', - } +} _TOKEN_NOT_ALLOWED = { 0x01: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tExp @@ -64,7 +68,7 @@ 0x2C: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tRefN 0x2D: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tAreaN # plus weird stuff like tMem* - }.get +}.get oBOOL = 3 oERR = 4 @@ -84,7 +88,7 @@ 3 : "oBOOL", 4 : "oERR", 5 : "oMSNG", - } +} listsep = ',' #### probably should depend on locale @@ -108,7 +112,7 @@ 50 : sztab3, 70 : sztab3, 80 : sztab4, - } +} # For debugging purposes ... the name for each opcode # (without the prefix "t" used on OOo docs) @@ -376,7 +380,7 @@ 377: ('ROUNDBAHTUP', 1, 1, 0x02, 1, 'V', 'V'), 378: ('THAIYEAR', 1, 1, 0x02, 1, 'V', 'V'), 379: ('RTD', 2, 5, 0x04, 1, 'V', 'V'), - } +} tAttrNames = { 0x00: "Skip??", # seen in SAMPLES.XLS which shipped with Excel 5.0 @@ -388,21 +392,18 @@ 0x20: "Assign", 0x40: "Space", 0x41: "SpaceVolatile", - } +} -_error_opcodes = {} -for _x in [0x07, 0x08, 0x0A, 0x0B, 0x1C, 0x1D, 0x2F]: - _error_opcodes[_x] = 1 -is_error_opcode = _error_opcodes.has_key +error_opcodes = set([0x07, 0x08, 0x0A, 0x0B, 0x1C, 0x1D, 0x2F]) tRangeFuncs = (min, max, min, max, min, max) tIsectFuncs = (max, min, max, min, max, min) def do_box_funcs(box_funcs, boxa, boxb): - return tuple([ + return tuple( func(numa, numb) for func, numa, numb in zip(box_funcs, boxa.coords, boxb.coords) - ]) + ) def adjust_cell_addr_biff8(rowval, colval, reldelta, browx=None, bcolx=None): row_rel = (colval >> 15) & 1 @@ -472,32 +473,32 @@ def get_externsheet_local_range(bk, refx, blah=0): try: info = bk._externsheet_info[refx] except IndexError: - print >> bk.logfile, "!!! get_externsheet_local_range: refx=%d, not in range(%d)" \ - % (refx, len(bk._externsheet_info)) + print("!!! get_externsheet_local_range: refx=%d, not in range(%d)" + % (refx, len(bk._externsheet_info)), file=bk.logfile) return (-101, -101) ref_recordx, ref_first_sheetx, ref_last_sheetx = info if ref_recordx == bk._supbook_addins_inx: if blah: - print >> bk.logfile, "/// get_externsheet_local_range(refx=%d) -> addins %r" % (refx, info) + print("/// get_externsheet_local_range(refx=%d) -> addins %r" % (refx, info), file=bk.logfile) assert ref_first_sheetx == 0xFFFE == ref_last_sheetx return (-5, -5) if ref_recordx != bk._supbook_locals_inx: if blah: - print >> bk.logfile, "/// get_externsheet_local_range(refx=%d) -> external %r" % (refx, info) + print("/// get_externsheet_local_range(refx=%d) -> external %r" % (refx, info), file=bk.logfile) return (-4, -4) # external reference if ref_first_sheetx == 0xFFFE == ref_last_sheetx: if blah: - print >> bk.logfile, "/// get_externsheet_local_range(refx=%d) -> unspecified sheet %r" % (refx, info) + print("/// get_externsheet_local_range(refx=%d) -> unspecified sheet %r" % (refx, info), file=bk.logfile) return (-1, -1) # internal reference, any sheet if ref_first_sheetx == 0xFFFF == ref_last_sheetx: if blah: - print >> bk.logfile, "/// get_externsheet_local_range(refx=%d) -> deleted sheet(s)" % (refx, ) + print("/// get_externsheet_local_range(refx=%d) -> deleted sheet(s)" % (refx, ), file=bk.logfile) return (-2, -2) # internal reference, deleted sheet(s) nsheets = len(bk._all_sheets_map) if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): if blah: - print >> bk.logfile, "/// get_externsheet_local_range(refx=%d) -> %r" % (refx, info) - print >> bk.logfile, "--- first/last sheet not in range(%d)" % nsheets + print("/// get_externsheet_local_range(refx=%d) -> %r" % (refx, info), file=bk.logfile) + print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile) return (-102, -102) # stuffed up somewhere :-( xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] @@ -509,16 +510,16 @@ def get_externsheet_local_range_b57( bk, raw_extshtx, ref_first_sheetx, ref_last_sheetx, blah=0): if raw_extshtx > 0: if blah: - print >> bk.logfile, "/// get_externsheet_local_range_b57(raw_extshtx=%d) -> external" % raw_extshtx + print("/// get_externsheet_local_range_b57(raw_extshtx=%d) -> external" % raw_extshtx, file=bk.logfile) return (-4, -4) # external reference if ref_first_sheetx == -1 and ref_last_sheetx == -1: return (-2, -2) # internal reference, deleted sheet(s) nsheets = len(bk._all_sheets_map) if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): if blah: - print >> bk.logfile, "/// get_externsheet_local_range_b57(%d, %d, %d) -> ???" \ - % (raw_extshtx, ref_first_sheetx, ref_last_sheetx) - print >> bk.logfile, "--- first/last sheet not in range(%d)" % nsheets + print("/// get_externsheet_local_range_b57(%d, %d, %d) -> ???" + % (raw_extshtx, ref_first_sheetx, ref_last_sheetx), file=bk.logfile) + print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile) return (-103, -103) # stuffed up somewhere :-( xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] @@ -530,80 +531,80 @@ class FormulaError(Exception): pass -## -# Used in evaluating formulas. -# The following table describes the kinds and how their values -# are represented. -# -#| Kind symbol | -#Kind number | -#Value representation | -#
|---|---|---|
| oBOOL | -#3 | -#integer: 0 => False; 1 => True | -#
| oERR | -#4 | -#None, or an int error code (same as XL_CELL_ERROR in the Cell class). -# | -#
| oMSNG | -#5 | -#Used by Excel as a placeholder for a missing (not supplied) function -# argument. Should *not* appear as a final formula result. Value is None. | -#
| oNUM | -#2 | -#A float. Note that there is no way of distinguishing dates. | -#
| oREF | -#-1 | -#The value is either None or a non-empty list of
-# absolute Ref3D instances. -# |
-#
| oREL | -#-2 | -#The value is None or a non-empty list of -# fully or partially relative Ref3D instances. -# | -#
| oSTRG | -#1 | -#A Unicode string. | -#
| oUNK | -#0 | -#The kind is unknown or ambiguous. The value is None | -#
| Kind symbol | +Kind number | +Value representation | +
|---|---|---|
| oBOOL | +3 | +integer: 0 => False; 1 => True | +
| oERR | +4 | +None, or an int error code (same as XL_CELL_ERROR in the Cell class). + | +
| oMSNG | +5 | +Used by Excel as a placeholder for a missing (not supplied) function + argument. Should *not* appear as a final formula result. Value is None. | +
| oNUM | +2 | +A float. Note that there is no way of distinguishing dates. | +
| oREF | +-1 | +The value is either None or a non-empty list of
+ absolute Ref3D instances. + |
+
| oREL | +-2 | +The value is None or a non-empty list of + fully or partially relative Ref3D instances. + | +
| oSTRG | +1 | +A Unicode string. | +
| oUNK | +0 | +The kind is unknown or ambiguous. The value is None | +
Represents an absolute or relative 3-dimensional reference to a box
-# of one or more cells.
-# -- New in version 0.6.0
-#
The coords attribute is a tuple of the form:
-# (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)
-# where 0 <= thingxlo <= thingx < thingxhi.
-# Note that it is quite possible to have thingx > nthings; for example
-# Print_Titles could have colxhi == 256 and/or rowxhi == 65536
-# irrespective of how many columns/rows are actually used in the worksheet.
-# The caller will need to decide how to handle this situation.
-# Keyword: IndexError :-)
-#
The components of the coords attribute are also available as individual -# attributes: shtxlo, shtxhi, rowxlo, rowxhi, colxlo, and colxhi.
-# -#The relflags attribute is a 6-tuple of flags which indicate whether
-# the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0). Portions copyright © 2005-2012 Stephen John Machin, Lingfo Pty Ltd This module is part of the xlrd package, which is released under a BSD-style licence. Contains the data for one worksheet. In the cell access functions, "rowx" is a row index, counting from zero, and "colx" is a
-# column index, counting from zero.
-# Negative values for row/column indexes and slice positions are supported in the expected fashion. For information about cell types and cell values, refer to the documentation of the {@link #Cell} class. WARNING: You don't call this class yourself. You access Sheet objects via the Book object that
-# was returned when you called xlrd.open_workbook("myfile.xls"). A list of {@link #Hyperlink} objects corresponding to HLINK records found
- # in the worksheet. A sparse mapping from (rowx, colx) to an item in {@link #Sheet.hyperlink_list}.
- # Cells not covered by a hyperlink are not mapped.
- # It is possible using the Excel UI to set up a hyperlink that
- # covers a larger-than-1x1 rectangle of cells.
- # Hyperlink rectangles may overlap (Excel doesn't check).
- # When a multiply-covered cell is clicked on, the hyperlink that is activated
- # (and the one that is mapped here) is the last in hyperlink_list.
- # A sparse mapping from (rowx, colx) to a {@link #Note} object.
- # Cells not containing a note ("comment") are not mapped.
- #
-# Note that there is necessarily no information available as to what cell(s)
-# the reference could possibly be relative to. The caller must decide what if
-# any use to make of oREL operands. Note also that a partially relative
-# reference may well be a typo.
-# For example, define name A1Z10 as $a$1:$z10 (missing $ after z)
-# while the cursor is on cell Sheet3!A27.
-# The resulting Ref3D instance will have coords = (2, 3, 0, -16, 0, 26)
-# and relflags = (0, 0, 0, 1, 0, 0).
-# So far, only one possibility of a sheet-relative component in
-# a reference has been noticed: a 2D reference located in the "current sheet".
-#
This will appear as coords = (0, 1, ...) and relflags = (1, 1, ...).
-
-class Ref3D(_ref3d_base):
+
+class Ref3D(tuple):
+ """
+ Represents an absolute or relative 3-dimensional reference to a box
+ of one or more cells.
+
+ The ``coords`` attribute is a tuple of the form::
+
+ (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)
+
+ where ``0 <= thingxlo <= thingx < thingxhi``.
+
+ .. note::
+ It is quite possible to have ``thingx > nthings``; for example
+ ``Print_Titles`` could have ``colxhi == 256`` and/or ``rowxhi == 65536``
+ irrespective of how many columns/rows are actually used in the worksheet.
+ The caller will need to decide how to handle this situation.
+ Keyword: :class:`IndexError` :-)
+
+ The components of the coords attribute are also available as individual
+ attributes: ``shtxlo``, ``shtxhi``, ``rowxlo``, ``rowxhi``, ``colxlo``, and
+ ``colxhi``.
+
+ The ``relflags`` attribute is a 6-tuple of flags which indicate whether
+ the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0).
+
+ .. note::
+ There is necessarily no information available as to what cell(s)
+ the reference could possibly be relative to. The caller must decide what
+ if any use to make of ``oREL`` operands.
+
+ .. note:
+ A partially relative reference may well be a typo.
+ For example, define name ``A1Z10`` as ``$a$1:$z10`` (missing ``$`` after
+ ``z``) while the cursor is on cell ``Sheet3!A27``.
+
+ The resulting :class:`Ref3D` instance will have
+ ``coords = (2, 3, 0, -16, 0, 26)``
+ and ``relflags = (0, 0, 0, 1, 0, 0).
+
+ So far, only one possibility of a sheet-relative component in
+ a reference has been noticed: a 2D reference located in the
+ "current sheet".
+
+ This will appear as ``coords = (0, 1, ...)`` and
+ ``relflags = (1, 1, ...)``.
+
+ .. versionadded:: 0.6.0
+ """
def __init__(self, atuple):
self.coords = atuple[0:6]
@@ -685,7 +696,6 @@ def __repr__(self):
tConcat = 0x08
tLT, tLE, tEQ, tGE, tGT, tNE = range(0x09, 0x0F)
-import operator as opr
def nop(x):
return x
@@ -700,8 +710,8 @@ def _opr_gt(x, y): return x > y
def _opr_ne(x, y): return x != y
def num2strg(num):
- """Attempt to emulate Excel's default conversion
- from number to string.
+ """
+ Attempt to emulate Excel's default conversion from number to string.
"""
s = str(num)
if s.endswith(".0"):
@@ -716,7 +726,7 @@ def num2strg(num):
tAdd: (_arith_argdict, oNUM, opr.add, 30, '+'),
tSub: (_arith_argdict, oNUM, opr.sub, 30, '-'),
tMul: (_arith_argdict, oNUM, opr.mul, 40, '*'),
- tDiv: (_arith_argdict, oNUM, opr.div, 40, '/'),
+ tDiv: (_arith_argdict, oNUM, opr.truediv, 40, '/'),
tPower: (_arith_argdict, oNUM, _opr_pow, 50, '^',),
tConcat:(_strg_argdict, oSTRG, opr.add, 20, '&'),
tLT: (_cmp_argdict, oBOOL, _opr_lt, 10, '<'),
@@ -725,13 +735,13 @@ def num2strg(num):
tGE: (_cmp_argdict, oBOOL, _opr_ge, 10, '>='),
tGT: (_cmp_argdict, oBOOL, _opr_gt, 10, '>'),
tNE: (_cmp_argdict, oBOOL, _opr_ne, 10, '<>'),
- }
+}
unop_rules = {
0x13: (lambda x: -x, 70, '-', ''), # unary minus
0x12: (lambda x: x, 70, '+', ''), # unary plus
0x14: (lambda x: x / 100.0, 60, '', '%'),# percent
- }
+}
LEAF_RANK = 90
FUNC_RANK = 90
@@ -747,8 +757,8 @@ def evaluate_name_formula(bk, nobj, namex, blah=0, level=0):
bv = bk.biff_version
reldelta = 1 # All defined name formulas use "Method B" [OOo docs]
if blah:
- print >> bk.logfile, "::: evaluate_name_formula %r %r %d %d %r level=%d" \
- % (namex, nobj.name, fmlalen, bv, data, level)
+ print("::: evaluate_name_formula %r %r %d %d %r level=%d"
+ % (namex, nobj.name, fmlalen, bv, data, level), file=bk.logfile)
hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
if level > STACK_PANIC_LEVEL:
raise XLRDError("Excessive indirect references in NAME formula")
@@ -775,7 +785,7 @@ def do_binop(opcd, stk):
'('[:bop.rank < rank],
bop.text,
')'[:bop.rank < rank],
- ])
+ ])
resop = Operand(result_kind, None, rank, otext)
try:
bconv = argdict[bop.kind]
@@ -790,7 +800,7 @@ def do_binop(opcd, stk):
aval = aconv(aop.value)
result = func(aval, bval)
if result_kind == oBOOL:
- result = intbool(result) # -> 1 or 0
+ result = 1 if result else 0
resop.value = result
stk.append(resop)
@@ -805,7 +815,7 @@ def do_unaryop(opcode, result_kind, stk):
aop.text,
')'[:aop.rank < rank],
sym2,
- ])
+ ])
if val is not None:
val = func(val)
stk.append(Operand(result_kind, val, rank, otext))
@@ -819,7 +829,7 @@ def not_in_name_formula(op_arg, oname_arg):
stack = [unk_opnd]
while 0 <= pos < fmlalen:
- op = ord(data[pos])
+ op = BYTES_ORD(data[pos])
opcode = op & 0x1f
optype = (op & 0x60) >> 5
if optype:
@@ -829,9 +839,9 @@ def not_in_name_formula(op_arg, oname_arg):
oname = onames[opx] # + [" RVA"][optype]
sz = sztab[opx]
if blah:
- print >> bk.logfile, "Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" \
- % (pos, op, oname, sz, opcode, optype)
- print >> bk.logfile, "Stack =", stack
+ print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh"
+ % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
+ print("Stack =", stack, file=bk.logfile)
if sz == -2:
msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \
% (op, oname, bv)
@@ -845,7 +855,7 @@ def not_in_name_formula(op_arg, oname_arg):
# tLT, ..., tNE
do_binop(opcode, stack)
elif opcode == 0x0F: # tIsect
- if blah: print >> bk.logfile, "tIsect pre", stack
+ if blah: print("tIsect pre", stack, file=bk.logfile)
assert len(stack) >= 2
bop = stack.pop()
aop = stack.pop()
@@ -859,7 +869,7 @@ def not_in_name_formula(op_arg, oname_arg):
'('[:bop.rank < rank],
bop.text,
')'[:bop.rank < rank],
- ])
+ ])
res = Operand(oREF)
res.text = otext
if bop.kind == oERR or aop.kind == oERR:
@@ -893,9 +903,9 @@ def not_in_name_formula(op_arg, oname_arg):
else:
pass
spush(res)
- if blah: print >> bk.logfile, "tIsect post", stack
+ if blah: print("tIsect post", stack, file=bk.logfile)
elif opcode == 0x10: # tList
- if blah: print >> bk.logfile, "tList pre", stack
+ if blah: print("tList pre", stack, file=bk.logfile)
assert len(stack) >= 2
bop = stack.pop()
aop = stack.pop()
@@ -909,7 +919,7 @@ def not_in_name_formula(op_arg, oname_arg):
'('[:bop.rank < rank],
bop.text,
')'[:bop.rank < rank],
- ])
+ ])
res = Operand(oREF, None, rank, otext)
if bop.kind == oERR or aop.kind == oERR:
res.kind = oERR
@@ -924,9 +934,9 @@ def not_in_name_formula(op_arg, oname_arg):
else:
pass
spush(res)
- if blah: print >> bk.logfile, "tList post", stack
+ if blah: print("tList post", stack, file=bk.logfile)
elif opcode == 0x11: # tRange
- if blah: print >> bk.logfile, "tRange pre", stack
+ if blah: print("tRange pre", stack, file=bk.logfile)
assert len(stack) >= 2
bop = stack.pop()
aop = stack.pop()
@@ -940,10 +950,10 @@ def not_in_name_formula(op_arg, oname_arg):
'('[:bop.rank < rank],
bop.text,
')'[:bop.rank < rank],
- ])
+ ])
res = Operand(oREF, None, rank, otext)
if bop.kind == oERR or aop.kind == oERR:
- res = oERR
+ res.kind = oERR
elif bop.kind == oREF == aop.kind:
if aop.value is not None and bop.value is not None:
assert len(aop.value) == 1
@@ -965,7 +975,7 @@ def not_in_name_formula(op_arg, oname_arg):
else:
pass
spush(res)
- if blah: print >> bk.logfile, "tRange post", stack
+ if blah: print("tRange post", stack, file=bk.logfile)
elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent
do_unaryop(opcode, oNUM, stack)
elif opcode == 0x15: # tParen
@@ -981,7 +991,7 @@ def not_in_name_formula(op_arg, oname_arg):
strg, newpos = unpack_unicode_update_pos(
data, pos+1, lenlen=1)
sz = newpos - pos
- if blah: print >> bk.logfile, " sz=%d strg=%r" % (sz, strg)
+ if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile)
text = '"' + strg.replace('"', '""') + '"'
spush(Operand(oSTRG, strg, LEAF_RANK, text))
elif opcode == 0x18: # tExtended
@@ -996,7 +1006,7 @@ def not_in_name_formula(op_arg, oname_arg):
sz = nc * 2 + 6
elif subop == 0x10: # Sum (single arg)
sz = 4
- if blah: print >> bk.logfile, "tAttrSum", stack
+ if blah: print("tAttrSum", stack, file=bk.logfile)
assert len(stack) >= 1
aop = stack[-1]
otext = 'SUM(%s)' % aop.text
@@ -1004,8 +1014,8 @@ def not_in_name_formula(op_arg, oname_arg):
else:
sz = 4
if blah:
- print >> bk.logfile, " subop=%02xh subname=t%s sz=%d nc=%02xh" \
- % (subop, subname, sz, nc)
+ print(" subop=%02xh subname=t%s sz=%d nc=%02xh"
+ % (subop, subname, sz, nc), file=bk.logfile)
elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet
assert bv < 50
raise FormulaError("tSheet & tEndsheet tokens not implemented")
@@ -1037,17 +1047,17 @@ def not_in_name_formula(op_arg, oname_arg):
funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0]
func_attrs = func_defs.get(funcx, None)
if not func_attrs:
- print >> bk.logfile, "*** formula/tFunc unknown FuncID:%d" \
- % funcx
+ print("*** formula/tFunc unknown FuncID:%d"
+ % funcx, file=bk.logfile)
spush(unk_opnd)
else:
func_name, nargs = func_attrs[:2]
if blah:
- print >> bk.logfile, " FuncID=%d name=%s nargs=%d" \
- % (funcx, func_name, nargs)
+ print(" FuncID=%d name=%s nargs=%d"
+ % (funcx, func_name, nargs), file=bk.logfile)
assert len(stack) >= nargs
if nargs:
- argtext = listsep.join([arg.text for arg in stack[-nargs:]])
+ argtext = listsep.join(arg.text for arg in stack[-nargs:])
otext = "%s(%s)" % (func_name, argtext)
del stack[-nargs:]
else:
@@ -1060,32 +1070,32 @@ def not_in_name_formula(op_arg, oname_arg):
prompt, nargs = divmod(nargs, 128)
macro, funcx = divmod(funcx, 32768)
if blah:
- print >> bk.logfile, " FuncID=%d nargs=%d macro=%d prompt=%d" \
- % (funcx, nargs, macro, prompt)
+ print(" FuncID=%d nargs=%d macro=%d prompt=%d"
+ % (funcx, nargs, macro, prompt), file=bk.logfile)
func_attrs = func_defs.get(funcx, None)
if not func_attrs:
- print >> bk.logfile, "*** formula/tFuncVar unknown FuncID:%d" \
- % funcx
+ print("*** formula/tFuncVar unknown FuncID:%d"
+ % funcx, file=bk.logfile)
spush(unk_opnd)
else:
func_name, minargs, maxargs = func_attrs[:3]
if blah:
- print >> bk.logfile, " name: %r, min~max args: %d~%d" \
- % (func_name, minargs, maxargs)
+ print(" name: %r, min~max args: %d~%d"
+ % (func_name, minargs, maxargs), file=bk.logfile)
assert minargs <= nargs <= maxargs
assert len(stack) >= nargs
assert len(stack) >= nargs
- argtext = listsep.join([arg.text for arg in stack[-nargs:]])
+ argtext = listsep.join(arg.text for arg in stack[-nargs:])
otext = "%s(%s)" % (func_name, argtext)
res = Operand(oUNK, None, FUNC_RANK, otext)
if funcx == 1: # IF
testarg = stack[-nargs]
if testarg.kind not in (oNUM, oBOOL):
if blah and testarg.kind != oUNK:
- print >> bk.logfile, "IF testarg kind?"
+ print("IF testarg kind?", file=bk.logfile)
elif testarg.value not in (0, 1):
if blah and testarg.value is not None:
- print >> bk.logfile, "IF testarg value?"
+ print("IF testarg value?", file=bk.logfile)
else:
if nargs == 2 and not testarg.value:
# IF(FALSE, tv) => FALSE
@@ -1098,7 +1108,7 @@ def not_in_name_formula(op_arg, oname_arg):
else:
res.kind, res.value = chosen.kind, chosen.value
if blah:
- print >> bk.logfile, "$$$$$$ IF => constant"
+ print("$$$$$$ IF => constant", file=bk.logfile)
elif funcx == 100: # CHOOSE
testarg = stack[-nargs]
if testarg.kind == oNUM:
@@ -1113,19 +1123,18 @@ def not_in_name_formula(op_arg, oname_arg):
elif opcode == 0x03: #tName
tgtnamex = unpack("
Ref3D((1, 4, 5, 20, 7, 10)) => 'Sheet2:Sheet3!$H$6:$J$20'
+ cellnamerel(rhi-1, chi-1, rhirel, chirel, browx, bcolx, r1c1),
+ )
+
+
def rangename3d(book, ref3d):
- """ Ref3D(1, 4, 5, 20, 7, 10) => 'Sheet2:Sheet3!$H$6:$J$20'
- (assuming Excel's default sheetnames) """
+ """
+ Utility function:
+ ``Ref3D(1, 4, 5, 20, 7, 10)`` =>
+ ``'Sheet2:Sheet3!$H$6:$J$20'``
+ (assuming Excel's default sheetnames)
+ """
coords = ref3d.coords
return "%s!%s" % (
sheetrange(book, *coords[:2]),
rangename2d(*coords[2:6]))
-##
-# Utility function:
-#
Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))
-# R1C1 mode => 'Sheet1!R[-32]C[-13]:R[-23]C[12]'
-# A1 mode => depends on base cell (browx, bcolx)
def rangename3drel(book, ref3d, browx=None, bcolx=None, r1c1=0):
+ """
+ Utility function:
+ ``Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))``
+
+ In R1C1 mode => ``'Sheet1!R[-32]C[-13]:R[-23]C[12]'``
+
+ In A1 mode => depends on base cell ``(browx, bcolx)``
+ """
coords = ref3d.coords
relflags = ref3d.relflags
shdesc = sheetrangerel(book, coords[:2], relflags[:2])
@@ -2164,7 +2165,7 @@ def quotedsheetname(shnames, shx):
-2: "internal; deleted sheet",
-3: "internal; macro sheet",
-4: "<
-- New in version 0.6.1. Populated only if open_workbook(formatting_info=True).
+
+ #: The map from a column index to a :class:`Colinfo` object. Often there is
+ #: an entry in ``COLINFO`` records for all column indexes in ``range(257)``.
+ #:
+ #: .. note::
+ #: xlrd ignores the entry for the non-existent
+ #: 257th column.
+ #:
+ #: On the other hand, there may be no entry for unused columns.
+ #:
+ #: .. versionadded:: 0.6.1
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
colinfo_map = {}
- ##
- # The map from a row index to a {@link #Rowinfo} object. Note that it is possible
- # to have missing entries -- at least one source of XLS files doesn't
- # bother writing ROW records.
- #
-- New in version 0.6.1. Populated only if open_workbook(formatting_info=True).
+ #: The map from a row index to a :class:`Rowinfo` object.
+ #:
+ #: ..note::
+ #: It is possible to have missing entries -- at least one source of
+ #: XLS files doesn't bother writing ``ROW`` records.
+ #:
+ #: .. versionadded:: 0.6.1
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
rowinfo_map = {}
- ##
- # List of address ranges of cells containing column labels.
- # These are set up in Excel by Insert > Name > Labels > Columns.
- #
-- New in version 0.6.0
- #
How to deconstruct the list:
- #
- # for crange in thesheet.col_label_ranges:
- # rlo, rhi, clo, chi = crange
- # for rx in xrange(rlo, rhi):
- # for cx in xrange(clo, chi):
- # print "Column label at (rowx=%d, colx=%d) is %r" \
- # (rx, cx, thesheet.cell_value(rx, cx))
- #
+ #: List of address ranges of cells containing column labels.
+ #: These are set up in Excel by Insert > Name > Labels > Columns.
+ #:
+ #: .. versionadded:: 0.6.0
+ #:
+ #: How to deconstruct the list:
+ #:
+ #: .. code-block:: python
+ #:
+ #: for crange in thesheet.col_label_ranges:
+ #: rlo, rhi, clo, chi = crange
+ #: for rx in xrange(rlo, rhi):
+ #: for cx in xrange(clo, chi):
+ #: print "Column label at (rowx=%d, colx=%d) is %r" \
+ #: (rx, cx, thesheet.cell_value(rx, cx))
col_label_ranges = []
- ##
- # List of address ranges of cells containing row labels.
- # For more details, see col_label_ranges above.
- #
-- New in version 0.6.0
+ #: List of address ranges of cells containing row labels.
+ #: For more details, see :attr:`col_label_ranges`.
+ #:
+ #: .. versionadded:: 0.6.0
row_label_ranges = []
- ##
- # List of address ranges of cells which have been merged.
- # These are set up in Excel by Format > Cells > Alignment, then ticking
- # the "Merge cells" box.
- #
-- New in version 0.6.1. Extracted only if open_workbook(formatting_info=True).
- #
How to deconstruct the list:
- #
- # for crange in thesheet.merged_cells:
- # rlo, rhi, clo, chi = crange
- # for rowx in xrange(rlo, rhi):
- # for colx in xrange(clo, chi):
- # # cell (rlo, clo) (the top left one) will carry the data
- # # and formatting info; the remainder will be recorded as
- # # blank cells, but a renderer will apply the formatting info
- # # for the top left cell (e.g. border, pattern) to all cells in
- # # the range.
- #
+ #: List of address ranges of cells which have been merged.
+ #: These are set up in Excel by Format > Cells > Alignment, then ticking
+ #: the "Merge cells" box.
+ #:
+ #: .. note::
+ #: The upper limits are exclusive: i.e. ``[2, 3, 7, 9]`` only
+ #: spans two cells.
+ #:
+ #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)``
+ #:
+ #: .. versionadded:: 0.6.1
+ #:
+ #: How to deconstruct the list:
+ #:
+ #: .. code-block:: python
+ #:
+ #: for crange in thesheet.merged_cells:
+ #: rlo, rhi, clo, chi = crange
+ #: for rowx in xrange(rlo, rhi):
+ #: for colx in xrange(clo, chi):
+ #: # cell (rlo, clo) (the top left one) will carry the data
+ #: # and formatting info; the remainder will be recorded as
+ #: # blank cells, but a renderer will apply the formatting info
+ #: # for the top left cell (e.g. border, pattern) to all cells in
+ #: # the range.
merged_cells = []
-
- ##
- # Mapping of (rowx, colx) to list of (offset, font_index) tuples. The offset
- # defines where in the string the font begins to be used.
- # Offsets are expected to be in ascending order.
- # If the first offset is not zero, the meaning is that the cell's XF's font should
- # be used from offset 0.
- #
This is a sparse mapping. There is no entry for cells that are not formatted with
- # rich text.
- #
How to use:
- #
- # runlist = thesheet.rich_text_runlist_map.get((rowx, colx))
- # if runlist:
- # for offset, font_index in runlist:
- # # do work here.
- # pass
- #
- # Populated only if open_workbook(formatting_info=True).
- #
-- New in version 0.7.2.
- #
- rich_text_runlist_map = {}
-
- ##
- # Default column width from DEFCOLWIDTH record, else None.
- # From the OOo docs:
- # """Column width in characters, using the width of the zero character
- # from default font (first FONT record in the file). Excel adds some
- # extra space to the default width, depending on the default font and
- # default font size. The algorithm how to exactly calculate the resulting
- # column width is not known.
- # Example: The default width of 8 set in this record results in a column
- # width of 8.43 using Arial font with a size of 10 points."""
- # For the default hierarchy, refer to the {@link #Colinfo} class.
- #
-- New in version 0.6.1
+
+ #: Mapping of ``(rowx, colx)`` to list of ``(offset, font_index)`` tuples.
+ #: The offset defines where in the string the font begins to be used.
+ #: Offsets are expected to be in ascending order.
+ #: If the first offset is not zero, the meaning is that the cell's ``XF``'s
+ #: font should be used from offset 0.
+ #:
+ #: This is a sparse mapping. There is no entry for cells that are not
+ #: formatted with rich text.
+ #:
+ #: How to use:
+ #:
+ #: .. code-block:: python
+ #:
+ #: runlist = thesheet.rich_text_runlist_map.get((rowx, colx))
+ #: if runlist:
+ #: for offset, font_index in runlist:
+ #: # do work here.
+ #: pass
+ #:
+ #: .. versionadded:: 0.7.2
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ rich_text_runlist_map = {}
+
+ #: Default column width from ``DEFCOLWIDTH`` record, else ``None``.
+ #: From the OOo docs:
+ #:
+ #: Column width in characters, using the width of the zero character
+ #: from default font (first FONT record in the file). Excel adds some
+ #: extra space to the default width, depending on the default font and
+ #: default font size. The algorithm how to exactly calculate the resulting
+ #: column width is not known.
+ #: Example: The default width of 8 set in this record results in a column
+ #: width of 8.43 using Arial font with a size of 10 points.
+ #:
+ #: For the default hierarchy, refer to the :class:`Colinfo` class.
+ #:
+ #: .. versionadded:: 0.6.1
defcolwidth = None
- ##
- # Default column width from STANDARDWIDTH record, else None.
- # From the OOo docs:
- # """Default width of the columns in 1/256 of the width of the zero
- # character, using default font (first FONT record in the file)."""
- # For the default hierarchy, refer to the {@link #Colinfo} class.
- #
-- New in version 0.6.1
+ #: Default column width from ``STANDARDWIDTH`` record, else ``None``.
+ #:
+ #: From the OOo docs:
+ #:
+ #: Default width of the columns in 1/256 of the width of the zero
+ #: character, using default font (first FONT record in the file).
+ #:
+ #: For the default hierarchy, refer to the :class:`Colinfo` class.
+ #:
+ #: .. versionadded:: 0.6.1
standardwidth = None
- ##
- # Default value to be used for a row if there is
- # no ROW record for that row.
- # From the optional DEFAULTROWHEIGHT record.
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
default_row_height = None
- ##
- # Default value to be used for a row if there is
- # no ROW record for that row.
- # From the optional DEFAULTROWHEIGHT record.
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
default_row_height_mismatch = None
- ##
- # Default value to be used for a row if there is
- # no ROW record for that row.
- # From the optional DEFAULTROWHEIGHT record.
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
default_row_hidden = None
- ##
- # Default value to be used for a row if there is
- # no ROW record for that row.
- # From the optional DEFAULTROWHEIGHT record.
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
default_additional_space_above = None
- ##
- # Default value to be used for a row if there is
- # no ROW record for that row.
- # From the optional DEFAULTROWHEIGHT record.
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
default_additional_space_below = None
- ##
- # Visibility of the sheet. 0 = visible, 1 = hidden (can be unhidden
- # by user -- Format/Sheet/Unhide), 2 = "very hidden" (can be unhidden
- # only by VBA macro).
+ #: Visibility of the sheet:
+ #: ::
+ #:
+ #: 0 = visible
+ #: 1 = hidden (can be unhidden by user -- Format -> Sheet -> Unhide)
+ #: 2 = "very hidden" (can be unhidden only by VBA macro).
visibility = 0
- ##
- # A 256-element tuple corresponding to the contents of the GCW record for this sheet.
- # If no such record, treat as all bits zero.
- # Applies to BIFF4-7 only. See docs of the {@link #Colinfo} class for discussion.
+ #: A 256-element tuple corresponding to the contents of the GCW record for
+ #: this sheet. If no such record, treat as all bits zero.
+ #: Applies to BIFF4-7 only. See docs of the :class:`Colinfo` class for
+ #: discussion.
gcw = (0, ) * 256
- ##
- #
-- New in version 0.7.2
-- New in version 0.7.2
-- New in version 0.7.2
-- New in version 0.7.2
+ #: A list of the horizontal page breaks in this sheet.
+ #: Breaks are tuples in the form
+ #: ``(index of row after break, start col index, end col index)``.
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ #:
+ #: .. versionadded:: 0.7.2
horizontal_page_breaks = []
- ##
- # A list of the vertical page breaks in this sheet.
- # Breaks are tuples in the form (index of col after break, start row index, end row index).
- # Populated only if open_workbook(formatting_info=True).
- #
-- New in version 0.7.2
+ #: A list of the vertical page breaks in this sheet.
+ #: Breaks are tuples in the form
+ #: ``(index of col after break, start row index, end row index)``.
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ #:
+ #: .. versionadded:: 0.7.2
vertical_page_breaks = []
-
def __init__(self, book, position, name, number):
self.book = book
self.biff_version = book.biff_version
self._position = position
self.logfile = book.logfile
- self.pickleable = book.pickleable
- if array_array and (CAN_PICKLE_ARRAY or not book.pickleable):
- # use array
- self.bt = array_array('B', [XL_CELL_EMPTY])
- self.bf = array_array('h', [-1])
- else:
- # don't use array
- self.bt = [XL_CELL_EMPTY]
- self.bf = [-1]
+ self.bt = array('B', [XL_CELL_EMPTY])
+ self.bf = array('h', [-1])
self.name = name
self.number = number
self.verbosity = book.verbosity
@@ -369,16 +381,13 @@ def __init__(self, book, position, name, number):
self.cooked_normal_view_mag_factor = 100
# Values (if any) actually stored on the XLS file
- self.cached_page_break_preview_mag_factor = None # from WINDOW2 record
- self.cached_normal_view_mag_factor = None # from WINDOW2 record
+ self.cached_page_break_preview_mag_factor = 0 # default (60%), from WINDOW2 record
+ self.cached_normal_view_mag_factor = 0 # default (100%), from WINDOW2 record
self.scl_mag_factor = None # from SCL record
self._ixfe = None # BIFF2 only
self._cell_attr_to_xfx = {} # BIFF2.0 only
- #### Don't initialise this here, use class attribute initialisation.
- #### self.gcw = (0, ) * 256 ####
-
if self.biff_version >= 80:
self.utter_max_rows = 65536
else:
@@ -392,10 +401,10 @@ def __init__(self, book, position, name, number):
# self._put_cell_rows_appended = 0
# self._put_cell_cells_appended = 0
-
- ##
- # {@link #Cell} object in the given row and column.
def cell(self, rowx, colx):
+ """
+ :class:`Cell` object in the given row and column.
+ """
if self.formatting_info:
xfx = self.cell_xf_index(rowx, colx)
else:
@@ -404,24 +413,27 @@ def cell(self, rowx, colx):
self._cell_types[rowx][colx],
self._cell_values[rowx][colx],
xfx,
- )
+ )
- ##
- # Value of the cell in the given row and column.
def cell_value(self, rowx, colx):
+ "Value of the cell in the given row and column."
return self._cell_values[rowx][colx]
- ##
- # Type of the cell in the given row and column.
- # Refer to the documentation of the {@link #Cell} class.
def cell_type(self, rowx, colx):
+ """
+ Type of the cell in the given row and column.
+
+ Refer to the documentation of the :class:`Cell` class.
+ """
return self._cell_types[rowx][colx]
- ##
- # XF index of the cell in the given row and column.
- # This is an index into Book.{@link #Book.xf_list}.
- #
-- New in version 0.6.1
def cell_xf_index(self, rowx, colx):
+ """
+ XF index of the cell in the given row and column.
+ This is an index into :attr:`~xlrd.book.Book.xf_list`.
+
+ .. versionadded:: 0.6.1
+ """
self.req_fmt_info()
xfx = self._cell_xf_indexes[rowx][colx]
if xfx > -1:
@@ -446,41 +458,66 @@ def cell_xf_index(self, rowx, colx):
self._xf_index_stats[3] += 1
return 15
- ##
- # Returns the effective number of cells in the given row. For use with
- # open_workbook(ragged_rows=True) which is likely to produce rows
- # with fewer than {@link #Sheet.ncols} cells.
- #
-- New in version 0.7.2
def row_len(self, rowx):
+ """
+ Returns the effective number of cells in the given row. For use with
+ ``open_workbook(ragged_rows=True)`` which is likely to produce rows
+ with fewer than :attr:`~Sheet.ncols` cells.
+
+ .. versionadded:: 0.7.2
+ """
return len(self._cell_values[rowx])
- ##
- # Returns a sequence of the {@link #Cell} objects in the given row.
def row(self, rowx):
+ """
+ Returns a sequence of the :class:`Cell` objects in the given row.
+ """
return [
self.cell(rowx, colx)
for colx in xrange(len(self._cell_values[rowx]))
- ]
+ ]
+
+ def __getitem__(self, item):
+ """
+ Takes either rowindex or (rowindex, colindex) as an index,
+ and returns either row or cell respectively.
+ """
+ try:
+ rowix, colix = item
+ except TypeError:
+ # it's not a tuple (or of right size), let's try indexing as is
+ # if this is a problem, let this error propagate back
+ return self.row(item)
+ else:
+ return self.cell(rowix, colix)
+
+ def get_rows(self):
+ "Returns a generator for iterating through each row."
+ return (self.row(index) for index in range(self.nrows))
+
+ # makes `for row in sheet` natural and intuitive
+ __iter__ = get_rows
- ##
- # Returns a slice of the types
- # of the cells in the given row.
def row_types(self, rowx, start_colx=0, end_colx=None):
+ """
+ Returns a slice of the types of the cells in the given row.
+ """
if end_colx is None:
return self._cell_types[rowx][start_colx:]
return self._cell_types[rowx][start_colx:end_colx]
- ##
- # Returns a slice of the values
- # of the cells in the given row.
def row_values(self, rowx, start_colx=0, end_colx=None):
+ """
+ Returns a slice of the values of the cells in the given row.
+ """
if end_colx is None:
return self._cell_values[rowx][start_colx:]
return self._cell_values[rowx][start_colx:end_colx]
- ##
- # Returns a slice of the {@link #Cell} objects in the given row.
def row_slice(self, rowx, start_colx=0, end_colx=None):
+ """
+ Returns a slice of the :class:`Cell` objects in the given row.
+ """
nc = len(self._cell_values[rowx])
if start_colx < 0:
start_colx += nc
@@ -493,11 +530,12 @@ def row_slice(self, rowx, start_colx=0, end_colx=None):
return [
self.cell(rowx, colx)
for colx in xrange(start_colx, end_colx)
- ]
+ ]
- ##
- # Returns a slice of the {@link #Cell} objects in the given column.
def col_slice(self, colx, start_rowx=0, end_rowx=None):
+ """
+ Returns a slice of the :class:`Cell` objects in the given column.
+ """
nr = self.nrows
if start_rowx < 0:
start_rowx += nr
@@ -510,11 +548,12 @@ def col_slice(self, colx, start_rowx=0, end_rowx=None):
return [
self.cell(rowx, colx)
for rowx in xrange(start_rowx, end_rowx)
- ]
+ ]
- ##
- # Returns a slice of the values of the cells in the given column.
def col_values(self, colx, start_rowx=0, end_rowx=None):
+ """
+ Returns a slice of the values of the cells in the given column.
+ """
nr = self.nrows
if start_rowx < 0:
start_rowx += nr
@@ -527,11 +566,12 @@ def col_values(self, colx, start_rowx=0, end_rowx=None):
return [
self._cell_values[rowx][colx]
for rowx in xrange(start_rowx, end_rowx)
- ]
+ ]
- ##
- # Returns a slice of the types of the cells in the given column.
def col_types(self, colx, start_rowx=0, end_rowx=None):
+ """
+ Returns a slice of the types of the cells in the given column.
+ """
nr = self.nrows
if start_rowx < 0:
start_rowx += nr
@@ -544,13 +584,8 @@ def col_types(self, colx, start_rowx=0, end_rowx=None):
return [
self._cell_types[rowx][colx]
for rowx in xrange(start_rowx, end_rowx)
- ]
+ ]
- ##
- # Returns a sequence of the {@link #Cell} objects in the given column.
- def col(self, colx):
- return self.col_slice(colx)
- # Above two lines just for the docs. Here's the real McCoy:
col = col_slice
# === Following methods are used in building the worksheet.
@@ -558,18 +593,18 @@ def col(self, colx):
def tidy_dimensions(self):
if self.verbosity >= 3:
- fprintf(self.logfile,
+ fprintf(
+ self.logfile,
"tidy_dimensions: nrows=%d ncols=%d \n",
self.nrows, self.ncols,
- )
+ )
if 1 and self.merged_cells:
nr = nc = 0
umaxrows = self.utter_max_rows
umaxcols = self.utter_max_cols
for crange in self.merged_cells:
rlo, rhi, clo, chi = crange
- if not (0 <= rlo < rhi <= umaxrows) \
- or not (0 <= clo < chi <= umaxcols):
+ if not (0 <= rlo < rhi <= umaxrows) or not (0 <= clo < chi <= umaxcols):
fprintf(self.logfile,
"*** WARNING: sheet #%d (%r), MERGEDCELLS bad range %r\n",
self.number, self.name, crange)
@@ -577,14 +612,16 @@ def tidy_dimensions(self):
if chi > nc: nc = chi
if nc > self.ncols:
self.ncols = nc
+ self._first_full_rowx = -2
if nr > self.nrows:
# we put one empty cell at (nr-1,0) to make sure
# we have the right number of rows. The ragged rows
# will sort out the rest if needed.
- self.put_cell(nr-1, 0, XL_CELL_EMPTY, -1)
- if self.verbosity >= 1 \
- and (self.nrows != self._dimnrows or self.ncols != self._dimncols):
- fprintf(self.logfile,
+ self.put_cell(nr-1, 0, XL_CELL_EMPTY, UNICODE_LITERAL(''), -1)
+ if (self.verbosity >= 1 and
+ (self.nrows != self._dimnrows or self.ncols != self._dimncols)):
+ fprintf(
+ self.logfile,
"NOTE *** sheet %d (%r): DIMENSIONS R,C = %d,%d should be %d,%d\n",
self.number,
self.name,
@@ -592,7 +629,7 @@ def tidy_dimensions(self):
self._dimncols,
self.nrows,
self.ncols,
- )
+ )
if not self.ragged_rows:
# fix ragged rows
ncols = self.ncols
@@ -610,7 +647,7 @@ def tidy_dimensions(self):
rlen = len(trow)
nextra = ncols - rlen
if nextra > 0:
- s_cell_values[rowx][rlen:] = [''] * nextra
+ s_cell_values[rowx][rlen:] = [UNICODE_LITERAL('')] * nextra
trow[rlen:] = self.bt * nextra
if s_fmt_info:
s_cell_xf_indexes[rowx][rlen:] = self.bf * nextra
@@ -659,19 +696,19 @@ def put_cell_ragged(self, rowx, colx, ctype, value, xf_index):
num_empty += 1
# self._put_cell_row_widenings += 1
# types_row.extend(self.bt * num_empty)
- # values_row.extend([''] * num_empty)
+ # values_row.extend([UNICODE_LITERAL('')] * num_empty)
# if fmt_info:
# fmt_row.extend(self.bf * num_empty)
types_row[ltr:] = self.bt * num_empty
- values_row[ltr:] = [''] * num_empty
+ values_row[ltr:] = [UNICODE_LITERAL('')] * num_empty
if fmt_info:
fmt_row[ltr:] = self.bf * num_empty
types_row[colx] = ctype
values_row[colx] = value
if fmt_info:
fmt_row[colx] = xf_index
- except:
- print >> self.logfile, "put_cell", rowx, colx
+ except Exception:
+ print("put_cell", rowx, colx, file=self.logfile)
raise
def put_cell_unragged(self, rowx, colx, ctype, value, xf_index):
@@ -702,7 +739,7 @@ def put_cell_unragged(self, rowx, colx, ctype, value, xf_index):
if nr < self.nrows:
# cell data is not in non-descending row order *AND*
# self.ncols has been bumped up.
- # This very rare case ruins this optmisation.
+ # This very rare case ruins this optimisation.
self._first_full_rowx = -2
elif rowx > self._first_full_rowx > -2:
self._first_full_rowx = rowx
@@ -717,7 +754,7 @@ def put_cell_unragged(self, rowx, colx, ctype, value, xf_index):
trow.extend(self.bt * nextra)
if self.formatting_info:
self._cell_xf_indexes[rowx].extend(self.bf * nextra)
- self._cell_values[rowx].extend([''] * nextra)
+ self._cell_values[rowx].extend([UNICODE_LITERAL('')] * nextra)
else:
scta = self._cell_types.append
scva = self._cell_values.append
@@ -729,7 +766,7 @@ def put_cell_unragged(self, rowx, colx, ctype, value, xf_index):
for _unused in xrange(self.nrows, nr):
# self._put_cell_rows_appended += 1
scta(bt * nc)
- scva([''] * nc)
+ scva([UNICODE_LITERAL('')] * nc)
if fmt_info:
scxa(bf * nc)
self.nrows = nr
@@ -739,12 +776,12 @@ def put_cell_unragged(self, rowx, colx, ctype, value, xf_index):
self._cell_values[rowx][colx] = value
if self.formatting_info:
self._cell_xf_indexes[rowx][colx] = xf_index
- except:
- print >> self.logfile, "put_cell", rowx, colx
+ except Exception:
+ print("put_cell", rowx, colx, file=self.logfile)
raise
- except:
- print >> self.logfile, "put_cell", rowx, colx
- raise
+ except Exception:
+ print("put_cell", rowx, colx, file=self.logfile)
+ raise
# === Methods after this line neither know nor care about how cells are stored.
@@ -761,7 +798,7 @@ def read(self, bk):
XL_SHRFMLA_ETC_ETC = (
XL_SHRFMLA, XL_ARRAY, XL_TABLEOP, XL_TABLEOP2,
XL_ARRAY2, XL_TABLEOP_B2,
- )
+ )
self_put_cell = self.put_cell
local_unpack = unpack
bk_get_record_parts = bk.get_record_parts
@@ -806,7 +843,7 @@ def read(self, bk):
rowx, colx, xf_index = local_unpack('
-- New in version 0.6.1
- #
- # @param colx Index of the queried column, range 0 to 255.
- # Note that it is possible to find out the width that will be used to display
- # columns with no cell information e.g. column IV (colx=255).
- # @return The column width that will be used for displaying
- # the given column by Excel, in units of 1/256th of the width of a
- # standard character (the digit zero in the first font).
-
def computed_column_width(self, colx):
+ """
+ Determine column display width.
+
+ :param colx:
+ Index of the queried column, range 0 to 255.
+ Note that it is possible to find out the width that will be used to
+ display columns with no cell information e.g. column IV (colx=255).
+
+ :return:
+ The column width that will be used for displaying
+ the given column by Excel, in units of 1/256th of the width of a
+ standard character (the digit zero in the first font).
+
+ .. versionadded:: 0.6.1
+ """
self.req_fmt_info()
if self.biff_version >= 80:
colinfo = self.colinfo_map.get(colx, None)
@@ -1703,13 +1745,13 @@ def computed_column_width(self, colx):
def handle_hlink(self, data):
# DEBUG = 1
- if DEBUG: print >> self.logfile, "\n=== hyperlink ==="
+ if DEBUG: print("\n=== hyperlink ===", file=self.logfile)
record_size = len(data)
h = Hyperlink()
h.frowx, h.lrowx, h.fcolx, h.lcolx, guid0, dummy, options = unpack('> self.logfile, "uplevels=%d shortpath=%r" % (uplevels, shortpath)
+ shortpath = b"..\\" * uplevels + data[offset:offset + nbytes - 1] #### BYTES, not unicode
+ if DEBUG: fprintf(self.logfile, "uplevels=%d shortpath=%r\n", uplevels, shortpath)
offset += nbytes
offset += 24 # OOo: "unknown byte sequence"
# above is version 0xDEAD + 20 reserved zero bytes
sz = unpack('> self.logfile, "sz=%d" % sz
+ if DEBUG: print("sz=%d" % sz, file=self.logfile)
offset += 4
if sz:
xl = unpack('> self.logfile, "*** unknown clsid %r" % clsid
+ fprintf(self.logfile, "*** unknown clsid %r\n", clsid)
elif options & 0x163 == 0x103: # UNC
- h.type = u'unc'
+ h.type = UNICODE_LITERAL('unc')
h.url_or_path, offset = get_nul_terminated_unicode(data, offset)
elif options & 0x16B == 8:
- h.type = u'workbook'
+ h.type = UNICODE_LITERAL('workbook')
else:
- h.type = u'unknown'
-
+ h.type = UNICODE_LITERAL('unknown')
+
if options & 0x8: # has textmark
h.textmark, offset = get_nul_terminated_unicode(data, offset)
- assert offset == record_size
- if DEBUG: h.dump(header="... object dump ...")
+ if DEBUG:
+ h.dump(header="... object dump ...")
+ print("offset=%d record_size=%d" % (offset, record_size))
+
+ extra_nbytes = record_size - offset
+ if extra_nbytes > 0:
+ fprintf(
+ self.logfile,
+ "*** WARNING: hyperlink at R%dC%d has %d extra data bytes: %s\n",
+ h.frowx + 1,
+ h.fcolx + 1,
+ extra_nbytes,
+ REPR(data[-extra_nbytes:]),
+ )
+ # Seen: b"\x00\x00" also b"A\x00", b"V\x00"
+ elif extra_nbytes < 0:
+ raise XLRDError("Bug or corrupt file, send copy of input file for debugging")
self.hyperlink_list.append(h)
for rowx in xrange(h.frowx, h.lrowx+1):
for colx in xrange(h.fcolx, h.lcolx+1):
self.hyperlink_map[rowx, colx] = h
-
+
def handle_quicktip(self, data):
rcx, frowx, lrowx, fcolx, lcolx = unpack('<5H', data[:10])
assert rcx == XL_QUICKTIP
assert self.hyperlink_list
h = self.hyperlink_list[-1]
assert (frowx, lrowx, fcolx, lcolx) == (h.frowx, h.lrowx, h.fcolx, h.lcolx)
- assert data[-2:] == '\x00\x00'
+ assert data[-2:] == b'\x00\x00'
h.quicktip = unicode(data[10:-2], 'utf_16_le')
def handle_msodrawingetc(self, recid, data_len, data):
@@ -1871,9 +1931,9 @@ def handle_obj(self, data):
( 9, 0x0200, 'scrollbar_flag'), # not documented in Excel 97 dev kit
(13, 0x2000, 'autofill'),
(14, 0x4000, 'autoline'),
- ))
+ ))
elif ft == 0x00:
- if data[pos:data_len] == BYTES_X00 * (data_len - pos):
+ if data[pos:data_len] == b'\0' * (data_len - pos):
# ignore "optional reserved" data at end of record
break
msg = "Unexpected data at end of OBJECT record"
@@ -1922,14 +1982,14 @@ def handle_note(self, data, txos):
expected_bytes -= nb
assert expected_bytes == 0
enc = self.book.encoding or self.book.derive_encoding()
- o.text = unicode(''.join(pieces), enc)
+ o.text = unicode(b''.join(pieces), enc)
o.rich_text_runlist = [(0, 0)]
o.show = 0
o.row_hidden = 0
o.col_hidden = 0
- o.author = u''
+ o.author = UNICODE_LITERAL('')
o._object_id = None
- self.cell_note_map[o.rowx, o.colx] = o
+ self.cell_note_map[o.rowx, o.colx] = o
return
# Excel 8.0+
o.rowx, o.colx, option_flags, o._object_id = unpack('<4H', data[:8])
@@ -1949,13 +2009,12 @@ def handle_note(self, data, txos):
if txo:
o.text = txo.text
o.rich_text_runlist = txo.rich_text_runlist
- self.cell_note_map[o.rowx, o.colx] = o
+ self.cell_note_map[o.rowx, o.colx] = o
def handle_txo(self, data):
if self.biff_version < 80:
return
o = MSTxo()
- data_len = len(data)
fmt = ' > self.logfile, o.rich_text_runlist
+ print(o.rich_text_runlist, file=self.logfile)
return o
def handle_feat11(self, data):
@@ -2022,7 +2081,7 @@ def handle_feat11(self, data):
assert rt == 0x872
assert fHdr == 0
assert Ref1 == Ref0
- print >> self.logfile, "FEAT11: grbitFrt=%d Ref0=%r cref=%d cbFeatData=%d" % (grbitFrt, Ref0, cref, cbFeatData)
+ print(self.logfile, "FEAT11: grbitFrt=%d Ref0=%r cref=%d cbFeatData=%d\n", grbitFrt, Ref0, cref, cbFeatData)
# lt: Table data source type:
# =0 for Excel Worksheet Table =1 for read-write SharePoint linked List
# =2 for XML mapper Table =3 for Query Table
@@ -2043,106 +2102,122 @@ def handle_feat11(self, data):
(lt, idList, crwHeader, crwTotals, idFieldNext, cbFSData,
rupBuild, unusedShort, listFlags, lPosStmCache, cbStmCache,
cchStmCache, lem, rgbHashParam, cchName) = unpack('
Represents a user "comment" or "note".
-# Note objects are accessible through Sheet.{@link #Sheet.cell_note_map}.
-#
-- New in version 0.7.2
-#
Contains the attributes of a hyperlink.
-# Hyperlink objects are accessible through Sheet.{@link #Sheet.hyperlink_list}
-# and Sheet.{@link #Sheet.hyperlink_map}.
-#
-- New in version 0.7.2
-#
WARNING: You don't call this class yourself. You access Cell objects -# via methods of the {@link #Sheet} object(s) that you found in the {@link #Book} object that -# was returned when you called xlrd.open_workbook("myfile.xls").
-#Cell objects have three attributes: ctype is an int, value -# (which depends on ctype) and xf_index. -# If "formatting_info" is not enabled when the workbook is opened, xf_index will be None. -# The following table describes the types of cells and how their values -# are represented in Python.
-# -#| Type symbol | -#Type number | -#Python value | -#
|---|---|---|
| XL_CELL_EMPTY | -#0 | -#empty string u'' | -#
| XL_CELL_TEXT | -#1 | -#a Unicode string | -#
| XL_CELL_NUMBER | -#2 | -#float | -#
| XL_CELL_DATE | -#3 | -#float | -#
| XL_CELL_BOOLEAN | -#4 | -#int; 1 means TRUE, 0 means FALSE | -#
| XL_CELL_ERROR | -#5 | -#int representing internal Excel codes; for a text representation, -# refer to the supplied dictionary error_text_from_code | -#
| XL_CELL_BLANK | -#6 | -#empty string u''. Note: this type will appear only when -# open_workbook(..., formatting_info=True) is used. | -#
| Type symbol | +Type number | +Python value | +
|---|---|---|
| XL_CELL_EMPTY | +0 | +empty string '' | +
| XL_CELL_TEXT | +1 | +a Unicode string | +
| XL_CELL_NUMBER | +2 | +float | +
| XL_CELL_DATE | +3 | +float | +
| XL_CELL_BOOLEAN | +4 | +int; 1 means TRUE, 0 means FALSE | +
| XL_CELL_ERROR | +5 | +int representing internal Excel codes; for a text representation, + refer to the supplied dictionary error_text_from_code | +
| XL_CELL_BLANK | +6 | +empty string ''. Note: this type will appear only when + open_workbook(..., formatting_info=True) is used. | +
Here is the default hierarchy for width, according to the OOo docs:
-#
-#
"""In BIFF3, if a COLINFO record is missing for a column,
-# the width specified in the record DEFCOLWIDTH is used instead.
-#
-#
In BIFF4-BIFF7, the width set in this [COLINFO] record is only used,
-# if the corresponding bit for this column is cleared in the GCW
-# record, otherwise the column width set in the DEFCOLWIDTH record
-# is used (the STANDARDWIDTH record is always ignored in this case [see footnote!]).
-#
-#
In BIFF8, if a COLINFO record is missing for a column,
-# the width specified in the record STANDARDWIDTH is used.
-# If this [STANDARDWIDTH] record is also missing,
-# the column width of the record DEFCOLWIDTH is used instead."""
-#
-#
-# Footnote: The docs on the GCW record say this:
-# """
-# If a bit is set, the corresponding column uses the width set in the STANDARDWIDTH
-# record. If a bit is cleared, the corresponding column uses the width set in the
-# COLINFO record for this column.
-#
If a bit is set, and the worksheet does not contain the STANDARDWIDTH record, or if
-# the bit is cleared, and the worksheet does not contain the COLINFO record, the DEFCOLWIDTH
-# record of the worksheet will be used instead.
-#
"""
-# At the moment (2007-01-17) xlrd is going with the GCW version of the story.
-# Reference to the source may be useful: see the computed_column_width(colx) method
-# of the Sheet class.
-#
-- New in version 0.6.1
-#
Height and default formatting information that applies to a row in a sheet.
-# Derived from ROW records.
-#
-- New in version 0.6.1
height: Height of the row, in twips. One twip == 1/20 of a point.
-# -#has_default_height: 0 = Row has custom height; 1 = Row has default height.
-# -#outline_level: Outline level of the row (0 to 7)
-# -#outline_group_starts_ends: 1 = Outline group starts or ends here (depending on where the -# outline buttons are located, see WSBOOL record [TODO ??]), -# and is collapsed
-# -#hidden: 1 = Row is hidden (manually, or by a filter or outline group)
-# -#height_mismatch: 1 = Row height and default font height do not match
-# -#has_default_xf_index: 1 = the xf_index attribute is usable; 0 = ignore it
-# -#xf_index: Index to default XF record for empty cells in this row. -# Don't use this if has_default_xf_index == 0.
-# -#additional_space_above: This flag is set, if the upper border of at least one cell in this row -# or if the lower border of at least one cell in the row above is -# formatted with a thick line style. Thin and medium line styles are not -# taken into account.
-# -#additional_space_below: This flag is set, if the lower border of at least one cell in this row -# or if the upper border of at least one cell in the row below is -# formatted with a medium or thick line style. Thin line styles are not -# taken into account.
class Rowinfo(BaseObject): + """ + Height and default formatting information that applies to a row in a sheet. + Derived from ``ROW`` records. + + .. versionadded:: 0.6.1 + """ if _USE_SLOTS: __slots__ = ( @@ -2365,18 +2419,46 @@ class Rowinfo(BaseObject): "xf_index", "additional_space_above", "additional_space_below", - ) + ) def __init__(self): + #: Height of the row, in twips. One twip == 1/20 of a point. self.height = None + + #: 0 = Row has custom height; 1 = Row has default height. self.has_default_height = None + + #: Outline level of the row (0 to 7) self.outline_level = None + + #: 1 = Outline group starts or ends here (depending on where the + #: outline buttons are located, see ``WSBOOL`` record, which is not + #: parsed by xlrd), *and* is collapsed. self.outline_group_starts_ends = None + + #: 1 = Row is hidden (manually, or by a filter or outline group) self.hidden = None + + #: 1 = Row height and default font height do not match. self.height_mismatch = None + + #: 1 = the xf_index attribute is usable; 0 = ignore it. self.has_default_xf_index = None + + #: Index to default :class:`~xlrd.formatting.XF` record for empty cells + #: in this row. Don't use this if ``has_default_xf_index == 0``. self.xf_index = None + + #: This flag is set if the upper border of at least one cell in this + #: row or if the lower border of at least one cell in the row above is + #: formatted with a thick line style. Thin and medium line styles are + #: not taken into account. self.additional_space_above = None + + #: This flag is set if the lower border of at least one cell in this row + #: or if the upper border of at least one cell in the row below is + #: formatted with a medium or thick line style. Thin line styles are not + #: taken into account. self.additional_space_below = None def __getstate__(self): @@ -2391,7 +2473,7 @@ def __getstate__(self): self.xf_index, self.additional_space_above, self.additional_space_below, - ) + ) def __setstate__(self, state): ( @@ -2405,4 +2487,4 @@ def __setstate__(self, state): self.xf_index, self.additional_space_above, self.additional_space_below, - ) = state + ) = state diff --git a/xlrd/timemachine.py b/xlrd/timemachine.py index cfdfbcdf..a519299e 100644 --- a/xlrd/timemachine.py +++ b/xlrd/timemachine.py @@ -1,91 +1,53 @@ -# -*- coding: ascii -*- - ## #Copyright (c) 2006-2012 Stephen John Machin, Lingfo Pty Ltd
#This module is part of the xlrd package, which is released under a BSD-style licence.
## # timemachine.py -- adaptation for single codebase. -# Currently supported: 2.1 to 2.7 +# Currently supported: 2.6 to 2.7, 3.2+ # usage: from timemachine import * -from __future__ import nested_scopes -import sys +from __future__ import print_function -python_version = sys.version_info[:2] # e.g. version 2.4 -> (2, 4) +import sys -CAN_PICKLE_ARRAY = python_version >= (2, 5) -CAN_SUBCLASS_BUILTIN = python_version >= (2, 2) +python_version = sys.version_info[:2] # e.g. version 2.6 -> (2, 6) -if python_version >= (3, 0): # Might work on 3.0 but absolutely no support! +if python_version >= (3, 0): + # Python 3 BYTES_LITERAL = lambda x: x.encode('latin1') + UNICODE_LITERAL = lambda x: x BYTES_ORD = lambda byte: byte - BYTES_NULL = bytes(0) # b'' - BYTES_X00 = bytes(1) # b'\x00' - BYTES_X01 = bytes([1]) # b'\x01' from io import BytesIO as BYTES_IO def fprintf(f, fmt, *vargs): fmt = fmt.replace("%r", "%a") - f.write(fmt % vargs) + if fmt.endswith('\n'): + print(fmt[:-1] % vargs, file=f) + else: + print(fmt % vargs, end=' ', file=f) EXCEL_TEXT_TYPES = (str, bytes, bytearray) # xlwt: isinstance(obj, EXCEL_TEXT_TYPES) REPR = ascii + xrange = range + unicode = lambda b, enc: b.decode(enc) + ensure_unicode = lambda s: s + unichr = chr else: + # Python 2 BYTES_LITERAL = lambda x: x + UNICODE_LITERAL = lambda x: x.decode('latin1') BYTES_ORD = ord - BYTES_NULL = '' - BYTES_X00 = '\x00' - BYTES_X01 = '\x01' from cStringIO import StringIO as BYTES_IO def fprintf(f, fmt, *vargs): - f.write(fmt % vargs) + if fmt.endswith('\n'): + print(fmt[:-1] % vargs, file=f) + else: + print(fmt % vargs, end=' ', file=f) try: EXCEL_TEXT_TYPES = basestring # xlwt: isinstance(obj, EXCEL_TEXT_TYPES) except NameError: EXCEL_TEXT_TYPES = (str, unicode) REPR = repr - -if python_version >= (2, 6): - def BUFFER(obj, offset=0, size=None): - if size is None: - return memoryview(obj)[offset:] - return memoryview(obj)[offset:offset+size] -else: - BUFFER = buffer - -try: - from array import array as array_array -except ImportError: - # old version of IronPython? - array_array = None - -try: - object -except NameError: - class object: - pass - -try: - True -except NameError: - setattr(sys.modules['__builtin__'], 'True', 1) - -try: - False -except NameError: - setattr(sys.modules['__builtin__'], 'False', 0) - - -def int_floor_div(x, y): - return divmod(x, y)[0] - -def intbool(x): - if x: - return 1 - return 0 - -if python_version < (2, 3): - def sum(sequence, start=0): - tot = start - for item in aseq: - tot += item - return tot + xrange = xrange + # following used only to overcome 2.x ElementTree gimmick which + # returns text as `str` if it's ascii, otherwise `unicode` + ensure_unicode = unicode # used only in xlsx.py diff --git a/xlrd/xldate.py b/xlrd/xldate.py index e5f75916..d84c6508 100644 --- a/xlrd/xldate.py +++ b/xlrd/xldate.py @@ -1,59 +1,93 @@ -# -*- coding: cp1252 -*- - +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2008 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. # No part of the content of this file was derived from the works of David Giffin. +""" +Tools for working with dates and times in Excel files. + +The conversion from ``days`` to ``(year, month, day)`` starts with +an integral "julian day number" aka JDN. +FWIW: -## -#Copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under a BSD-style licence.
-# -#Provides function(s) for dealing with Microsoft Excel ™ dates.
-## +- JDN 0 corresponds to noon on Monday November 24 in Gregorian year -4713. -# 2008-10-18 SJM Fix bug in xldate_from_date_tuple (affected some years after 2099) +More importantly: -# The conversion from days to (year, month, day) starts with -# an integral "julian day number" aka JDN. -# FWIW, JDN 0 corresponds to noon on Monday November 24 in Gregorian year -4713. -# More importantly: -# Noon on Gregorian 1900-03-01 (day 61 in the 1900-based system) is JDN 2415080.0 -# Noon on Gregorian 1904-01-02 (day 1 in the 1904-based system) is JDN 2416482.0 +- Noon on Gregorian 1900-03-01 (day 61 in the 1900-based system) is JDN 2415080.0 +- Noon on Gregorian 1904-01-02 (day 1 in the 1904-based system) is JDN 2416482.0 -from timemachine import int_floor_div as ifd +""" +import datetime _JDN_delta = (2415080 - 61, 2416482 - 1) assert _JDN_delta[1] - _JDN_delta[0] == 1462 -class XLDateError(ValueError): pass - -class XLDateNegative(XLDateError): pass -class XLDateAmbiguous(XLDateError): pass -class XLDateTooLarge(XLDateError): pass -class XLDateBadDatemode(XLDateError): pass -class XLDateBadTuple(XLDateError): pass - -_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01 - -## -# Convert an Excel number (presumed to represent a date, a datetime or a time) into -# a tuple suitable for feeding to datetime or mx.DateTime constructors. -# @param xldate The Excel number -# @param datemode 0: 1900-based, 1: 1904-based. -#Portions copyright (c) 2008-2012 Stephen John Machin, Lingfo Pty Ltd
-#This module is part of the xlrd package, which is released under a BSD-style licence.
-## - -DEBUG = 0 - -import sys, zipfile, pprint -import re -from timemachine import * -from book import Book, Name -from biffh import error_text_from_code, XLRDError, XL_CELL_BLANK, XL_CELL_TEXT, XL_CELL_BOOLEAN, XL_CELL_ERROR -from formatting import is_date_format_string, Format, XF -from sheet import Sheet - -DLF = sys.stdout # Default Log File - -ET = None -ET_has_iterparse = False - -def ensure_elementtree_imported(verbosity, logfile): - global ET, ET_has_iterparse - if ET is not None: - return - if "IronPython" in sys.version: - import xml.etree.ElementTree as ET - #### 2.7.2.1: fails later with - #### NotImplementedError: iterparse is not supported on IronPython. (CP #31923) - else: - try: import xml.etree.cElementTree as ET - except ImportError: - try: import cElementTree as ET - except ImportError: - try: import lxml.etree as ET - except ImportError: - try: import xml.etree.ElementTree as ET - except ImportError: - try: import elementtree.ElementTree as ET - except ImportError: - raise Exception("Failed to import an ElementTree implementation") - if hasattr(ET, 'iterparse'): - _dummy_stream = BYTES_IO(BYTES_NULL) - try: - ET.iterparse(_dummy_stream) - ET_has_iterparse = True - except NotImplementedError: - pass - if verbosity: - etree_version = repr([ - (item, getattr(ET, item)) - for item in ET.__dict__.keys() - if item.lower().replace('_', '') == 'version' - ]) - print >> logfile, ET.__file__, ET.__name__, etree_version, ET_has_iterparse - -def split_tag(tag): - pos = tag.rfind('}') + 1 - if pos >= 2: - return tag[:pos], tag[pos:] - return '', tag - -def augment_keys(adict, uri): - # uri must already be enclosed in {} - for x in adict.keys(): - adict[uri + x] = adict[x] - -_UPPERCASE_1_REL_INDEX = {} # Used in fast conversion of column names (e.g. "XFD") to indices (16383) -for _x in xrange(26): - _UPPERCASE_1_REL_INDEX["ABCDEFGHIJKLMNOPQRSTUVWXYZ"[_x]] = _x + 1 -for _x in "123456789": - _UPPERCASE_1_REL_INDEX[_x] = 0 -del _x - -def cell_name_to_rowx_colx(cell_name, letter_value=_UPPERCASE_1_REL_INDEX): - # Extract column index from cell name - # A