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.2.0.tar.gz (22.0 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.2.0-py3-none-any.whl (26.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hyprland_config-0.2.0.tar.gz
  • Upload date:
  • Size: 22.0 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.2.0.tar.gz
Algorithm Hash digest
SHA256 b925921e0a683f734d7c0f6c1de88a42737bd7842dd7d22325f782de7aacef04
MD5 266c46b69ba488d4ba2fd6a4aeff9fda
BLAKE2b-256 bff7cc282968b86685804d914bad99ecb21d28c26b0a619b80e01b25a07fc022

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for hyprland_config-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a82571242a0521f5606e3867c57186c4b759262fb30b919a322ae02187113d8
MD5 c20388ca9c692ebfeb994a74dedbf574
BLAKE2b-256 1405eb4c62c7e69f7b406cd8249d8e2bc838779d3b7e2ef943d92f44e7351264

See more details on using hashes here.

Provenance

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