Skip to main content

Python implementation of the Embedded Sass Host using the Dart Sass embedded protocol

Project description

Python Dart Sass

A Python implementation of the Embedded Sass Host, providing a Python API for the Dart Sass compiler using the embedded protocol.

Features

  • 🚀 Modern Architecture - Uses Dart Sass embedded protocol for compilation
  • 🔄 Async Support - Both synchronous and asynchronous APIs
  • 🎯 Custom Functions - Register Python functions callable from Sass
  • 📁 Custom Importers - Handle custom import logic in Python
  • 🛠️ Full Sass Support - Complete Sass language feature support via Dart Sass
  • 🧪 Well Tested - Comprehensive test suite with 278 passing tests

Installation

Prerequisites

This package requires the Dart Sass compiler to be installed on your system. You have several options:

Option 1: Standalone Dart Sass (Recommended)

Download the standalone Dart Sass binary from the official releases:

# Linux x64
wget https://github.com/sass/dart-sass/releases/download/1.81.0/dart-sass-1.81.0-linux-x64.tar.gz
tar -xzf dart-sass-1.81.0-linux-x64.tar.gz
sudo mv dart-sass /usr/local/bin/

# macOS x64
wget https://github.com/sass/dart-sass/releases/download/1.81.0/dart-sass-1.81.0-macos-x64.tar.gz
tar -xzf dart-sass-1.81.0-macos-x64.tar.gz
sudo mv dart-sass /usr/local/bin/

# Windows x64
# Download dart-sass-1.81.0-windows-x64.zip and extract to your PATH

Option 2: Using Package Managers

# Homebrew (macOS/Linux)
brew install sass/sass/sass

# Chocolatey (Windows)
choco install sass

# npm (if you have Node.js)
npm install -g sass

Option 3: Custom Installation Path

If you install Dart Sass to a custom location, set the environment variable:

export SASS_EMBEDDED_COMPILER_PATH="/path/to/your/sass"

Installing Python Dart Sass

Note: This package is not yet published to PyPI. To use it, you'll need to install from source or wait for the official release.

# Install from source (when available)
pip install git+https://github.com/hmcqueen/python-dart-sass.git

# Or install locally for development
pip install -e .

# When published to PyPI (future):
pip install python-dart-sass

Quick Start

Basic Usage

import dart_sass as sass

# Compile from string
result = sass.compile_string("""
$primary: #007bff;
.button {
    background-color: $primary;
    padding: 0.5rem 1rem;
}
""")
print(result.css)

# Compile from file
result = sass.compile('styles.scss')
print(result.css)

Async Usage

import dart_sass as sass
import asyncio

async def compile_sass():
    result = await sass.compile_async('styles.scss')
    print(result.css)

asyncio.run(compile_sass())

Advanced Features

Custom Functions

Register Python functions that can be called from Sass:

import dart_sass as sass
from dart_sass.value import SassString, SassNumber

def pow_function(args):
    """Calculate power: pow($base, $exponent)"""
    base = args[0].assert_number().value
    exponent = args[1].assert_number().value
    return SassNumber(base ** exponent)

# Compile with custom function
result = sass.compile_string("""
.element {
    width: pow(2, 3) * 1px; // Results in 8px
}
""", functions={
    'pow($base, $exponent)': pow_function
})

Custom Importers

Handle custom import logic:

import dart_sass as sass
from dart_sass.importer import Importer, ImportResult

class ThemeImporter(Importer):
    def canonicalize(self, url, context):
        if url.startswith('theme:'):
            return f'file:///themes/{url[6:]}.scss'
        return None
    
    def load(self, canonical_url):
        # Load theme file content
        theme_name = canonical_url.split('/')[-1].replace('.scss', '')
        content = f"$theme: '{theme_name}';"
        return ImportResult(content, syntax='scss')

result = sass.compile_string("""
@import 'theme:dark';
.app { color: $theme; }
""", importers=[ThemeImporter()])

File Importers

For simpler file-based imports:

from dart_sass.importer import FileImporter

class NodeModulesImporter(FileImporter):
    def find_file_url(self, url, context):
        if not url.startswith('~'):
            return None
        
        # Handle npm-style imports: @import '~bootstrap/scss/bootstrap'
        package_path = url[1:]  # Remove ~
        file_path = f'node_modules/{package_path}'
        
        if os.path.exists(f'{file_path}.scss'):
            return f'file://{os.path.abspath(file_path)}.scss'
        return None

result = sass.compile_string("""
@import '~bootstrap/scss/variables';
""", importers=[NodeModulesImporter()])

Sass Value Types

The package provides Python representations of all Sass value types:

from dart_sass.value import *

# Numbers
num = SassNumber(42, unit='px')
print(num.value)  # 42
print(num.unit)   # 'px'

# Strings
string = SassString('hello world', quoted=True)
print(string.text)    # 'hello world'
print(string.quoted)  # True

# Colors
color = SassColor.rgb(255, 0, 0)  # Red
print(color.red)    # 255
print(color.green)  # 0
print(color.blue)   # 0

# Lists
sass_list = SassList([
    SassNumber(1),
    SassNumber(2),
    SassNumber(3)
], separator=',')

# Maps
sass_map = SassMap({
    SassString('primary'): SassColor.rgb(0, 123, 255),
    SassString('secondary'): SassColor.rgb(108, 117, 125)
})

# Booleans and null
sass_true = SassBoolean.sass_true
sass_false = SassBoolean.sass_false
sass_null = SassNull.sass_null

Compilation Options

result = sass.compile_string(scss_content, {
    # Output style
    'style': 'compressed',  # 'expanded', 'compressed'
    
    # Source maps
    'source_map': True,
    
    # Load paths for @import
    'load_paths': ['/path/to/sass', '/another/path'],
    
    # Custom functions
    'functions': {
        'custom-function($arg)': my_function
    },
    
    # Custom importers
    'importers': [MyImporter()],
    
    # Charset handling
    'charset': True,
    
    # Quiet dependency warnings
    'quiet_deps': True,
    
    # Verbose output
    'verbose': False
})

print(result.css)
print(result.source_map)  # If source_map=True
print(result.loaded_urls)  # List of imported file URLs

Error Handling

from dart_sass.exception import CompileException

try:
    result = sass.compile_string("""
    .invalid {
        color: $undefined-variable;
    }
    """)
except CompileException as e:
    print(f"Compilation failed: {e}")
    # Exception includes detailed error information with line numbers

Performance Considerations

Async vs Sync

The asynchronous API may be beneficial for multiple compilations since it runs Dart Sass in a separate process:

import asyncio
import dart_sass as sass

async def compile_multiple():
    tasks = [
        sass.compile_async('file1.scss'),
        sass.compile_async('file2.scss'),
        sass.compile_async('file3.scss')
    ]
    results = await asyncio.gather(*tasks)
    return results

# This allows concurrent compilation vs sequential sync API
results = asyncio.run(compile_multiple())

Compiler Lifecycle

For multiple compilations, you can manage the compiler lifecycle to avoid repeated initialization:

# Initialize once, use multiple times
compiler = sass.init_async_compiler()

try:
    result1 = await compiler.compile_string(scss1)
    result2 = await compiler.compile_string(scss2)
    result3 = await compiler.compile_string(scss3)
finally:
    await compiler.dispose()  # Clean up resources

Architecture

This package implements the Embedded Sass Protocol to communicate with Dart Sass:

  1. Protocol Communication: Uses protocol buffers over stdin/stdout
  2. Message Handling: Reactive streams for handling compilation requests/responses
  3. Value Conversion: Bidirectional conversion between Python and Sass value types
  4. Process Management: Manages Dart Sass subprocess lifecycle
  5. Cross-Platform: Works on Linux, macOS, and Windows

Key Components

  • Compilers: AsyncCompiler and SyncCompiler for different usage patterns
  • Value System: Complete Sass value type implementations
  • Protocol Layer: Message encoding/decoding and transport
  • Importers: File and custom importer interfaces
  • Functions: Custom function registration and calling

Development

This project uses uv for dependency management:

# Install dependencies
uv sync

# Run tests
uv run pytest

# Run with coverage
uv run pytest --cov=sass_embedded

# Linting and formatting
uv run ruff check
uv run black --check .
uv run isort --check-only .

# Format code
uv run black .
uv run isort .

Testing

The test suite includes:

  • Unit tests for all value types
  • Integration tests with real Sass compilation
  • Protocol communication tests
  • Cross-platform compatibility tests
# Run specific test categories
uv run pytest tests/test_values.py      # Value type tests
uv run pytest tests/test_compiler.py    # Compiler tests
uv run pytest tests/test_protocol.py    # Protocol tests

Compatibility

  • Python: 3.10+
  • Dart Sass: 1.45.0+ (embedded protocol support)
  • Platforms: Linux, macOS, Windows
  • Architecture: x86_64, ARM64

Comparison with Other Sass Packages

Feature python-dart-sass libsass-python pysass
Sass Version Latest Dart Sass LibSass (deprecated) LibSass
Architecture Separate process Native extension Native extension
Async Support
Custom Functions
Custom Importers Limited
Source Maps
Active Development

License

MIT License

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass (uv run pytest)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Acknowledgments

This Python implementation was developed by Harvey McQueen with assistance from Amazon Q Command-line. It is:

This is an independent Python implementation and is not affiliated with or endorsed by the official Sass team.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_dart_sass-0.1.0.tar.gz (104.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_dart_sass-0.1.0-py3-none-any.whl (89.9 kB view details)

Uploaded Python 3

File details

Details for the file python_dart_sass-0.1.0.tar.gz.

File metadata

  • Download URL: python_dart_sass-0.1.0.tar.gz
  • Upload date:
  • Size: 104.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for python_dart_sass-0.1.0.tar.gz
Algorithm Hash digest
SHA256 481ac587887eb3097a9685d30e423f63d4b0109a007476a8e029f810e03027f4
MD5 0b4319bf782a4c01f93ccbd1ec849d7d
BLAKE2b-256 87d7da4e41114131d58b9df8f5be17e19ed182198442ad28063d0c55baa092f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_dart_sass-0.1.0.tar.gz:

Publisher: publish.yml on hmcqueen/python-dart-sass

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_dart_sass-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for python_dart_sass-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1c78d2aabf2948d2d4ed7de06b2f2d4ba10cf7910478acc8135c917c67f4bec6
MD5 0d0820b3ffcfeffd78f46cf825847aab
BLAKE2b-256 9b098d87f9ba8293538d4c49ebeeeeea11eb697d9d6ae2a6bfc95101b7762750

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_dart_sass-0.1.0-py3-none-any.whl:

Publisher: publish.yml on hmcqueen/python-dart-sass

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page