python-calamine 0.4.0__tar.gz → 0.5.1__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 (44) hide show
  1. {python_calamine-0.4.0 → python_calamine-0.5.1}/.github/workflows/CI.yml +26 -39
  2. {python_calamine-0.4.0 → python_calamine-0.5.1}/.pre-commit-config.yaml +0 -2
  3. {python_calamine-0.4.0 → python_calamine-0.5.1}/Cargo.lock +39 -64
  4. python_calamine-0.5.1/Cargo.toml +22 -0
  5. {python_calamine-0.4.0 → python_calamine-0.5.1}/PKG-INFO +6 -26
  6. {python_calamine-0.4.0 → python_calamine-0.5.1}/README.md +0 -13
  7. {python_calamine-0.4.0 → python_calamine-0.5.1}/pyproject.toml +9 -9
  8. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/lib.rs +1 -1
  9. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/types/cell.rs +32 -4
  10. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/types/sheet.rs +9 -6
  11. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/types/workbook.rs +16 -16
  12. python_calamine-0.5.1/tests/data/issue139.xlsx +0 -0
  13. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/test_base.py +7 -0
  14. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/test_pandas.py +0 -13
  15. python_calamine-0.4.0/Cargo.toml +0 -27
  16. python_calamine-0.4.0/python/python_calamine/pandas.py +0 -136
  17. {python_calamine-0.4.0 → python_calamine-0.5.1}/.gitignore +0 -0
  18. {python_calamine-0.4.0 → python_calamine-0.5.1}/LICENSE +0 -0
  19. {python_calamine-0.4.0 → python_calamine-0.5.1}/build.rs +0 -0
  20. {python_calamine-0.4.0 → python_calamine-0.5.1}/python/python_calamine/__init__.py +0 -0
  21. {python_calamine-0.4.0 → python_calamine-0.5.1}/python/python_calamine/_python_calamine.pyi +0 -0
  22. {python_calamine-0.4.0 → python_calamine-0.5.1}/python/python_calamine/py.typed +0 -0
  23. {python_calamine-0.4.0 → python_calamine-0.5.1}/renovate.json +0 -0
  24. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/types/errors.rs +0 -0
  25. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/types/mod.rs +0 -0
  26. {python_calamine-0.4.0 → python_calamine-0.5.1}/src/utils.rs +0 -0
  27. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/__init__.py +0 -0
  28. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/conftest.py +0 -0
  29. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/any_sheets.ods +0 -0
  30. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/any_sheets.xls +0 -0
  31. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/any_sheets.xlsb +0 -0
  32. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/any_sheets.xlsx +0 -0
  33. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/base.ods +0 -0
  34. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/base.xls +0 -0
  35. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/base.xlsb +0 -0
  36. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/base.xlsx +0 -0
  37. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/empty_file.ods +0 -0
  38. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/empty_file.xlsb +0 -0
  39. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/empty_file.xlsx +0 -0
  40. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/password.ods +0 -0
  41. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/password.xls +0 -0
  42. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/password.xlsb +0 -0
  43. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/data/password.xlsx +0 -0
  44. {python_calamine-0.4.0 → python_calamine-0.5.1}/tests/test_sheet_type.py +0 -0
@@ -13,33 +13,29 @@ on:
13
13
 
14
14
  jobs:
15
15
  test:
16
- name: test ${{ matrix.python-version }} pandas ${{ matrix.pandas-version }} ${{ matrix.disable-gil && 'free-threaded' || '' }}
16
+ name: test ${{ matrix.python-version }} pandas ${{ matrix.pandas-version }}
17
17
  strategy:
18
18
  fail-fast: false
19
19
  matrix:
20
20
  python-version:
21
- - "3.9"
22
21
  - "3.10"
23
22
  - "3.11"
24
23
  - "3.12"
24
+ - "3.13"
25
+ - "3.14"
25
26
  pandas-version:
26
- - "2.1.*"
27
27
  - "2.2.*"
28
+ - "2.3.*"
28
29
  include:
29
- - python-version: "3.8"
30
- pandas-version: "none"
31
30
  # https://github.com/pandas-dev/pandas/issues/42509
32
- - python-version: "pypy3.9"
31
+ - python-version: "pypy3.11"
33
32
  pandas-version: "none"
34
- # https://github.com/pandas-dev/pandas/issues/42509
35
- - python-version: "pypy3.10"
33
+ # https://github.com/pandas-dev/pandas/issues/60016
34
+ - python-version: "3.13t"
36
35
  pandas-version: "none"
37
- - python-version: "3.13"
38
- pandas-version: "2.2.*"
39
36
  # https://github.com/pandas-dev/pandas/issues/60016
40
- - python-version: "3.13"
37
+ - python-version: "3.14t"
41
38
  pandas-version: "none"
42
- disable-gil: true
43
39
 
44
40
  runs-on: ubuntu-latest
45
41
 
@@ -47,7 +43,7 @@ jobs:
47
43
  PYTHON: ${{ matrix.python-version }}
48
44
 
49
45
  steps:
50
- - uses: actions/checkout@v4
46
+ - uses: actions/checkout@v5
51
47
 
52
48
  - name: install rust stable
53
49
  uses: dtolnay/rust-toolchain@master
@@ -61,29 +57,22 @@ jobs:
61
57
  key: v3
62
58
 
63
59
  - name: set up python
64
- uses: actions/setup-python@v5
60
+ uses: actions/setup-python@v6
65
61
  if: "${{ !matrix.disable-gil }}"
66
62
  with:
67
63
  python-version: ${{ matrix.python-version }}
68
64
  allow-prereleases: true
69
65
 
70
- - name: set up python (free-threaded)
71
- uses: deadsnakes/action@v3.2.0
72
- if: "${{ matrix.disable-gil }}"
73
- with:
74
- python-version: ${{ matrix.python-version }}
75
- nogil: ${{ matrix.disable-gil }}
76
-
77
66
  - id: cache-py
78
67
  name: cache python
79
68
  uses: actions/cache@v4
80
- if: "${{ !matrix.disable-gil }}"
81
69
  with:
82
70
  path: ${{ env.pythonLocation }}
83
71
  key: >
84
72
  py
85
73
  ${{ runner.os }}
86
- ${{ env.pythonLocation }}
74
+ ${{ matrix.python-version }}
75
+ ${{ matrix.pandas-version }}
87
76
 
88
77
  - run: |
89
78
  if [ ${{ matrix.pandas-version }} == 'none' ]; then
@@ -105,7 +94,7 @@ jobs:
105
94
  runs-on: ubuntu-latest
106
95
 
107
96
  steps:
108
- - uses: actions/checkout@v4
97
+ - uses: actions/checkout@v5
109
98
 
110
99
  - name: install rust stable
111
100
  uses: dtolnay/rust-toolchain@stable
@@ -115,7 +104,7 @@ jobs:
115
104
  - name: cache rust
116
105
  uses: Swatinem/rust-cache@v2
117
106
 
118
- - uses: actions/setup-python@v5
107
+ - uses: actions/setup-python@v6
119
108
  with:
120
109
  python-version: "3.13"
121
110
 
@@ -155,7 +144,7 @@ jobs:
155
144
  strategy:
156
145
  fail-fast: false
157
146
  matrix:
158
- os: [ubuntu, macos, windows]
147
+ os: [ubuntu, macos]
159
148
  target: [x86_64, aarch64]
160
149
  manylinux: [auto]
161
150
  include:
@@ -163,15 +152,17 @@ jobs:
163
152
  platform: linux
164
153
  - os: windows
165
154
  ls: dir
155
+ target: x86_64
156
+ interpreter: 3.10 3.11 3.12 3.13 3.13t
166
157
  - os: windows
167
158
  ls: dir
168
159
  target: i686
169
160
  python-architecture: x86
170
- interpreter: 3.8 3.9 3.10 3.11 3.12 3.13
161
+ interpreter: 3.10 3.11 3.12 3.13 3.13t
171
162
  - os: windows
172
163
  ls: dir
173
164
  target: aarch64
174
- interpreter: 3.11 3.12 3.13
165
+ interpreter: 3.11 3.12 3.13 3.13t
175
166
  - os: ubuntu
176
167
  platform: linux
177
168
  target: i686
@@ -181,7 +172,7 @@ jobs:
181
172
  - os: ubuntu
182
173
  platform: linux
183
174
  target: armv7
184
- interpreter: 3.8 3.9 3.10 3.11 3.12 3.13
175
+ interpreter: 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11
185
176
  # musllinux
186
177
  - os: ubuntu
187
178
  platform: linux
@@ -194,22 +185,18 @@ jobs:
194
185
  - os: ubuntu
195
186
  platform: linux
196
187
  target: ppc64le
197
- interpreter: 3.8 3.9 3.10 3.11 3.12 3.13
188
+ interpreter: 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11
198
189
  - os: ubuntu
199
190
  platform: linux
200
191
  target: s390x
201
- interpreter: 3.8 3.9 3.10 3.11 3.12 3.13
202
- exclude:
203
- # Windows on arm64 only supports Python 3.11+
204
- - os: windows
205
- target: aarch64
192
+ interpreter: 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11
206
193
 
207
194
  runs-on: ${{ matrix.os }}-latest
208
195
  steps:
209
- - uses: actions/checkout@v4
196
+ - uses: actions/checkout@v5
210
197
 
211
198
  - name: set up python
212
- uses: actions/setup-python@v5
199
+ uses: actions/setup-python@v6
213
200
  with:
214
201
  python-version: "3.13"
215
202
  architecture: ${{ matrix.python-architecture || 'x64' }}
@@ -229,7 +216,7 @@ jobs:
229
216
  with:
230
217
  target: ${{ matrix.target }}
231
218
  manylinux: ${{ matrix.manylinux || 'auto' }}
232
- args: --release --out dist --interpreter ${{ matrix.interpreter || '3.8 3.9 3.10 3.11 3.12 3.13 pypy3.9 pypy3.10' }}
219
+ args: --release --out dist --interpreter ${{ matrix.interpreter || '3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11' }}
233
220
  rust-toolchain: stable
234
221
 
235
222
  - run: ${{ matrix.ls || 'ls -lh' }} dist/
@@ -256,7 +243,7 @@ jobs:
256
243
 
257
244
  steps:
258
245
  - name: Download All Artifacts
259
- uses: actions/download-artifact@v4
246
+ uses: actions/download-artifact@v5
260
247
  with:
261
248
  path: dist
262
249
  pattern: wheels-*
@@ -38,8 +38,6 @@ repos:
38
38
  rev: v1.8.0
39
39
  hooks:
40
40
  - id: mypy
41
- additional_dependencies:
42
- - pandas-stubs
43
41
  exclude: ^tests/.*$
44
42
  - repo: local
45
43
  hooks:
@@ -70,8 +70,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
70
70
 
71
71
  [[package]]
72
72
  name = "calamine"
73
- version = "0.26.1"
74
- source = "git+https://github.com/tafia/calamine.git?rev=5cda5c734661f656bdfb7ac7a1b95e77bf89aa09#5cda5c734661f656bdfb7ac7a1b95e77bf89aa09"
73
+ version = "0.30.0"
74
+ source = "registry+https://github.com/rust-lang/crates.io-index"
75
+ checksum = "a9aeb09f84576a634da713630e11e431a744b91f1f8114c2ff0760189783a8a1"
75
76
  dependencies = [
76
77
  "atoi_simd",
77
78
  "byteorder",
@@ -170,12 +171,6 @@ dependencies = [
170
171
  "cfg-if",
171
172
  ]
172
173
 
173
- [[package]]
174
- name = "crossbeam-utils"
175
- version = "0.8.21"
176
- source = "registry+https://github.com/rust-lang/crates.io-index"
177
- checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
178
-
179
174
  [[package]]
180
175
  name = "derive_arbitrary"
181
176
  version = "1.4.1"
@@ -187,17 +182,6 @@ dependencies = [
187
182
  "syn",
188
183
  ]
189
184
 
190
- [[package]]
191
- name = "displaydoc"
192
- version = "0.2.5"
193
- source = "registry+https://github.com/rust-lang/crates.io-index"
194
- checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
195
- dependencies = [
196
- "proc-macro2",
197
- "quote",
198
- "syn",
199
- ]
200
-
201
185
  [[package]]
202
186
  name = "encoding_rs"
203
187
  version = "0.8.35"
@@ -246,11 +230,12 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
246
230
 
247
231
  [[package]]
248
232
  name = "flate2"
249
- version = "1.0.35"
233
+ version = "1.1.2"
250
234
  source = "registry+https://github.com/rust-lang/crates.io-index"
251
- checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
235
+ checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
252
236
  dependencies = [
253
237
  "crc32fast",
238
+ "libz-rs-sys",
254
239
  "miniz_oxide",
255
240
  ]
256
241
 
@@ -344,6 +329,15 @@ version = "0.2.169"
344
329
  source = "registry+https://github.com/rust-lang/crates.io-index"
345
330
  checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
346
331
 
332
+ [[package]]
333
+ name = "libz-rs-sys"
334
+ version = "0.5.1"
335
+ source = "registry+https://github.com/rust-lang/crates.io-index"
336
+ checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221"
337
+ dependencies = [
338
+ "zlib-rs",
339
+ ]
340
+
347
341
  [[package]]
348
342
  name = "linux-raw-sys"
349
343
  version = "0.4.15"
@@ -379,9 +373,9 @@ dependencies = [
379
373
 
380
374
  [[package]]
381
375
  name = "miniz_oxide"
382
- version = "0.8.2"
376
+ version = "0.8.9"
383
377
  source = "registry+https://github.com/rust-lang/crates.io-index"
384
- checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
378
+ checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
385
379
  dependencies = [
386
380
  "adler2",
387
381
  ]
@@ -397,9 +391,9 @@ dependencies = [
397
391
 
398
392
  [[package]]
399
393
  name = "once_cell"
400
- version = "1.20.2"
394
+ version = "1.21.3"
401
395
  source = "registry+https://github.com/rust-lang/crates.io-index"
402
- checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
396
+ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
403
397
 
404
398
  [[package]]
405
399
  name = "portable-atomic"
@@ -429,9 +423,9 @@ dependencies = [
429
423
 
430
424
  [[package]]
431
425
  name = "pyo3"
432
- version = "0.25.1"
426
+ version = "0.26.0"
433
427
  source = "registry+https://github.com/rust-lang/crates.io-index"
434
- checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
428
+ checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383"
435
429
  dependencies = [
436
430
  "chrono",
437
431
  "indoc",
@@ -447,20 +441,19 @@ dependencies = [
447
441
 
448
442
  [[package]]
449
443
  name = "pyo3-build-config"
450
- version = "0.25.1"
444
+ version = "0.26.0"
451
445
  source = "registry+https://github.com/rust-lang/crates.io-index"
452
- checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
446
+ checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f"
453
447
  dependencies = [
454
- "once_cell",
455
448
  "python3-dll-a",
456
449
  "target-lexicon",
457
450
  ]
458
451
 
459
452
  [[package]]
460
453
  name = "pyo3-ffi"
461
- version = "0.25.1"
454
+ version = "0.26.0"
462
455
  source = "registry+https://github.com/rust-lang/crates.io-index"
463
- checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
456
+ checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105"
464
457
  dependencies = [
465
458
  "libc",
466
459
  "pyo3-build-config",
@@ -469,8 +462,7 @@ dependencies = [
469
462
  [[package]]
470
463
  name = "pyo3-file"
471
464
  version = "0.13.0"
472
- source = "registry+https://github.com/rust-lang/crates.io-index"
473
- checksum = "75916300df50dbfa6958ec05fe53473e69474542dac7fd1275aa6869a18a241a"
465
+ source = "git+https://github.com/dimastbk/pyo3-file.git?rev=da8ddb5d774d042555b867dd6571f3ce128bc349#da8ddb5d774d042555b867dd6571f3ce128bc349"
474
466
  dependencies = [
475
467
  "pyo3",
476
468
  "skeptic",
@@ -478,9 +470,9 @@ dependencies = [
478
470
 
479
471
  [[package]]
480
472
  name = "pyo3-macros"
481
- version = "0.25.1"
473
+ version = "0.26.0"
482
474
  source = "registry+https://github.com/rust-lang/crates.io-index"
483
- checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
475
+ checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded"
484
476
  dependencies = [
485
477
  "proc-macro2",
486
478
  "pyo3-macros-backend",
@@ -490,9 +482,9 @@ dependencies = [
490
482
 
491
483
  [[package]]
492
484
  name = "pyo3-macros-backend"
493
- version = "0.25.1"
485
+ version = "0.26.0"
494
486
  source = "registry+https://github.com/rust-lang/crates.io-index"
495
- checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
487
+ checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf"
496
488
  dependencies = [
497
489
  "heck",
498
490
  "proc-macro2",
@@ -503,7 +495,7 @@ dependencies = [
503
495
 
504
496
  [[package]]
505
497
  name = "python-calamine"
506
- version = "0.4.0"
498
+ version = "0.5.1"
507
499
  dependencies = [
508
500
  "calamine",
509
501
  "chrono",
@@ -667,26 +659,6 @@ dependencies = [
667
659
  "windows-sys",
668
660
  ]
669
661
 
670
- [[package]]
671
- name = "thiserror"
672
- version = "2.0.10"
673
- source = "registry+https://github.com/rust-lang/crates.io-index"
674
- checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3"
675
- dependencies = [
676
- "thiserror-impl",
677
- ]
678
-
679
- [[package]]
680
- name = "thiserror-impl"
681
- version = "2.0.10"
682
- source = "registry+https://github.com/rust-lang/crates.io-index"
683
- checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb"
684
- dependencies = [
685
- "proc-macro2",
686
- "quote",
687
- "syn",
688
- ]
689
-
690
662
  [[package]]
691
663
  name = "unicase"
692
664
  version = "2.8.1"
@@ -880,21 +852,24 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
880
852
 
881
853
  [[package]]
882
854
  name = "zip"
883
- version = "2.4.1"
855
+ version = "4.5.0"
884
856
  source = "registry+https://github.com/rust-lang/crates.io-index"
885
- checksum = "938cc23ac49778ac8340e366ddc422b2227ea176edb447e23fc0627608dddadd"
857
+ checksum = "8835eb39822904d39cb19465de1159e05d371973f0c6df3a365ad50565ddc8b9"
886
858
  dependencies = [
887
859
  "arbitrary",
888
860
  "crc32fast",
889
- "crossbeam-utils",
890
- "displaydoc",
891
861
  "flate2",
892
862
  "indexmap",
893
863
  "memchr",
894
- "thiserror",
895
864
  "zopfli",
896
865
  ]
897
866
 
867
+ [[package]]
868
+ name = "zlib-rs"
869
+ version = "0.5.1"
870
+ source = "registry+https://github.com/rust-lang/crates.io-index"
871
+ checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a"
872
+
898
873
  [[package]]
899
874
  name = "zopfli"
900
875
  version = "0.8.1"
@@ -0,0 +1,22 @@
1
+ [package]
2
+ name = "python-calamine"
3
+ version = "0.5.1"
4
+ edition = "2021"
5
+ readme = "README.md"
6
+
7
+ [lib]
8
+ name = "python_calamine"
9
+ crate-type = ["cdylib"]
10
+
11
+ [dependencies]
12
+ calamine = { version = "0.30.0", features = ["dates"] }
13
+ pyo3 = { version = "0.26.0", features = [
14
+ "extension-module",
15
+ "chrono",
16
+ "generate-import-lib",
17
+ ] }
18
+ chrono = { version = "0.4.38", features = ["serde"] }
19
+ pyo3-file = { git = "https://github.com/dimastbk/pyo3-file.git", rev = "da8ddb5d774d042555b867dd6571f3ce128bc349" }
20
+
21
+ [build-dependencies]
22
+ pyo3-build-config = "0.26.0"
@@ -1,31 +1,24 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-calamine
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Classifier: Development Status :: 4 - Beta
5
- Classifier: License :: OSI Approved :: MIT License
6
5
  Classifier: Programming Language :: Rust
7
- Classifier: Programming Language :: Python :: 3.8
8
- Classifier: Programming Language :: Python :: 3.9
9
6
  Classifier: Programming Language :: Python :: 3.10
10
7
  Classifier: Programming Language :: Python :: 3.11
11
8
  Classifier: Programming Language :: Python :: 3.12
12
9
  Classifier: Programming Language :: Python :: 3.13
13
- Requires-Dist: packaging>=23.1
10
+ Classifier: Programming Language :: Python :: 3.14
14
11
  Requires-Dist: maturin~=1.0 ; extra == 'dev'
15
- Requires-Dist: pre-commit~=3.0 ; extra == 'dev'
12
+ Requires-Dist: pre-commit~=4.3 ; extra == 'dev'
16
13
  Requires-Dist: pytest~=8.0 ; extra == 'dev'
17
- Requires-Dist: pandas[excel]~=2.0 ; extra == 'dev'
18
- Requires-Dist: numpy~=1.0 ; extra == 'dev'
14
+ Requires-Dist: pandas[excel]~=2.2 ; extra == 'dev'
19
15
  Provides-Extra: dev
20
16
  License-File: LICENSE
21
17
  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
18
  Author-email: Dmitriy <dimastbk@proton.me>
25
- License: MIT
26
- Requires-Python: >=3.8
19
+ License-Expression: MIT
20
+ Requires-Python: >=3.10
27
21
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
28
- Project-URL: Source Code, https://github.com/dimastbk/python-calamine
29
22
 
30
23
  # python-calamine
31
24
  [![PyPI - Version](https://img.shields.io/pypi/v/python-calamine)](https://pypi.org/project/python-calamine/)
@@ -79,20 +72,7 @@ workbook = CalamineWorkbook.from_path("file.xlsx").get_sheet_by_name("Sheet1").t
79
72
  # ]
80
73
  ```
81
74
 
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
75
  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
76
 
97
77
  Also, you can find additional examples in [tests](https://github.com/dimastbk/python-calamine/blob/master/tests/test_base.py).
98
78
 
@@ -50,20 +50,7 @@ workbook = CalamineWorkbook.from_path("file.xlsx").get_sheet_by_name("Sheet1").t
50
50
  # ]
51
51
  ```
52
52
 
53
- 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).
54
53
  Pandas 2.2 and above have built-in support of python-calamine.
55
- ```python
56
- from pandas import read_excel
57
- from python_calamine.pandas import pandas_monkeypatch
58
-
59
-
60
- pandas_monkeypatch()
61
-
62
- read_excel("file.xlsx", engine="calamine")
63
- # 1 2 3 4 5 6 7
64
- # 0 1 2 3 4 5 6 7
65
- # 1 1 2 3 4 5 6 7
66
- ```
67
54
 
68
55
  Also, you can find additional examples in [tests](https://github.com/dimastbk/python-calamine/blob/master/tests/test_base.py).
69
56
 
@@ -1,27 +1,27 @@
1
1
  [project]
2
2
  name = "python-calamine"
3
- requires-python = ">=3.8"
3
+ description = "Python binding for Rust's library for reading excel and odf file - calamine"
4
+ readme = "README.md"
5
+ requires-python = ">=3.10"
6
+ license = "MIT"
7
+ authors = [{ name = "Dmitriy", email = "dimastbk@proton.me" }]
4
8
  classifiers = [
5
9
  "Development Status :: 4 - Beta",
6
- "License :: OSI Approved :: MIT License",
7
10
  "Programming Language :: Rust",
8
- "Programming Language :: Python :: 3.8",
9
- "Programming Language :: Python :: 3.9",
10
11
  "Programming Language :: Python :: 3.10",
11
12
  "Programming Language :: Python :: 3.11",
12
13
  "Programming Language :: Python :: 3.12",
13
14
  "Programming Language :: Python :: 3.13",
15
+ "Programming Language :: Python :: 3.14",
14
16
  ]
15
- dependencies = ["packaging>=23.1"]
16
17
  dynamic = ["version"]
17
18
 
18
19
  [project.optional-dependencies]
19
20
  dev = [
20
21
  "maturin~=1.0",
21
- "pre-commit~=3.0",
22
+ "pre-commit~=4.3",
22
23
  "pytest~=8.0",
23
- "pandas[excel]~=2.0",
24
- "numpy~=1.0",
24
+ "pandas[excel]~=2.2",
25
25
  ]
26
26
 
27
27
  [tool.isort]
@@ -31,7 +31,7 @@ multi_line_output = 3
31
31
  profile = "black"
32
32
 
33
33
  [tool.mypy]
34
- python_version = "3.8"
34
+ python_version = "3.10"
35
35
  ignore_missing_imports = false
36
36
  disallow_untyped_defs = true
37
37
  check_untyped_defs = true
@@ -8,7 +8,7 @@ use crate::types::{
8
8
  };
9
9
 
10
10
  #[pyfunction]
11
- fn load_workbook(py: Python, path_or_filelike: PyObject) -> PyResult<CalamineWorkbook> {
11
+ fn load_workbook(py: Python, path_or_filelike: Py<PyAny>) -> PyResult<CalamineWorkbook> {
12
12
  CalamineWorkbook::from_object(py, path_or_filelike)
13
13
  }
14
14
 
@@ -1,8 +1,16 @@
1
1
  use std::convert::From;
2
2
 
3
3
  use calamine::DataType;
4
+ use chrono::Datelike;
4
5
  use pyo3::prelude::*;
5
6
 
7
+ /// https://docs.python.org/3/library/datetime.html#constants
8
+ /// The smallest year number allowed in a date or datetime object. MINYEAR is 1.
9
+ const MINYEAR: i32 = 1;
10
+
11
+ /// The largest year number allowed in a date or datetime object. MAXYEAR is 9999.
12
+ const MAXYEAR: i32 = 9999;
13
+
6
14
  #[derive(Debug, Clone)]
7
15
  pub enum CellValue {
8
16
  Int(i64),
@@ -16,6 +24,14 @@ pub enum CellValue {
16
24
  Empty,
17
25
  }
18
26
 
27
+ fn check_year_range<DT: Datelike>(value: DT) -> Option<DT> {
28
+ if value.year() < MINYEAR || value.year() > MAXYEAR {
29
+ None
30
+ } else {
31
+ Some(value)
32
+ }
33
+ }
34
+
19
35
  impl<'py> IntoPyObject<'py> for CellValue {
20
36
  type Target = PyAny;
21
37
  type Output = Bound<'py, Self::Target>;
@@ -64,19 +80,31 @@ where
64
80
  } else if v < 1.0 {
65
81
  value.as_time().map(CellValue::Time)
66
82
  } else if v == (v as u64) as f64 {
67
- value.as_date().map(CellValue::Date)
83
+ value
84
+ .as_date()
85
+ .and_then(check_year_range)
86
+ .map(CellValue::Date)
68
87
  } else {
69
- value.as_datetime().map(CellValue::DateTime)
88
+ value
89
+ .as_datetime()
90
+ .and_then(check_year_range)
91
+ .map(CellValue::DateTime)
70
92
  }
71
93
  .unwrap_or(CellValue::Float(v))
72
94
  } else if value.is_datetime_iso() {
73
95
  let v = value.get_datetime_iso().unwrap();
74
96
  if v.contains('T') {
75
- value.as_datetime().map(CellValue::DateTime)
97
+ value
98
+ .as_datetime()
99
+ .and_then(check_year_range)
100
+ .map(CellValue::DateTime)
76
101
  } else if v.contains(':') {
77
102
  value.as_time().map(CellValue::Time)
78
103
  } else {
79
- value.as_date().map(CellValue::Date)
104
+ value
105
+ .as_date()
106
+ .and_then(check_year_range)
107
+ .map(CellValue::Date)
80
108
  }
81
109
  .unwrap_or(CellValue::String(v.to_owned()))
82
110
  } else if value.is_duration_iso() {
@@ -209,12 +209,15 @@ impl CalamineSheet {
209
209
  Arc::clone(&slf.range)
210
210
  };
211
211
 
212
- PyList::new(
213
- slf.py(),
214
- range.rows().take(nrows as usize).map(|row| {
215
- PyList::new(slf.py(), row.iter().map(<&Data as Into<CellValue>>::into)).unwrap()
216
- }),
217
- )
212
+ let py_list = PyList::empty(slf.py());
213
+
214
+ for row in range.rows().take(nrows as usize) {
215
+ let py_row = PyList::new(slf.py(), row.iter().map(<&Data as Into<CellValue>>::into))?;
216
+
217
+ py_list.append(py_row)?;
218
+ }
219
+
220
+ Ok(py_list)
218
221
  }
219
222
 
220
223
  fn iter_rows(&self) -> CalamineCellIterator {
@@ -102,7 +102,7 @@ impl CalamineWorkbook {
102
102
  fn py_from_object(
103
103
  _cls: &Bound<'_, PyType>,
104
104
  py: Python<'_>,
105
- path_or_filelike: PyObject,
105
+ path_or_filelike: Py<PyAny>,
106
106
  ) -> PyResult<Self> {
107
107
  Self::from_object(py, path_or_filelike)
108
108
  }
@@ -112,22 +112,22 @@ impl CalamineWorkbook {
112
112
  fn py_from_filelike(
113
113
  _cls: &Bound<'_, PyType>,
114
114
  py: Python<'_>,
115
- filelike: PyObject,
115
+ filelike: Py<PyAny>,
116
116
  ) -> PyResult<Self> {
117
- py.allow_threads(|| Self::from_filelike(filelike))
117
+ py.detach(|| Self::from_filelike(filelike))
118
118
  }
119
119
 
120
120
  #[classmethod]
121
121
  #[pyo3(name = "from_path")]
122
- fn py_from_path(_cls: &Bound<'_, PyType>, py: Python<'_>, path: PyObject) -> PyResult<Self> {
122
+ fn py_from_path(_cls: &Bound<'_, PyType>, py: Python<'_>, path: Py<PyAny>) -> PyResult<Self> {
123
123
  if let Ok(string_ref) = path.downcast_bound::<PyString>(py) {
124
124
  let path = string_ref.to_string_lossy().to_string();
125
- return py.allow_threads(|| Self::from_path(&path));
125
+ return py.detach(|| Self::from_path(&path));
126
126
  }
127
127
 
128
128
  if let Ok(string_ref) = path.extract::<PathBuf>(py) {
129
129
  let path = string_ref.to_string_lossy().to_string();
130
- return py.allow_threads(|| Self::from_path(&path));
130
+ return py.detach(|| Self::from_path(&path));
131
131
  }
132
132
 
133
133
  Err(PyTypeError::new_err(""))
@@ -135,12 +135,12 @@ impl CalamineWorkbook {
135
135
 
136
136
  #[pyo3(name = "get_sheet_by_name")]
137
137
  fn py_get_sheet_by_name(&mut self, py: Python<'_>, name: &str) -> PyResult<CalamineSheet> {
138
- py.allow_threads(|| self.get_sheet_by_name(name))
138
+ py.detach(|| self.get_sheet_by_name(name))
139
139
  }
140
140
 
141
141
  #[pyo3(name = "get_sheet_by_index")]
142
142
  fn py_get_sheet_by_index(&mut self, py: Python<'_>, index: usize) -> PyResult<CalamineSheet> {
143
- py.allow_threads(|| self.get_sheet_by_index(index))
143
+ py.detach(|| self.get_sheet_by_index(index))
144
144
  }
145
145
 
146
146
  fn close(&mut self) -> PyResult<()> {
@@ -160,30 +160,30 @@ impl CalamineWorkbook {
160
160
 
161
161
  fn __exit__(
162
162
  &mut self,
163
- _exc_type: PyObject,
164
- _exc_value: PyObject,
165
- _traceback: PyObject,
163
+ _exc_type: Py<PyAny>,
164
+ _exc_value: Py<PyAny>,
165
+ _traceback: Py<PyAny>,
166
166
  ) -> PyResult<()> {
167
167
  self.close()
168
168
  }
169
169
  }
170
170
 
171
171
  impl CalamineWorkbook {
172
- pub fn from_object(py: Python<'_>, path_or_filelike: PyObject) -> PyResult<Self> {
172
+ pub fn from_object(py: Python<'_>, path_or_filelike: Py<PyAny>) -> PyResult<Self> {
173
173
  if let Ok(string_ref) = path_or_filelike.downcast_bound::<PyString>(py) {
174
174
  let path = string_ref.to_string_lossy().to_string();
175
- return py.allow_threads(|| Self::from_path(&path));
175
+ return py.detach(|| Self::from_path(&path));
176
176
  }
177
177
 
178
178
  if let Ok(string_ref) = path_or_filelike.extract::<PathBuf>(py) {
179
179
  let path = string_ref.to_string_lossy().to_string();
180
- return py.allow_threads(|| Self::from_path(&path));
180
+ return py.detach(|| Self::from_path(&path));
181
181
  }
182
182
 
183
- py.allow_threads(|| Self::from_filelike(path_or_filelike))
183
+ py.detach(|| Self::from_filelike(path_or_filelike))
184
184
  }
185
185
 
186
- pub fn from_filelike(filelike: PyObject) -> PyResult<Self> {
186
+ pub fn from_filelike(filelike: Py<PyAny>) -> PyResult<Self> {
187
187
  let mut buf = vec![];
188
188
  PyFileLikeObject::with_requirements(filelike, true, false, true, false)?
189
189
  .read_to_end(&mut buf)?;
@@ -375,3 +375,10 @@ def test_close_workbook_double():
375
375
 
376
376
  with pytest.raises(WorkbookClosed):
377
377
  reader.close()
378
+
379
+
380
+ def test_datetime_overflow():
381
+ reader = CalamineWorkbook.from_path(PATH / "issue139.xlsx")
382
+
383
+ # 2994626.0 = 10099-01-01
384
+ assert reader.get_sheet_by_index(0).to_python() == [["date"], [2994626.0]]
@@ -1,8 +1,6 @@
1
- from importlib.metadata import version
2
1
  from pathlib import Path
3
2
 
4
3
  import pytest
5
- from packaging.version import Version, parse
6
4
 
7
5
  try:
8
6
  import pandas as pd
@@ -14,17 +12,6 @@ except ImportError:
14
12
  PATH = Path(__file__).parent / "data"
15
13
 
16
14
 
17
- @pytest.fixture(scope="module", autouse=True)
18
- def pandas_monkeypatch():
19
- if parse(version("pandas")) >= Version("2.2.0"):
20
- yield
21
- else:
22
- from python_calamine.pandas import pandas_monkeypatch
23
-
24
- pandas_monkeypatch()
25
- yield
26
-
27
-
28
15
  @pytest.mark.skipif(not pd, reason="pandas is required")
29
16
  def test_ods_pandas(expected_df_ods):
30
17
  result = pd.read_excel(PATH / "base.ods", sheet_name="Sheet1", engine="calamine")
@@ -1,27 +0,0 @@
1
- [package]
2
- name = "python-calamine"
3
- version = "0.4.0"
4
- authors = ["Dmitriy <dimastbk@proton.me>"]
5
- description = "Python binding for Rust's library for reading excel and odf file - calamine"
6
- edition = "2021"
7
- homepage = "https://github.com/dimastbk/python-calamine"
8
- license = "MIT"
9
- readme = "README.md"
10
- repository = "https://github.com/dimastbk/python-calamine"
11
-
12
- [lib]
13
- name = "python_calamine"
14
- crate-type = ["cdylib"]
15
-
16
- [dependencies]
17
- calamine = { git = "https://github.com/tafia/calamine.git", rev = "5cda5c734661f656bdfb7ac7a1b95e77bf89aa09", features = ["dates"] }
18
- pyo3 = { version = "0.25.1", features = [
19
- "extension-module",
20
- "chrono",
21
- "generate-import-lib",
22
- ] }
23
- chrono = { version = "0.4.38", features = ["serde"] }
24
- pyo3-file = "0.13.0"
25
-
26
- [build-dependencies]
27
- pyo3-build-config = "0.25.1"
@@ -1,136 +0,0 @@
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