xlsxturbo 0.8.0__tar.gz → 0.10.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.
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/CHANGELOG.md +73 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/Cargo.lock +5 -5
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/Cargo.toml +1 -1
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/PKG-INFO +408 -14
- xlsxturbo-0.10.1/README.md +844 -0
- xlsxturbo-0.10.1/ROADMAP.md +102 -0
- xlsxturbo-0.10.1/benchmarks/benchmark.py +652 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/pyproject.toml +1 -1
- xlsxturbo-0.10.1/python/xlsxturbo/__init__.pyi +271 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/src/lib.rs +1530 -45
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/tests/test_features.py +364 -0
- xlsxturbo-0.8.0/README.md +0 -450
- xlsxturbo-0.8.0/ROADMAP.md +0 -57
- xlsxturbo-0.8.0/benchmark.py +0 -237
- xlsxturbo-0.8.0/python/xlsxturbo/__init__.pyi +0 -144
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/.github/dependabot.yml +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/.github/workflows/ci.yml +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/.github/workflows/release.yml +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/.gitignore +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/BUILD.md +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/LICENSE +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1/benchmarks}/benchmark_parallel.py +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/python/xlsxturbo/__init__.py +0 -0
- {xlsxturbo-0.8.0 → xlsxturbo-0.10.1}/src/main.rs +0 -0
|
@@ -5,6 +5,76 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.10.1] - 2026-01-16
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Benchmark suite reorganization** - Moved benchmarks to `benchmarks/` directory
|
|
12
|
+
- New `benchmarks/benchmark.py` - comprehensive comparison vs polars, pandas+openpyxl, pandas+xlsxwriter
|
|
13
|
+
- Moved `benchmark_parallel.py` to `benchmarks/`
|
|
14
|
+
- Removed obsolete `benchmark.py` (referenced old Rust binary)
|
|
15
|
+
- **README Performance section** - Updated with reproducible benchmark methodology
|
|
16
|
+
- Changed performance claim from "~25x faster" to "~6x faster" (accurate for typical workloads)
|
|
17
|
+
- Added disclaimer that results vary by system
|
|
18
|
+
- Linked to Benchmarking section for running your own tests
|
|
19
|
+
|
|
20
|
+
## [0.10.0] - 2026-01-16
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **Comments/Notes** - Add cell annotations with optional author
|
|
24
|
+
- Simple text: `comments={'A1': 'Note text'}`
|
|
25
|
+
- With author: `comments={'A1': {'text': 'Note', 'author': 'John'}}`
|
|
26
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
27
|
+
- **Data Validation** - Add dropdowns and constraints to columns
|
|
28
|
+
- List (dropdown): `validations={'Status': {'type': 'list', 'values': ['Open', 'Closed']}}`
|
|
29
|
+
- Whole number: `validations={'Score': {'type': 'whole_number', 'min': 0, 'max': 100}}`
|
|
30
|
+
- Decimal: `validations={'Price': {'type': 'decimal', 'min': 0.0, 'max': 999.99}}`
|
|
31
|
+
- Text length: `validations={'Code': {'type': 'text_length', 'min': 3, 'max': 10}}`
|
|
32
|
+
- Supports input/error messages: `input_title`, `input_message`, `error_title`, `error_message`
|
|
33
|
+
- Supports column patterns (like `column_formats`)
|
|
34
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
35
|
+
- **Rich Text** - Multiple formats within a single cell
|
|
36
|
+
- Format segments: `rich_text={'A1': [('Bold', {'bold': True}), ' normal text']}`
|
|
37
|
+
- Supports: `bold`, `italic`, `font_color`, `bg_color`, `font_size`, `underline`
|
|
38
|
+
- Mix formatted and plain text segments
|
|
39
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
40
|
+
- **Images** - Embed PNG, JPEG, GIF, BMP images in cells
|
|
41
|
+
- Simple path: `images={'B5': 'logo.png'}`
|
|
42
|
+
- With options: `images={'B5': {'path': 'logo.png', 'scale_width': 0.5, 'scale_height': 0.5}}`
|
|
43
|
+
- Options: `path`, `scale_width`, `scale_height`, `alt_text`
|
|
44
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
45
|
+
|
|
46
|
+
### Notes
|
|
47
|
+
- All new features are disabled in `constant_memory` mode (they require random access)
|
|
48
|
+
- Data validation list values are limited to 255 total characters (Excel limitation)
|
|
49
|
+
|
|
50
|
+
## [0.9.0] - 2026-01-15
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
- **Conditional formatting** - Visual formatting based on cell values
|
|
54
|
+
- `2_color_scale`: Gradient from min_color to max_color
|
|
55
|
+
- `3_color_scale`: Three-color gradient with min/mid/max colors
|
|
56
|
+
- `data_bar`: In-cell bar chart with customizable color, direction, solid fill
|
|
57
|
+
- `icon_set`: Traffic lights, arrows, flags (3/4/5 icons), with reverse and icons_only options
|
|
58
|
+
- Supports column name patterns: `'price_*': {'type': 'data_bar', ...}`
|
|
59
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
60
|
+
- Example: `conditional_formats={'score': {'type': '2_color_scale', 'min_color': '#FF0000', 'max_color': '#00FF00'}}`
|
|
61
|
+
- **Formula columns** - Add calculated columns with Excel formulas
|
|
62
|
+
- Use `{row}` placeholder for row numbers (1-based)
|
|
63
|
+
- Columns appear after data columns
|
|
64
|
+
- Order preserved (first formula = first new column)
|
|
65
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
66
|
+
- Example: `formula_columns={'Total': '=A{row}+B{row}', 'Percentage': '=C{row}/D{row}*100'}`
|
|
67
|
+
- **Merged cells** - Merge cell ranges for headers, titles, and grouped labels
|
|
68
|
+
- Uses Excel notation for ranges (e.g., 'A1:D1')
|
|
69
|
+
- Optional formatting with HeaderFormat options (bold, colors, etc.)
|
|
70
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
71
|
+
- Example: `merged_ranges=[('A1:C1', 'Title'), ('A2:C2', 'Subtitle', {'bold': True})]`
|
|
72
|
+
- **Hyperlinks** - Add clickable links to cells
|
|
73
|
+
- Uses Excel notation for cell reference (e.g., 'A1', 'B5')
|
|
74
|
+
- Optional display text (defaults to URL if not provided)
|
|
75
|
+
- Available in both `df_to_xlsx()` and `dfs_to_xlsx()` with per-sheet overrides
|
|
76
|
+
- Example: `hyperlinks=[('A2', 'https://example.com'), ('B2', 'https://google.com', 'Google')]`
|
|
77
|
+
|
|
8
78
|
## [0.8.0] - 2026-01-15
|
|
9
79
|
|
|
10
80
|
### Added
|
|
@@ -143,6 +213,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
143
213
|
- Support for custom sheet names
|
|
144
214
|
- Verbose mode for progress reporting
|
|
145
215
|
|
|
216
|
+
[0.10.1]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.10.1
|
|
217
|
+
[0.10.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.10.0
|
|
218
|
+
[0.9.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.9.0
|
|
146
219
|
[0.8.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.8.0
|
|
147
220
|
[0.7.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.7.0
|
|
148
221
|
[0.6.0]: https://github.com/tstone-1/xlsxturbo/releases/tag/v0.6.0
|
|
@@ -673,9 +673,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|
|
673
673
|
|
|
674
674
|
[[package]]
|
|
675
675
|
name = "wasip2"
|
|
676
|
-
version = "1.0.
|
|
676
|
+
version = "1.0.2+wasi-0.2.9"
|
|
677
677
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
678
|
-
checksum = "
|
|
678
|
+
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
|
679
679
|
dependencies = [
|
|
680
680
|
"wit-bindgen",
|
|
681
681
|
]
|
|
@@ -795,13 +795,13 @@ dependencies = [
|
|
|
795
795
|
|
|
796
796
|
[[package]]
|
|
797
797
|
name = "wit-bindgen"
|
|
798
|
-
version = "0.
|
|
798
|
+
version = "0.51.0"
|
|
799
799
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
800
|
-
checksum = "
|
|
800
|
+
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
|
801
801
|
|
|
802
802
|
[[package]]
|
|
803
803
|
name = "xlsxturbo"
|
|
804
|
-
version = "0.
|
|
804
|
+
version = "0.10.1"
|
|
805
805
|
dependencies = [
|
|
806
806
|
"chrono",
|
|
807
807
|
"clap",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xlsxturbo
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -39,6 +39,14 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
|
|
|
39
39
|
|
|
40
40
|
- **Direct DataFrame support** for pandas and polars
|
|
41
41
|
- **Excel tables** - filterable tables with 61 built-in styles (banded rows, autofilter)
|
|
42
|
+
- **Conditional formatting** - color scales, data bars, icon sets for visual data analysis
|
|
43
|
+
- **Formula columns** - add calculated columns with Excel formulas
|
|
44
|
+
- **Merged cells** - merge cell ranges for headers and titles
|
|
45
|
+
- **Hyperlinks** - add clickable links to cells
|
|
46
|
+
- **Comments/Notes** - add cell annotations with optional author
|
|
47
|
+
- **Data validation** - dropdowns, number ranges, text length constraints
|
|
48
|
+
- **Rich text** - multiple formats within a single cell
|
|
49
|
+
- **Images** - embed PNG, JPEG, GIF, BMP in cells
|
|
42
50
|
- **Auto-fit columns** - automatically adjust column widths to fit content
|
|
43
51
|
- **Custom column widths** - set specific widths per column or cap all with _all
|
|
44
52
|
- **Header styling** - bold, colors, font size for header row
|
|
@@ -56,7 +64,7 @@ High-performance Excel writer with automatic type detection. Written in Rust, us
|
|
|
56
64
|
- Datetimes (ISO 8601) → Excel datetimes
|
|
57
65
|
- `NaN`/`Inf` → Empty cells (graceful handling)
|
|
58
66
|
- Everything else → Text
|
|
59
|
-
- **~
|
|
67
|
+
- **~6x faster** than pandas + openpyxl (see [benchmarks](#performance))
|
|
60
68
|
- **Memory efficient** - streams data with 1MB buffer
|
|
61
69
|
- Available as both **Python library** and **CLI tool**
|
|
62
70
|
|
|
@@ -337,6 +345,370 @@ Available per-sheet options:
|
|
|
337
345
|
- `table_name` (str): Custom Excel table name
|
|
338
346
|
- `header_format` (dict): Header cell styling
|
|
339
347
|
- `column_formats` (dict): Column formatting with pattern matching
|
|
348
|
+
- `conditional_formats` (dict): Conditional formatting (color scales, data bars, icons)
|
|
349
|
+
- `formula_columns` (dict): Calculated columns with Excel formulas (column name -> formula template)
|
|
350
|
+
- `merged_ranges` (list): List of (range, text) or (range, text, format) tuples to merge cells
|
|
351
|
+
- `hyperlinks` (list): List of (cell, url) or (cell, url, display_text) tuples to add clickable links
|
|
352
|
+
- `comments` (dict): Cell comments/notes (cell_ref -> text or {text, author})
|
|
353
|
+
- `validations` (dict): Data validation rules (column name/pattern -> validation config)
|
|
354
|
+
- `rich_text` (dict): Rich text with multiple formats (cell_ref -> list of segments)
|
|
355
|
+
- `images` (dict): Embedded images (cell_ref -> path or {path, scale_width, scale_height, alt_text})
|
|
356
|
+
|
|
357
|
+
### Conditional Formatting
|
|
358
|
+
|
|
359
|
+
Apply visual formatting based on cell values:
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
import xlsxturbo
|
|
363
|
+
import pandas as pd
|
|
364
|
+
|
|
365
|
+
df = pd.DataFrame({
|
|
366
|
+
'name': ['Alice', 'Bob', 'Charlie', 'Diana'],
|
|
367
|
+
'score': [95, 72, 88, 45],
|
|
368
|
+
'progress': [0.9, 0.5, 0.75, 0.3],
|
|
369
|
+
'status': [3, 2, 3, 1]
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
373
|
+
autofit=True,
|
|
374
|
+
conditional_formats={
|
|
375
|
+
# 2-color gradient: red (low) to green (high)
|
|
376
|
+
'score': {
|
|
377
|
+
'type': '2_color_scale',
|
|
378
|
+
'min_color': '#FF6B6B',
|
|
379
|
+
'max_color': '#51CF66'
|
|
380
|
+
},
|
|
381
|
+
# Data bars: in-cell bar chart
|
|
382
|
+
'progress': {
|
|
383
|
+
'type': 'data_bar',
|
|
384
|
+
'bar_color': '#339AF0',
|
|
385
|
+
'solid': True # Solid fill instead of gradient
|
|
386
|
+
},
|
|
387
|
+
# Icon set: traffic lights
|
|
388
|
+
'status': {
|
|
389
|
+
'type': 'icon_set',
|
|
390
|
+
'icon_type': '3_traffic_lights'
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Supported conditional format types:**
|
|
397
|
+
|
|
398
|
+
| Type | Options |
|
|
399
|
+
|------|---------|
|
|
400
|
+
| `2_color_scale` | `min_color`, `max_color` |
|
|
401
|
+
| `3_color_scale` | `min_color`, `mid_color`, `max_color` |
|
|
402
|
+
| `data_bar` | `bar_color`, `border_color`, `solid`, `direction` |
|
|
403
|
+
| `icon_set` | `icon_type`, `reverse`, `icons_only` |
|
|
404
|
+
|
|
405
|
+
**Available icon types:**
|
|
406
|
+
- 3 icons: `3_arrows`, `3_arrows_gray`, `3_flags`, `3_traffic_lights`, `3_traffic_lights_rimmed`, `3_signs`, `3_symbols`, `3_symbols_uncircled`
|
|
407
|
+
- 4 icons: `4_arrows`, `4_arrows_gray`, `4_traffic_lights`, `4_rating`
|
|
408
|
+
- 5 icons: `5_arrows`, `5_arrows_gray`, `5_quarters`, `5_rating`
|
|
409
|
+
|
|
410
|
+
Column patterns work with conditional formats:
|
|
411
|
+
```python
|
|
412
|
+
# Apply data bars to all columns starting with "price_"
|
|
413
|
+
conditional_formats={'price_*': {'type': 'data_bar', 'bar_color': '#9B59B6'}}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Formula Columns
|
|
417
|
+
|
|
418
|
+
Add calculated columns to your Excel output. Formulas are written after data columns and use `{row}` as a placeholder for the row number:
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
import xlsxturbo
|
|
422
|
+
import pandas as pd
|
|
423
|
+
|
|
424
|
+
df = pd.DataFrame({
|
|
425
|
+
'price': [100, 200, 150],
|
|
426
|
+
'quantity': [5, 3, 8],
|
|
427
|
+
'tax_rate': [0.1, 0.1, 0.2]
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
xlsxturbo.df_to_xlsx(df, "sales.xlsx",
|
|
431
|
+
autofit=True,
|
|
432
|
+
formula_columns={
|
|
433
|
+
'Subtotal': '=A{row}*B{row}', # price * quantity
|
|
434
|
+
'Tax': '=D{row}*C{row}', # subtotal * tax_rate
|
|
435
|
+
'Total': '=D{row}+E{row}' # subtotal + tax
|
|
436
|
+
}
|
|
437
|
+
)
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Formula columns appear after data columns (A=price, B=quantity, C=tax_rate, D=Subtotal, E=Tax, F=Total).
|
|
441
|
+
|
|
442
|
+
**Notes:**
|
|
443
|
+
- `{row}` is replaced with the Excel row number (1-based, starting at 2 for data rows when header=True)
|
|
444
|
+
- Formula columns inherit header formatting if specified
|
|
445
|
+
- Column order is preserved (first formula = first new column)
|
|
446
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
447
|
+
|
|
448
|
+
### Merged Cells
|
|
449
|
+
|
|
450
|
+
Merge cell ranges to create headers, titles, or grouped labels:
|
|
451
|
+
|
|
452
|
+
```python
|
|
453
|
+
import xlsxturbo
|
|
454
|
+
import pandas as pd
|
|
455
|
+
|
|
456
|
+
df = pd.DataFrame({
|
|
457
|
+
'product': ['Widget A', 'Widget B'],
|
|
458
|
+
'sales': [1500, 2300],
|
|
459
|
+
'revenue': [7500, 11500]
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
# Merge cells for a title above the data
|
|
463
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
464
|
+
header=True,
|
|
465
|
+
merged_ranges=[
|
|
466
|
+
# Simple merge with text (auto-centered)
|
|
467
|
+
('A1:C1', 'Q4 Sales Report'),
|
|
468
|
+
# Merge with custom formatting
|
|
469
|
+
('A2:C2', 'Regional Data', {
|
|
470
|
+
'bold': True,
|
|
471
|
+
'bg_color': '#4F81BD',
|
|
472
|
+
'font_color': 'white'
|
|
473
|
+
})
|
|
474
|
+
]
|
|
475
|
+
)
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Merged range format:**
|
|
479
|
+
- Tuple of `(range, text)` or `(range, text, format_dict)`
|
|
480
|
+
- Range uses Excel notation: `'A1:D1'`, `'B3:B10'`, etc.
|
|
481
|
+
- Format options same as `header_format`: bold, italic, font_color, bg_color, font_size, underline
|
|
482
|
+
|
|
483
|
+
**Notes:**
|
|
484
|
+
- Merged cells are applied after data is written, so plan row positions accordingly
|
|
485
|
+
- When using with `header=True`, data starts at row 2 (Excel row 2)
|
|
486
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
487
|
+
|
|
488
|
+
### Hyperlinks
|
|
489
|
+
|
|
490
|
+
Add clickable links to cells:
|
|
491
|
+
|
|
492
|
+
```python
|
|
493
|
+
import xlsxturbo
|
|
494
|
+
import pandas as pd
|
|
495
|
+
|
|
496
|
+
df = pd.DataFrame({
|
|
497
|
+
'company': ['Anthropic', 'Google', 'Microsoft'],
|
|
498
|
+
'product': ['Claude', 'Gemini', 'Copilot'],
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
# Add hyperlinks to a new column (D) after the data columns (A, B, C with header)
|
|
502
|
+
xlsxturbo.df_to_xlsx(df, "companies.xlsx",
|
|
503
|
+
autofit=True,
|
|
504
|
+
hyperlinks=[
|
|
505
|
+
# Header for the links column
|
|
506
|
+
('C1', 'https://example.com', 'Website'),
|
|
507
|
+
# Links with company names as display text
|
|
508
|
+
('C2', 'https://anthropic.com', 'anthropic.com'),
|
|
509
|
+
('C3', 'https://google.com', 'google.com'),
|
|
510
|
+
('C4', 'https://microsoft.com', 'microsoft.com'),
|
|
511
|
+
]
|
|
512
|
+
)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Hyperlink format:**
|
|
516
|
+
- Tuple of `(cell, url)` or `(cell, url, display_text)`
|
|
517
|
+
- Cell uses Excel notation: `'A1'`, `'B5'`, etc.
|
|
518
|
+
- Display text is optional; if omitted, the URL is shown
|
|
519
|
+
|
|
520
|
+
**Notes:**
|
|
521
|
+
- Hyperlinks write to the specified cell position (overwrites existing content)
|
|
522
|
+
- To add a "links column", target cells beyond your DataFrame columns (as shown above)
|
|
523
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
524
|
+
- Not available in constant memory mode
|
|
525
|
+
|
|
526
|
+
### Comments/Notes
|
|
527
|
+
|
|
528
|
+
Add cell annotations (hover to view):
|
|
529
|
+
|
|
530
|
+
```python
|
|
531
|
+
import xlsxturbo
|
|
532
|
+
import pandas as pd
|
|
533
|
+
|
|
534
|
+
df = pd.DataFrame({
|
|
535
|
+
'product': ['Widget A', 'Widget B'],
|
|
536
|
+
'price': [19.99, 29.99]
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
540
|
+
comments={
|
|
541
|
+
# Simple text comment
|
|
542
|
+
'A1': 'This column contains product names',
|
|
543
|
+
# Comment with author
|
|
544
|
+
'B1': {'text': 'Prices in USD', 'author': 'Finance Team'}
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Comment format:**
|
|
550
|
+
- Simple: `{'A1': 'Note text'}`
|
|
551
|
+
- With author: `{'A1': {'text': 'Note text', 'author': 'Name'}}`
|
|
552
|
+
|
|
553
|
+
**Notes:**
|
|
554
|
+
- Comments appear as small red triangles in the cell corner
|
|
555
|
+
- Hover over the cell to see the comment
|
|
556
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
557
|
+
- Not available in constant memory mode
|
|
558
|
+
|
|
559
|
+
### Data Validation
|
|
560
|
+
|
|
561
|
+
Add dropdowns and input constraints:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
import xlsxturbo
|
|
565
|
+
import pandas as pd
|
|
566
|
+
|
|
567
|
+
df = pd.DataFrame({
|
|
568
|
+
'status': ['Open', 'Closed'],
|
|
569
|
+
'score': [85, 92],
|
|
570
|
+
'price': [19.99, 29.99],
|
|
571
|
+
'code': ['ABC', 'XYZ']
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
xlsxturbo.df_to_xlsx(df, "validated.xlsx",
|
|
575
|
+
validations={
|
|
576
|
+
# Dropdown list
|
|
577
|
+
'status': {
|
|
578
|
+
'type': 'list',
|
|
579
|
+
'values': ['Open', 'Closed', 'Pending', 'Review']
|
|
580
|
+
},
|
|
581
|
+
# Whole number range (0-100)
|
|
582
|
+
'score': {
|
|
583
|
+
'type': 'whole_number',
|
|
584
|
+
'min': 0,
|
|
585
|
+
'max': 100,
|
|
586
|
+
'error_title': 'Invalid Score',
|
|
587
|
+
'error_message': 'Score must be between 0 and 100'
|
|
588
|
+
},
|
|
589
|
+
# Decimal range
|
|
590
|
+
'price': {
|
|
591
|
+
'type': 'decimal',
|
|
592
|
+
'min': 0.0,
|
|
593
|
+
'max': 999.99
|
|
594
|
+
},
|
|
595
|
+
# Text length constraint
|
|
596
|
+
'code': {
|
|
597
|
+
'type': 'text_length',
|
|
598
|
+
'min': 3,
|
|
599
|
+
'max': 10
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
)
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**Validation types:**
|
|
606
|
+
|
|
607
|
+
| Type | Aliases | Description | Options |
|
|
608
|
+
|------|---------|-------------|---------|
|
|
609
|
+
| `list` | - | Dropdown menu | `values` (list of strings, max 255 chars total) |
|
|
610
|
+
| `whole_number` | `whole`, `integer` | Integer range | `min`, `max` |
|
|
611
|
+
| `decimal` | `number` | Decimal range | `min`, `max` |
|
|
612
|
+
| `text_length` | `textlength`, `length` | Character count | `min`, `max` |
|
|
613
|
+
|
|
614
|
+
**Optional message options:**
|
|
615
|
+
- `input_title`, `input_message`: Prompt shown when cell is selected
|
|
616
|
+
- `error_title`, `error_message`: Message shown when invalid data is entered
|
|
617
|
+
|
|
618
|
+
**Notes:**
|
|
619
|
+
- Validations apply to the data rows of the specified column
|
|
620
|
+
- Column patterns work: `'score_*': {...}` matches all columns starting with `score_`
|
|
621
|
+
- If only `min` or only `max` is specified, the other defaults to the type's extreme value
|
|
622
|
+
- List validation values are limited to 255 total characters (Excel limitation)
|
|
623
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
624
|
+
- Not available in constant memory mode
|
|
625
|
+
|
|
626
|
+
### Rich Text
|
|
627
|
+
|
|
628
|
+
Multiple formats within a single cell:
|
|
629
|
+
|
|
630
|
+
```python
|
|
631
|
+
import xlsxturbo
|
|
632
|
+
import pandas as pd
|
|
633
|
+
|
|
634
|
+
df = pd.DataFrame({'A': [1, 2, 3]})
|
|
635
|
+
|
|
636
|
+
xlsxturbo.df_to_xlsx(df, "rich.xlsx",
|
|
637
|
+
rich_text={
|
|
638
|
+
'D1': [
|
|
639
|
+
('Important: ', {'bold': True, 'font_color': 'red'}),
|
|
640
|
+
'Please review ',
|
|
641
|
+
('all', {'italic': True}),
|
|
642
|
+
' values'
|
|
643
|
+
],
|
|
644
|
+
'D2': [
|
|
645
|
+
('Status: ', {'bold': True}),
|
|
646
|
+
('OK', {'font_color': 'green', 'bold': True})
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
)
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Segment format:**
|
|
653
|
+
- Formatted: `('text', {'bold': True, 'font_color': 'blue'})`
|
|
654
|
+
- Plain: `'plain text'` (no formatting)
|
|
655
|
+
|
|
656
|
+
**Available format options:**
|
|
657
|
+
- `bold` (bool)
|
|
658
|
+
- `italic` (bool)
|
|
659
|
+
- `font_color` (str): '#RRGGBB' or named color
|
|
660
|
+
- `bg_color` (str): Background color
|
|
661
|
+
- `font_size` (float)
|
|
662
|
+
- `underline` (bool)
|
|
663
|
+
|
|
664
|
+
**Notes:**
|
|
665
|
+
- Rich text writes to the specified cell position (overwrites existing content)
|
|
666
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
667
|
+
- Not available in constant memory mode
|
|
668
|
+
|
|
669
|
+
### Images
|
|
670
|
+
|
|
671
|
+
Embed images in cells:
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
import xlsxturbo
|
|
675
|
+
import pandas as pd
|
|
676
|
+
|
|
677
|
+
df = pd.DataFrame({'Product': ['Widget A', 'Widget B'], 'Price': [19.99, 29.99]})
|
|
678
|
+
|
|
679
|
+
xlsxturbo.df_to_xlsx(df, "catalog.xlsx",
|
|
680
|
+
autofit=True,
|
|
681
|
+
images={
|
|
682
|
+
# Simple path
|
|
683
|
+
'C2': 'images/widget_a.png',
|
|
684
|
+
# With options
|
|
685
|
+
'C3': {
|
|
686
|
+
'path': 'images/widget_b.png',
|
|
687
|
+
'scale_width': 0.5,
|
|
688
|
+
'scale_height': 0.5,
|
|
689
|
+
'alt_text': 'Widget B photo'
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
**Image format:**
|
|
696
|
+
- Simple: `{'C2': 'path/to/image.png'}`
|
|
697
|
+
- With options: `{'C2': {'path': '...', 'scale_width': 0.5, ...}}`
|
|
698
|
+
|
|
699
|
+
**Available options:**
|
|
700
|
+
- `path` (str, required): Path to image file
|
|
701
|
+
- `scale_width` (float): Width scale factor (1.0 = original)
|
|
702
|
+
- `scale_height` (float): Height scale factor (1.0 = original)
|
|
703
|
+
- `alt_text` (str): Alternative text for accessibility
|
|
704
|
+
|
|
705
|
+
**Supported formats:** PNG, JPEG, GIF, BMP
|
|
706
|
+
|
|
707
|
+
**Notes:**
|
|
708
|
+
- Images are positioned at the specified cell (overlays any existing content)
|
|
709
|
+
- Image file must exist; non-existent files will raise an error
|
|
710
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
711
|
+
- Not available in constant memory mode
|
|
340
712
|
|
|
341
713
|
### Constant Memory Mode (Large Files)
|
|
342
714
|
|
|
@@ -365,7 +737,18 @@ xlsxturbo.dfs_to_xlsx([
|
|
|
365
737
|
- `table_style` (Excel tables)
|
|
366
738
|
- `freeze_panes`
|
|
367
739
|
- `row_heights`
|
|
740
|
+
- `conditional_formats`
|
|
741
|
+
- `merged_ranges`
|
|
742
|
+
- `hyperlinks`
|
|
743
|
+
- `comments`
|
|
744
|
+
- `validations`
|
|
745
|
+
- `rich_text`
|
|
746
|
+
- `images`
|
|
368
747
|
- `autofit`
|
|
748
|
+
- `conditional_formats`
|
|
749
|
+
- `formula_columns`
|
|
750
|
+
- `merged_ranges`
|
|
751
|
+
- `hyperlinks`
|
|
369
752
|
|
|
370
753
|
Column widths still work in constant memory mode.
|
|
371
754
|
|
|
@@ -424,15 +807,16 @@ xlsxturbo sales.csv report.xlsx -d eu -v --sheet-name "Q4 Sales"
|
|
|
424
807
|
|
|
425
808
|
## Performance
|
|
426
809
|
|
|
427
|
-
|
|
810
|
+
*Reference benchmark on 100,000 rows x 50 columns with mixed data types. Your results will vary by system - run the benchmark yourself (see [Benchmarking](#benchmarking)).*
|
|
811
|
+
|
|
812
|
+
| Library | Time (s) | Rows/sec | vs xlsxturbo |
|
|
813
|
+
|---------|----------|----------|--------------|
|
|
814
|
+
| **xlsxturbo** | **6.65** | **15,033** | **1.0x** |
|
|
815
|
+
| polars | 25.07 | 3,988 | 3.8x |
|
|
816
|
+
| pandas + xlsxwriter | 35.60 | 2,809 | 5.4x |
|
|
817
|
+
| pandas + openpyxl | 38.85 | 2,574 | 5.8x |
|
|
428
818
|
|
|
429
|
-
|
|
430
|
-
|--------|------|---------|
|
|
431
|
-
| **xlsxturbo** | 28.5s | **26.7x** |
|
|
432
|
-
| PyExcelerate | 107s | 7.1x |
|
|
433
|
-
| pandas + xlsxwriter | 374s | 2.0x |
|
|
434
|
-
| pandas + openpyxl | 762s | 1.0x |
|
|
435
|
-
| polars.write_excel | 1039s | 0.7x |
|
|
819
|
+
*Test system: Windows 11, Python 3.14, AMD Ryzen 9 (32 threads)*
|
|
436
820
|
|
|
437
821
|
## Type Detection Examples
|
|
438
822
|
|
|
@@ -468,14 +852,24 @@ maturin build --release
|
|
|
468
852
|
|
|
469
853
|
## Benchmarking
|
|
470
854
|
|
|
471
|
-
Run the included benchmark
|
|
855
|
+
Run the included benchmark scripts:
|
|
472
856
|
|
|
473
857
|
```bash
|
|
474
|
-
#
|
|
475
|
-
python benchmark.py
|
|
858
|
+
# Compare xlsxturbo vs other libraries (100K rows default)
|
|
859
|
+
python benchmarks/benchmark.py
|
|
860
|
+
|
|
861
|
+
# Full benchmark: small, medium, large datasets
|
|
862
|
+
python benchmarks/benchmark.py --full
|
|
476
863
|
|
|
477
864
|
# Custom size
|
|
478
|
-
python benchmark.py --rows 500000 --cols 100
|
|
865
|
+
python benchmarks/benchmark.py --rows 500000 --cols 100
|
|
866
|
+
|
|
867
|
+
# Output formats for CI/documentation
|
|
868
|
+
python benchmarks/benchmark.py --markdown
|
|
869
|
+
python benchmarks/benchmark.py --json
|
|
870
|
+
|
|
871
|
+
# Test parallel vs single-threaded CSV conversion
|
|
872
|
+
python benchmarks/benchmark_parallel.py
|
|
479
873
|
```
|
|
480
874
|
|
|
481
875
|
## License
|