cayaml 0.1.0.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cayaml/__init__.py
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
"""
|
2
|
+
Minimal YAML Parser and Unparser
|
3
|
+
|
4
|
+
This module provides a very basic YAML parser and unparser that supports a subset
|
5
|
+
of YAML syntax (mappings and sequences, with simple scalars). It uses only Python's
|
6
|
+
built-in libraries and is intended for simple use cases.
|
7
|
+
|
8
|
+
Usage:
|
9
|
+
data = parse_yaml(yaml_string)
|
10
|
+
yaml_string = unparse_yaml(data)
|
11
|
+
"""
|
12
|
+
|
13
|
+
|
14
|
+
def get_indent(line):
|
15
|
+
"""Return the number of leading spaces in a line."""
|
16
|
+
return len(line) - len(line.lstrip(" "))
|
17
|
+
|
18
|
+
|
19
|
+
def parse_scalar(value):
|
20
|
+
"""Convert a scalar string into int, float, bool, None or leave as string."""
|
21
|
+
# Remove quotes if present
|
22
|
+
if (value.startswith('"') and value.endswith('"')) or (
|
23
|
+
value.startswith("'") and value.endswith("'")
|
24
|
+
):
|
25
|
+
return value[1:-1]
|
26
|
+
lower = value.lower()
|
27
|
+
if lower == "true":
|
28
|
+
return True
|
29
|
+
if lower == "false":
|
30
|
+
return False
|
31
|
+
if lower in ["null", "~"]:
|
32
|
+
return None
|
33
|
+
try:
|
34
|
+
return int(value)
|
35
|
+
except ValueError:
|
36
|
+
pass
|
37
|
+
try:
|
38
|
+
return float(value)
|
39
|
+
except ValueError:
|
40
|
+
pass
|
41
|
+
return value
|
42
|
+
|
43
|
+
|
44
|
+
def parse_mapping(lines, indent):
|
45
|
+
"""Parse a block of lines representing a mapping."""
|
46
|
+
mapping = {}
|
47
|
+
while lines and get_indent(lines[0]) >= indent:
|
48
|
+
if get_indent(lines[0]) != indent:
|
49
|
+
break
|
50
|
+
line = lines.pop(0)
|
51
|
+
# Ignore comment lines
|
52
|
+
if line.strip().startswith("#"):
|
53
|
+
continue
|
54
|
+
if ":" not in line:
|
55
|
+
continue # Skip lines that do not look like key: value pairs
|
56
|
+
key, _, value = line.strip().partition(":")
|
57
|
+
key = key.strip()
|
58
|
+
value = value.strip()
|
59
|
+
if value == "":
|
60
|
+
# If no inline value, check if a nested block follows.
|
61
|
+
if lines and get_indent(lines[0]) > indent:
|
62
|
+
value, _ = parse_block(lines, get_indent(lines[0]))
|
63
|
+
else:
|
64
|
+
value = None
|
65
|
+
else:
|
66
|
+
value = parse_scalar(value)
|
67
|
+
mapping[key] = value
|
68
|
+
return mapping
|
69
|
+
|
70
|
+
|
71
|
+
def parse_list(lines, indent):
|
72
|
+
"""Parse a block of lines representing a list."""
|
73
|
+
lst = []
|
74
|
+
while (
|
75
|
+
lines and get_indent(lines[0]) == indent and lines[0].lstrip().startswith("-")
|
76
|
+
):
|
77
|
+
line = lines.pop(0)
|
78
|
+
# Remove the dash marker and get the content.
|
79
|
+
content = line.lstrip()[1:].strip()
|
80
|
+
if content == "":
|
81
|
+
# If nothing follows the dash, check for an indented block.
|
82
|
+
if lines and get_indent(lines[0]) > indent:
|
83
|
+
item, _ = parse_block(lines, get_indent(lines[0]))
|
84
|
+
else:
|
85
|
+
item = None
|
86
|
+
else:
|
87
|
+
item = parse_scalar(content)
|
88
|
+
# If the next line is indented more, treat it as a nested block.
|
89
|
+
if lines and get_indent(lines[0]) > indent:
|
90
|
+
extra, _ = parse_block(lines, get_indent(lines[0]))
|
91
|
+
# If the inline value is a mapping, merge the extra block.
|
92
|
+
if isinstance(item, dict) and isinstance(extra, dict):
|
93
|
+
item.update(extra)
|
94
|
+
else:
|
95
|
+
item = extra
|
96
|
+
lst.append(item)
|
97
|
+
return lst
|
98
|
+
|
99
|
+
|
100
|
+
def parse_block(lines, indent):
|
101
|
+
"""Determine if the current block is a list or mapping and parse accordingly."""
|
102
|
+
if not lines:
|
103
|
+
return None, lines
|
104
|
+
# If the current line starts with a dash, treat as a list; otherwise, a mapping.
|
105
|
+
if lines[0].lstrip().startswith("-"):
|
106
|
+
result = parse_list(lines, indent)
|
107
|
+
else:
|
108
|
+
result = parse_mapping(lines, indent)
|
109
|
+
return result, lines
|
110
|
+
|
111
|
+
|
112
|
+
def parse_yaml(yaml_str):
|
113
|
+
"""
|
114
|
+
Parse a YAML string and return the corresponding Python data structure.
|
115
|
+
Supports a minimal subset of YAML.
|
116
|
+
"""
|
117
|
+
lines = yaml_str.splitlines()
|
118
|
+
# Remove completely blank lines.
|
119
|
+
lines = [line for line in lines if line.strip() != ""]
|
120
|
+
result, _ = parse_block(lines, 0)
|
121
|
+
return result
|
122
|
+
|
123
|
+
|
124
|
+
def format_scalar(value):
|
125
|
+
"""Format a scalar value as a YAML string."""
|
126
|
+
if value is None:
|
127
|
+
return "null"
|
128
|
+
if isinstance(value, bool):
|
129
|
+
return "true" if value else "false"
|
130
|
+
if isinstance(value, (int, float)):
|
131
|
+
return str(value)
|
132
|
+
if isinstance(value, str):
|
133
|
+
# Quote the string if it contains spaces or special characters.
|
134
|
+
if not value or any(c in value for c in [" ", ":", "-", "#"]):
|
135
|
+
escaped = value.replace('"', '\\"')
|
136
|
+
return f'"{escaped}"'
|
137
|
+
return value
|
138
|
+
return str(value)
|
139
|
+
|
140
|
+
|
141
|
+
def unparse_yaml(data, indent=0):
|
142
|
+
"""
|
143
|
+
Convert a Python data structure into a YAML-formatted string.
|
144
|
+
Supports a minimal subset of YAML.
|
145
|
+
"""
|
146
|
+
lines = []
|
147
|
+
prefix = " " * indent
|
148
|
+
if isinstance(data, dict):
|
149
|
+
for key, value in data.items():
|
150
|
+
if isinstance(value, (dict, list)):
|
151
|
+
lines.append(f"{prefix}{key}:")
|
152
|
+
lines.append(unparse_yaml(value, indent + 2))
|
153
|
+
else:
|
154
|
+
lines.append(f"{prefix}{key}: {format_scalar(value)}")
|
155
|
+
elif isinstance(data, list):
|
156
|
+
for item in data:
|
157
|
+
if isinstance(item, (dict, list)):
|
158
|
+
lines.append(f"{prefix}-")
|
159
|
+
lines.append(unparse_yaml(item, indent + 2))
|
160
|
+
else:
|
161
|
+
lines.append(f"{prefix}- {format_scalar(item)}")
|
162
|
+
else:
|
163
|
+
lines.append(f"{prefix}{format_scalar(data)}")
|
164
|
+
return "\n".join(lines)
|
165
|
+
|
166
|
+
|
167
|
+
# Public API
|
168
|
+
__all__ = ["parse_yaml", "unparse_yaml"]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: cayaml
|
3
|
+
Version: 0.1.0.dev2
|
4
|
+
Summary: Swarmauri's Canon Yaml Handler
|
5
|
+
License: Apache-2.0
|
6
|
+
Author: Jacob Stewart
|
7
|
+
Author-email: jacob@swarmauri.com
|
8
|
+
Requires-Python: >=3.10,<3.13
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,4 @@
|
|
1
|
+
cayaml/__init__.py,sha256=7T-rcRLCZTemVt0grS40pvaYg3OB-YjPyRiZibxp4C0,5421
|
2
|
+
cayaml-0.1.0.dev2.dist-info/METADATA,sha256=q3r1lmMc372TZ6GiqXehQt-fwgIMLoEbulza6yFv0Tg,459
|
3
|
+
cayaml-0.1.0.dev2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
4
|
+
cayaml-0.1.0.dev2.dist-info/RECORD,,
|