Skip to main content

Round-trip parser and editor for Hyprland configuration files

Project description

hyprland-config

Round-trip parser and editor for Hyprland configuration files.

Quick start

from hyprland_config import load

config = load()
config.set("general:gaps_in", 20)
config.save()

That's it. load() reads ~/.config/hypr/hyprland.conf, follows all source directives, and builds a navigable document tree. set() finds the option in whichever sourced file defines it and updates it in place. save() writes only the files that were actually modified.

Installation

pip install hyprland-config

Requires Python 3.12+. Zero runtime dependencies.

Why this library

This is a round-trip parser. It keeps comments, blank lines, variable definitions, and formatting intact — editing one option doesn't rewrite the rest of the file.

It follows source directives across multiple files, resolves globs (including absolute paths for NixOS/home-manager setups), detects cycles, and only writes back files that actually changed. Writes are atomic (temp file + fsync + rename) so a crash mid-save won't corrupt your config.

300+ tests, including property-based and fuzz testing with Hypothesis.

Usage

Edit config options

from hyprland_config import load

config = load()

# Update existing options (finds them across all sourced files)
config.set("general:gaps_in", 10)
config.set("decoration:rounding", 8)
config.set("decoration:blur:enabled", True)

# Remove an option
config.remove("misc:vfr")

# Add a keybind (appends after existing binds)
config.append("bind", "SUPER, T, exec, kitty")

# Remove a specific keybind
config.remove_where("bind", lambda v: "killactive" in v)

# Remove an animation by name
config.remove_where("animation", lambda v: v.startswith("windows,"))

# Check which files have pending changes
config.dirty_files()
# [PosixPath('/home/user/.config/hypr/hyprland.conf.d/02_general.conf'),
#  PosixPath('/home/user/.config/hypr/hyprland.conf.d/03_decoration.conf')]

# Save only the files that changed
config.save()

Read config as a flat dict

from hyprland_config import parse_to_dict

options = parse_to_dict("~/.config/hypr/hyprland.conf")

# Unique keys are strings
print(options["general:gaps_in"])  # "5"

# Repeated keys become lists
print(options["bind"])  # ["SUPER, Q, killactive,", "SUPER, Return, exec, kitty", ...]

Read option values

from hyprland_config import load

config = load()

# Get a value (returns string or None)
gaps = config.get("general:gaps_in")           # "5"
missing = config.get("nonexistent", "default") # "default"

# Get all values for a repeated key
all_binds = config.get_all("bind")  # ["SUPER, Q, killactive,", ...]

# Get the full node for more details
node = config.find("general:gaps_in")
print(f"{node.full_key} = {node.value} (line {node.lineno})")

# Find all binds as nodes
binds = config.find_all("bind")

# Expand variables
print(config.expand("$mainMod + Q"))  # "SUPER + Q"

# Navigate sourced files
from hyprland_config import Source
for line in config.lines:
    if isinstance(line, Source):
        for sub_doc in line.documents:
            print(f"{sub_doc.path.name}: {len(sub_doc.lines)} lines")

Parse from a string

from hyprland_config import parse_string

doc = parse_string("""
general {
    gaps_in = 5
    gaps_out = 10
}
bind = SUPER, Q, killactive,
""")

print(doc.get("general:gaps_in"))  # "5"

Lenient mode

By default, the parser raises ParseError on malformed input. In lenient mode, unparseable lines are preserved as error nodes instead, so you can work with partially valid configs:

config = load(lenient=True)

# Inspect any lines that couldn't be parsed
for err in config.errors:
    print(f"{err.source_name}:{err.lineno}: {err.raw}")

Check for deprecations

Track Hyprland deprecations across versions and apply automatic migrations:

from hyprland_config import load, check_deprecated, migrate

config = load()

# Check for deprecated options (covers v0.33–v0.53+)
warnings = check_deprecated(config)
for w in warnings:
    print(f"{w.key}: {w.message} (deprecated in v{w.version_deprecated})")

# Auto-migrate what can be migrated
result = migrate(config)
print(f"Applied {len(result.applied)} migrations")
config.save()

Features

  • Nested category { } blocks, including device[name] { }
  • Inline category syntax (general:gaps_in = 5)
  • One-line blocks (general { gaps_in = 5 })
  • source = path following with glob and ~ expansion, cycle detection
  • $variable definitions and expansion
  • Expression evaluation ({{2 + 2}}) with \{{ escape support
  • Conditional directives (# hyprlang if/elif/else/endif) and # hyprlang noerror
  • Comments, inline comments, ## escape, blank lines
  • Special keywords: bind (all flag variants), monitor, animation, bezier, env, exec, workspace, windowrule, and more
  • Comment-preserving round-trip editing
  • Lenient parsing mode for malformed or partial configs
  • Deprecation checking and automatic migration (v0.33–v0.53+)
  • Section listing and iteration
  • Dirty tracking — only modified files are written to disk
  • Atomic writes (temp file + fsync + rename)
  • ParseError with file name and line number on malformed input
  • Fully typed with py.typed marker

License

MIT

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

hyprland_config-0.4.4.tar.gz (25.7 kB view details)

Uploaded Source

Built Distribution

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

hyprland_config-0.4.4-py3-none-any.whl (30.4 kB view details)

Uploaded Python 3

File details

Details for the file hyprland_config-0.4.4.tar.gz.

File metadata

  • Download URL: hyprland_config-0.4.4.tar.gz
  • Upload date:
  • Size: 25.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hyprland_config-0.4.4.tar.gz
Algorithm Hash digest
SHA256 c1b6e8083fdee4144a9ddc604c4c9509146933b89a44f651043aee3b13d8aafc
MD5 6632087a82168bab98b882fc114a5fd1
BLAKE2b-256 169620e0844b5cd0b6fc70e4cf8c6ae2d45e6554b1a59f845e0ce8a1182266c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for hyprland_config-0.4.4.tar.gz:

Publisher: publish.yml on BlueManCZ/hyprland-config

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

File details

Details for the file hyprland_config-0.4.4-py3-none-any.whl.

File metadata

  • Download URL: hyprland_config-0.4.4-py3-none-any.whl
  • Upload date:
  • Size: 30.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hyprland_config-0.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 2256c5f09a7fd2c2f43ac0d806b153758b300f5f895aa59ddd267aac3fa60fda
MD5 8805af3a43e38738bd72b2e24d03e201
BLAKE2b-256 e5bb09091dbaa7ac2155d5c611ea8e68979312890762a2c32fbcb9cae80477c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for hyprland_config-0.4.4-py3-none-any.whl:

Publisher: publish.yml on BlueManCZ/hyprland-config

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