Dynamic grammar generator for Lark parsing toolkit
Project description
gardener - simple tree manipulation module
Basic usage
from gardener import Node, register_hook
@register_hook("child")
def child(node):
return Node.another_child(
values=[value * 2 for value in node.values]
)
tree = Node.root(
value=56,
some_property=Node.child(
values=[10, 20, 80]
)
)
print(tree.pretty())
"""
{
"key": "root",
"props": {
"value": 56,
"some_property": {
"key": "another_child",
"props": {
"values": [
20,
40,
160
]
}
}
}
}
"""
Installation
python -m pip install gardener
Getting started
Gardener is useful for tree manipulation (building and transforming complex trees)
To start working with it, you need to understand a few concepts:
hook is a function called on node creation. It can create a brand new node or change current one.
node is an object with a key - string identifier and props - an arbitrary dictionary
Building a tree
To build a tree, you can use Node.name[.name2.name3...](**props)
:
from gardener import Node
root = Node.root() # props={}, key="root"
oak = Node.tree.oak(age=23) # props={"age": 23}, key="tree:oak"
long_name = Node.very.long.node.key() # props={}, key="very:long:node:key"
Arbitrary name length makes it possible to create namespaces:
from gardener import Node
tree = Node.tree
tree.oak(age=23) # props={"age": 23}, key="tree:oak"
tree.pine(age=2) # props={"age": 2}, key="tree:pine"
Hooks
To transform your tree, you can define any number of hooks.
Hooks are called on node creation, in order they were defined - hooks defined earlier would be called earlier.
A hook is just a function getting node as argument and returning a transformed node.
To define hook, use register_hook(key)
:
from gardener import Node, register_hook
@register_hook("car")
def make_any_car_faster(node):
node.engine.horsepower = node.engine.horsepower * 5
return node
@register_hook("engine")
def engine_tweak(node):
node.horsepower -= 1
return node
@register_hook("car")
def no_red_cars(node):
if node.color == "red":
node.color = "black"
return node
@register_hook("car")
def make_supercar(node):
if node.engine.horsepower > 500:
return Node.supercar(**node.props)
return node
parking_lot = Node.lot(
cars=[
Node.car(
engine=Node.engine(horsepower=100),
color="red"
),
Node.car(
engine=Node.engine(horsepower=200),
color="white"
),
Node.car(
engine=Node.engine(horsepower=205),
color="red"
),
]
)
# notice how car.engine.horsepower is (x - 1) * 5 because engine is created before the car
print(parking_lot.pretty())
"""
{
"key": "lot",
"props": {
"cars": [
{
"key": "car",
"props": {
"engine": {
"key": "engine",
"props": {
"horsepower": 495
}
},
"color": "black"
}
},
{
"key": "supercar",
"props": {
"engine": {
"key": "engine",
"props": {
"horsepower": 995
}
},
"color": "white"
}
},
{
"key": "supercar",
"props": {
"engine": {
"key": "engine",
"props": {
"horsepower": 1020
}
},
"color": "black"
}
}
]
}
}
"""
Important notes:
- if your hook changes existing node (but key remains the same) - use node.copy() or make changes in place.
- if you change the key of the node, make a new node.
First is needed to avoid infinite recursion (otherwise your hook would be called again and again on the same node).
Second is needed to ensure hooks are called on the new node.
Pretty printing
Gardener uses built-in json
module to make a pretty-printed tree:
from gardener import Node
print(Node.tree.oak(age=23).pretty())
"""
{
"key": "tree:oak",
"props": {
"age": 23
}
}
"""
If your values are JSON serializable, this would work out-of-the-box.
Otherwise you will need to extend GardenerJSON class:
from gardener import Node, GardenerJSON
class MyGardenerJSON(GardenerJSON):
def default(self, obj):
if obj is Ellipsis:
return "..." # return any JSON serializable object
return super().default(obj)
# Ellipsis is not JSON serializable
print(Node.tree.oak(cool_object=Ellipsis).pretty(cls=MyGardenerJSON))
"""
{
"key": "tree:oak",
"props": {
"cool_object": "..."
}
}
"""
In fact, Node.pretty
accepts any keyword argument of json.dumps
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
File details
Details for the file gardener-1.0.0.tar.gz
.
File metadata
- Download URL: gardener-1.0.0.tar.gz
- Upload date:
- Size: 4.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.7.1 importlib_metadata/4.9.0 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d9be8c8a858624f5cfa9ea63decc78fbde5e0497942651ce004c5c3f09c3dda4 |
|
MD5 | 43dafd8bc4301734fdef3cd0f71b82cd |
|
BLAKE2b-256 | c199c4cb794d8d61113eba3e019409038f611dd2e93a8da531d85fc8895959c0 |