_xlrd.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. from __future__ import annotations
  2. from datetime import time
  3. import numpy as np
  4. from pandas._typing import (
  5. Scalar,
  6. StorageOptions,
  7. )
  8. from pandas.compat._optional import import_optional_dependency
  9. from pandas.util._decorators import doc
  10. from pandas.core.shared_docs import _shared_docs
  11. from pandas.io.excel._base import BaseExcelReader
  12. class XlrdReader(BaseExcelReader):
  13. @doc(storage_options=_shared_docs["storage_options"])
  14. def __init__(
  15. self, filepath_or_buffer, storage_options: StorageOptions = None
  16. ) -> None:
  17. """
  18. Reader using xlrd engine.
  19. Parameters
  20. ----------
  21. filepath_or_buffer : str, path object or Workbook
  22. Object to be parsed.
  23. {storage_options}
  24. """
  25. err_msg = "Install xlrd >= 2.0.1 for xls Excel support"
  26. import_optional_dependency("xlrd", extra=err_msg)
  27. super().__init__(filepath_or_buffer, storage_options=storage_options)
  28. @property
  29. def _workbook_class(self):
  30. from xlrd import Book
  31. return Book
  32. def load_workbook(self, filepath_or_buffer):
  33. from xlrd import open_workbook
  34. if hasattr(filepath_or_buffer, "read"):
  35. data = filepath_or_buffer.read()
  36. return open_workbook(file_contents=data)
  37. else:
  38. return open_workbook(filepath_or_buffer)
  39. @property
  40. def sheet_names(self):
  41. return self.book.sheet_names()
  42. def get_sheet_by_name(self, name):
  43. self.raise_if_bad_sheet_by_name(name)
  44. return self.book.sheet_by_name(name)
  45. def get_sheet_by_index(self, index):
  46. self.raise_if_bad_sheet_by_index(index)
  47. return self.book.sheet_by_index(index)
  48. def get_sheet_data(
  49. self, sheet, file_rows_needed: int | None = None
  50. ) -> list[list[Scalar]]:
  51. from xlrd import (
  52. XL_CELL_BOOLEAN,
  53. XL_CELL_DATE,
  54. XL_CELL_ERROR,
  55. XL_CELL_NUMBER,
  56. xldate,
  57. )
  58. epoch1904 = self.book.datemode
  59. def _parse_cell(cell_contents, cell_typ):
  60. """
  61. converts the contents of the cell into a pandas appropriate object
  62. """
  63. if cell_typ == XL_CELL_DATE:
  64. # Use the newer xlrd datetime handling.
  65. try:
  66. cell_contents = xldate.xldate_as_datetime(cell_contents, epoch1904)
  67. except OverflowError:
  68. return cell_contents
  69. # Excel doesn't distinguish between dates and time,
  70. # so we treat dates on the epoch as times only.
  71. # Also, Excel supports 1900 and 1904 epochs.
  72. year = (cell_contents.timetuple())[0:3]
  73. if (not epoch1904 and year == (1899, 12, 31)) or (
  74. epoch1904 and year == (1904, 1, 1)
  75. ):
  76. cell_contents = time(
  77. cell_contents.hour,
  78. cell_contents.minute,
  79. cell_contents.second,
  80. cell_contents.microsecond,
  81. )
  82. elif cell_typ == XL_CELL_ERROR:
  83. cell_contents = np.nan
  84. elif cell_typ == XL_CELL_BOOLEAN:
  85. cell_contents = bool(cell_contents)
  86. elif cell_typ == XL_CELL_NUMBER:
  87. # GH5394 - Excel 'numbers' are always floats
  88. # it's a minimal perf hit and less surprising
  89. val = int(cell_contents)
  90. if val == cell_contents:
  91. cell_contents = val
  92. return cell_contents
  93. data = []
  94. nrows = sheet.nrows
  95. if file_rows_needed is not None:
  96. nrows = min(nrows, file_rows_needed)
  97. for i in range(nrows):
  98. row = [
  99. _parse_cell(value, typ)
  100. for value, typ in zip(sheet.row_values(i), sheet.row_types(i))
  101. ]
  102. data.append(row)
  103. return data