Skip to main content

NEON for Python

PyPI Tests License

 

✅ A NEON parser that keeps every comment and space
✅ Edit a configuration file and the diff is only your change
✅ neon-lint for editors, CI and AI agents
✅ No dependencies, fully typed, Python 3.13+

 

NEON parser, encoder and lossless editor for Python. NEON is the human-friendly configuration format of the Nette framework, a YAML alternative with entities, and the format of phpstan.neon. This library parses it into a concrete syntax tree in which every character of the file has its place, so printing the tree gives the file back byte for byte:

doc = parse(text)
doc.print() == text   # True, for any input NEON accepts

That is what makes editing safe. Change a value, add a key, remove an item, and the rest of the file stays as its author wrote it:

doc.set_value(["database", "host"], "db.example.com")
- 	host: localhost   # the server
+ 	host: db.example.com   # the server

On that you build scripts that bump a setting in every phpstan.neon of a monorepo, installers that register an extension in config.neon, migrations that rename a service, and tools that read the configuration of a PHP project without running PHP.

The library comes from David Grudl, the author of Nette, Latte and Tracy. NEON has been the configuration format of Nette since 2011, read by every Nette application, and this implementation follows the same specification as nette/neon for PHP and @nette/neon for JavaScript.

 

NEON in one minute

# mappings and sequences, indented by tabs or spaces
database:
	host: localhost
	port: 3306

roles: [guest, member, admin]   # inline arrays

# entities: a value with attributes, the way Nette writes services
mailer: App\Mailer(@smtp, timeout: 30)

note: '''
	multiline strings
	without escaping
'''

JSON is a subset of NEON, except for a colon at the start of a line glued to its value ("a" + new line + :1), which NEON reads as a literal. The format is described at ne-on.org and in the manual.

▶ Runnable examples: examples/edge-cases

 

Format-preserving editing

Most YAML libraries for Python, like PyYAML, drop comments and formatting when they write a file. Here keeping them is an invariant, not an effort: every edit changes the tokens it edits, and the four guarantees of the specification (the file reads back as the same tree, with the expected value, with the change only where it was made) are checked after every operation of a large corpus of edits.

from neon import CommentPolicy, parse

doc = parse(text)
doc.set_value(["database", "port"], 3307)          # 0x0CEA stays hexadecimal: 0x0CEB
doc.set_value(["database", "debug"], False)        # yes becomes no, not false
doc.set_value(["timeout"], 30)                     # the same value: no change at all
doc.set_value(["parameters", "admin"], "a@b.cz")   # a new key, indented like its siblings
doc.add_item(["services"], "App\\OrderFacade")     # a new item of a list
doc.remove_item(["database", "driver"])            # with the comment that describes it
doc.remove_item(["mail"], comments=CommentPolicy.MOVE_TO_NEXT_TOKEN)

A missing key in the middle of a path is an error, so a typo does not create a new section. Removing the last item of a collection leaves it empty (services: []), because in a configuration empty and missing often mean different things; an item marked nullable loses its value instead. A refused edit changes nothing at all.

▶ Runnable examples: examples/editing, examples/config-editor

 

Installation

uv add nette-neon        # or: pip install nette-neon

The package is imported as neon. Python 3.13 or newer, fully typed (py.typed), no dependencies.

 

The tree: nodes, slots, tokens, trivia

item = doc.find_item(["database", "user"])
item.get_value()                    # 'root'
item.get_trailing_comments()        # the comment on its line
item.value.get_first_token().text   # the text exactly as written
item.get_position()                 # Position(line, column, offset), from the current tree

Eight node classes (DocumentNode, BlockArrayNode, InlineArrayNode, ArrayItemNode, EntityNode, EntityChainNode, StringNode, LiteralNode) with named slots, tokens with their trivia (whitespace, line endings, comments), find(), find_ancestor(), siblings, and Traverser for a walk with replacements.

▶ Runnable examples: examples/tree

 

Decoding and encoding

from neon import decode, decode_file, encode

decode("a: [1, 2]")                  # {'a': [1, 2]}
decode_file("phpstan.neon")          # the value of a file
encode({"a": [1, 2]})                # '{a: [1, 2]}'
encode({"a": [1, 2]}, True)          # 'a:\n\t- 1\n\t- 2\n\n'
encode({"a": [1, 2]}, lambda _, path: not path)        # 'a: [1, 2]\n', one level of blocks
encode(config, lambda value, _: not isinstance(value, list))   # lists on one line
NEON Python
null, true, yes ... None, bool
integer int, of any size
float float
string str
date Date (components and offset as written; to_datetime() for a datetime)
entity, chain Entity
list list
map dict with string keys, in the order of the file

The mapping is one-to-one: 1.0 stays a float, a big integer stays exact, {} is a dict and [] a list, and a key keeps its place. encode() takes datetime and date as well. decode() is typed as json.loads() is, Any; to_value() of the tree returns the exact union neon.Value.

▶ Runnable examples: examples/parsing, examples/encoding

 

One format, one specification

The format, the value model, the tree and the semantics of editing are written down in a language-neutral specification with a conformance corpus, and every implementation runs that corpus in its tests. The values and the error messages were checked against nette/neon for PHP over 30,000 real-world files and 100,000 generated ones. The corpus is public; if you write an implementation of NEON for another language, use it.

 

For editors and AI agents

neon-lint config/
config/local.neon:3:2: Duplicated key 'user'

One line per finding, the exit code as the verdict, --json for tools; the same command, output and exit codes as neon-lint of the other implementations. Without installing anything: uvx --from nette-neon neon-lint config/.

For AI agents, run it after every edit of a .neon file and hand a finding back as an error, so the agent fixes the file before it goes on. See docs/skills/agents.md.

▶ Runnable examples: examples/linting

 

Limits

  • No formatter: the library keeps your formatting, it does not impose one.
  • No schema or validation of values.
  • No error recovery: an invalid file is reported, not partially parsed.
  • Positions are computed on request, from the current tree.

 

Documentation and credits

The examples are the documentation of the API, each chapter with the real output of its programs. The format itself is documented in the Nette manual.

Released under the MIT license.

Metadata

Release files for nette-neon 1.0.0

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

Source distribution (sdist)

Source distribution for nette-neon 1.0.0
File Size Uploaded Provenance Actions
nette_neon-1.0.0.tar.gz 90.0 kB Without provenance Details for nette_neon-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for nette-neon 1.0.0
File Interpreter ABI Platform Actions
nette_neon-1.0.0-py3-none-any.whl Python 3 none any Details for nette_neon-1.0.0-py3-none-any.whl

Total release size: 147.1 kB

Release files / nette_neon-1.0.0.tar.gz

Download URL nette_neon-1.0.0.tar.gz
Size 90.0 kB
Tags Source
SHA-256 checksum
How to use checksums
51fd628fbcded370347ade9bc8c7642d4936c620b9be1b5a4cd2180f611bc4d7
BLAKE2b-256 checksum
How to use checksums
b03dcced2b486b125ecb994e2a8adbd3900020dcdb6f8e8345f2ddb91c6c75c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / nette_neon-1.0.0-py3-none-any.whl

Download URL nette_neon-1.0.0-py3-none-any.whl
Size 57.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
620bf34d1edb4c6685be8564c495b5364577ac45fb398de1a18633dbb57f280d
BLAKE2b-256 checksum
How to use checksums
e18cbe9546914771a9dae73534cf47f054ce08ef782c49ae2077c3d4a7c57a82
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for nette-neon 1.0.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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