yamlstar 0.1.2__tar.gz

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.
@@ -0,0 +1,338 @@
1
+ Metadata-Version: 2.4
2
+ Name: yamlstar
3
+ Version: 0.1.2
4
+ Summary: Python bindings for YAMLStar - YAML 1.2 loader
5
+ Home-page: https://github.com/yaml/yamlstar
6
+ Author: Ingy döt Net
7
+ Author-email: ingy@ingy.net
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.6
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.6, <4
22
+ Description-Content-Type: text/markdown
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: license
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # YAMLStar Python Bindings
34
+
35
+ Python bindings for YAMLStar - a pure YAML 1.2 loader implemented in Clojure.
36
+
37
+ ## Features
38
+
39
+ - **YAML 1.2 Spec Compliance**: 100% compliant with YAML 1.2 core schema
40
+ - **Pure Implementation**: No dependencies on SnakeYAML or other external parsers
41
+ - **Fast Native Performance**: Uses GraalVM native-image shared library
42
+ - **Simple API**: Load YAML documents with a single function call
43
+ - **Multi-Document Support**: Load multiple YAML documents from a single string
44
+
45
+ ## Installation
46
+
47
+ ### Prerequisites
48
+
49
+ First, build and install the shared library:
50
+
51
+ ```bash
52
+ cd ../libyamlstar
53
+ make native
54
+ sudo make install PREFIX=/usr/local
55
+ ```
56
+
57
+ Or install to user-local directory:
58
+
59
+ ```bash
60
+ cd ../libyamlstar
61
+ make native
62
+ make install PREFIX=~/.local
63
+ ```
64
+
65
+ ### Install Python Package
66
+
67
+ ```bash
68
+ pip install .
69
+ ```
70
+
71
+ For development:
72
+
73
+ ```bash
74
+ pip install -e .
75
+ ```
76
+
77
+ ## Quick Start
78
+
79
+ ```python
80
+ import yamlstar
81
+
82
+ # Create a YAMLStar instance
83
+ ys = yamlstar.YAMLStar()
84
+
85
+ # Load a simple YAML string
86
+ data = ys.load("key: value")
87
+ print(data) # {'key': 'value'}
88
+ ```
89
+
90
+ ## Usage Examples
91
+
92
+ ### Basic Types
93
+
94
+ ```python
95
+ import yamlstar
96
+
97
+ ys = yamlstar.YAMLStar()
98
+
99
+ # Strings
100
+ ys.load("hello") # 'hello'
101
+
102
+ # Integers
103
+ ys.load("42") # 42
104
+
105
+ # Floats
106
+ ys.load("3.14") # 3.14
107
+
108
+ # Booleans
109
+ ys.load("true") # True
110
+ ys.load("false") # False
111
+
112
+ # Null
113
+ ys.load("null") # None
114
+ ```
115
+
116
+ ### Collections
117
+
118
+ ```python
119
+ # Mappings (dictionaries)
120
+ data = ys.load("""
121
+ name: Alice
122
+ age: 30
123
+ city: Seattle
124
+ """)
125
+ # {'name': 'Alice', 'age': 30, 'city': 'Seattle'}
126
+
127
+ # Sequences (lists)
128
+ data = ys.load("""
129
+ - apple
130
+ - banana
131
+ - orange
132
+ """)
133
+ # ['apple', 'banana', 'orange']
134
+
135
+ # Flow style
136
+ data = ys.load("[a, b, c]")
137
+ # ['a', 'b', 'c']
138
+ ```
139
+
140
+ ### Nested Structures
141
+
142
+ ```python
143
+ data = ys.load("""
144
+ person:
145
+ name: Alice
146
+ age: 30
147
+ hobbies:
148
+ - reading
149
+ - coding
150
+ - hiking
151
+ """)
152
+ # {
153
+ # 'person': {
154
+ # 'name': 'Alice',
155
+ # 'age': 30,
156
+ # 'hobbies': ['reading', 'coding', 'hiking']
157
+ # }
158
+ # }
159
+ ```
160
+
161
+ ### Multi-Document YAML
162
+
163
+ ```python
164
+ # Load all documents from a multi-document YAML string
165
+ docs = ys.load_all("""---
166
+ name: Document 1
167
+ ---
168
+ name: Document 2
169
+ ---
170
+ name: Document 3
171
+ """)
172
+ # [
173
+ # {'name': 'Document 1'},
174
+ # {'name': 'Document 2'},
175
+ # {'name': 'Document 3'}
176
+ # ]
177
+ ```
178
+
179
+ ### Type Coercion
180
+
181
+ YAMLStar follows YAML 1.2 core schema type inference:
182
+
183
+ ```python
184
+ data = ys.load("""
185
+ string: hello
186
+ integer: 42
187
+ float: 3.14
188
+ bool_true: true
189
+ bool_false: false
190
+ null_value: null
191
+ """)
192
+ # {
193
+ # 'string': 'hello',
194
+ # 'integer': 42,
195
+ # 'float': 3.14,
196
+ # 'bool_true': True,
197
+ # 'bool_false': False,
198
+ # 'null_value': None
199
+ # }
200
+ ```
201
+
202
+ ### Error Handling
203
+
204
+ ```python
205
+ try:
206
+ data = ys.load("invalid: yaml: syntax")
207
+ except Exception as e:
208
+ print(f"Error loading YAML: {e}")
209
+ ```
210
+
211
+ ### Version Information
212
+
213
+ ```python
214
+ # Get YAMLStar version
215
+ version = ys.version()
216
+ print(f"YAMLStar version: {version}")
217
+ ```
218
+
219
+ ## API Reference
220
+
221
+ ### `YAMLStar` Class
222
+
223
+ #### `__init__()`
224
+ Create a new YAMLStar instance. Each instance maintains its own GraalVM isolate.
225
+
226
+ ```python
227
+ ys = yamlstar.YAMLStar()
228
+ ```
229
+
230
+ #### `load(yaml_input)`
231
+ Load a single YAML document.
232
+
233
+ **Parameters:**
234
+ - `yaml_input` (str): String containing YAML content
235
+
236
+ **Returns:**
237
+ - Python object representing the YAML document (dict, list, str, int, float, bool, or None)
238
+
239
+ **Raises:**
240
+ - `Exception` if the YAML is malformed
241
+
242
+ **Example:**
243
+ ```python
244
+ data = ys.load("key: value")
245
+ ```
246
+
247
+ #### `load_all(yaml_input)`
248
+ Load all YAML documents from a multi-document string.
249
+
250
+ **Parameters:**
251
+ - `yaml_input` (str): String containing one or more YAML documents
252
+
253
+ **Returns:**
254
+ - List of Python objects, one per YAML document
255
+
256
+ **Raises:**
257
+ - `Exception` if the YAML is malformed
258
+
259
+ **Example:**
260
+ ```python
261
+ docs = ys.load_all("---\ndoc1\n---\ndoc2")
262
+ ```
263
+
264
+ #### `version()`
265
+ Get the YAMLStar version string.
266
+
267
+ **Returns:**
268
+ - str: Version string
269
+
270
+ **Example:**
271
+ ```python
272
+ version = ys.version()
273
+ ```
274
+
275
+ ## Development
276
+
277
+ ### Running Tests
278
+
279
+ ```bash
280
+ # Run all tests
281
+ make test
282
+
283
+ # Run only pytest tests
284
+ make test-pytest
285
+
286
+ # Run only FFI tests
287
+ make test-ffi
288
+ ```
289
+
290
+ ### Building Distribution
291
+
292
+ ```bash
293
+ # Build source distribution
294
+ make dist
295
+
296
+ # Build and install in development mode
297
+ make install
298
+ ```
299
+
300
+ ## Requirements
301
+
302
+ - **Python**: 3.6 or higher
303
+ - **libyamlstar**: Shared library (installed separately)
304
+ - **System**: Linux or macOS
305
+
306
+ ## Library Search Path
307
+
308
+ The package searches for `libyamlstar.so` (or `.dylib` on macOS) in:
309
+
310
+ 1. Development path (relative to package)
311
+ 2. Directories in `LD_LIBRARY_PATH` environment variable
312
+ 3. `/usr/local/lib` (default install location)
313
+ 4. `~/.local/lib` (user-local install location)
314
+
315
+ ## Comparison to PyYAML
316
+
317
+ | Feature | YAMLStar | PyYAML |
318
+ |---------|----------|--------|
319
+ | YAML Version | 1.2 | 1.1 |
320
+ | Implementation | Pure Clojure | C + Python |
321
+ | Type Inference | YAML 1.2 core schema | YAML 1.1 + custom |
322
+ | Native Performance | Yes (GraalVM) | Yes (C extension) |
323
+ | Dependencies | libyamlstar.so | None |
324
+
325
+ ## License
326
+
327
+ MIT License - See [License](License) file
328
+
329
+ ## Credits
330
+
331
+ Created by Ingy döt Net, inventor of YAML.
332
+
333
+ YAMLStar is built on the YAML Reference Parser (pure Clojure implementation).
334
+
335
+ ## Links
336
+
337
+ - **GitHub**: https://github.com/yaml/yamlstar
338
+ - **YAML Specification**: https://yaml.org/spec/1.2/spec.html
@@ -0,0 +1,209 @@
1
+ # Copyright 2024 yaml.org
2
+ # MIT License
3
+
4
+ """
5
+ Python binding/API for the libyamlstar shared library.
6
+
7
+ This module provides a Python interface to YAMLStar, a pure YAML 1.2 loader.
8
+ The YAMLStar class has methods for loading YAML documents and converting
9
+ them to Python objects.
10
+ """
11
+
12
+ # Version matching the yamlstar shared library
13
+ yamlstar_version = '0.1.2'
14
+
15
+ import os
16
+ import sys
17
+ import ctypes
18
+ import json
19
+
20
+ # Require Python 3.6 or greater:
21
+ assert sys.version_info >= (3, 6), \
22
+ "Python 3.6 or greater required for 'yamlstar'."
23
+
24
+ # Find the libyamlstar shared library file path:
25
+ def find_libyamlstar_path():
26
+ # Confirm platform and determine file extension:
27
+ if sys.platform == 'linux':
28
+ so = 'so'
29
+ elif sys.platform == 'darwin':
30
+ so = 'dylib'
31
+ elif sys.platform == 'win32':
32
+ so = 'dll'
33
+ else:
34
+ raise Exception(
35
+ "Unsupported platform '%s' for yamlstar." % sys.platform)
36
+
37
+ # We currently bind to an exact version of libyamlstar.
38
+ # eg 'libyamlstar.so.0.1.2-SNAPSHOT'
39
+ libyamlstar_name = \
40
+ "libyamlstar.%s.%s" % (so, yamlstar_version)
41
+
42
+ # Use LD_LIBRARY_PATH to find libyamlstar shared library, or default to
43
+ # '/usr/local/lib' (where it is installed by default):
44
+ ld_library_path = os.environ.get('LD_LIBRARY_PATH')
45
+ ld_library_paths = ld_library_path.split(':') if ld_library_path else []
46
+ ld_library_paths.append('/usr/local/lib')
47
+ ld_library_paths.append(os.environ.get('HOME') + '/.local/lib')
48
+
49
+ # Also check relative to this file (for development)
50
+ lib_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), '..', 'libyamlstar', 'lib')
51
+ lib_path = os.path.abspath(lib_path)
52
+ ld_library_paths.insert(0, lib_path)
53
+
54
+ libyamlstar_path = None
55
+ for path in ld_library_paths:
56
+ full_path = os.path.join(path, libyamlstar_name)
57
+ if os.path.isfile(full_path):
58
+ libyamlstar_path = full_path
59
+ break
60
+
61
+ if not libyamlstar_path:
62
+ raise Exception(
63
+ """\
64
+ Shared library file '%s' not found
65
+ Search paths: %s
66
+ Build with: cd libyamlstar && make native
67
+ """ % (libyamlstar_name, ':'.join(ld_library_paths)))
68
+
69
+ return libyamlstar_path
70
+
71
+ # Load libyamlstar shared library:
72
+ libyamlstar = ctypes.CDLL(find_libyamlstar_path())
73
+
74
+ # Create bindings to library functions:
75
+ yamlstar_load = libyamlstar.yamlstar_load
76
+ yamlstar_load.restype = ctypes.c_char_p
77
+
78
+ yamlstar_load_all = libyamlstar.yamlstar_load_all
79
+ yamlstar_load_all.restype = ctypes.c_char_p
80
+
81
+ yamlstar_version_fn = libyamlstar.yamlstar_version
82
+ yamlstar_version_fn.restype = ctypes.c_char_p
83
+
84
+
85
+ # The YAMLStar class is the main user facing API for this module.
86
+ class YAMLStar():
87
+ """
88
+ Interface with the libyamlstar shared library.
89
+
90
+ Usage:
91
+ import yamlstar
92
+ ys = yamlstar.YAMLStar()
93
+ data = ys.load("key: value")
94
+ # Returns: {'key': 'value'}
95
+
96
+ docs = ys.load_all("---\\ndoc1\\n---\\ndoc2")
97
+ # Returns: ['doc1', 'doc2']
98
+ """
99
+
100
+ # YAMLStar instance constructor:
101
+ def __init__(self):
102
+ # Create a new GraalVM isolatethread for life of the YAMLStar instance:
103
+ self.isolatethread = ctypes.c_void_p()
104
+
105
+ # Create a new GraalVM isolate:
106
+ rc = libyamlstar.graal_create_isolate(
107
+ None,
108
+ None,
109
+ ctypes.byref(self.isolatethread),
110
+ )
111
+
112
+ if rc != 0:
113
+ raise Exception("Failed to create isolate")
114
+
115
+ # Load a single YAML document and return the result:
116
+ def load(self, yaml_input):
117
+ """
118
+ Load a single YAML document.
119
+
120
+ Args:
121
+ yaml_input: String containing YAML content
122
+
123
+ Returns:
124
+ Python object representing the YAML document
125
+
126
+ Raises:
127
+ Exception if the YAML is malformed
128
+ """
129
+ # Reset any previous error:
130
+ self.error = None
131
+
132
+ # Call 'yamlstar_load' function in libyamlstar shared library:
133
+ data_json = yamlstar_load(
134
+ self.isolatethread,
135
+ ctypes.c_char_p(bytes(yaml_input, "utf8")),
136
+ ).decode()
137
+
138
+ # Decode the JSON response:
139
+ resp = json.loads(data_json)
140
+
141
+ # Check for libyamlstar error in JSON response:
142
+ self.error = resp.get('error')
143
+ if self.error:
144
+ raise Exception(self.error['cause'])
145
+
146
+ # Get the response object from loading the YAML string:
147
+ if 'data' not in resp:
148
+ raise Exception("Unexpected response from 'libyamlstar'")
149
+ data = resp.get('data')
150
+
151
+ # Return the response object:
152
+ return data
153
+
154
+ # Load all YAML documents and return the results:
155
+ def load_all(self, yaml_input):
156
+ """
157
+ Load all YAML documents from a multi-document string.
158
+
159
+ Args:
160
+ yaml_input: String containing one or more YAML documents
161
+
162
+ Returns:
163
+ List of Python objects, one per YAML document
164
+
165
+ Raises:
166
+ Exception if the YAML is malformed
167
+ """
168
+ # Reset any previous error:
169
+ self.error = None
170
+
171
+ # Call 'yamlstar_load_all' function in libyamlstar shared library:
172
+ data_json = yamlstar_load_all(
173
+ self.isolatethread,
174
+ ctypes.c_char_p(bytes(yaml_input, "utf8")),
175
+ ).decode()
176
+
177
+ # Decode the JSON response:
178
+ resp = json.loads(data_json)
179
+
180
+ # Check for libyamlstar error in JSON response:
181
+ self.error = resp.get('error')
182
+ if self.error:
183
+ raise Exception(self.error['cause'])
184
+
185
+ # Get the response object from loading the YAML string:
186
+ if 'data' not in resp:
187
+ raise Exception("Unexpected response from 'libyamlstar'")
188
+ data = resp.get('data')
189
+
190
+ # Return the response object:
191
+ return data
192
+
193
+ # Get the YAMLStar version:
194
+ def version(self):
195
+ """
196
+ Get the YAMLStar version string.
197
+
198
+ Returns:
199
+ Version string
200
+ """
201
+ return yamlstar_version_fn(self.isolatethread).decode()
202
+
203
+ # YAMLStar instance destructor:
204
+ def __del__(self):
205
+ # Tear down the isolate thread to free resources:
206
+ if hasattr(self, 'isolatethread'):
207
+ rc = libyamlstar.graal_tear_down_isolate(self.isolatethread)
208
+ if rc != 0:
209
+ raise Exception("Failed to tear down isolate")
@@ -0,0 +1,338 @@
1
+ Metadata-Version: 2.4
2
+ Name: yamlstar
3
+ Version: 0.1.2
4
+ Summary: Python bindings for YAMLStar - YAML 1.2 loader
5
+ Home-page: https://github.com/yaml/yamlstar
6
+ Author: Ingy döt Net
7
+ Author-email: ingy@ingy.net
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.6
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.6, <4
22
+ Description-Content-Type: text/markdown
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: license
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # YAMLStar Python Bindings
34
+
35
+ Python bindings for YAMLStar - a pure YAML 1.2 loader implemented in Clojure.
36
+
37
+ ## Features
38
+
39
+ - **YAML 1.2 Spec Compliance**: 100% compliant with YAML 1.2 core schema
40
+ - **Pure Implementation**: No dependencies on SnakeYAML or other external parsers
41
+ - **Fast Native Performance**: Uses GraalVM native-image shared library
42
+ - **Simple API**: Load YAML documents with a single function call
43
+ - **Multi-Document Support**: Load multiple YAML documents from a single string
44
+
45
+ ## Installation
46
+
47
+ ### Prerequisites
48
+
49
+ First, build and install the shared library:
50
+
51
+ ```bash
52
+ cd ../libyamlstar
53
+ make native
54
+ sudo make install PREFIX=/usr/local
55
+ ```
56
+
57
+ Or install to user-local directory:
58
+
59
+ ```bash
60
+ cd ../libyamlstar
61
+ make native
62
+ make install PREFIX=~/.local
63
+ ```
64
+
65
+ ### Install Python Package
66
+
67
+ ```bash
68
+ pip install .
69
+ ```
70
+
71
+ For development:
72
+
73
+ ```bash
74
+ pip install -e .
75
+ ```
76
+
77
+ ## Quick Start
78
+
79
+ ```python
80
+ import yamlstar
81
+
82
+ # Create a YAMLStar instance
83
+ ys = yamlstar.YAMLStar()
84
+
85
+ # Load a simple YAML string
86
+ data = ys.load("key: value")
87
+ print(data) # {'key': 'value'}
88
+ ```
89
+
90
+ ## Usage Examples
91
+
92
+ ### Basic Types
93
+
94
+ ```python
95
+ import yamlstar
96
+
97
+ ys = yamlstar.YAMLStar()
98
+
99
+ # Strings
100
+ ys.load("hello") # 'hello'
101
+
102
+ # Integers
103
+ ys.load("42") # 42
104
+
105
+ # Floats
106
+ ys.load("3.14") # 3.14
107
+
108
+ # Booleans
109
+ ys.load("true") # True
110
+ ys.load("false") # False
111
+
112
+ # Null
113
+ ys.load("null") # None
114
+ ```
115
+
116
+ ### Collections
117
+
118
+ ```python
119
+ # Mappings (dictionaries)
120
+ data = ys.load("""
121
+ name: Alice
122
+ age: 30
123
+ city: Seattle
124
+ """)
125
+ # {'name': 'Alice', 'age': 30, 'city': 'Seattle'}
126
+
127
+ # Sequences (lists)
128
+ data = ys.load("""
129
+ - apple
130
+ - banana
131
+ - orange
132
+ """)
133
+ # ['apple', 'banana', 'orange']
134
+
135
+ # Flow style
136
+ data = ys.load("[a, b, c]")
137
+ # ['a', 'b', 'c']
138
+ ```
139
+
140
+ ### Nested Structures
141
+
142
+ ```python
143
+ data = ys.load("""
144
+ person:
145
+ name: Alice
146
+ age: 30
147
+ hobbies:
148
+ - reading
149
+ - coding
150
+ - hiking
151
+ """)
152
+ # {
153
+ # 'person': {
154
+ # 'name': 'Alice',
155
+ # 'age': 30,
156
+ # 'hobbies': ['reading', 'coding', 'hiking']
157
+ # }
158
+ # }
159
+ ```
160
+
161
+ ### Multi-Document YAML
162
+
163
+ ```python
164
+ # Load all documents from a multi-document YAML string
165
+ docs = ys.load_all("""---
166
+ name: Document 1
167
+ ---
168
+ name: Document 2
169
+ ---
170
+ name: Document 3
171
+ """)
172
+ # [
173
+ # {'name': 'Document 1'},
174
+ # {'name': 'Document 2'},
175
+ # {'name': 'Document 3'}
176
+ # ]
177
+ ```
178
+
179
+ ### Type Coercion
180
+
181
+ YAMLStar follows YAML 1.2 core schema type inference:
182
+
183
+ ```python
184
+ data = ys.load("""
185
+ string: hello
186
+ integer: 42
187
+ float: 3.14
188
+ bool_true: true
189
+ bool_false: false
190
+ null_value: null
191
+ """)
192
+ # {
193
+ # 'string': 'hello',
194
+ # 'integer': 42,
195
+ # 'float': 3.14,
196
+ # 'bool_true': True,
197
+ # 'bool_false': False,
198
+ # 'null_value': None
199
+ # }
200
+ ```
201
+
202
+ ### Error Handling
203
+
204
+ ```python
205
+ try:
206
+ data = ys.load("invalid: yaml: syntax")
207
+ except Exception as e:
208
+ print(f"Error loading YAML: {e}")
209
+ ```
210
+
211
+ ### Version Information
212
+
213
+ ```python
214
+ # Get YAMLStar version
215
+ version = ys.version()
216
+ print(f"YAMLStar version: {version}")
217
+ ```
218
+
219
+ ## API Reference
220
+
221
+ ### `YAMLStar` Class
222
+
223
+ #### `__init__()`
224
+ Create a new YAMLStar instance. Each instance maintains its own GraalVM isolate.
225
+
226
+ ```python
227
+ ys = yamlstar.YAMLStar()
228
+ ```
229
+
230
+ #### `load(yaml_input)`
231
+ Load a single YAML document.
232
+
233
+ **Parameters:**
234
+ - `yaml_input` (str): String containing YAML content
235
+
236
+ **Returns:**
237
+ - Python object representing the YAML document (dict, list, str, int, float, bool, or None)
238
+
239
+ **Raises:**
240
+ - `Exception` if the YAML is malformed
241
+
242
+ **Example:**
243
+ ```python
244
+ data = ys.load("key: value")
245
+ ```
246
+
247
+ #### `load_all(yaml_input)`
248
+ Load all YAML documents from a multi-document string.
249
+
250
+ **Parameters:**
251
+ - `yaml_input` (str): String containing one or more YAML documents
252
+
253
+ **Returns:**
254
+ - List of Python objects, one per YAML document
255
+
256
+ **Raises:**
257
+ - `Exception` if the YAML is malformed
258
+
259
+ **Example:**
260
+ ```python
261
+ docs = ys.load_all("---\ndoc1\n---\ndoc2")
262
+ ```
263
+
264
+ #### `version()`
265
+ Get the YAMLStar version string.
266
+
267
+ **Returns:**
268
+ - str: Version string
269
+
270
+ **Example:**
271
+ ```python
272
+ version = ys.version()
273
+ ```
274
+
275
+ ## Development
276
+
277
+ ### Running Tests
278
+
279
+ ```bash
280
+ # Run all tests
281
+ make test
282
+
283
+ # Run only pytest tests
284
+ make test-pytest
285
+
286
+ # Run only FFI tests
287
+ make test-ffi
288
+ ```
289
+
290
+ ### Building Distribution
291
+
292
+ ```bash
293
+ # Build source distribution
294
+ make dist
295
+
296
+ # Build and install in development mode
297
+ make install
298
+ ```
299
+
300
+ ## Requirements
301
+
302
+ - **Python**: 3.6 or higher
303
+ - **libyamlstar**: Shared library (installed separately)
304
+ - **System**: Linux or macOS
305
+
306
+ ## Library Search Path
307
+
308
+ The package searches for `libyamlstar.so` (or `.dylib` on macOS) in:
309
+
310
+ 1. Development path (relative to package)
311
+ 2. Directories in `LD_LIBRARY_PATH` environment variable
312
+ 3. `/usr/local/lib` (default install location)
313
+ 4. `~/.local/lib` (user-local install location)
314
+
315
+ ## Comparison to PyYAML
316
+
317
+ | Feature | YAMLStar | PyYAML |
318
+ |---------|----------|--------|
319
+ | YAML Version | 1.2 | 1.1 |
320
+ | Implementation | Pure Clojure | C + Python |
321
+ | Type Inference | YAML 1.2 core schema | YAML 1.1 + custom |
322
+ | Native Performance | Yes (GraalVM) | Yes (C extension) |
323
+ | Dependencies | libyamlstar.so | None |
324
+
325
+ ## License
326
+
327
+ MIT License - See [License](License) file
328
+
329
+ ## Credits
330
+
331
+ Created by Ingy döt Net, inventor of YAML.
332
+
333
+ YAMLStar is built on the YAML Reference Parser (pure Clojure implementation).
334
+
335
+ ## Links
336
+
337
+ - **GitHub**: https://github.com/yaml/yamlstar
338
+ - **YAML Specification**: https://yaml.org/spec/1.2/spec.html
@@ -0,0 +1,8 @@
1
+ setup.cfg
2
+ setup.py
3
+ lib/yamlstar/__init__.py
4
+ lib/yamlstar.egg-info/PKG-INFO
5
+ lib/yamlstar.egg-info/SOURCES.txt
6
+ lib/yamlstar.egg-info/dependency_links.txt
7
+ lib/yamlstar.egg-info/top_level.txt
8
+ test/test_yamlstar.py
@@ -0,0 +1 @@
1
+ yamlstar
@@ -0,0 +1,7 @@
1
+ [metadata]
2
+ description_file = ReadMe.md
3
+
4
+ [egg_info]
5
+ tag_build =
6
+ tag_date = 0
7
+
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from setuptools import setup
4
+ import re
5
+ from pathlib import Path
6
+
7
+ # Read version from ../Meta file
8
+ meta_file = Path(__file__).parent.parent / 'Meta'
9
+ meta_content = meta_file.read_text()
10
+ match = re.search(r'^version:\s*(.+)$', meta_content, re.MULTILINE)
11
+ version = match.group(1) if match else None
12
+
13
+ # Validate version format
14
+ if not version or not re.match(r'^\d+\.\d+\.\d+', version):
15
+ raise ValueError(f"Invalid or missing version in Meta file: {version}")
16
+
17
+ setup(
18
+ name = 'yamlstar',
19
+ version = version,
20
+ description = 'Python bindings for YAMLStar - YAML 1.2 loader',
21
+ long_description = open('ReadMe.md').read() if __name__ == '__main__' else '',
22
+ long_description_content_type = 'text/markdown',
23
+ author = 'Ingy döt Net',
24
+ author_email = 'ingy@ingy.net',
25
+ url = 'https://github.com/yaml/yamlstar',
26
+ license = 'MIT',
27
+ packages = ['yamlstar'],
28
+ package_dir = {'': 'lib'},
29
+ python_requires = '>=3.6, <4',
30
+ install_requires = [],
31
+ setup_requires = ['wheel'],
32
+ classifiers = [
33
+ 'Development Status :: 3 - Alpha',
34
+ 'Intended Audience :: Developers',
35
+ 'License :: OSI Approved :: MIT License',
36
+ 'Programming Language :: Python :: 3',
37
+ 'Programming Language :: Python :: 3.6',
38
+ 'Programming Language :: Python :: 3.7',
39
+ 'Programming Language :: Python :: 3.8',
40
+ 'Programming Language :: Python :: 3.9',
41
+ 'Programming Language :: Python :: 3.10',
42
+ 'Programming Language :: Python :: 3.11',
43
+ 'Programming Language :: Python :: 3.12',
44
+ 'Topic :: Software Development :: Libraries',
45
+ ],
46
+ )
@@ -0,0 +1,228 @@
1
+ """
2
+ Tests for yamlstar Python package.
3
+ """
4
+ import pytest
5
+ import sys
6
+ import os
7
+
8
+ # Add lib directory to path for testing
9
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'lib'))
10
+
11
+ import yamlstar
12
+
13
+
14
+ @pytest.fixture
15
+ def ys():
16
+ """Create a YAMLStar instance for testing."""
17
+ return yamlstar.YAMLStar()
18
+
19
+
20
+ def test_load_simple_scalar(ys):
21
+ """Test loading a simple scalar value."""
22
+ result = ys.load("hello")
23
+ assert result == "hello"
24
+
25
+
26
+ def test_load_integer(ys):
27
+ """Test loading an integer."""
28
+ result = ys.load("42")
29
+ assert result == 42
30
+ assert isinstance(result, int)
31
+
32
+
33
+ def test_load_float(ys):
34
+ """Test loading a float."""
35
+ result = ys.load("3.14")
36
+ assert result == 3.14
37
+ assert isinstance(result, float)
38
+
39
+
40
+ def test_load_boolean_true(ys):
41
+ """Test loading boolean true."""
42
+ result = ys.load("true")
43
+ assert result is True
44
+
45
+
46
+ def test_load_boolean_false(ys):
47
+ """Test loading boolean false."""
48
+ result = ys.load("false")
49
+ assert result is False
50
+
51
+
52
+ def test_load_null(ys):
53
+ """Test loading null value."""
54
+ result = ys.load("null")
55
+ assert result is None
56
+
57
+
58
+ def test_load_simple_mapping(ys):
59
+ """Test loading a simple mapping."""
60
+ result = ys.load("key: value")
61
+ assert result == {"key": "value"}
62
+
63
+
64
+ def test_load_nested_mapping(ys):
65
+ """Test loading nested mappings."""
66
+ yaml_str = """
67
+ outer:
68
+ inner: value
69
+ """
70
+ result = ys.load(yaml_str)
71
+ assert result == {"outer": {"inner": "value"}}
72
+
73
+
74
+ def test_load_mapping_multiple_keys(ys):
75
+ """Test loading a mapping with multiple keys."""
76
+ yaml_str = """
77
+ key1: value1
78
+ key2: value2
79
+ key3: value3
80
+ """
81
+ result = ys.load(yaml_str)
82
+ assert result == {
83
+ "key1": "value1",
84
+ "key2": "value2",
85
+ "key3": "value3"
86
+ }
87
+
88
+
89
+ def test_load_simple_sequence(ys):
90
+ """Test loading a simple sequence."""
91
+ yaml_str = """
92
+ - item1
93
+ - item2
94
+ - item3
95
+ """
96
+ result = ys.load(yaml_str)
97
+ assert result == ["item1", "item2", "item3"]
98
+
99
+
100
+ def test_load_flow_sequence(ys):
101
+ """Test loading flow-style sequence."""
102
+ result = ys.load("[a, b, c]")
103
+ assert result == ["a", "b", "c"]
104
+
105
+
106
+ def test_load_type_coercion(ys):
107
+ """Test YAML 1.2 type coercion."""
108
+ yaml_str = """
109
+ string: hello
110
+ integer: 42
111
+ float: 3.14
112
+ bool_true: true
113
+ bool_false: false
114
+ null_value: null
115
+ """
116
+ result = ys.load(yaml_str)
117
+ assert result == {
118
+ "string": "hello",
119
+ "integer": 42,
120
+ "float": 3.14,
121
+ "bool_true": True,
122
+ "bool_false": False,
123
+ "null_value": None
124
+ }
125
+
126
+
127
+ def test_load_sequence_of_mappings(ys):
128
+ """Test loading a sequence of mappings."""
129
+ yaml_str = """
130
+ - name: Alice
131
+ age: 30
132
+ - name: Bob
133
+ age: 25
134
+ """
135
+ result = ys.load(yaml_str)
136
+ assert result == [
137
+ {"name": "Alice", "age": 30},
138
+ {"name": "Bob", "age": 25}
139
+ ]
140
+
141
+
142
+ def test_load_mapping_with_sequence_values(ys):
143
+ """Test loading a mapping with sequence values."""
144
+ yaml_str = """
145
+ fruits:
146
+ - apple
147
+ - banana
148
+ colors:
149
+ - red
150
+ - blue
151
+ """
152
+ result = ys.load(yaml_str)
153
+ assert result == {
154
+ "fruits": ["apple", "banana"],
155
+ "colors": ["red", "blue"]
156
+ }
157
+
158
+
159
+ def test_load_all_single_document(ys):
160
+ """Test load_all with a single document."""
161
+ result = ys.load_all("hello")
162
+ assert result == ["hello"]
163
+
164
+
165
+ def test_load_all_multiple_documents(ys):
166
+ """Test load_all with multiple documents."""
167
+ yaml_str = """---
168
+ doc1
169
+ ---
170
+ doc2
171
+ ---
172
+ doc3"""
173
+ result = ys.load_all(yaml_str)
174
+ assert result == ["doc1", "doc2", "doc3"]
175
+
176
+
177
+ def test_load_all_with_explicit_markers(ys):
178
+ """Test load_all with explicit document markers."""
179
+ yaml_str = """---
180
+ a: 1
181
+ ...
182
+ ---
183
+ b: 2
184
+ ..."""
185
+ result = ys.load_all(yaml_str)
186
+ assert result == [{"a": 1}, {"b": 2}]
187
+
188
+
189
+ def test_version(ys):
190
+ """Test getting the version string."""
191
+ version = ys.version()
192
+ assert isinstance(version, str)
193
+ assert len(version) > 0
194
+
195
+
196
+ def test_error_handling_malformed_yaml(ys):
197
+ """Test that malformed YAML raises an exception."""
198
+ # Unclosed quote is truly malformed
199
+ malformed_yaml = 'key: "unclosed'
200
+ with pytest.raises(Exception):
201
+ ys.load(malformed_yaml)
202
+
203
+
204
+ def test_empty_document(ys):
205
+ """Test loading an empty document."""
206
+ result = ys.load("")
207
+ assert result is None
208
+
209
+
210
+ def test_whitespace_only(ys):
211
+ """Test loading whitespace-only document."""
212
+ result = ys.load(" \n \n ")
213
+ assert result is None
214
+
215
+
216
+ def test_quoted_strings(ys):
217
+ """Test loading quoted strings."""
218
+ result1 = ys.load("'hello world'")
219
+ assert result1 == "hello world"
220
+
221
+ result2 = ys.load('"hello world"')
222
+ assert result2 == "hello world"
223
+
224
+
225
+ def test_module_version():
226
+ """Test that the module has a version attribute."""
227
+ assert hasattr(yamlstar, 'yamlstar_version')
228
+ assert isinstance(yamlstar.yamlstar_version, str)