python-fragments 0.16__tar.gz → 0.18__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.
Files changed (45) hide show
  1. {python_fragments-0.16 → python_fragments-0.18}/PKG-INFO +1 -1
  2. {python_fragments-0.16 → python_fragments-0.18}/fragments/ast_nodes.py +3 -2
  3. {python_fragments-0.16 → python_fragments-0.18}/fragments/grammar.py +1 -1
  4. {python_fragments-0.16 → python_fragments-0.18}/pyproject.toml +1 -1
  5. {python_fragments-0.16 → python_fragments-0.18}/python_fragments.egg-info/PKG-INFO +1 -1
  6. {python_fragments-0.16 → python_fragments-0.18}/tests/test_grammar.py +67 -0
  7. {python_fragments-0.16 → python_fragments-0.18}/README.md +0 -0
  8. {python_fragments-0.16 → python_fragments-0.18}/fragments/__init__.py +0 -0
  9. {python_fragments-0.16 → python_fragments-0.18}/fragments/cli.py +0 -0
  10. {python_fragments-0.16 → python_fragments-0.18}/fragments/html/__init__.py +0 -0
  11. {python_fragments-0.16 → python_fragments-0.18}/fragments/html/elements.py +0 -0
  12. {python_fragments-0.16 → python_fragments-0.18}/fragments/loader.py +0 -0
  13. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/__init__.py +0 -0
  14. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/based_proxy.py +0 -0
  15. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/__init__.py +0 -0
  16. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/code_actions.py +0 -0
  17. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/completion.py +0 -0
  18. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/definition.py +0 -0
  19. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/diagnostics.py +0 -0
  20. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/document_highlight.py +0 -0
  21. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/document_symbols.py +0 -0
  22. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/folding_range.py +0 -0
  23. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/hover.py +0 -0
  24. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/inlay_hints.py +0 -0
  25. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/lifecycle.py +0 -0
  26. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/references.py +0 -0
  27. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/rename.py +0 -0
  28. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/semantic_tokens.py +0 -0
  29. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/client_message_handlers/signature_help.py +0 -0
  30. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/file_state.py +0 -0
  31. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/message_queue.py +0 -0
  32. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/pyright_notification_handlers/__init__.py +0 -0
  33. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/pyright_notification_handlers/capability.py +0 -0
  34. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/pyright_notification_handlers/configuration.py +0 -0
  35. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/pyright_notification_handlers/diagnostics.py +0 -0
  36. {python_fragments-0.16 → python_fragments-0.18}/fragments/lsp/types.py +0 -0
  37. {python_fragments-0.16 → python_fragments-0.18}/fragments/source.py +0 -0
  38. {python_fragments-0.16 → python_fragments-0.18}/fragments/transpiler.py +0 -0
  39. {python_fragments-0.16 → python_fragments-0.18}/python_fragments.egg-info/SOURCES.txt +0 -0
  40. {python_fragments-0.16 → python_fragments-0.18}/python_fragments.egg-info/dependency_links.txt +0 -0
  41. {python_fragments-0.16 → python_fragments-0.18}/python_fragments.egg-info/entry_points.txt +0 -0
  42. {python_fragments-0.16 → python_fragments-0.18}/python_fragments.egg-info/requires.txt +0 -0
  43. {python_fragments-0.16 → python_fragments-0.18}/python_fragments.egg-info/top_level.txt +0 -0
  44. {python_fragments-0.16 → python_fragments-0.18}/setup.cfg +0 -0
  45. {python_fragments-0.16 → python_fragments-0.18}/tests/test_source_map.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fragments
3
- Version: 0.16
3
+ Version: 0.18
4
4
  Summary: Modern HTML template rendering in Python
5
5
  Author-email: The Running Algorithm <services@therunningalgorithm.info>
6
6
  License: Proprietary
@@ -389,7 +389,8 @@ class ASTHTMLAttribute:
389
389
  self.transpiled_start = transpiled_start
390
390
 
391
391
  if self.string_literal is not None:
392
- self.transpiled_content = self.__template__.format(self.name, self.string_literal)
392
+ escaped_literal = self.string_literal.replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r").replace('"', '"')
393
+ self.transpiled_content = self.__template__.format(self.name, escaped_literal)
393
394
  self.transpiled_end = self.transpiled_start + len(self.transpiled_content)
394
395
  return
395
396
 
@@ -435,7 +436,7 @@ class ASTHTMLText:
435
436
  __template__: str = '"{}"'
436
437
 
437
438
  def transpile(self, transpiled_start: int) -> None:
438
- self.transpiled_content = self.__template__.format(self.text)
439
+ self.transpiled_content = self.__template__.format(self.text.replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r").replace('"', '"'))
439
440
  self.transpiled_start = transpiled_start
440
441
  self.transpiled_end = self.transpiled_start + len(self.transpiled_content)
441
442
 
@@ -19,7 +19,7 @@ from fragments.source import Source
19
19
 
20
20
  HTML_IDENTIFIER = r"[a-zA-Z][a-zA-Z0-9_:.-]*"
21
21
  HTML_ATTRIBUTE_NAME = r"[a-zA-Z:-_][a-zA-Z0-9_:.-]*"
22
- HTML_TEXT = r"(.*?)(?=<|{{)"
22
+ HTML_TEXT = r"([\s\S]*?)(?=<|{{)"
23
23
 
24
24
 
25
25
  class ParsingError(Exception):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-fragments"
7
- version = "0.16"
7
+ version = "0.18"
8
8
  description = "Modern HTML template rendering in Python"
9
9
  authors = [{ name = "The Running Algorithm", email = "services@therunningalgorithm.info" }]
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fragments
3
- Version: 0.16
3
+ Version: 0.18
4
4
  Summary: Modern HTML template rendering in Python
5
5
  Author-email: The Running Algorithm <services@therunningalgorithm.info>
6
6
  License: Proprietary
@@ -94,6 +94,26 @@ def test_html_element_interpolation_attribute():
94
94
  ]))
95
95
 
96
96
 
97
+ def test_html_attribute_multiline_string_literal_escaped_in_transpile():
98
+ source = Source.from_string('<><div class="foo\nbar">text</div></>')
99
+ _, fragment = grammar.expect_fragment(source)
100
+ fragment.transpile(0)
101
+ element = fragment.children[0]
102
+ assert isinstance(element, ASTHTMLElement)
103
+ assert "\\n" in element.attributes["class"].transpiled_content
104
+ assert "\n" not in element.attributes["class"].transpiled_content
105
+
106
+
107
+ def test_html_attribute_tab_escaped_in_transpile():
108
+ source = Source.from_string('<><div class="foo\tbar">text</div></>')
109
+ _, fragment = grammar.expect_fragment(source)
110
+ fragment.transpile(0)
111
+ element = fragment.children[0]
112
+ assert isinstance(element, ASTHTMLElement)
113
+ assert "\\t" in element.attributes["class"].transpiled_content
114
+ assert "\t" not in element.attributes["class"].transpiled_content
115
+
116
+
97
117
  def test_html_element_boolean_attribute():
98
118
  source = Source.from_string("<><input disabled /></>")
99
119
  source, fragment = grammar.expect_fragment(source)
@@ -294,6 +314,53 @@ def test_lowercase_not_parsed_as_component():
294
314
  assert isinstance(fragment.children[0], ASTHTMLElement)
295
315
 
296
316
 
317
+ # ---------------------------------------------------------------------------
318
+ # HTML text
319
+ # ---------------------------------------------------------------------------
320
+
321
+
322
+ def test_html_text_single_line():
323
+ source = Source.from_string("<><p>hello world</p></>")
324
+ source, fragment = grammar.expect_fragment(source)
325
+ assert source.at_end()
326
+ assert _transpiled(fragment) == _transpiled(ASTFragment(-1, -1, [
327
+ ASTHTMLElement(-1, -1, "p", {}, [ASTHTMLText(-1, -1, "hello world")], False)
328
+ ]))
329
+
330
+
331
+ def test_html_text_multiline():
332
+ source = Source.from_string("<><p>hello\nworld</p></>")
333
+ source, fragment = grammar.expect_fragment(source)
334
+ assert source.at_end()
335
+ assert _transpiled(fragment) == _transpiled(ASTFragment(-1, -1, [
336
+ ASTHTMLElement(-1, -1, "p", {}, [ASTHTMLText(-1, -1, "hello\nworld")], False)
337
+ ]))
338
+
339
+
340
+ def test_html_text_multiline_before_interpolation():
341
+ source = Source.from_string("<><p>hello\n{{ name }}</p></>")
342
+ source, fragment = grammar.expect_fragment(source)
343
+ assert source.at_end()
344
+ assert _transpiled(fragment) == _transpiled(ASTFragment(-1, -1, [
345
+ ASTHTMLElement(-1, -1, "p", {}, [
346
+ ASTHTMLText(-1, -1, "hello\n"),
347
+ ASTInterpolation(-1, -1, "name", 1, 1),
348
+ ], False)
349
+ ]))
350
+
351
+
352
+ def test_html_text_multiline_before_child_element():
353
+ source = Source.from_string("<><p>hello\n<span>world</span></p></>")
354
+ source, fragment = grammar.expect_fragment(source)
355
+ assert source.at_end()
356
+ assert _transpiled(fragment) == _transpiled(ASTFragment(-1, -1, [
357
+ ASTHTMLElement(-1, -1, "p", {}, [
358
+ ASTHTMLText(-1, -1, "hello\n"),
359
+ ASTHTMLElement(-1, -1, "span", {}, [ASTHTMLText(-1, -1, "world")], False),
360
+ ], False)
361
+ ]))
362
+
363
+
297
364
  # ---------------------------------------------------------------------------
298
365
  # Full integration (updated for new AST structure)
299
366
  # ---------------------------------------------------------------------------