Skip to main content
zig-hocon

Read HOCON config files in Python, to the spec. Fast, because the parser is written in Zig. Familiar, because the API is pyhocon's.

HOCON is JSON made for people: comments, no quotes needed, units like 30s and 512MiB, files that include other files, later settings that merge over earlier ones, and values taken from other keys or from environment variables. One base file, one small file per environment on top.

# app.conf
db {
  host = localhost
  pool = 4
}
timeout = 30s
cache = 512MiB
retention = 3 months
debug = false
ratio = 0.75
include "prod.conf"
# prod.conf
db.host = db.example.org
db.pool = ${?DB_POOL}   # only when DB_POOL is set
from zig_hocon import ConfigFactory

conf = ConfigFactory.parse_file("app.conf")
conf.get_string("db.host")   # "db.example.org"
conf.get_int("db.pool")      # 4, or $DB_POOL when it is set

The rest of this page reads these two files.

Why zig-hocon

  • Correct. Passes 444 of the 445 tests in the conformance suite, one test for each sentence of the spec.*
  • Fast. Orders of magnitude faster than what Python usually has.**
  • Clear errors. File, line, column, and a caret under the mistake.
  • No dependencies. A thin ctypes layer over one bundled library.

* The other one is include url("x.conf"): the spec does not say what a URL without a scheme does, and lightbend/config#887 proposes the error zig-hocon gives.
** A 6 KB config in 1 ms, a 10 MB one in 0.3 s, on an Apple M4 Pro.

Install

pip install zig-hocon

One wheel per platform, for every Python from 3.10 on, free-threaded builds and PyPy included: macOS (arm64, x86_64), Linux (x86_64, aarch64; glibc 2.17+ and musl) and Windows (x86_64). The import name is zig_hocon. Anywhere else, build it from source (below).

Beta: the API can still change before 1.0. BSD-3-Clause.

Use

from zig_hocon import ConfigFactory, KEEP

conf = ConfigFactory.parse_file("app.conf")                       # DB_POOL from os.environ
conf = ConfigFactory.parse_file("app.conf", env={"DB_POOL": "8"}) # from a dict
conf = ConfigFactory.parse_file("app.conf", env=None)             # no variables: pool stays 4
conf = ConfigFactory.parse_file("app.conf", env=KEEP)             # ${?DB_POOL} kept, for later
conf = ConfigFactory.parse_file("app.conf", resolve=False)        # every ${…} as written
conf = ConfigFactory.parse_file("app.conf", http_timeout=5)       # an http(s) include waits 5 s, not 30
conf = ConfigFactory.parse_string('include "app.conf"', basedir=".")  # text, includes from basedir

conf.get_string("db.host")      # "db.example.org"
conf.get_int("db.pool")         # 4
conf.get_float("ratio")         # 0.75
conf.get_bool("debug")          # False: true/yes/on, false/no/off
conf.get_duration("timeout")    # timedelta(seconds=30)
conf.get_bytes("cache")         # 536870912: "512MB" counts by 1000, "512MiB" by 1024
conf.get_period("retention")    # {"years": 0, "months": 3, "days": 0}: a timedelta has no months
conf.get_config("db")           # the object at db, read the same way
conf.get_int("db.size", default=10)  # 10: nothing at db.size

conf.as_dict()                  # plain dicts and lists
conf.to_json()                  # bytes, not str: for msgspec.json.decode
conf.to_hocon()                 # HOCON text

A path is written as in the file: db.host, a."b.c". A getter's default is its second argument, as in pyhocon; parse_file and parse_string take everything after the path by keyword. The package is typed (py.typed): get_int("a") is an int, get_int("a", None) an int | None. One document can be read from many threads at once.

Errors are ConfigExceptions, with name, file, line, column and source_line:

exception when
ConfigParseException the text is not valid HOCON, or an include fails
ConfigSubstitutionException a ${…} with no value, or a cycle
ConfigMissingException (also a KeyError) nothing at the path, or null
ConfigWrongTypeException (also a ValueError) the value is not what the getter wants

One file to deploy. env=KEEP resolves includes, overrides and references between keys, and leaves the environment variables in; to_hocon() writes the result, to be read on the server with its own environment:

from pathlib import Path

Path("deploy.conf").write_text(ConfigFactory.parse_file("app.conf", env=KEEP).to_hocon())

Durations and sizes have helpers with the same rules as the getters: zig_hocon.parse_duration("30s") gives a timedelta, zig_hocon.parse_bytes("512MiB") an int.

From the shell, python -m zig_hocon [--json] [--env=process|none|keep] app.conf prints the file resolved.

Coming from pyhocon

Change the import: ConfigFactory, the getters and the exception names are the same. What reads differently:

  • The tree is not a dict. conf["db"]["host"] and json.dumps(conf) fail; take conf.as_dict() first. as_plain_ordered_dict() is as_dict().
  • Fewer methods. get, get_list, with_fallback and put are not there: a typed getter, an include, or the dict does the job.
  • Durations stay text. 30s is "30s" in as_dict() until a getter or a model reads it.
  • Strict syntax. Input pyhocon quietly accepted, such as a..b = 1 or a = b:c, is an error with its line and column.
  • No include package(...). It is pyhocon's own; include by path.

The migration guide has every difference, with what to write instead.

Into a model

The package hands over a dict (as_dict()) or JSON bytes (to_json()); a model library does the checking. The same app.conf into the three usual ones.

pydantic reads the dict. A duration is text in the file, "30s", so a BeforeValidator turns it into a timedelta first:

from datetime import timedelta
from typing import Annotated

import zig_hocon
from pydantic import BaseModel, BeforeValidator

Duration = Annotated[timedelta, BeforeValidator(zig_hocon.parse_duration)]

class Db(BaseModel):
    host: str
    pool: int

class Settings(BaseModel):
    db: Db
    timeout: Duration
    debug: bool

conf = zig_hocon.ConfigFactory.parse_file("app.conf")
settings = Settings.model_validate(conf.as_dict())

msgspec decodes the JSON bytes straight into a Struct. It reads a timedelta itself only from ISO 8601 ("PT30S"), so the field gets a type of its own that msgspec hands to dec_hook. An environment variable is always a string, so strict=False lets DB_POOL=16 into the int:

from datetime import timedelta

import msgspec
import zig_hocon

class Duration(timedelta):
    pass

def dec_hook(type, value):
    if type is Duration:
        d = zig_hocon.parse_duration(value)
        return Duration(d.days, d.seconds, d.microseconds)
    raise NotImplementedError(type)

class Db(msgspec.Struct):
    host: str
    pool: int

class Settings(msgspec.Struct):
    db: Db
    timeout: Duration
    debug: bool

conf = zig_hocon.ConfigFactory.parse_file("app.conf")
settings = msgspec.json.decode(conf.to_json(), type=Settings, dec_hook=dec_hook, strict=False)

@dataclass converts no types and does not build Db from a dict, so msgspec builds it, with the Duration, dec_hook and conf from above:

from dataclasses import dataclass

@dataclass
class Db:
    host: str
    pool: int

@dataclass
class Settings:
    db: Db
    timeout: Duration
    debug: bool

settings = msgspec.json.decode(conf.to_json(), type=Settings, dec_hook=dec_hook, strict=False)

All three give settings.db.host == "db.example.org" and settings.timeout == timedelta(seconds=30), and fail with the model's own error, path included, on a value of the wrong type. A size works the same way through zig_hocon.parse_bytes. The examples have these as tests, with layered environments, errors and one-file deploys.

Building from source

zig build python -Doptimize=ReleaseSafe   # libhocon into python/zig_hocon/
pip install python/
zig build test-python                     # the package's tests, with uv
zig build wheels                          # a wheel for every platform, into zig-out/wheels/

Needs Zig 0.16. Source, issues and the Zig and C libraries: github.com/lejmr/zig-hocon. BSD-3-Clause.

Metadata

Release files for zig-hocon 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for zig-hocon 0.5.1
File
zig_hocon-0.5.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
zig_hocon-0.5.1-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
zig_hocon-0.5.1-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
zig_hocon-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
zig_hocon-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
zig_hocon-0.5.1-py3-none-macosx_13_0_x86_64.whl Python 3 none macOS 13.0+ x86-64 Details
zig_hocon-0.5.1-py3-none-macosx_13_0_arm64.whl Python 3 none macOS 13.0+ ARM64 Details

Total release size: 3.7 MB

Release files / zig_hocon-0.5.1-py3-none-win_amd64.whl

Download URL zig_hocon-0.5.1-py3-none-win_amd64.whl
Size 588.2 kB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
47af9de41d8312dd71de9ac72a50659b0a642b942bf94b53b89d45668e78bbb7
BLAKE2b-256 checksum
How to use checksums
12c1c6cc12f678aa70270f8ccec766a408579cfab4f43de0c9fb8638259862d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / zig_hocon-0.5.1-py3-none-musllinux_1_2_x86_64.whl

Download URL zig_hocon-0.5.1-py3-none-musllinux_1_2_x86_64.whl
Size 539.2 kB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
8a09a73b9263baf58cf69234a8279b4fc4ab75418ee8c7c3a9dfa7644a87c06f
BLAKE2b-256 checksum
How to use checksums
5a975957c78d972309556594c9ed5aaa27720c4aba16391a86ace41a568352b1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / zig_hocon-0.5.1-py3-none-musllinux_1_2_aarch64.whl

Download URL zig_hocon-0.5.1-py3-none-musllinux_1_2_aarch64.whl
Size 522.5 kB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
d351522583b89ba9610e5df54a0f74e2c35d58969af31c1ab4050a106cd04fe0
BLAKE2b-256 checksum
How to use checksums
f94e9f9016dbabbccce7adb0755ec5ca1fc4e8111e67a130c06dd28933153a0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / zig_hocon-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL zig_hocon-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 541.0 kB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
744a622f38c5a9fac34dd8cb8f05c0bc8e5192ffc58b12ec9b905a527af3d719
BLAKE2b-256 checksum
How to use checksums
516ea906fc47196d42be8439ec91e752a21ccc370b277b46ee0950de655b4477
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / zig_hocon-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL zig_hocon-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 523.5 kB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
e71dde4fa4581a3d0879772e2c42403fe71fea7e79edd7538b35d9d648f2e26e
BLAKE2b-256 checksum
How to use checksums
73e7d76d17910df2a8f6dd679268c6a53da6c5b3c1d826a833fdacbd19a0c801
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / zig_hocon-0.5.1-py3-none-macosx_13_0_x86_64.whl

Download URL zig_hocon-0.5.1-py3-none-macosx_13_0_x86_64.whl
Size 518.4 kB
Tags Python 3 macOS 13.0+ x86-64
SHA-256 checksum
How to use checksums
f32c27cba0cbefb2886ce6e51eea84a2850467c7558052327c5e7f81fc5bc8f5
BLAKE2b-256 checksum
How to use checksums
a7732adbfc4e57e82e3f6d529b2aa3e2a3ab7f761d1872004b0a60f224db399d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / zig_hocon-0.5.1-py3-none-macosx_13_0_arm64.whl

Download URL zig_hocon-0.5.1-py3-none-macosx_13_0_arm64.whl
Size 474.3 kB
Tags Python 3 macOS 13.0+ ARM64
SHA-256 checksum
How to use checksums
078f5557db3003fe0e04503986866d18f0f05885c213924842b0e94e01e02017
BLAKE2b-256 checksum
How to use checksums
10f79348fc2739d48d192247747ce2dab117821ef203266e63212825feb8fc04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for zig-hocon 0.5.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 7 files in this release have been uploaded with provenance metadata.

Select from 7 files

Attestation for zig_hocon-0.5.1-py3-none-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page