Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ YAML 1.2 is the default, so `yes`/`no`/`on`/`off` are plain strings. Opt into th
1.1 schema when you need its booleans and octals:

```python
yamlrocks.loads(b"enabled: yes") # {'enabled': 'yes'}
yamlrocks.loads(b"enabled: yes") # {'enabled': 'yes'}
yamlrocks.loads(b"enabled: yes", option=yamlrocks.OPT_YAML_1_1) # {'enabled': True}
```

Expand All @@ -122,8 +122,8 @@ ease off the legacy spellings without a manual conversion step.
```python
doc = yamlrocks.loads(content, option=yamlrocks.OPT_ROUND_TRIP)

doc["server"]["host"] = "example.com" # deep edits write through to the AST
print(doc.to_yaml().decode()) # comments and formatting preserved
doc["server"]["host"] = "example.com" # deep edits write through to the AST
print(doc.to_yaml().decode()) # comments and formatting preserved
```

An unmodified document re-emits byte-for-byte identical; only the nodes you touch
Expand All @@ -138,7 +138,7 @@ doc = yamlrocks.loads(
include_dir="/config",
)

doc["automation"][0]["trigger"] = "state" # edit a value from an included file
doc["automation"][0]["trigger"] = "state" # edit a value from an included file

yamlrocks.dump_includes(doc, include_dir="/config")
# Only the modified included file is rewritten; the root config is untouched.
Expand All @@ -151,8 +151,8 @@ Supported tags: `!include`, `!include_dir_named`, `!include_dir_list`,

```python
data = yamlrocks.loads(content, option=yamlrocks.OPT_ANNOTATED)
data.__line__ # 1
data["server"].__line__ # 3
data.__line__ # 1
data["server"].__line__ # 3
```

`YAMLRocksAnnotatedDict`/`YAMLRocksAnnotatedList`/`YAMLRocksAnnotatedStr` subclass `dict`/`list`/`str`, so
Expand Down
20 changes: 11 additions & 9 deletions docs/src/content/docs/comparisons/vs-pyyaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ yourself with a `tag_handler` or `OPT_PASSTHROUGH_TAG`:
import yamlrocks

# A tag never executes anything. The value is just the scalar underneath.
yamlrocks.loads(b"value: !something 42") # {'value': '42'}
yamlrocks.loads(b"value: !something 42") # {'value': '42'}

# Opt in to interpret a tag, on your terms.
yamlrocks.loads(
b"value: !double 5",
tag_handler=lambda tag, value: int(value) * 2 if tag == "!double" else value,
) # {'value': 10}
) # {'value': 10}
```

There is no `yamlrocks.load` that behaves like `yaml.load`. The safe behavior is
Expand All @@ -65,8 +65,8 @@ defaults to YAML 1.2, where those are plain strings:
```python
import yamlrocks

yamlrocks.loads(b"country: NO") # {'country': 'NO'}
yamlrocks.loads(b"enabled: yes") # {'enabled': 'yes'}
yamlrocks.loads(b"country: NO") # {'country': 'NO'}
yamlrocks.loads(b"enabled: yes") # {'enabled': 'yes'}
```

If you need the old behavior for a specific document, opt in with
Expand All @@ -76,7 +76,7 @@ legacy 1.1 files to canonical 1.2:
```python
import yamlrocks

yamlrocks.loads(b"enabled: yes", option=yamlrocks.OPT_YAML_1_1) # {'enabled': True}
yamlrocks.loads(b"enabled: yes", option=yamlrocks.OPT_YAML_1_1) # {'enabled': True}
yamlrocks.upgrade(b"enabled: yes\nmode: on\n")
# b'%YAML 1.2\n---\nenabled: true\nmode: true\n'
```
Expand All @@ -91,8 +91,10 @@ an explicit opt-in that never touches other 1.1 forms:
import datetime
import yamlrocks

yamlrocks.loads(b"at: 13:30:45") # {'at': '13:30:45'}
yamlrocks.loads(b"on: 2024-01-15", option=yamlrocks.OPT_TIMESTAMPS) # {'on': datetime.date(2024, 1, 15)}
yamlrocks.loads(b"at: 13:30:45") # {'at': '13:30:45'}
yamlrocks.loads(
b"on: 2024-01-15", option=yamlrocks.OPT_TIMESTAMPS
) # {'on': datetime.date(2024, 1, 15)}
```

See [YAML 1.1 vs 1.2](/guides/yaml-11-vs-12/) for the full list of differences,
Expand Down Expand Up @@ -209,8 +211,8 @@ near drop-in switch:
```python
import yamlrocks.compat as yaml

yaml.safe_load(b"a: 1") # {'a': 1}
yaml.safe_dump({"a": 1}) # 'a: 1\n' (a str, matching PyYAML)
yaml.safe_load(b"a: 1") # {'a': 1}
yaml.safe_dump({"a": 1}) # 'a: 1\n' (a str, matching PyYAML)
```

`safe_load`, `safe_load_all`, `safe_dump`, and `safe_dump_all` map straight
Expand Down
18 changes: 10 additions & 8 deletions docs/src/content/docs/comparisons/vs-ruamel.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@ The editing surface is a `YAMLRocksDocument` with `dict`/`list`-style access plu
```python
import yamlrocks

doc = yamlrocks.loads(b"server:\n host: localhost\n port: 8080\n", option=yamlrocks.OPT_ROUND_TRIP)

doc.keys() # ['server']
doc["server"]["port"] = 9090 # nested edit writes through
doc.to_dict() # {'server': {'host': 'localhost', 'port': 9090}}
doc.walk() # [(('server', 'host'), 'localhost'), (('server', 'port'), 9090)]
doc = yamlrocks.loads(
b"server:\n host: localhost\n port: 8080\n", option=yamlrocks.OPT_ROUND_TRIP
)

doc.keys() # ['server']
doc["server"]["port"] = 9090 # nested edit writes through
doc.to_dict() # {'server': {'host': 'localhost', 'port': 9090}}
doc.walk() # [(('server', 'host'), 'localhost'), (('server', 'port'), 9090)]
```

See [round-trip editing](/guides/round-trip/) and the
Expand Down Expand Up @@ -135,9 +137,9 @@ import yamlrocks

doc = yamlrocks.loads(b"name: app\nport: 8080\n", option=yamlrocks.OPT_ROUND_TRIP)

doc.node["port"].comment = "the listen port" # inline, no '#'
doc.node["port"].comment = "the listen port" # inline, no '#'
doc.node["name"].comment_before = "service identity" # standalone line above
doc.node.comment_after = "end of config" # trailing block (foot)
doc.node.comment_after = "end of config" # trailing block (foot)
doc.to_yaml()
# b'# service identity\nname: app\nport: 8080 # the listen port\n# end of config\n'
```
Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/comparisons/vs-yamlium.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ targets, and its scalar resolution lands between the two in surprising ways:
```python
import yamlrocks

yamlrocks.loads(b"mode: 0777") # {'mode': '0777'} a string, per YAML 1.2
yamlrocks.loads(b"mask: 0o17") # {'mask': 15} the 1.2 octal
yamlrocks.loads(b"mode: 0777") # {'mode': '0777'} a string, per YAML 1.2
yamlrocks.loads(b"mask: 0o17") # {'mask': 15} the 1.2 octal
# yamlium returns {'mode': 777} and {'mask': '0o17'}.
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ ref: *a
"""

data = yamlrocks.loads(source, option=yamlrocks.OPT_ANNOTATED)
data["base"] is data["ref"] # True, the same object (as in PyYAML)
data["base"] is data["ref"] # True, the same object (as in PyYAML)
data["base"]["k"] = 99
data["ref"]["k"] # 99, seen through the shared reference
data["ref"]["k"] # 99, seen through the shared reference
```

The plain fast path (`loads` with no options, which is what the `compat` shim
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ illustrative (it needs ruamel installed), while the YAMLRocks block runs as writ
```python
# ruamel.yaml
from ruamel.yaml import YAML

yaml = YAML(typ="safe")
data = yaml.load("name: app\nport: 8080")
```
Expand All @@ -58,13 +59,15 @@ for text), or writes to a path or stream through `yamlrocks.dump`:
```python
# ruamel.yaml
import sys

yaml.dump(data, sys.stdout)
```

```python
# yamlrocks
import sys
import yamlrocks

sys.stdout.write(yamlrocks.dumps({"name": "app", "port": 8080}).decode())
# name: app
# port: 8080
Expand All @@ -83,7 +86,8 @@ into it, assign, and re-emit.
# ruamel.yaml
from ruamel.yaml import YAML
import sys
yaml = YAML() # typ="rt" is the default

yaml = YAML() # typ="rt" is the default
doc = yaml.load("# config\nname: app # service\nport: 8080\n")
doc["port"] = 9090
yaml.dump(doc, sys.stdout)
Expand Down
66 changes: 33 additions & 33 deletions docs/src/content/docs/guides/annotated.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ data = yamlrocks.loads(
option=yamlrocks.OPT_ANNOTATED,
)

isinstance(data, dict) # True (a real dict subclass)
data.__line__ # 1
data.__column__ # 1
isinstance(data, dict) # True (a real dict subclass)
data.__line__ # 1
data.__column__ # 1

data["server"].__line__ # 3 (the mapping body starts here)
data["server"]["host"].__line__ # 3
data["server"]["host"].__column__ # 9
data["server"].__line__ # 3 (the mapping body starts here)
data["server"]["host"].__line__ # 3
data["server"]["host"].__column__ # 9
```

Every annotated node exposes five attributes:
Expand Down Expand Up @@ -57,9 +57,9 @@ PyYAML exposes as `node.start_mark`/`node.end_mark`.
import yamlrocks

data = yamlrocks.loads(b"key: value\nbroad: x\n", option=yamlrocks.OPT_ANNOTATED)
key = list(data)[1] # the 'broad' key
(key.__line__, key.__column__) # (2, 1)
(key.__end_line__, key.__end_column__) # (2, 6) (just past 'broad')
key = list(data)[1] # the 'broad' key
(key.__line__, key.__column__) # (2, 1)
(key.__end_line__, key.__end_column__) # (2, 6) (just past 'broad')
```

:::note[End positions are exact, quotes included]
Expand Down Expand Up @@ -93,14 +93,14 @@ server:
data = yamlrocks.loads(source, option=yamlrocks.OPT_ANNOTATED)

# Dict behavior.
list(data.keys()) # ['name', 'server']
{**data["server"]} # {'host': 'localhost'}
list(data.keys()) # ['name', 'server']
{**data["server"]} # {'host': 'localhost'}

# Str behavior on a scalar.
host = data["server"]["host"]
host.upper() # 'LOCALHOST'
host == "localhost" # True
host + ":8080" # 'localhost:8080'
host.upper() # 'LOCALHOST'
host == "localhost" # True
host + ":8080" # 'localhost:8080'
```

Because they are genuine subclasses, you can pass annotated values to any function
Expand All @@ -121,7 +121,7 @@ data = yamlrocks.loads(b"name: app", option=yamlrocks.OPT_ANNOTATED)

# Attach a class attribute (here a method) to the annotated string type.
type(data["name"]).__shout__ = lambda self: self.upper() + "!"
data["name"].__shout__() # 'APP!'
data["name"].__shout__() # 'APP!'
```

:::note[Class attributes, not instance attributes]
Expand All @@ -148,10 +148,10 @@ data = yamlrocks.loads(
option=yamlrocks.OPT_ANNOTATED,
)

type(data).__name__ # 'YAMLRocksAnnotatedDict'
next(iter(data)).__line__ # 1 (the `server` key's own line)
type(data["server"]["host"]).__name__ # 'YAMLRocksAnnotatedStr'
type(data["server"]["port"]).__name__ # 'int' (plain by default)
type(data).__name__ # 'YAMLRocksAnnotatedDict'
next(iter(data)).__line__ # 1 (the `server` key's own line)
type(data["server"]["host"]).__name__ # 'YAMLRocksAnnotatedStr'
type(data["server"]["port"]).__name__ # 'int' (plain by default)
```

So by default a string value like `host` carries `__line__`/`__column__`, but an
Expand All @@ -173,9 +173,9 @@ data = yamlrocks.loads(
b"port: 8080\n",
option=yamlrocks.OPT_ANNOTATED | yamlrocks.OPT_ANNOTATE_NUMBERS,
)
data["port"] # 8080
data["port"].__line__ # 1
data["port"] + 1 # 8081 (still an int in every way that matters)
data["port"] # 8080
data["port"].__line__ # 1
data["port"] + 1 # 8081 (still an int in every way that matters)
```

An annotated number is an `int`/`float` _subclass_: `isinstance(x, int)`,
Expand Down Expand Up @@ -211,8 +211,8 @@ block: |
"""

data = yamlrocks.loads(source, option=yamlrocks.OPT_ANNOTATED)
data["inline"].__style__ # 'plain'
data["block"].__style__ # 'literal' (a | block; content starts at __line__ + 1)
data["inline"].__style__ # 'plain'
data["block"].__style__ # 'literal' (a | block; content starts at __line__ + 1)
```

### Knowing which tag produced a value: `__source_tag__`
Expand Down Expand Up @@ -241,10 +241,10 @@ with open(os.path.join(workdir, "configuration.yaml"), "w") as f:
opt = yamlrocks.OPT_ANNOTATED | yamlrocks.OPT_SECRETS | yamlrocks.OPT_INCLUDES
data = yamlrocks.load(os.path.join(workdir, "configuration.yaml"), option=opt)

data["api_key"].is_secret # True (from `api_key: !secret api_key`)
data["api_key"].__source_tag__ # '!secret'
data["api_key"].is_secret # True (from `api_key: !secret api_key`)
data["api_key"].__source_tag__ # '!secret'
data["api_key"].__source_target__ # 'api_key' (the directive's argument)
data["title"].__source_tag__ # None (a plain inline value)
data["title"].__source_tag__ # None (a plain inline value)
```

`__source_target__` carries the directive's _argument_: the secret name for
Expand Down Expand Up @@ -285,10 +285,10 @@ import yamlrocks

data = yamlrocks.loads(b"items:\n - a\n - b\n", option=yamlrocks.OPT_ANNOTATED)

type(data["items"]).__name__ # 'YAMLRocksAnnotatedList'
data["items"].__line__ # 2
data["items"][0].__line__ # 2
data["items"][1].__line__ # 3
type(data["items"]).__name__ # 'YAMLRocksAnnotatedList'
data["items"].__line__ # 2
data["items"][0].__line__ # 2
data["items"][1].__line__ # 3
```

## Tracking the originating file
Expand Down Expand Up @@ -341,9 +341,9 @@ ref: *a

data = yamlrocks.loads(source, option=yamlrocks.OPT_ANNOTATED)

data["base"] is data["ref"] # True, the same object
data["base"] is data["ref"] # True, the same object
data["base"]["k"] = 99
data["ref"]["k"] # 99, seen through the shared reference
data["ref"]["k"] # 99, seen through the shared reference
```

This matters for tools that define a block once under an anchor and reuse it in
Expand Down
Loading
Loading