python-calamine 0.3.2__cp312-cp312-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ from ._python_calamine import (
2
+ CalamineError,
3
+ CalamineSheet,
4
+ CalamineWorkbook,
5
+ PasswordError,
6
+ SheetMetadata,
7
+ SheetTypeEnum,
8
+ SheetVisibleEnum,
9
+ WorkbookClosed,
10
+ WorksheetNotFound,
11
+ XmlError,
12
+ ZipError,
13
+ load_workbook,
14
+ )
15
+
16
+ __all__ = (
17
+ "CalamineError",
18
+ "CalamineSheet",
19
+ "CalamineWorkbook",
20
+ "PasswordError",
21
+ "SheetMetadata",
22
+ "SheetTypeEnum",
23
+ "SheetVisibleEnum",
24
+ "WorksheetNotFound",
25
+ "XmlError",
26
+ "ZipError",
27
+ "WorkbookClosed",
28
+ "load_workbook",
29
+ )
@@ -0,0 +1,182 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import datetime
5
+ import enum
6
+ import os
7
+ import types
8
+ import typing
9
+
10
+ @typing.type_check_only
11
+ class ReadBuffer(typing.Protocol):
12
+ def seek(self, __offset: int, __whence: int = ...) -> int: ...
13
+ def read(self, __size: int = ...) -> bytes | None: ...
14
+
15
+ @typing.final
16
+ class SheetTypeEnum(enum.Enum):
17
+ WorkSheet = ...
18
+ DialogSheet = ...
19
+ MacroSheet = ...
20
+ ChartSheet = ...
21
+ Vba = ...
22
+
23
+ @typing.final
24
+ class SheetVisibleEnum(enum.Enum):
25
+ Visible = ...
26
+ Hidden = ...
27
+ VeryHidden = ...
28
+
29
+ @typing.final
30
+ class SheetMetadata:
31
+ name: str
32
+ typ: SheetTypeEnum
33
+ visible: SheetVisibleEnum
34
+
35
+ def __init__(
36
+ self, name: str, typ: SheetTypeEnum, visible: SheetVisibleEnum
37
+ ) -> None: ...
38
+
39
+ @typing.final
40
+ class CalamineSheet:
41
+ name: str
42
+ @property
43
+ def height(self) -> int: ...
44
+ @property
45
+ def width(self) -> int: ...
46
+ @property
47
+ def total_height(self) -> int: ...
48
+ @property
49
+ def total_width(self) -> int: ...
50
+ @property
51
+ def start(self) -> tuple[int, int] | None: ...
52
+ @property
53
+ def end(self) -> tuple[int, int] | None: ...
54
+ def to_python(
55
+ self, skip_empty_area: bool = True, nrows: int | None = None
56
+ ) -> list[
57
+ list[
58
+ int
59
+ | float
60
+ | str
61
+ | bool
62
+ | datetime.time
63
+ | datetime.date
64
+ | datetime.datetime
65
+ | datetime.timedelta
66
+ ]
67
+ ]:
68
+ """Retunrning data from sheet as list of lists.
69
+
70
+ Args:
71
+ skip_empty_area (bool):
72
+ By default, calamine skips empty rows/cols before data.
73
+ For suppress this behaviour, set `skip_empty_area` to `False`.
74
+ """
75
+
76
+ def iter_rows(
77
+ self,
78
+ ) -> typing.Iterator[
79
+ list[
80
+ int
81
+ | float
82
+ | str
83
+ | bool
84
+ | datetime.time
85
+ | datetime.date
86
+ | datetime.datetime
87
+ | datetime.timedelta
88
+ ]
89
+ ]:
90
+ """Retunrning data from sheet as iterator of lists."""
91
+
92
+ @typing.final
93
+ class CalamineWorkbook(contextlib.AbstractContextManager):
94
+ path: str | None
95
+ sheet_names: list[str]
96
+ sheets_metadata: list[SheetMetadata]
97
+ @classmethod
98
+ def from_object(
99
+ cls, path_or_filelike: str | os.PathLike | ReadBuffer
100
+ ) -> "CalamineWorkbook":
101
+ """Determining type of pyobject and reading from it.
102
+
103
+ Args:
104
+ path_or_filelike (str | os.PathLike | ReadBuffer): path to file or IO (must imlpement read/seek methods).
105
+ """
106
+
107
+ @classmethod
108
+ def from_path(cls, path: str | os.PathLike) -> "CalamineWorkbook":
109
+ """Reading file from path.
110
+
111
+ Args:
112
+ path (str | os.PathLike): path to file.
113
+ """
114
+
115
+ @classmethod
116
+ def from_filelike(cls, filelike: ReadBuffer) -> "CalamineWorkbook":
117
+ """Reading file from IO.
118
+
119
+ Args:
120
+ filelike : IO (must imlpement read/seek methods).
121
+ """
122
+
123
+ def close(self) -> None:
124
+ """Close the workbook.
125
+
126
+ Drop internal rust structure from workbook (and close the file under the hood).
127
+ `get_sheet_by_name`/`get_sheet_by_index` will raise WorkbookClosed after calling that method.
128
+
129
+ Raises:
130
+ WorkbookClosed: If workbook already closed.
131
+ """
132
+
133
+ def __enter__(self) -> "CalamineWorkbook": ...
134
+ def __exit__(
135
+ self,
136
+ exc_type: type[BaseException] | None,
137
+ exc_val: BaseException | None,
138
+ exc_tb: types.TracebackType | None,
139
+ ) -> None: ...
140
+ def get_sheet_by_name(self, name: str) -> CalamineSheet:
141
+ """Get worksheet by name.
142
+
143
+ Args:
144
+ name(str): name of worksheet
145
+
146
+ Returns:
147
+ CalamineSheet
148
+
149
+ Raises:
150
+ WorkbookClosed: If workbook already closed.
151
+ WorksheetNotFound: If worksheet not found in workbook.
152
+ """
153
+
154
+ def get_sheet_by_index(self, index: int) -> CalamineSheet:
155
+ """Get worksheet by index.
156
+
157
+ Args:
158
+ index(int): index of worksheet
159
+
160
+ Returns:
161
+ CalamineSheet
162
+
163
+ Raises:
164
+ WorkbookClosed: If workbook already closed.
165
+ WorksheetNotFound: If worksheet not found in workbook.
166
+ """
167
+
168
+ class CalamineError(Exception): ...
169
+ class PasswordError(CalamineError): ...
170
+ class WorksheetNotFound(CalamineError): ...
171
+ class XmlError(CalamineError): ...
172
+ class ZipError(CalamineError): ...
173
+ class WorkbookClosed(CalamineError): ...
174
+
175
+ def load_workbook(
176
+ path_or_filelike: str | os.PathLike | ReadBuffer,
177
+ ) -> CalamineWorkbook:
178
+ """Determining type of pyobject and reading from it.
179
+
180
+ Args:
181
+ path_or_filelike (str | os.PathLike | ReadBuffer): path to file or IO (must imlpement read/seek methods).
182
+ """
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import date, datetime, time, timedelta
4
+ from importlib.metadata import version
5
+ from typing import TYPE_CHECKING, Union, cast
6
+
7
+ import pandas as pd
8
+ from packaging.version import Version, parse
9
+ from pandas._typing import Scalar
10
+ from pandas.compat._optional import import_optional_dependency
11
+ from pandas.core.shared_docs import _shared_docs
12
+ from pandas.io.excel import ExcelFile
13
+ from pandas.io.excel._base import ( # type:ignore[attr-defined] # missing in pandas-stubs
14
+ BaseExcelReader,
15
+ )
16
+ from pandas.util._decorators import ( # type:ignore[attr-defined] # missing in pandas-stubs
17
+ doc,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from pandas._typing import FilePath, ReadBuffer, StorageOptions
22
+ from python_calamine import CalamineSheet, CalamineWorkbook
23
+
24
+ _CellValueT = Union[int, float, str, bool, time, date, datetime, timedelta]
25
+
26
+
27
+ PANDAS_VERSION = parse(version("pandas"))
28
+
29
+
30
+ class CalamineExcelReader(BaseExcelReader):
31
+ book: CalamineWorkbook
32
+
33
+ @doc(storage_options=_shared_docs["storage_options"])
34
+ def __init__(
35
+ self,
36
+ filepath_or_buffer: FilePath | ReadBuffer[bytes],
37
+ storage_options: StorageOptions | None = None,
38
+ engine_kwargs: dict | None = None,
39
+ ) -> None:
40
+ """
41
+ Reader using calamine engine (xlsx/xls/xlsb/ods).
42
+
43
+ Parameters
44
+ ----------
45
+ filepath_or_buffer : str, path to be parsed or
46
+ an open readable stream.
47
+ {storage_options}
48
+ engine_kwargs : dict, optional
49
+ Arbitrary keyword arguments passed to excel engine.
50
+ """
51
+ import_optional_dependency("python_calamine")
52
+ if PANDAS_VERSION >= Version("2.2.0"):
53
+ raise ValueError("Pandas >= 2.2.0 has builtin support of calamine")
54
+ elif PANDAS_VERSION >= Version("2.1.0"):
55
+ super().__init__(
56
+ filepath_or_buffer,
57
+ storage_options=storage_options,
58
+ engine_kwargs=engine_kwargs,
59
+ )
60
+ elif PANDAS_VERSION >= Version("2.0.0"):
61
+ super().__init__(
62
+ filepath_or_buffer,
63
+ storage_options=storage_options,
64
+ )
65
+ else:
66
+ raise ValueError("Pandas >= 2 is only supported")
67
+
68
+ @property
69
+ def _workbook_class(self) -> type[CalamineWorkbook]:
70
+ from python_calamine import CalamineWorkbook
71
+
72
+ return CalamineWorkbook
73
+
74
+ def load_workbook(
75
+ self,
76
+ filepath_or_buffer: FilePath | ReadBuffer[bytes],
77
+ engine_kwargs: dict | None = None,
78
+ ) -> CalamineWorkbook:
79
+ from python_calamine import load_workbook
80
+
81
+ return load_workbook(filepath_or_buffer, **(engine_kwargs or {}))
82
+
83
+ @property
84
+ def sheet_names(self) -> list[str]:
85
+ from python_calamine import SheetTypeEnum
86
+
87
+ return [
88
+ sheet.name
89
+ for sheet in self.book.sheets_metadata
90
+ if sheet.typ == SheetTypeEnum.WorkSheet
91
+ ]
92
+
93
+ def get_sheet_by_name(self, name: str) -> CalamineSheet:
94
+ self.raise_if_bad_sheet_by_name(name)
95
+ return self.book.get_sheet_by_name(name)
96
+
97
+ def get_sheet_by_index(self, index: int) -> CalamineSheet:
98
+ self.raise_if_bad_sheet_by_index(index)
99
+ return self.book.get_sheet_by_index(index)
100
+
101
+ def get_sheet_data(
102
+ self, sheet: CalamineSheet, file_rows_needed: int | None = None
103
+ ) -> list[list[Scalar]]:
104
+ def _convert_cell(value: _CellValueT) -> Scalar:
105
+ if isinstance(value, float):
106
+ val = int(value)
107
+ if val == value:
108
+ return val
109
+ else:
110
+ return value
111
+ elif isinstance(value, date):
112
+ return pd.Timestamp(value)
113
+ elif isinstance(value, timedelta):
114
+ return pd.Timedelta(value)
115
+ elif isinstance(value, time):
116
+ # cast needed here because Scalar doesn't include datetime.time
117
+ return cast(Scalar, value)
118
+
119
+ return value
120
+
121
+ rows: list[list[_CellValueT]] = sheet.to_python(skip_empty_area=False)
122
+ data: list[list[Scalar]] = []
123
+
124
+ for row in rows:
125
+ data.append([_convert_cell(cell) for cell in row])
126
+ if file_rows_needed is not None and len(data) >= file_rows_needed:
127
+ break
128
+
129
+ return data
130
+
131
+
132
+ def pandas_monkeypatch() -> None:
133
+ ExcelFile._engines = { # type:ignore[attr-defined] # missing in pandas-stubs
134
+ "calamine": CalamineExcelReader,
135
+ **ExcelFile._engines, # type:ignore[attr-defined] # missing in pandas-stubs
136
+ }
File without changes
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-calamine
3
+ Version: 0.3.2
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: License :: OSI Approved :: MIT License
6
+ Classifier: Programming Language :: Rust
7
+ Classifier: Programming Language :: Python :: 3.8
8
+ Classifier: Programming Language :: Python :: 3.9
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: packaging>=23.1
14
+ Requires-Dist: maturin~=1.0 ; extra == 'dev'
15
+ Requires-Dist: pre-commit~=3.0 ; extra == 'dev'
16
+ Requires-Dist: pytest~=8.0 ; extra == 'dev'
17
+ Requires-Dist: pandas[excel]~=2.0 ; extra == 'dev'
18
+ Requires-Dist: numpy~=1.0 ; extra == 'dev'
19
+ Provides-Extra: dev
20
+ License-File: LICENSE
21
+ Summary: Python binding for Rust's library for reading excel and odf file - calamine
22
+ Home-Page: https://github.com/dimastbk/python-calamine
23
+ Author: Dmitriy <dimastbk@proton.me>
24
+ Author-email: Dmitriy <dimastbk@proton.me>
25
+ License: MIT
26
+ Requires-Python: >=3.8
27
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
28
+ Project-URL: Source Code, https://github.com/dimastbk/python-calamine
29
+
30
+ # python-calamine
31
+ [![PyPI - Version](https://img.shields.io/pypi/v/python-calamine)](https://pypi.org/project/python-calamine/)
32
+ [![Conda Version](https://img.shields.io/conda/vn/conda-forge/python-calamine.svg)](https://anaconda.org/conda-forge/python-calamine)
33
+ ![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fdimastbk%2Fpython-calamine%2Fmaster%2Fpyproject.toml)
34
+
35
+
36
+ Python binding for beautiful Rust's library for reading excel and odf file - [calamine](https://github.com/tafia/calamine).
37
+
38
+ ### Is used
39
+ * [calamine](https://github.com/tafia/calamine)
40
+ * [pyo3](https://github.com/PyO3/pyo3)
41
+ * [maturin](https://github.com/PyO3/maturin)
42
+
43
+ ### Installation
44
+ Pypi:
45
+ ```
46
+ pip install python-calamine
47
+ ```
48
+ Conda:
49
+ ```
50
+ conda install -c conda-forge python-calamine
51
+ ```
52
+
53
+ ### Example
54
+ ```python
55
+ from python_calamine import CalamineWorkbook
56
+
57
+ workbook = CalamineWorkbook.from_path("file.xlsx")
58
+ workbook.sheet_names
59
+ # ["Sheet1", "Sheet2"]
60
+
61
+ workbook.get_sheet_by_name("Sheet1").to_python()
62
+ # [
63
+ # ["1", "2", "3", "4", "5", "6", "7"],
64
+ # ["1", "2", "3", "4", "5", "6", "7"],
65
+ # ["1", "2", "3", "4", "5", "6", "7"],
66
+ # ]
67
+ ```
68
+
69
+ By default, calamine skips empty rows/cols before data. For suppress this behaviour, set `skip_empty_area` to `False`.
70
+ ```python
71
+ from python_calamine import CalamineWorkbook
72
+
73
+ workbook = CalamineWorkbook.from_path("file.xlsx").get_sheet_by_name("Sheet1").to_python(skip_empty_area=False)
74
+ # [
75
+ # [", ", ", ", ", ", "],
76
+ # ["1", "2", "3", "4", "5", "6", "7"],
77
+ # ["1", "2", "3", "4", "5", "6", "7"],
78
+ # ["1", "2", "3", "4", "5", "6", "7"],
79
+ # ]
80
+ ```
81
+
82
+ Also, you can use monkeypatch for pandas for use this library as engine in `read_excel()` (only pandas 2.0 and 2.1 are supported).
83
+ Pandas 2.2 and above have built-in support of python-calamine.
84
+ ```python
85
+ from pandas import read_excel
86
+ from python_calamine.pandas import pandas_monkeypatch
87
+
88
+
89
+ pandas_monkeypatch()
90
+
91
+ read_excel("file.xlsx", engine="calamine")
92
+ # 1 2 3 4 5 6 7
93
+ # 0 1 2 3 4 5 6 7
94
+ # 1 1 2 3 4 5 6 7
95
+ ```
96
+
97
+ Also, you can find additional examples in [tests](https://github.com/dimastbk/python-calamine/blob/master/tests/test_base.py).
98
+
99
+ ### Development
100
+
101
+ You'll need rust [installed](https://rustup.rs/).
102
+
103
+ ```python
104
+ # clone this repo or your fork
105
+ git clone git@github.com:dimastbk/python-calamine.git
106
+ cd python-calamine
107
+ # create a new virtual env
108
+ python3 -m venv env
109
+ source env/bin/activate
110
+ # install dev dependencies and install python-calamine
111
+ pip install -e .[dev]
112
+ # lint code
113
+ pre-commit run --all-files
114
+ # test code
115
+ pytest
116
+ ```
117
+
@@ -0,0 +1,9 @@
1
+ python_calamine-0.3.2.dist-info/METADATA,sha256=aOKwJP8J3tnHw8OEmtvCAuZW8MTsW_8UKBcDZP0CIag,3960
2
+ python_calamine-0.3.2.dist-info/WHEEL,sha256=Cpy7yubMq7TIrQDSgiRDNCIdF4xsALHiC6zoU60uVnU,92
3
+ python_calamine-0.3.2.dist-info/licenses/LICENSE,sha256=cYURXgSnjCTOT2jQ_McTGn-AhDycRqzJa11QzR38Sn4,1086
4
+ python_calamine/pandas.py,sha256=QL-cy_9TMEsjb4bRCNB40tWGPPYlBmUBwEmKBFFuTGc,4734
5
+ python_calamine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ python_calamine/_python_calamine.pyi,sha256=nPoUpA4zWdJvoBpX8SGmxVx8X1b8EjLYkbsD5ZyPOGA,4973
7
+ python_calamine/__init__.py,sha256=evxSwIkpatTkygn2mRmWUYPslvdj08q_pNkayyyptfI,560
8
+ python_calamine/_python_calamine.cp312-win32.pyd,sha256=jitFkbN5dhnroomxlHU4NYvS41opftGl5fTD6-JmjAg,1253888
9
+ python_calamine-0.3.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.8.3)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-win32
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 dimastbk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.