python-calamine 0.3.2__tar.gz → 0.4.0__tar.gz

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.
Files changed (46) hide show
  1. {python_calamine-0.3.2 → python_calamine-0.4.0}/Cargo.lock +15 -16
  2. {python_calamine-0.3.2 → python_calamine-0.4.0}/Cargo.toml +4 -4
  3. {python_calamine-0.3.2 → python_calamine-0.4.0}/PKG-INFO +1 -1
  4. {python_calamine-0.3.2 → python_calamine-0.4.0}/python/python_calamine/_python_calamine.pyi +12 -0
  5. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/types/sheet.rs +19 -4
  6. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/types/workbook.rs +40 -4
  7. python_calamine-0.4.0/tests/data/base.ods +0 -0
  8. python_calamine-0.4.0/tests/data/base.xls +0 -0
  9. python_calamine-0.4.0/tests/data/base.xlsb +0 -0
  10. python_calamine-0.4.0/tests/data/base.xlsx +0 -0
  11. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/test_base.py +17 -5
  12. python_calamine-0.3.2/tests/data/base.ods +0 -0
  13. python_calamine-0.3.2/tests/data/base.xls +0 -0
  14. python_calamine-0.3.2/tests/data/base.xlsb +0 -0
  15. python_calamine-0.3.2/tests/data/base.xlsx +0 -0
  16. {python_calamine-0.3.2 → python_calamine-0.4.0}/.github/workflows/CI.yml +0 -0
  17. {python_calamine-0.3.2 → python_calamine-0.4.0}/.gitignore +0 -0
  18. {python_calamine-0.3.2 → python_calamine-0.4.0}/.pre-commit-config.yaml +0 -0
  19. {python_calamine-0.3.2 → python_calamine-0.4.0}/LICENSE +0 -0
  20. {python_calamine-0.3.2 → python_calamine-0.4.0}/README.md +0 -0
  21. {python_calamine-0.3.2 → python_calamine-0.4.0}/build.rs +0 -0
  22. {python_calamine-0.3.2 → python_calamine-0.4.0}/pyproject.toml +0 -0
  23. {python_calamine-0.3.2 → python_calamine-0.4.0}/python/python_calamine/__init__.py +0 -0
  24. {python_calamine-0.3.2 → python_calamine-0.4.0}/python/python_calamine/pandas.py +0 -0
  25. {python_calamine-0.3.2 → python_calamine-0.4.0}/python/python_calamine/py.typed +0 -0
  26. {python_calamine-0.3.2 → python_calamine-0.4.0}/renovate.json +0 -0
  27. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/lib.rs +0 -0
  28. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/types/cell.rs +0 -0
  29. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/types/errors.rs +0 -0
  30. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/types/mod.rs +0 -0
  31. {python_calamine-0.3.2 → python_calamine-0.4.0}/src/utils.rs +0 -0
  32. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/__init__.py +0 -0
  33. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/conftest.py +0 -0
  34. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/any_sheets.ods +0 -0
  35. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/any_sheets.xls +0 -0
  36. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/any_sheets.xlsb +0 -0
  37. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/any_sheets.xlsx +0 -0
  38. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/empty_file.ods +0 -0
  39. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/empty_file.xlsb +0 -0
  40. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/empty_file.xlsx +0 -0
  41. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/password.ods +0 -0
  42. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/password.xls +0 -0
  43. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/password.xlsb +0 -0
  44. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/data/password.xlsx +0 -0
  45. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/test_pandas.py +0 -0
  46. {python_calamine-0.3.2 → python_calamine-0.4.0}/tests/test_sheet_type.py +0 -0
@@ -133,9 +133,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
133
133
 
134
134
  [[package]]
135
135
  name = "chrono"
136
- version = "0.4.40"
136
+ version = "0.4.41"
137
137
  source = "registry+https://github.com/rust-lang/crates.io-index"
138
- checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
138
+ checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
139
139
  dependencies = [
140
140
  "android-tzdata",
141
141
  "iana-time-zone",
@@ -429,11 +429,10 @@ dependencies = [
429
429
 
430
430
  [[package]]
431
431
  name = "pyo3"
432
- version = "0.24.1"
432
+ version = "0.25.1"
433
433
  source = "registry+https://github.com/rust-lang/crates.io-index"
434
- checksum = "17da310086b068fbdcefbba30aeb3721d5bb9af8db4987d6735b2183ca567229"
434
+ checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
435
435
  dependencies = [
436
- "cfg-if",
437
436
  "chrono",
438
437
  "indoc",
439
438
  "libc",
@@ -448,9 +447,9 @@ dependencies = [
448
447
 
449
448
  [[package]]
450
449
  name = "pyo3-build-config"
451
- version = "0.24.1"
450
+ version = "0.25.1"
452
451
  source = "registry+https://github.com/rust-lang/crates.io-index"
453
- checksum = "e27165889bd793000a098bb966adc4300c312497ea25cf7a690a9f0ac5aa5fc1"
452
+ checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
454
453
  dependencies = [
455
454
  "once_cell",
456
455
  "python3-dll-a",
@@ -459,9 +458,9 @@ dependencies = [
459
458
 
460
459
  [[package]]
461
460
  name = "pyo3-ffi"
462
- version = "0.24.1"
461
+ version = "0.25.1"
463
462
  source = "registry+https://github.com/rust-lang/crates.io-index"
464
- checksum = "05280526e1dbf6b420062f3ef228b78c0c54ba94e157f5cb724a609d0f2faabc"
463
+ checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
465
464
  dependencies = [
466
465
  "libc",
467
466
  "pyo3-build-config",
@@ -469,9 +468,9 @@ dependencies = [
469
468
 
470
469
  [[package]]
471
470
  name = "pyo3-file"
472
- version = "0.12.0"
471
+ version = "0.13.0"
473
472
  source = "registry+https://github.com/rust-lang/crates.io-index"
474
- checksum = "8d97fbb05588c29774af01833e598fae7e714a3f87d36b286e0466f6b678e4a7"
473
+ checksum = "75916300df50dbfa6958ec05fe53473e69474542dac7fd1275aa6869a18a241a"
475
474
  dependencies = [
476
475
  "pyo3",
477
476
  "skeptic",
@@ -479,9 +478,9 @@ dependencies = [
479
478
 
480
479
  [[package]]
481
480
  name = "pyo3-macros"
482
- version = "0.24.1"
481
+ version = "0.25.1"
483
482
  source = "registry+https://github.com/rust-lang/crates.io-index"
484
- checksum = "5c3ce5686aa4d3f63359a5100c62a127c9f15e8398e5fdeb5deef1fed5cd5f44"
483
+ checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
485
484
  dependencies = [
486
485
  "proc-macro2",
487
486
  "pyo3-macros-backend",
@@ -491,9 +490,9 @@ dependencies = [
491
490
 
492
491
  [[package]]
493
492
  name = "pyo3-macros-backend"
494
- version = "0.24.1"
493
+ version = "0.25.1"
495
494
  source = "registry+https://github.com/rust-lang/crates.io-index"
496
- checksum = "f4cf6faa0cbfb0ed08e89beb8103ae9724eb4750e3a78084ba4017cbe94f3855"
495
+ checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
497
496
  dependencies = [
498
497
  "heck",
499
498
  "proc-macro2",
@@ -504,7 +503,7 @@ dependencies = [
504
503
 
505
504
  [[package]]
506
505
  name = "python-calamine"
507
- version = "0.3.2"
506
+ version = "0.4.0"
508
507
  dependencies = [
509
508
  "calamine",
510
509
  "chrono",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "python-calamine"
3
- version = "0.3.2"
3
+ version = "0.4.0"
4
4
  authors = ["Dmitriy <dimastbk@proton.me>"]
5
5
  description = "Python binding for Rust's library for reading excel and odf file - calamine"
6
6
  edition = "2021"
@@ -15,13 +15,13 @@ crate-type = ["cdylib"]
15
15
 
16
16
  [dependencies]
17
17
  calamine = { git = "https://github.com/tafia/calamine.git", rev = "5cda5c734661f656bdfb7ac7a1b95e77bf89aa09", features = ["dates"] }
18
- pyo3 = { version = "0.24.0", features = [
18
+ pyo3 = { version = "0.25.1", features = [
19
19
  "extension-module",
20
20
  "chrono",
21
21
  "generate-import-lib",
22
22
  ] }
23
23
  chrono = { version = "0.4.38", features = ["serde"] }
24
- pyo3-file = "0.12.0"
24
+ pyo3-file = "0.13.0"
25
25
 
26
26
  [build-dependencies]
27
- pyo3-build-config = "0.24.0"
27
+ pyo3-build-config = "0.25.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-calamine
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Programming Language :: Rust
@@ -89,6 +89,18 @@ class CalamineSheet:
89
89
  ]:
90
90
  """Retunrning data from sheet as iterator of lists."""
91
91
 
92
+ @property
93
+ def merged_cell_ranges(
94
+ self,
95
+ ) -> list[tuple[tuple[int, int], tuple[int, int]]] | None:
96
+ """Return a copy of merged cell ranges.
97
+
98
+ Support only for xlsx/xls.
99
+
100
+ Returns:
101
+ list of merged cell ranges (tuple[start coordinate, end coordinate]) or None for unsuported format
102
+ """
103
+
92
104
  @typing.final
93
105
  class CalamineWorkbook(contextlib.AbstractContextManager):
94
106
  path: str | None
@@ -1,7 +1,7 @@
1
1
  use std::fmt::Display;
2
2
  use std::sync::Arc;
3
3
 
4
- use calamine::{Data, Range, Rows, SheetType, SheetVisible};
4
+ use calamine::{Data, Dimensions, Range, Rows, SheetType, SheetVisible};
5
5
  use pyo3::class::basic::CompareOp;
6
6
  use pyo3::prelude::*;
7
7
  use pyo3::types::PyList;
@@ -25,7 +25,7 @@ pub enum SheetTypeEnum {
25
25
 
26
26
  impl Display for SheetTypeEnum {
27
27
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28
- write!(f, "SheetTypeEnum.{:?}", self)
28
+ write!(f, "SheetTypeEnum.{self:?}")
29
29
  }
30
30
  }
31
31
 
@@ -54,7 +54,7 @@ pub enum SheetVisibleEnum {
54
54
 
55
55
  impl Display for SheetVisibleEnum {
56
56
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57
- write!(f, "SheetVisibleEnum.{:?}", self)
57
+ write!(f, "SheetVisibleEnum.{self:?}")
58
58
  }
59
59
  }
60
60
 
@@ -68,6 +68,8 @@ impl From<SheetVisible> for SheetVisibleEnum {
68
68
  }
69
69
  }
70
70
 
71
+ type MergedCellRange = ((u32, u32), (u32, u32));
72
+
71
73
  #[pyclass]
72
74
  #[derive(Clone, PartialEq)]
73
75
  pub struct SheetMetadata {
@@ -132,13 +134,19 @@ pub struct CalamineSheet {
132
134
  #[pyo3(get)]
133
135
  name: String,
134
136
  range: Arc<Range<Data>>,
137
+ merged_cell_ranges: Option<Vec<Dimensions>>,
135
138
  }
136
139
 
137
140
  impl CalamineSheet {
138
- pub fn new(name: String, range: Range<Data>) -> Self {
141
+ pub fn new(
142
+ name: String,
143
+ range: Range<Data>,
144
+ merged_cell_ranges: Option<Vec<Dimensions>>,
145
+ ) -> Self {
139
146
  CalamineSheet {
140
147
  name,
141
148
  range: Arc::new(range),
149
+ merged_cell_ranges,
142
150
  }
143
151
  }
144
152
  }
@@ -212,6 +220,13 @@ impl CalamineSheet {
212
220
  fn iter_rows(&self) -> CalamineCellIterator {
213
221
  CalamineCellIterator::from_range(Arc::clone(&self.range))
214
222
  }
223
+
224
+ #[getter]
225
+ fn merged_cell_ranges(slf: PyRef<'_, Self>) -> Option<Vec<MergedCellRange>> {
226
+ slf.merged_cell_ranges
227
+ .as_ref()
228
+ .map(|r| r.iter().map(|d| (d.start, d.end)).collect())
229
+ }
215
230
  }
216
231
 
217
232
  #[pyclass]
@@ -2,7 +2,9 @@ use std::fs::File;
2
2
  use std::io::{BufReader, Cursor, Read};
3
3
  use std::path::PathBuf;
4
4
 
5
- use calamine::{open_workbook_auto, open_workbook_auto_from_rs, Reader, Sheets};
5
+ use calamine::{
6
+ open_workbook_auto, open_workbook_auto_from_rs, Error as CalamineCrateError, Reader, Sheets,
7
+ };
6
8
  use pyo3::exceptions::PyTypeError;
7
9
  use pyo3::prelude::*;
8
10
  use pyo3::types::{PyString, PyType};
@@ -44,6 +46,35 @@ impl SheetsEnum {
44
46
  SheetsEnum::None => Err(Error::WorkbookClosed),
45
47
  }
46
48
  }
49
+
50
+ fn worksheet_merge_cells(
51
+ &mut self,
52
+ name: &str,
53
+ ) -> Result<Option<Vec<calamine::Dimensions>>, Error> {
54
+ match self {
55
+ SheetsEnum::File(f) => match f {
56
+ Sheets::Xls(xls_f) => Ok(xls_f.worksheet_merge_cells(name)),
57
+ Sheets::Xlsx(xlsx_f) => xlsx_f
58
+ .worksheet_merge_cells(name)
59
+ .transpose()
60
+ .map_err(CalamineCrateError::Xlsx)
61
+ .map_err(Error::Calamine)
62
+ .map(|inner| inner.or(Some(Vec::new()))),
63
+ _ => Ok(None),
64
+ },
65
+ SheetsEnum::FileLike(f) => match f {
66
+ Sheets::Xls(xls_f) => Ok(xls_f.worksheet_merge_cells(name)),
67
+ Sheets::Xlsx(xlsx_f) => xlsx_f
68
+ .worksheet_merge_cells(name)
69
+ .transpose()
70
+ .map_err(CalamineCrateError::Xlsx)
71
+ .map_err(Error::Calamine)
72
+ .map(|inner| inner.or(Some(Vec::new()))),
73
+ _ => Ok(None),
74
+ },
75
+ SheetsEnum::None => Err(Error::WorkbookClosed),
76
+ }
77
+ }
47
78
  }
48
79
 
49
80
  #[pyclass]
@@ -61,7 +92,7 @@ pub struct CalamineWorkbook {
61
92
  impl CalamineWorkbook {
62
93
  fn __repr__(&self) -> PyResult<String> {
63
94
  match &self.path {
64
- Some(path) => Ok(format!("CalamineWorkbook(path='{}')", path)),
95
+ Some(path) => Ok(format!("CalamineWorkbook(path='{path}')")),
65
96
  None => Ok("CalamineWorkbook(path='bytes')".to_string()),
66
97
  }
67
98
  }
@@ -192,14 +223,19 @@ impl CalamineWorkbook {
192
223
 
193
224
  fn get_sheet_by_name(&mut self, name: &str) -> PyResult<CalamineSheet> {
194
225
  let range = self.sheets.worksheet_range(name).map_err(err_to_py)?;
195
- Ok(CalamineSheet::new(name.to_owned(), range))
226
+ let merge_cells_range = self.sheets.worksheet_merge_cells(name).map_err(err_to_py)?;
227
+ Ok(CalamineSheet::new(
228
+ name.to_owned(),
229
+ range,
230
+ merge_cells_range,
231
+ ))
196
232
  }
197
233
 
198
234
  fn get_sheet_by_index(&mut self, index: usize) -> PyResult<CalamineSheet> {
199
235
  let name = self
200
236
  .sheet_names
201
237
  .get(index)
202
- .ok_or_else(|| WorksheetNotFound::new_err(format!("Worksheet '{}' not found", index)))?
238
+ .ok_or_else(|| WorksheetNotFound::new_err(format!("Worksheet '{index}' not found")))?
203
239
  .to_string();
204
240
  self.get_sheet_by_name(&name)
205
241
  }
@@ -15,7 +15,7 @@ PATH = Path(__file__).parent / "data"
15
15
 
16
16
 
17
17
  def test_ods_read():
18
- names = ["Sheet1", "Sheet2"]
18
+ names = ["Sheet1", "Sheet2", "Merged Cells"]
19
19
  data = [
20
20
  ["", "", "", "", "", "", "", "", "", ""],
21
21
  [
@@ -59,9 +59,11 @@ def test_ods_read():
59
59
  assert [] == reader.get_sheet_by_index(1).to_python()
60
60
  assert [] == reader.get_sheet_by_index(1).to_python(skip_empty_area=False)
61
61
 
62
+ assert None is reader.get_sheet_by_name("Merged Cells").merged_cell_ranges
63
+
62
64
 
63
65
  def test_xls_read():
64
- names = ["Sheet1", "Sheet2"]
66
+ names = ["Sheet1", "Sheet2", "Merged Cells"]
65
67
  data = [
66
68
  ["", "", "", "", "", "", "", "", "", ""],
67
69
  [
@@ -101,9 +103,13 @@ def test_xls_read():
101
103
  assert [] == reader.get_sheet_by_index(1).to_python()
102
104
  assert [] == reader.get_sheet_by_index(1).to_python(skip_empty_area=False)
103
105
 
106
+ assert [((0, 0), (1, 0)), ((0, 1), (1, 1))] == reader.get_sheet_by_name(
107
+ "Merged Cells"
108
+ ).merged_cell_ranges
109
+
104
110
 
105
111
  def test_xlsb_read():
106
- names = ["Sheet1", "Sheet2", "Sheet3"]
112
+ names = ["Sheet1", "Sheet2", "Sheet3", "Merged Cells"]
107
113
  data = [
108
114
  ["", "", "", "", "", "", "", "", "", ""],
109
115
  [
@@ -143,9 +149,11 @@ def test_xlsb_read():
143
149
  assert [] == reader.get_sheet_by_index(1).to_python()
144
150
  assert [] == reader.get_sheet_by_index(1).to_python(skip_empty_area=False)
145
151
 
152
+ assert None is reader.get_sheet_by_name("Merged Cells").merged_cell_ranges
153
+
146
154
 
147
155
  def test_xlsx_read():
148
- names = ["Sheet1", "Sheet2", "Sheet3"]
156
+ names = ["Sheet1", "Sheet2", "Sheet3", "Merged Cells"]
149
157
  data = [
150
158
  ["", "", "", "", "", "", "", "", "", ""],
151
159
  [
@@ -185,9 +193,13 @@ def test_xlsx_read():
185
193
  assert [] == reader.get_sheet_by_index(1).to_python()
186
194
  assert [] == reader.get_sheet_by_index(1).to_python(skip_empty_area=False)
187
195
 
196
+ assert [((0, 0), (1, 0)), ((0, 1), (1, 1))] == reader.get_sheet_by_name(
197
+ "Merged Cells"
198
+ ).merged_cell_ranges
199
+
188
200
 
189
201
  def test_xlsx_iter_rows():
190
- names = ["Sheet1", "Sheet2", "Sheet3"]
202
+ names = ["Sheet1", "Sheet2", "Sheet3", "Merged Cells"]
191
203
  data = [
192
204
  ["", "", "", "", "", "", "", "", "", ""],
193
205
  [
Binary file
Binary file
Binary file
Binary file
File without changes