Metadata-Version: 2.4
Name: configuration-file
Version: 2.1.2
Summary: A Python implementation of the Configuration File (CF) format - a human-readable, machine-parseable data serialization format
Project-URL: Homepage, https://codeberg.org/configuration-file/py
Project-URL: Documentation, https://codeberg.org/configuration-file/py#readme
Project-URL: Repository, https://codeberg.org/configuration-file/py.git
Project-URL: Issues, https://codeberg.org/configuration-file/py/issues
Project-URL: Specification, https://codeberg.org/configuration-file/spec
Author: Dejan Lekic
License-Expression: BSD-3-Clause
License-File: LICENSE
Keywords: cf,config,configuration,configuration-file,data-format,human-readable,parser,serialization,settings
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
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: Topic :: File Formats
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Text Processing :: Markup
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# configuration-file

A Python implementation of the [Configuration File (CF) format](https://codeberg.org/configuration-file/spec) - a human-readable, machine-parseable data serialization format for configuration files.

[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
[![PyPI](https://img.shields.io/pypi/v/configuration-file.svg)](https://pypi.org/project/configuration-file/)

## What is CF?

CF (Configuration File) is a modern configuration format designed to address common shortcomings of existing formats like JSON, YAML, TOML, and INI:

- **Human-readable**: Clean, minimal syntax with comments
- **Machine-parseable**: Simple, unambiguous LL(1) grammar
- **Whitespace-independent**: Structure uses explicit delimiters (`{ }` and `[ ]`), not indentation
- **Copy-paste safe**: Configuration survives email, chat, wikis, and forums
- **Type-safe**: Explicit typing with no implicit conversions
- **Extensible**: Includes for configuration reuse across files

See the [official CF Specification](https://codeberg.org/configuration-file/spec) for complete details, or the [local copy](doc/CF-SPEC-1.0.md) included in this repository.

## Installation

```bash
pip install configuration-file
```

For development:

```bash
pip install configuration-file[dev]
```

## Usage

### Simple API (dict-based)

```python
import cf

# Parse a CF file
config = cf.load("config.cf")

# Parse a CF string
config = cf.loads('''
    app_name = "my-service"
    debug = true
''')

# Access values
print(config["app_name"])  # "my-service"
print(config["server"]["port"])  # 8080

# Serialize to CF format
cf_string = cf.dumps(config)

# Write to a file
cf.dump(config, "output.cf")
```

### Roundtrip API (preserves formatting)

```python
import cf

# Parse into a CFDocument that preserves comments, whitespace,
# quote styles, and separator choices
doc = cf.loads_document('''
    # Server configuration
    server {
        host = "localhost"
        port = 8080
    }
''')

# Read values
host = doc.get("server.host")  # "localhost"

# Modify values (only changed values are re-rendered)
doc.set("server.port", 9090)

# Serialize back with minimal diffs
output = doc.serialize()
# Comments, whitespace, and formatting are preserved

# Convert to plain dict when formatting isn't needed
config = doc.to_dict()
```

## Features

- **Full CF 1.0 specification support**
- **Comments**: Hash (`#`), double-slash (`//`), and block (`/* */`) comments
- **Data types**: Strings, integers, floats, booleans, null, dates, times, datetimes
- **Collections**: Objects and arrays with flexible syntax
- **String variants**: Double-quoted, single-quoted, triple-quoted, and raw strings
- **Environment variable substitution**: `${VAR}`, `${VAR:-default}`, `${VAR:?error}`
- **File includes**: `include "path/to/file.cf"`
- **Roundtrip preservation**: Parse, modify, and serialize with minimal diffs

## Development

### Setup

1. Clone the repository:

```bash
git clone https://codeberg.org/configuration-file/py.git
cd py
```

2. Create a virtual environment and install dependencies:

```bash
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```

### Running Tests

```bash
pytest
```

With coverage:

```bash
pytest --cov=cf --cov-report=html
```

### Code Quality

Lint and format code:

```bash
ruff check .
ruff format .
```

Type checking:

```bash
mypy src/cf
```

## Full Example

CF files use the `.cf` extension. Here is a comprehensive example demonstrating all features of the CF format:

```cf
# =============================================================================
# Complete CF 1.0 Feature Demonstration
# =============================================================================

# -----------------------------------------------------------------------------
# Comments
# -----------------------------------------------------------------------------
# Hash comments (like this one)
// Double-slash comments (C++ style)
/* Block comments
   can span multiple
   lines */

# -----------------------------------------------------------------------------
# String Types
# -----------------------------------------------------------------------------
strings {
    double_quoted = "Hello, World!"
    with_escapes = "Line 1\nLine 2\tTabbed"
    with_unicode = "Caf\u00E9 \U0001F600"
    with_quotes = "She said \"Hello\""

    single_quoted = 'It\'s working'
    contains_double = 'He said "hello"'

    # Triple-quoted strings (no escape processing, preserves whitespace)
    multiline = """
        SELECT id, name, email
        FROM users
        WHERE active = true
        ORDER BY created_at DESC
    """

    # Raw strings (no escape processing)
    raw_double = r"C:\Program Files\App\config.ini"
    raw_single = r'^\d{3}-\d{4}$'
    regex_pattern = r"https?://[\w\-\.]+\.[a-z]{2,}"

    empty_double = ""
    empty_single = ''
}

# -----------------------------------------------------------------------------
# Number Types
# -----------------------------------------------------------------------------
numbers {
    decimal = 42
    negative = -17
    zero = 0
    large = 9223372036854775807
    with_separators = 1_000_000_000

    hexadecimal = 0xFF
    octal = 0o755
    binary = 0b10101010

    simple_float = 3.14159
    negative_float = -273.15
    scientific = 6.022e23
    float_with_sep = 1_234.567_890

    positive_infinity = inf
    negative_infinity = -inf
    not_a_number = nan
}

# -----------------------------------------------------------------------------
# Boolean and Null Types
# -----------------------------------------------------------------------------
primitives {
    enabled = true
    disabled = false
    optional_value = null
}

# -----------------------------------------------------------------------------
# Date and Time Types (ISO 8601)
# -----------------------------------------------------------------------------
temporal {
    release_date = 2024-06-15
    meeting_time = 14:30:00
    precise_time = 09:15:30.123456

    local_datetime = 2024-06-15T10:30:00
    utc_datetime = 2024-06-15T10:30:00Z
    positive_offset = 2024-06-15T10:30:00+05:30
    negative_offset = 2024-06-15T10:30:00-08:00
    with_microseconds = 2024-06-15T10:30:00.123456Z
}

# -----------------------------------------------------------------------------
# Objects
# -----------------------------------------------------------------------------
# HCL-style (without =)
server {
    host = "localhost"
    port = 8080
}

# JSON-style (with =)
database = {
    driver = "postgres"
    host = "db.example.com"
}

# Inline with semicolons or commas
point = { x = 10; y = 20; z = 30 }
color = { r = 255, g = 128, b = 64 }

# Empty object
empty_config = {}

# Deeply nested
deeply_nested {
    level1 {
        level2 {
            level3 {
                value = "deep"
            }
        }
    }
}

# -----------------------------------------------------------------------------
# Arrays
# -----------------------------------------------------------------------------
arrays {
    numbers = [1, 2, 3, 4, 5]
    strings = ["apple", "banana", "cherry"]

    # Multi-line with newline separators
    fruits = [
        "apple"
        "banana"
        "cherry"
        "date"
    ]

    # Semicolon separators
    ports = [8080; 8443; 9000]

    # Heterogeneous arrays (mixed types)
    mixed_types = [42, "hello", true, null, 3.14, 2024-06-15]

    # Nested arrays (matrix)
    matrix = [
        [1, 2, 3]
        [4, 5, 6]
        [7, 8, 9]
    ]

    # Array of objects
    users = [
        { name = "Alice"; role = "admin"; active = true }
        { name = "Bob"; role = "user"; active = true }
        { name = "Charlie"; role = "user"; active = false }
    ]

    empty = []
}

# -----------------------------------------------------------------------------
# Identifiers (Keys)
# -----------------------------------------------------------------------------
identifiers {
    simple = "value"
    with_underscore = "value"
    _private = "value"
    my-key = "value"

    # Unicode identifiers
    café = "coffee"
    日本語 = "Japanese"

    # Quoted keys for special characters
    "123numeric" = "starts with digit"
    "hello world" = "contains space"
    "key=value" = "contains special char"
}

# -----------------------------------------------------------------------------
# Environment Variable Substitution
# -----------------------------------------------------------------------------
environment {
    home_dir = ${HOME}
    server_host = ${SERVER_HOST:-localhost}
    server_port = ${SERVER_PORT:-8080}

    # String interpolation
    database_url = "postgresql://${DB_USER:-postgres}:${DB_PASS:-secret}@${DB_HOST:-localhost}:${DB_PORT:-5432}/${DB_NAME:-myapp}"
    log_path = "${LOG_DIR:-/var/log}/app.log"

    # Escaped (literal ${})
    literal_syntax = "Use \${VAR} for variable syntax"
}

# -----------------------------------------------------------------------------
# Real-World Example
# -----------------------------------------------------------------------------
application {
    name = "MyApplication"
    version = "2.0.0"

    server {
        host = "0.0.0.0"
        port = 8080
        workers = 4

        ssl {
            enabled = true
            cert = "/etc/ssl/certs/app.crt"
            key = "/etc/ssl/private/app.key"
            protocols = ["TLSv1.2", "TLSv1.3"]
        }

        timeouts { connect = 5; read = 30; write = 10 }
    }

    database {
        primary {
            driver = "postgres"
            host = ${DB_HOST:-db.example.com}
            port = 5432
            pool = { min = 5, max = 20, idle_timeout = 300 }
        }
    }

    features = ["auth", "api", "webhooks", "analytics"]

    cron_jobs = [
        { name = "cleanup"; schedule = "0 2 * * *"; command = "cleanup.sh" }
        { name = "backup"; schedule = "0 3 * * *"; command = "backup.sh" }
    ]

    metadata {
        created = 2024-01-15T10:30:00Z
        updated = 2024-06-15T14:45:00Z
        maintainer = "devops@example.com"
    }
}
```

## License

This project is licensed under the BSD-3-Clause License - see the [LICENSE](LICENSE) file for details.

Copyright (c) 2026 Dejan Lekić.
