Skip to content

Latest commit

 

History

History
167 lines (118 loc) · 9.94 KB

File metadata and controls

167 lines (118 loc) · 9.94 KB

简中文档

logo

jsonc_sdict 即典

PyPI - Version

Round-trip comments for JSONC/HJSON, with dict-like APIs for config editing.

  • Keep comments on read and write (loads → edit → body / full).
  • Work with nested structures via sdictdeepmerge + deepdiff + benedict
  • Use weak-reference ordered containers via weakList/OrderedWeakSet.

Comments are data too, just like codes are data too by John von Neumann

Warning

This project is currently 0.2.x and still evolving.

Usage

Install

pip install jsonc-sdict

For local development:

pip install -e ".[dev]"

Quick usage

import hjson
from jsonc_sdict import jsoncDict, Within, NONE

raw = """
{
  "a": 1,
  "b": 2
}
""".strip()

jc = jsoncDict(raw, loads=hjson.loads, dumps=hjson.dumps)
jc["b"] = 3
jc[Within(NONE, "a")] = "// inserted before a"

print(jc.full)

Advanced usage

import hjson
from jsonc_sdict import jsoncDict, Within, NONE

raw = """
// header
{
  "a": 1, // inline
  "b": 2
}
// footer
""".strip()

jc = jsoncDict(raw, loads=hjson.loads, dumps=hjson.dumps)
jc[Within(NONE, "a")] = "// before a"
jc[Within("a")] = {
    Within("k", ":"): "/* key slot */",
    Within(":", "v"): "/* value slot */",
    Within("v", ","): "/* tail slot */",
}

print(jc.full)

Comment model

jsoncDict.comments stores comment positions with Within(...) keys.

  • Within(left, right) means a comment between two logical items.
  • Within(key) means comments attached to one pair's internal slots.
  • Slot comments use a dict with Within("k", ":"), Within(":", "v"), Within("v", ",").
  • Within(NONE, first_key) and Within(last_key, NONE) handle boundary comments.

Examples:

jc.comments[Within("a", "b")] = "// between a and b"
jc.comments[Within("b")] = {
    Within("k", ":"): "/* before colon */",
    Within(":", "v"): "/* before value */",
}

Edge cases

Invalid JSONC examples:

// /* this is still single-line comment
so this line is illegal */
/* // this is block comment */ trailing-text-is-illegal

Develop

env

LOG=DEBUG enables debug-level logging in project loggers.

Common setup:

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
LOG=DEBUG pytest -q

Internal design

jsonc

  • jsoncDict.loads() parses comments with tree-sitter and stores them into comments.
  • jsoncDict.body renders the current value with comments restored.
  • jsoncDict.full returns header + body + footer.
  • Within(key) comments are stored as slot maps, not raw strings, so key/colon/value/comma placement stays explicit.

sdict (common pitfalls)

  • sdict wraps both mapping and iterable nodes; nested access may return sdict views, not raw dict/list.
  • jsoncDict output depends on comment/data mutation paths; bypassing public APIs can leave internal state inconsistent.
  • dfs() warns against mutating yielded data during iteration.
  • insert(update, key=...|index=...) is ordering-oriented: it inserts by reordering keys after update.

weakList (common pitfalls)

  • Items must support both __hash__ and weak references (__weakref__); built-in int/str/list/dict do not qualify.
  • Weak references can disappear when no strong references exist; list length can shrink unexpectedly.
  • WeakList(noRepeat=True) is not identical to OrderedWeakSet: repeated append/insert can move item position.

Related projects

json loads()

pypi commits issues about lack
spyoungtech/json-five ⭐ 🕒 LAST🕒 🎯 🎯close Python JSON5 parser with round-trip preservation of comments can keep comment, but in AST-tree style with lots of re-defined concepts (e.g: BlockComment/wsc_before)
tusharsadhwani/json5kit ⭐ 🕒 LAST🕒 🎯 🎯close A Roundtrip parser and CST for JSON, JSONC and JSON5.
dpranke/pyjson5 ⭐ 🕒 LAST🕒 🎯 🎯close A Python implementation of the JSON5 data format
austinyu/ujson5 ⭐ 🕒 LAST🕒 🎯 🎯close A fast JSON5 encoder/decoder for Python
qvecs/qjson5 ⭐ 🕒 LAST🕒 🎯 🎯close 📎 A quick JSON5 implementation written in C, with Python bindings.

other format that support round-trip