dotprompt-handlebars 0.0.1.dev1__cp312-cp312-manylinux_2_33_aarch64.whl → 0.1.1__cp312-cp312-manylinux_2_33_aarch64.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.
- {dotprompt_handlebars-0.0.1.dev1.dist-info → dotprompt_handlebars-0.1.1.dist-info}/METADATA +1 -1
- dotprompt_handlebars-0.1.1.dist-info/RECORD +8 -0
- handlebarrz/__init__.py +149 -21
- handlebarrz/_native.cpython-312-aarch64-linux-gnu.so +0 -0
- handlebarrz/_native.pyi +10 -0
- dotprompt_handlebars-0.0.1.dev1.dist-info/RECORD +0 -8
- {dotprompt_handlebars-0.0.1.dev1.dist-info → dotprompt_handlebars-0.1.1.dist-info}/WHEEL +0 -0
- {dotprompt_handlebars-0.0.1.dev1.dist-info → dotprompt_handlebars-0.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
dotprompt_handlebars-0.1.1.dist-info/METADATA,sha256=xetUj3NI052dvFAy1UXVOA0dFWKFtDsj2Mdg28kiruA,1169
|
|
2
|
+
dotprompt_handlebars-0.1.1.dist-info/WHEEL,sha256=AB65alw64Me6t53a6SXTkmn8HmlUtkGRrmAow8OqOnM,109
|
|
3
|
+
dotprompt_handlebars-0.1.1.dist-info/licenses/LICENSE,sha256=bsvE5_qSn_2LH2G-haMvT_AoIeINhX6fvzZTlyq2xJY,11340
|
|
4
|
+
handlebarrz/_native.pyi,sha256=dfLbPXf2DYH-lxurBEUWk6hChe96uVQ2Ss3kOT4kPDw,2386
|
|
5
|
+
handlebarrz/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
handlebarrz/__init__.py,sha256=_wK1CTsv3NmXcQ20Jl1wvn_z19QNsHbGurMuXZcAjTI,23127
|
|
7
|
+
handlebarrz/_native.cpython-312-aarch64-linux-gnu.so,sha256=s8_z_yQOjYda4wyRFzFQ4m_DhsbL-qFIuxke9DgHymE,1535104
|
|
8
|
+
dotprompt_handlebars-0.1.1.dist-info/RECORD,,
|
handlebarrz/__init__.py
CHANGED
|
@@ -67,11 +67,14 @@ result = handlebars.render('formatted', {'name': 'World'}) # "Hello WORLD!"
|
|
|
67
67
|
```
|
|
68
68
|
"""
|
|
69
69
|
|
|
70
|
+
from __future__ import annotations
|
|
71
|
+
|
|
70
72
|
import json
|
|
73
|
+
import re
|
|
71
74
|
import sys # noqa
|
|
72
75
|
from collections.abc import Callable
|
|
73
76
|
from pathlib import Path
|
|
74
|
-
from typing import Any
|
|
77
|
+
from typing import Any, TypedDict
|
|
75
78
|
|
|
76
79
|
import structlog
|
|
77
80
|
|
|
@@ -81,6 +84,7 @@ else: # noqa
|
|
|
81
84
|
from enum import StrEnum # noqa
|
|
82
85
|
|
|
83
86
|
from ._native import (
|
|
87
|
+
HandlebarrzHelperOptions,
|
|
84
88
|
HandlebarrzTemplate,
|
|
85
89
|
html_escape,
|
|
86
90
|
no_escape,
|
|
@@ -89,8 +93,23 @@ from ._native import (
|
|
|
89
93
|
logger = structlog.get_logger(__name__)
|
|
90
94
|
|
|
91
95
|
|
|
92
|
-
HelperFn = Callable[[list[Any],
|
|
93
|
-
NativeHelperFn = Callable[[str,
|
|
96
|
+
HelperFn = Callable[[list[Any], 'HelperOptions'], str]
|
|
97
|
+
NativeHelperFn = Callable[[str, HandlebarrzHelperOptions], str]
|
|
98
|
+
Context = dict[str, Any]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class RuntimeOptions(TypedDict):
|
|
102
|
+
"""Options for the runtime of a Handlebars template.
|
|
103
|
+
|
|
104
|
+
These options are used to configure the runtime behavior of a Handlebars
|
|
105
|
+
template. They can be passed to the compiled template function to customize
|
|
106
|
+
the rendering process.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
data: dict[str, Any] | None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
CompiledRenderer = Callable[[Context, RuntimeOptions | None], str]
|
|
94
113
|
|
|
95
114
|
|
|
96
115
|
class EscapeFunction(StrEnum):
|
|
@@ -159,6 +178,7 @@ class Template:
|
|
|
159
178
|
self._template.set_escape_fn(escape_fn)
|
|
160
179
|
self._template.set_strict_mode(strict_mode)
|
|
161
180
|
self._template.set_dev_mode(dev_mode)
|
|
181
|
+
self._known_partials: set[str] = set()
|
|
162
182
|
|
|
163
183
|
@property
|
|
164
184
|
def strict_mode(self) -> bool:
|
|
@@ -226,7 +246,7 @@ class Template:
|
|
|
226
246
|
'function': escape_fn,
|
|
227
247
|
})
|
|
228
248
|
except ValueError as e:
|
|
229
|
-
logger.
|
|
249
|
+
logger.exception({'event': 'escape_function_error', 'error': str(e)})
|
|
230
250
|
raise
|
|
231
251
|
|
|
232
252
|
def register_template(self, name: str, template_string: str) -> None:
|
|
@@ -247,7 +267,7 @@ class Template:
|
|
|
247
267
|
self._template.register_template(name, template_string)
|
|
248
268
|
logger.debug({'event': 'template_registered', 'name': name})
|
|
249
269
|
except ValueError as e:
|
|
250
|
-
logger.
|
|
270
|
+
logger.exception({
|
|
251
271
|
'event': 'template_registration_error',
|
|
252
272
|
'name': name,
|
|
253
273
|
'error': str(e),
|
|
@@ -270,15 +290,27 @@ class Template:
|
|
|
270
290
|
"""
|
|
271
291
|
try:
|
|
272
292
|
self._template.register_partial(name, template_string)
|
|
293
|
+
self._known_partials.add(name)
|
|
273
294
|
logger.debug({'event': 'partial_registered', 'name': name})
|
|
274
|
-
except
|
|
275
|
-
logger.
|
|
295
|
+
except Exception as e:
|
|
296
|
+
logger.exception({
|
|
276
297
|
'event': 'partial_registration_error',
|
|
277
298
|
'name': name,
|
|
278
299
|
'error': str(e),
|
|
279
300
|
})
|
|
280
301
|
raise
|
|
281
302
|
|
|
303
|
+
def has_partial(self, name: str) -> bool:
|
|
304
|
+
"""Check if a partial is registered.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
name: The name of the partial to check.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
True if the partial is registered, False otherwise.
|
|
311
|
+
"""
|
|
312
|
+
return name in self._known_partials
|
|
313
|
+
|
|
282
314
|
def register_template_file(self, name: str, file_path: str | Path) -> None:
|
|
283
315
|
"""Register a template from a file.
|
|
284
316
|
|
|
@@ -303,7 +335,7 @@ class Template:
|
|
|
303
335
|
'path': file_path_str,
|
|
304
336
|
})
|
|
305
337
|
except (FileNotFoundError, ValueError) as e:
|
|
306
|
-
logger.
|
|
338
|
+
logger.exception({
|
|
307
339
|
'event': 'template_file_registration_error',
|
|
308
340
|
'name': name,
|
|
309
341
|
'path': file_path_str,
|
|
@@ -335,7 +367,7 @@ class Template:
|
|
|
335
367
|
'extension': extension,
|
|
336
368
|
})
|
|
337
369
|
except (FileNotFoundError, ValueError) as e:
|
|
338
|
-
logger.
|
|
370
|
+
logger.exception({
|
|
339
371
|
'event': 'templates_directory_registration_error',
|
|
340
372
|
'path': dir_path_str,
|
|
341
373
|
'extension': extension,
|
|
@@ -383,9 +415,9 @@ class Template:
|
|
|
383
415
|
try:
|
|
384
416
|
# TODO: Fix this type error.
|
|
385
417
|
self._template.register_helper(name, create_helper(helper_fn)) # type: ignore[arg-type]
|
|
386
|
-
logger.debug({'event': 'helper_registered', 'name': name})
|
|
418
|
+
# logger.debug({'event': 'helper_registered', 'name': name})
|
|
387
419
|
except Exception as e:
|
|
388
|
-
logger.
|
|
420
|
+
logger.exception({
|
|
389
421
|
'event': 'helper_registration_error',
|
|
390
422
|
'name': name,
|
|
391
423
|
'error': str(e),
|
|
@@ -414,7 +446,7 @@ class Template:
|
|
|
414
446
|
self._template.unregister_template(name)
|
|
415
447
|
logger.debug({'event': 'template_unregistered', 'name': name})
|
|
416
448
|
|
|
417
|
-
def render(self, name: str, data: dict[str, Any]) -> str:
|
|
449
|
+
def render(self, name: str, data: dict[str, Any], options: RuntimeOptions | None = None) -> str:
|
|
418
450
|
"""Render a template with the given data.
|
|
419
451
|
|
|
420
452
|
Renders a previously registered template using the provided data
|
|
@@ -424,6 +456,7 @@ class Template:
|
|
|
424
456
|
Args:
|
|
425
457
|
name: The name of the template to render
|
|
426
458
|
data: The data to render the template with
|
|
459
|
+
options: Additional options for the template.
|
|
427
460
|
|
|
428
461
|
Returns:
|
|
429
462
|
str: The rendered template string
|
|
@@ -432,19 +465,21 @@ class Template:
|
|
|
432
465
|
ValueError: If the template does not exist or there is a rendering
|
|
433
466
|
error.
|
|
434
467
|
"""
|
|
468
|
+
# TODO: options is currently ignored; need to add support for it.
|
|
469
|
+
|
|
435
470
|
try:
|
|
436
471
|
result = self._template.render(name, json.dumps(data))
|
|
437
472
|
logger.debug({'event': 'template_rendered', 'name': name})
|
|
438
473
|
return result
|
|
439
474
|
except ValueError as e:
|
|
440
|
-
logger.
|
|
475
|
+
logger.exception({
|
|
441
476
|
'event': 'template_rendering_error',
|
|
442
477
|
'name': name,
|
|
443
478
|
'error': str(e),
|
|
444
479
|
})
|
|
445
480
|
raise
|
|
446
481
|
|
|
447
|
-
def render_template(self, template_string: str, data: dict[str, Any]) -> str:
|
|
482
|
+
def render_template(self, template_string: str, data: dict[str, Any], options: RuntimeOptions | None = None) -> str:
|
|
448
483
|
"""Render a template string directly without registering it.
|
|
449
484
|
|
|
450
485
|
Parses and renders the template string in one step. This is useful for
|
|
@@ -454,6 +489,7 @@ class Template:
|
|
|
454
489
|
Args:
|
|
455
490
|
template_string: The template string to render
|
|
456
491
|
data: The data to render the template with
|
|
492
|
+
options: Additional options for the template.
|
|
457
493
|
|
|
458
494
|
Returns:
|
|
459
495
|
Rendered template string.
|
|
@@ -463,16 +499,79 @@ class Template:
|
|
|
463
499
|
rendering error.
|
|
464
500
|
"""
|
|
465
501
|
try:
|
|
502
|
+
# Merging runtime data with the data dictionary.
|
|
503
|
+
runtime_data = (options.get('data') if options is not None else {}) or {}
|
|
504
|
+
for k, v in runtime_data.items():
|
|
505
|
+
data[k] = v
|
|
506
|
+
|
|
507
|
+
# TODO: get rid of this once the Rust library has a local variables
|
|
508
|
+
# support.
|
|
509
|
+
|
|
510
|
+
# Local variables support workaround:
|
|
511
|
+
# Context: Handlebars Rust implementation currently doesn't support
|
|
512
|
+
# local variables (e.g. {{@my_variable}}), so instead we are
|
|
513
|
+
# dynamically replacing those with global variables
|
|
514
|
+
# (e.g. {{my_variable}}) to make things work.
|
|
515
|
+
# This is of course not an ideal solution, and it comes with some
|
|
516
|
+
# overhead, but so far we weren't able to detect a use case where
|
|
517
|
+
# this could be a blocker (but time will tell).
|
|
518
|
+
matches = re.findall(r'{{@.*?}}', template_string)
|
|
519
|
+
for m in set(matches):
|
|
520
|
+
key = m.strip('{}@').split('.')[0]
|
|
521
|
+
if key in runtime_data:
|
|
522
|
+
template_string = template_string.replace(m, m.replace('@', ''))
|
|
523
|
+
|
|
524
|
+
# Render the template.
|
|
466
525
|
result = self._template.render_template(template_string, json.dumps(data))
|
|
467
526
|
logger.debug({'event': 'template_string_rendered'})
|
|
468
527
|
return result
|
|
469
528
|
except ValueError as e:
|
|
470
|
-
logger.
|
|
529
|
+
logger.exception({
|
|
471
530
|
'event': 'template_string_rendering_error',
|
|
472
531
|
'error': str(e),
|
|
473
532
|
})
|
|
474
533
|
raise
|
|
475
534
|
|
|
535
|
+
def compile(self, template_string: str) -> CompiledRenderer:
|
|
536
|
+
"""Compile a template string into a reusable function.
|
|
537
|
+
|
|
538
|
+
This method provides an interface similar to Handlebars.js's `compile`.
|
|
539
|
+
It takes a template string and returns a function that can be called
|
|
540
|
+
with different data contexts to render the template.
|
|
541
|
+
|
|
542
|
+
Note: Unlike the JS version which can bake options into the compiled
|
|
543
|
+
template, this implementation returns a function that uses the current
|
|
544
|
+
configuration (strict mode, escape function, registered helpers, etc.)
|
|
545
|
+
of the `Template` instance at the time the returned function is called.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
template_string: The Handlebars template string to compile.
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
A callable function that takes a data dictionary and some runtime
|
|
552
|
+
options and returns the rendered string.
|
|
553
|
+
|
|
554
|
+
Raises:
|
|
555
|
+
ValueError: If there is a syntax error during the initial parse
|
|
556
|
+
check performed by render_template (depending on Rust implementation
|
|
557
|
+
details). Rendering errors will occur when the returned function is
|
|
558
|
+
called.
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
def compiled(context: Context, options: RuntimeOptions | None = None) -> str:
|
|
562
|
+
"""Compiled template function.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
context: The data to render the template with.
|
|
566
|
+
options: Additional options for the template.
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
The rendered template string.
|
|
570
|
+
"""
|
|
571
|
+
return self.render_template(template_string, context, options)
|
|
572
|
+
|
|
573
|
+
return compiled
|
|
574
|
+
|
|
476
575
|
def register_extra_helpers(self) -> None:
|
|
477
576
|
"""Registers extra helper functions.
|
|
478
577
|
|
|
@@ -487,13 +586,45 @@ class Template:
|
|
|
487
586
|
self._template.register_extra_helpers()
|
|
488
587
|
logger.debug({'event': 'extra_helpers_registered'})
|
|
489
588
|
except Exception as e:
|
|
490
|
-
logger.
|
|
589
|
+
logger.exception({
|
|
491
590
|
'event': 'extra_helpers_registration_error',
|
|
492
591
|
'error': str(e),
|
|
493
592
|
})
|
|
494
593
|
raise
|
|
495
594
|
|
|
496
595
|
|
|
596
|
+
class HelperOptions:
|
|
597
|
+
"""Handlebars helper options."""
|
|
598
|
+
|
|
599
|
+
def __init__(self, options: HandlebarrzHelperOptions) -> None:
|
|
600
|
+
self._options: HandlebarrzHelperOptions = options
|
|
601
|
+
|
|
602
|
+
def context(self) -> dict[str, Any]:
|
|
603
|
+
"""Get a representation of a context."""
|
|
604
|
+
context_json = self._options.context_json()
|
|
605
|
+
return json.loads(context_json) or {}
|
|
606
|
+
|
|
607
|
+
def hash_value(self, key: str) -> Any:
|
|
608
|
+
"""Get a hash value for the given key (resolved within the context).
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
key: The key corresponding for the hash required.
|
|
612
|
+
|
|
613
|
+
Returns:
|
|
614
|
+
The hash value.
|
|
615
|
+
"""
|
|
616
|
+
hash_value_json = self._options.hash_value_json(key)
|
|
617
|
+
return json.loads(hash_value_json) if hash_value_json else ''
|
|
618
|
+
|
|
619
|
+
def fn(self) -> str:
|
|
620
|
+
"""Renders a default inner template (if the helper is a block helper)."""
|
|
621
|
+
return self._options.template()
|
|
622
|
+
|
|
623
|
+
def inverse(self) -> str:
|
|
624
|
+
"""Renders the template of the else branch (if any)."""
|
|
625
|
+
return self._options.inverse()
|
|
626
|
+
|
|
627
|
+
|
|
497
628
|
def create_helper(
|
|
498
629
|
fn: HelperFn,
|
|
499
630
|
) -> NativeHelperFn:
|
|
@@ -518,12 +649,9 @@ def create_helper(
|
|
|
518
649
|
Function compatible with the Rust interface.
|
|
519
650
|
"""
|
|
520
651
|
|
|
521
|
-
def wrapper(params_json: str,
|
|
652
|
+
def wrapper(params_json: str, options: HandlebarrzHelperOptions) -> str:
|
|
522
653
|
params = json.loads(params_json)
|
|
523
|
-
|
|
524
|
-
ctx = json.loads(ctx_json)
|
|
525
|
-
|
|
526
|
-
result = fn(params, hash, ctx)
|
|
654
|
+
result = fn(params, HelperOptions(options))
|
|
527
655
|
return result
|
|
528
656
|
|
|
529
657
|
return wrapper
|
|
Binary file
|
handlebarrz/_native.pyi
CHANGED
|
@@ -17,10 +17,20 @@
|
|
|
17
17
|
"""Stub type annotations for native Handlebars."""
|
|
18
18
|
|
|
19
19
|
from collections.abc import Callable
|
|
20
|
+
from typing import Any
|
|
20
21
|
|
|
21
22
|
def html_escape(text: str) -> str: ...
|
|
22
23
|
def no_escape(text: str) -> str: ...
|
|
23
24
|
|
|
25
|
+
class HandlebarrzHelperOptions:
|
|
26
|
+
"""Stub type annotations for native Handlebars helper options."""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None: ...
|
|
29
|
+
def context_json(self) -> str: ...
|
|
30
|
+
def hash_value_json(self, key: str) -> str: ...
|
|
31
|
+
def template(self) -> str: ...
|
|
32
|
+
def inverse(self) -> str: ...
|
|
33
|
+
|
|
24
34
|
class HandlebarrzTemplate:
|
|
25
35
|
"""Stub type annotations for native Handlebars."""
|
|
26
36
|
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
dotprompt_handlebars-0.0.1.dev1.dist-info/METADATA,sha256=9_xBpQnwIVm71CTHLHmtbtuCE--s2vPbFOXSwzTs6_E,1174
|
|
2
|
-
dotprompt_handlebars-0.0.1.dev1.dist-info/WHEEL,sha256=AB65alw64Me6t53a6SXTkmn8HmlUtkGRrmAow8OqOnM,109
|
|
3
|
-
dotprompt_handlebars-0.0.1.dev1.dist-info/licenses/LICENSE,sha256=bsvE5_qSn_2LH2G-haMvT_AoIeINhX6fvzZTlyq2xJY,11340
|
|
4
|
-
handlebarrz/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
handlebarrz/__init__.py,sha256=pxIV7rtRAfnOlv6q3oPiNT1Srs2KR3CYrWtVAksDCKw,18124
|
|
6
|
-
handlebarrz/_native.pyi,sha256=GhUc2bN9WHgl4OKBRLMjvPi4mg7FzVrXBsMpgajsiVo,2063
|
|
7
|
-
handlebarrz/_native.cpython-312-aarch64-linux-gnu.so,sha256=sk3nPZ0hKrfnJ4R1-cKoxDwjOOSrOjuURxTS45AVKH4,1523752
|
|
8
|
-
dotprompt_handlebars-0.0.1.dev1.dist-info/RECORD,,
|
|
File without changes
|
{dotprompt_handlebars-0.0.1.dev1.dist-info → dotprompt_handlebars-0.1.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|