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.0.tar.gz (22.2 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.0-py3-none-any.whl (26.9 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for hyprland_config-0.4.0.tar.gz
Algorithm Hash digest
SHA256 bff2fb9541415b7702317efcc6930138cd0420981e6eccaf615e0487acc4487a
MD5 6cb3cf03c4a0095ac49641a265ae20a0
BLAKE2b-256 8675f0bbe5fe79ccc05118e6c15fc16ae50a81ef5f5c20c2094c625ec4b5483a

See more details on using hashes here.

Provenance

The following attestation bundles were made for hyprland_config-0.4.0.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.0-py3-none-any.whl.

File metadata

File hashes

Hashes for hyprland_config-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f14864b63b6273bfa212b4ed6608d5326958a849f7b96f12ae826b90702e7c0e
MD5 c0ddd58fee0dd0a375ba78685b9dbafb
BLAKE2b-256 f8b59d2d41fbdc99c57cc19014713f576baffc4078691eba3f7c2687cb4d5b61

See more details on using hashes here.

Provenance

The following attestation bundles were made for hyprland_config-0.4.0-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