https://github.com/momiji-rs/sasso-python
Python bindings for sasso — a pure-Rust SCSS→CSS compiler (a dart-sass alternative), via its C ABI. In-process, no Node, no Dart VM.
https://github.com/momiji-rs/sasso-python
compiler css css-preprocessor ctypes dart-sass ffi python python-bindings rust sass sass-compiler scss
Last synced: 2 days ago
JSON representation
Python bindings for sasso — a pure-Rust SCSS→CSS compiler (a dart-sass alternative), via its C ABI. In-process, no Node, no Dart VM.
- Host: GitHub
- URL: https://github.com/momiji-rs/sasso-python
- Owner: momiji-rs
- License: apache-2.0
- Created: 2026-06-15T20:25:05.000Z (12 days ago)
- Default Branch: main
- Last Pushed: 2026-06-15T23:26:38.000Z (12 days ago)
- Last Synced: 2026-06-16T00:16:48.609Z (12 days ago)
- Topics: compiler, css, css-preprocessor, ctypes, dart-sass, ffi, python, python-bindings, rust, sass, sass-compiler, scss
- Language: Rust
- Size: 52.7 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# sasso
[](https://pypi.org/project/sasso/)
Python bindings for [**sasso**](https://github.com/momiji-rs/sasso) — a fast,
pure-Rust SCSS/Sass → CSS compiler — over its `libsasso` C ABI.
`sasso` runs **in-process** (no Node, no subprocess, no system `sass` binary)
via a small `ctypes` layer over a prebuilt native library. Each wheel bundles
the compiled `libsasso` for its platform, so `pip install sasso` is all you
need.
```bash
pip install sasso
```
## Usage
```python
import sasso
css = sasso.compile(
"""
$brand: #336699;
.card {
color: $brand;
.title { font-weight: bold; }
}
"""
)
print(css)
```
```python
# Compressed output, .sass (indented) syntax, filesystem load paths:
sasso.compile(src, style="compressed")
sasso.compile(indented_src, syntax="sass")
sasso.compile(src, load_paths=["styles", "vendor"])
```
### Errors
A compile failure raises `sasso.SassoError`, carrying the diagnostic message
plus the 1-based source location:
```python
try:
sasso.compile(".broken { color: ; }")
except sasso.SassoError as e:
print(e.message, e.line, e.column)
```
### Custom importers
Resolve `@use` / `@forward` / `@import` yourself (from a database, a bundler's
virtual filesystem, HTTP, …) by subclassing `sasso.Importer`. It mirrors
dart-sass's two-phase model:
```python
class DictImporter(sasso.Importer):
def __init__(self, files):
self.files = files
def canonicalize(self, url, *, from_import, containing_url):
# Map a (possibly relative) URL to a stable canonical key, or None.
return url if url in self.files else None
def load(self, canonical):
# Fetch the source for a canonical key, or None.
return sasso.LoadResult(contents=self.files[canonical], syntax="scss")
css = sasso.compile('@use "theme";', importer=DictImporter({"theme": "$c: red;"}))
```
An exception raised inside an importer aborts the compile and surfaces as a
`SassoError` with the original exception chained as `__cause__`.
## API
| Symbol | Description |
| --- | --- |
| `compile(source, *, style="expanded", syntax="scss", load_paths=None, url=None, importer=None) -> str` | Compile a stylesheet string to CSS. |
| `SassoError` | Raised on failure; has `.message: str`, `.line: int \| None`, `.column: int \| None`. |
| `Importer` | ABC for custom resolution: `canonicalize(url, *, from_import, containing_url)` + `load(canonical) -> LoadResult \| None`. |
| `LoadResult(contents, syntax="scss", source_map_url=None)` | Return value of `Importer.load`. |
| `compiler_version() -> str` | Version of the bundled native `sasso` compiler. |
| `__version__` | Version of this Python package (independent of the compiler version). |
`style` is `"expanded"` or `"compressed"`; `syntax` is `"scss"`, `"sass"`, or
`"css"`.
## Performance
`sasso` compiles in-process, so it avoids the per-call process-spawn overhead of
shelling out to the `sass` CLI. Compiling a non-trivial stylesheet 200× on an
Apple-silicon Mac (`benchmark.py`, vs. spawning the dart-sass binary per
compile):
```
output parity (sasso vs dart-sass): IDENTICAL
sasso (in-process) : 0.0068 s total (0.034 ms/compile)
dart-sass (subprocess/ea) : 4.7381 s total (23.691 ms/compile)
speedup: sasso is 701.0x faster for 200 compiles in this workload
```
Most of that gap is process-startup cost that an in-process binding removes
entirely; the comparison reflects the realistic "shell out to `sass`" path a
Python app would otherwise take.
## Versioning — which compiler is bundled?
The **package** version (`sasso.__version__`) floats independently of the
**compiler** version it bundles (`sasso.compiler_version()`):
| sasso (PyPI) | bundles core `sasso` crate |
| --- | --- |
| 0.1.0 | 0.6.0 |
Each release notes its bundled core version in the [CHANGELOG](CHANGELOG.md).
## How it works
The package is a `ctypes` binding — **not** a CPython C-extension — over the
[`libsasso` C ABI](https://github.com/momiji-rs/sasso/tree/master/ffi). Because
it has no Python-ABI linkage, a single native library per `(OS, arch)` serves
every CPython 3.x (and PyPy), so each release ships one platform wheel per
target rather than one per Python version.
The native crate is vendored from `momiji-rs/sasso` `ffi/` and builds against
the **published** `sasso` crate from crates.io.
## License
MIT OR Apache-2.0, at your option. See [LICENSE-MIT](LICENSE-MIT) and
[LICENSE-APACHE](LICENSE-APACHE).