Metadata-Version: 2.2
Name: turboxl
Version: 0.2.4
Summary: Fast XLSX to CSV converter (C++ core with Python bindings)
Keywords: xlsx,csv,excel,converter,fast,c++
Author-Email: Michail Kaseris <mich.kaseris@gmail.com>
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: C++
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: XML
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: isort>=5.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Description-Content-Type: text/markdown

# TurboXL

<p align="center">
  <img src="assets/logo.svg" alt="TurboXL Logo" width="400"/>
</p>
Fast, read-only XLSX to CSV converter with C++20 core and Python bindings.

## Performance

**Real-world benchmarks** on Chicago Crime dataset (21.9MB, 146,574 rows):

| Metric         | TurboXL         | OpenPyXL       | Improvement      |
| -------------- | --------------- | -------------- | ---------------- |
| **Speed**      | 2.4s            | 63.1s          | **26.7x faster** |
| **Memory**     | 33.5MB          | 66.9MB         | **2.0x less**    |
| **Throughput** | 62,040 rows/sec | 2,321 rows/sec | **26.7x faster** |

_Dataset: [Chicago Crimes 2025](https://data.cityofchicago.org/Public-Safety/Crimes-2025/t7ek-mgzi/about_data)_

🚀 **Recent Optimizations Implemented:**

- **zlib-ng integration** - Up to 2.5x faster ZIP decompression
- **Release build optimizations** - `-O3 -march=native -flto` for GCC/Clang, `/O2 /GL /arch:AVX2` for MSVC
- **Arena-based shared strings** - Memory-efficient string storage
- **Chunked ZIP reading** - 512 KiB buffer optimization

## What It Does

- ✅ Read XLSX files and convert to CSV
- ✅ Handle shared strings, numbers, dates, booleans
- ✅ Process multiple worksheets
- ✅ Memory-efficient streaming (33.5MB for 146k rows)
- ✅ Cross-platform (Linux, macOS, Windows)

## What It Doesn't Do

- ❌ Write or modify XLSX files
- ❌ Formula evaluation (uses cached values)
- ❌ Charts, images, pivot tables
- ❌ Password-protected files

## Quick Start

### Python

```python
import turboxl

# Convert first sheet
csv_data = turboxl.read_sheet_to_csv("data.xlsx")

# Convert specific sheet
csv_data = turboxl.read_sheet_to_csv("data.xlsx", sheet="Sheet2")

# Custom options
csv_data = turboxl.read_sheet_to_csv(
    "data.xlsx",
    sheet=0,
    delimiter=";",
    date_mode="iso"
)

# Save to file
with open("output.csv", "w", encoding="utf-8") as f:
    f.write(csv_data)
```

### C++

```cpp
#include <xlsxcsv.hpp>
#include <iostream>

int main() {
    try {
        std::string csv = xlsxcsv::readSheetToCsv("data.xlsx");
        std::cout << csv << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}
```

## Building

### Prerequisites

Install system dependencies (used via pkg-config/CMake):

```bash
# macOS (Recommended for best performance)
brew install libxml2 minizip-ng zlib-ng cmake pkg-config

# Ubuntu/Debian (Recommended for best performance)
sudo apt-get install -y libxml2-dev libminizip-dev cmake build-essential pkg-config
# For zlib-ng on Ubuntu/Debian, build from source:
# git clone https://github.com/zlib-ng/zlib-ng.git
# cd zlib-ng && cmake -B build && cmake --build build -j && sudo cmake --install build

# Windows (vcpkg)
vcpkg install --triplet x64-windows-static-md
```

**Performance Note:** Installing `zlib-ng` provides significant performance improvements (up to 2.5x faster decompression). The build system automatically detects and uses zlib-ng if available, falling back to standard zlib otherwise.

### Build C++ Core (library only)

Build the C++ core without Python bindings (no Python/nanobind required):

```bash
# From repo root
cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_TESTS=OFF \
  -DBUILD_PYTHON=OFF \
  -DBUILD_CLI=OFF
cmake --build build -j4
```

Artifacts:

- Static library: `build/libturboxl_core.a`

**Build Modes:**

- **Release** (Recommended): Enables `-O3 -march=native -flto` optimizations
- **Debug**: Enables debugging symbols and assertions

### Build Options

- `BUILD_TESTS=ON/OFF` - Build test suite (default: ON)
- `BUILD_PYTHON=ON/OFF` - Build Python bindings (default: ON)
- `BUILD_CLI=ON/OFF` - Build command-line tool (default: OFF)

---

## Python Wheel

TurboXL ships a PEP 517/518 build powered by scikit-build-core. The wheel builds the C++ core and Python extension in Release mode using CMake.

### Python prerequisites

```bash
python3 -m pip install -U pip build scikit-build-core nanobind
```

System dependencies listed above (libxml2, minizip-ng, zlib-ng, cmake, compiler) must be installed and discoverable by CMake/pkg-config.

### Build the wheel

```bash
# From repo root
python3 -m build -w
```

Outputs go to `dist/`, for example:

- `dist/turboxl-0.2.3-<python>-<abi>-<platform>.whl`

Install the built wheel locally:

```bash
pip install dist/turboxl-*.whl
```

Tips:

- Parallel CMake build: `CMAKE_BUILD_PARALLEL_LEVEL=4 python3 -m build -w`
- macOS arch (defaults to the host architecture): to override, you can pass
  `--config-setting=cmake.define.CMAKE_OSX_ARCHITECTURES="arm64;x86_64"` to `python -m build`.

## Requirements

- **C++**: C++20 compiler (GCC 10+, Clang 12+, MSVC 2019+)
- **Build**: CMake 3.20+
- **Python**: 3.10+ (CPython; free-threaded builds are not supported)

## API Reference

### Python

```python
turboxl.read_sheet_to_csv(
    xlsx_path: str,
    sheet: Union[str, int] = None,  # First sheet if None
    delimiter: str = ",",
    newline: Literal["LF", "CRLF"] = "LF",
    include_bom: bool = False,
    date_mode: Literal["iso", "rawNumber"] = "iso"
) -> str
```

### C++

```cpp
struct CsvOptions {
    std::string sheetByName;
    int sheetByIndex = -1;
    char delimiter = ',';
    bool includeBom = false;
    // ... more options
};

std::string readSheetToCsv(
    const std::string& xlsxPath,
    const CsvOptions& opts = {}
);
```

## License

MIT License - see [LICENSE](LICENSE) file for details.

## CI and releases

CI builds all 12 wheels and runs the native Debug tests on every PR and push to
`main`. The stable branch-protection check is **CI passed**. The four wheel targets
are Linux x64 (glibc 2.28+), Windows x64, and macOS 15+ on Intel and Apple Silicon.
Each gets CPython 3.10 and 3.11 wheels plus a CPython 3.12 ABI3 wheel, tested on
3.12, 3.13, and 3.14. Windows 32-bit is no longer supported.

Python package versions come from the CMake `project()` version, including in
source archives without Git metadata. Wheels use portable CPU flags and Release
IPO; native Debug tests do not. Python wheels contain the extension and runtime
libraries; a normal CMake install still supplies native development files.
The platform dependency scripts live in `tools/ci/`. CI pins Python build tools
using `tools/ci/constraints.txt`; Homebrew and distro packages remain rolling
inputs, so builds are not claimed to be bit-for-bit reproducible.

### One-time repository setup

1. Make **CI passed** required on `main`.
2. Keep the existing `PYPI_API_TOKEN` repository secret available to the release
   workflow. The token is used only by the PyPI publication job after all builds
   and release validation succeed.
3. Protect release tags against changes and deletion.

### Rehearse and release

Run **CI → Run workflow** on the intended branch first. This builds and tests the
complete distribution set without publishing. Merge a reviewed CMake version bump
and wait for CI, then tag that exact commit:

```bash
git switch main
git pull --ff-only
# Replace X.Y.Z with the version already recorded in CMakeLists.txt.
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin refs/tags/vX.Y.Z
```

Only the tag push triggers publication. The tag must match CMake and point to a
commit in `main` history. A release publishes the validated source archive and
12 wheels to PyPI, then attaches those same files and SHA256SUMS to a GitHub
Release with generated notes. No workflow automatically creates a tag.

If publication fails, use **Re-run failed jobs**, preserving the successful build
artifacts (retained for 30 days). Existing PyPI files are skipped only after their
SHA256 hashes match the retained artifacts. Differences fail closed; never move a
released tag or overwrite a version. If build artifacts have expired and cannot
be recovered, fix the problem and release a new version. Re-running only the
GitHub Release job is safe after a successful PyPI upload.

### Local checks

```bash
python -m pip install -c tools/ci/constraints.txt build scikit-build-core nanobind packaging
python -m unittest discover -s tools/ci -v
cmake -S . -B out/native -DBUILD_PYTHON=OFF -DBUILD_TESTS=ON \
  -DCMAKE_BUILD_TYPE=Debug -DTURBOXL_ENABLE_IPO=OFF -DTURBOXL_PORTABLE_BUILD=ON
cmake --build out/native --config Debug --parallel 4
ctest --test-dir out/native -C Debug --output-on-failure
python -m build --sdist --no-isolation
```

Native tests use Python's standard-library `zipfile` to create fixtures; no Unix
`zip` executable is required. Fixture creation errors fail the tests.
