pywire 0.1.0__py3-none-any.whl → 0.1.1__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.
Files changed (101) hide show
  1. {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/METADATA +23 -1
  2. pywire-0.1.1.dist-info/RECORD +9 -0
  3. pywire/__init__.py +0 -2
  4. pywire/cli/__init__.py +0 -1
  5. pywire/cli/generators.py +0 -48
  6. pywire/cli/main.py +0 -309
  7. pywire/cli/tui.py +0 -563
  8. pywire/cli/validate.py +0 -26
  9. pywire/client/.prettierignore +0 -8
  10. pywire/client/.prettierrc +0 -7
  11. pywire/client/build.mjs +0 -73
  12. pywire/client/eslint.config.js +0 -46
  13. pywire/client/package.json +0 -39
  14. pywire/client/pnpm-lock.yaml +0 -2971
  15. pywire/client/src/core/app.ts +0 -263
  16. pywire/client/src/core/dom-updater.test.ts +0 -78
  17. pywire/client/src/core/dom-updater.ts +0 -321
  18. pywire/client/src/core/index.ts +0 -5
  19. pywire/client/src/core/transport-manager.test.ts +0 -179
  20. pywire/client/src/core/transport-manager.ts +0 -159
  21. pywire/client/src/core/transports/base.ts +0 -122
  22. pywire/client/src/core/transports/http.ts +0 -142
  23. pywire/client/src/core/transports/index.ts +0 -13
  24. pywire/client/src/core/transports/websocket.ts +0 -97
  25. pywire/client/src/core/transports/webtransport.ts +0 -149
  26. pywire/client/src/dev/dev-app.ts +0 -93
  27. pywire/client/src/dev/error-trace.test.ts +0 -97
  28. pywire/client/src/dev/error-trace.ts +0 -76
  29. pywire/client/src/dev/index.ts +0 -4
  30. pywire/client/src/dev/status-overlay.ts +0 -63
  31. pywire/client/src/events/handler.test.ts +0 -318
  32. pywire/client/src/events/handler.ts +0 -454
  33. pywire/client/src/pywire.core.ts +0 -22
  34. pywire/client/src/pywire.dev.ts +0 -27
  35. pywire/client/tsconfig.json +0 -17
  36. pywire/client/vitest.config.ts +0 -15
  37. pywire/compiler/__init__.py +0 -6
  38. pywire/compiler/ast_nodes.py +0 -304
  39. pywire/compiler/attributes/__init__.py +0 -6
  40. pywire/compiler/attributes/base.py +0 -24
  41. pywire/compiler/attributes/conditional.py +0 -37
  42. pywire/compiler/attributes/events.py +0 -55
  43. pywire/compiler/attributes/form.py +0 -37
  44. pywire/compiler/attributes/loop.py +0 -75
  45. pywire/compiler/attributes/reactive.py +0 -34
  46. pywire/compiler/build.py +0 -28
  47. pywire/compiler/build_artifacts.py +0 -342
  48. pywire/compiler/codegen/__init__.py +0 -5
  49. pywire/compiler/codegen/attributes/__init__.py +0 -6
  50. pywire/compiler/codegen/attributes/base.py +0 -19
  51. pywire/compiler/codegen/attributes/events.py +0 -35
  52. pywire/compiler/codegen/directives/__init__.py +0 -6
  53. pywire/compiler/codegen/directives/base.py +0 -16
  54. pywire/compiler/codegen/directives/path.py +0 -53
  55. pywire/compiler/codegen/generator.py +0 -2341
  56. pywire/compiler/codegen/template.py +0 -2178
  57. pywire/compiler/directives/__init__.py +0 -7
  58. pywire/compiler/directives/base.py +0 -20
  59. pywire/compiler/directives/component.py +0 -33
  60. pywire/compiler/directives/context.py +0 -93
  61. pywire/compiler/directives/layout.py +0 -49
  62. pywire/compiler/directives/no_spa.py +0 -24
  63. pywire/compiler/directives/path.py +0 -71
  64. pywire/compiler/directives/props.py +0 -88
  65. pywire/compiler/exceptions.py +0 -19
  66. pywire/compiler/interpolation/__init__.py +0 -6
  67. pywire/compiler/interpolation/base.py +0 -28
  68. pywire/compiler/interpolation/jinja.py +0 -272
  69. pywire/compiler/parser.py +0 -750
  70. pywire/compiler/paths.py +0 -29
  71. pywire/compiler/preprocessor.py +0 -43
  72. pywire/core/wire.py +0 -119
  73. pywire/py.typed +0 -0
  74. pywire/runtime/__init__.py +0 -7
  75. pywire/runtime/aioquic_server.py +0 -194
  76. pywire/runtime/app.py +0 -889
  77. pywire/runtime/compile_error_page.py +0 -195
  78. pywire/runtime/debug.py +0 -203
  79. pywire/runtime/dev_server.py +0 -434
  80. pywire/runtime/dev_server.py.broken +0 -268
  81. pywire/runtime/error_page.py +0 -64
  82. pywire/runtime/error_renderer.py +0 -23
  83. pywire/runtime/escape.py +0 -23
  84. pywire/runtime/files.py +0 -40
  85. pywire/runtime/helpers.py +0 -97
  86. pywire/runtime/http_transport.py +0 -253
  87. pywire/runtime/loader.py +0 -272
  88. pywire/runtime/logging.py +0 -72
  89. pywire/runtime/page.py +0 -384
  90. pywire/runtime/pydantic_integration.py +0 -52
  91. pywire/runtime/router.py +0 -229
  92. pywire/runtime/server.py +0 -25
  93. pywire/runtime/style_collector.py +0 -31
  94. pywire/runtime/upload_manager.py +0 -76
  95. pywire/runtime/validation.py +0 -449
  96. pywire/runtime/websocket.py +0 -665
  97. pywire/runtime/webtransport_handler.py +0 -195
  98. pywire-0.1.0.dist-info/RECORD +0 -104
  99. {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/WHEEL +0 -0
  100. {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/entry_points.txt +0 -0
  101. {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,272 +0,0 @@
1
- """Jinja2-based interpolation parser."""
2
-
3
- import ast
4
- from typing import List, Union
5
-
6
- from jinja2 import Environment
7
-
8
- from pywire.compiler.ast_nodes import InterpolationNode
9
- from pywire.compiler.interpolation.base import InterpolationParser
10
-
11
-
12
- class JinjaInterpolationParser(InterpolationParser):
13
- """Jinja2-based interpolation parser."""
14
-
15
- def __init__(self) -> None:
16
- self.env = Environment(
17
- variable_start_string="{",
18
- variable_end_string="}",
19
- autoescape=True, # XSS protection
20
- )
21
-
22
- def _is_valid_python(self, text: str) -> bool:
23
- """Check if text is valid Python expression (or with format spec)."""
24
- stripped = text.strip()
25
-
26
- # 1. Try simple parse
27
- # 1. Try simple parse
28
- try:
29
- from pywire.compiler.preprocessor import preprocess_python_code
30
-
31
- preprocessed = preprocess_python_code(stripped)
32
- ast.parse(preprocessed, mode="eval")
33
- return True
34
- except SyntaxError:
35
- pass
36
-
37
- # 2. CSS-like check: If unparseable and contains semicolon, assume CSS
38
- if ";" in text:
39
- return False
40
-
41
- # 3. Format specifier check: Find top-level colon
42
- balance = 0
43
- quote = None
44
- split_idx = -1
45
-
46
- for i, char in enumerate(text):
47
- if quote:
48
- if char == quote:
49
- if i > 0 and text[i - 1] != "\\":
50
- quote = None
51
- else:
52
- if char in "\"'":
53
- quote = char
54
- elif char in "{[(":
55
- balance += 1
56
- elif char in "}])":
57
- balance -= 1
58
- elif char == ":" and balance == 0:
59
- split_idx = i
60
- break
61
-
62
- if split_idx != -1:
63
- # We strip the expression part to allow "{ x :.2f }"
64
- expr = text[:split_idx].strip()
65
- try:
66
- ast.parse(expr, mode="eval")
67
- return True
68
- except SyntaxError:
69
- pass
70
-
71
- return False
72
-
73
- def parse(
74
- self, text: str, line: int, col: int
75
- ) -> List[Union[str, InterpolationNode]]:
76
- """
77
- Parse text with {expression} into mix of strings and InterpolationNodes.
78
- Returns: ['Hello, ', InterpolationNode(expr='name'), '!']
79
- Supports complex expressions: {"text" if condition else "other"}
80
- """
81
- if not text:
82
- return [""]
83
-
84
- tokens: List[Union[str, InterpolationNode]] = []
85
- i = 0
86
- last_end = 0
87
-
88
- while i < len(text):
89
- if text[i] == "{":
90
- # Find matching closing brace
91
- brace_count = 1
92
- j = i + 1
93
- while j < len(text) and brace_count > 0:
94
- if text[j] == "{":
95
- brace_count += 1
96
- elif text[j] == "}":
97
- brace_count -= 1
98
- j += 1
99
-
100
- if brace_count == 0:
101
- # Found matching brace
102
- # Add any text before this brace
103
- if i > last_end:
104
- tokens.append(text[last_end:i])
105
-
106
- expr = text[i + 1 : j - 1] # Extract expression without braces
107
-
108
- # Check for {$html expr} syntax for raw/unescaped output
109
- is_raw = False
110
- if expr.lstrip().startswith("$html "):
111
- is_raw = True
112
- expr = expr.lstrip()[6:] # Strip "$html " prefix
113
-
114
- if self._is_valid_python(expr):
115
- # Calculate accurate line/col
116
- # Count newlines before this position (relative to start of text)
117
- prefix = text[:i]
118
- newlines = prefix.count("\n")
119
- current_line = line + newlines
120
-
121
- last_nl_index = prefix.rfind("\n")
122
- if last_nl_index != -1:
123
- # Column is offset from last newline
124
- current_column = i - last_nl_index - 1 # 0-indexed column?
125
- # If text lines are 0-indexed column wise?
126
- # Standard is 0-indexed usually for AST col_offset.
127
- else:
128
- # No newline, add to start col
129
- current_column = col + i
130
-
131
- tokens.append(
132
- InterpolationNode(
133
- expression=expr,
134
- line=current_line,
135
- column=current_column,
136
- is_raw=is_raw,
137
- )
138
- )
139
- else:
140
- # Treat as literal
141
- tokens.append(text[i:j])
142
-
143
- last_end = j
144
- i = j
145
- else:
146
- # Unmatched brace
147
- i += 1
148
- else:
149
- i += 1
150
-
151
- # Add any remaining text
152
- if last_end < len(text):
153
- tokens.append(text[last_end:])
154
-
155
- # Post-process to merge adjacent strings
156
- if not tokens:
157
- return [text]
158
-
159
- result: List[Union[str, InterpolationNode]] = []
160
- current_str: List[str] = []
161
-
162
- for token in tokens:
163
- if isinstance(token, str):
164
- current_str.append(token)
165
- else:
166
- if current_str:
167
- merged = "".join(current_str)
168
- if merged:
169
- result.append(merged)
170
- current_str = []
171
- result.append(token)
172
-
173
- if current_str:
174
- merged = "".join(current_str)
175
- if merged:
176
- result.append(merged)
177
-
178
- return result if result else [""]
179
-
180
- def compile(self, text: str) -> str:
181
- """
182
- Compile to Python f-string code for runtime.
183
- 'Hello {name}!' → f'Hello {self.name}!'
184
- 'Hello {"text" if cond else "other"}' → f'Hello {"text" if self.cond else "other"}'
185
- """
186
- if not text:
187
- return "''"
188
-
189
- # For now, use simple replacement for self. references
190
- # This is a simplification - ideally we'd parse the expression AST
191
- import re
192
-
193
- result = []
194
- i = 0
195
- last_end = 0
196
-
197
- while i < len(text):
198
- if text[i] == "{":
199
- # Add text before brace
200
- if i > last_end:
201
- result.append(text[last_end:i])
202
-
203
- # Find matching closing brace
204
- brace_count = 1
205
- j = i + 1
206
- while j < len(text) and brace_count > 0:
207
- if text[j] == "{":
208
- brace_count += 1
209
- elif text[j] == "}":
210
- brace_count -= 1
211
- j += 1
212
-
213
- if brace_count == 0:
214
- # Found matching brace
215
- # CHECK IF VALID PYTHON before trying to compile
216
- # (Though compile is usually called on text that parse() has
217
- # already mostly validated,
218
- # parse() returns nodes for valid interpolations.
219
- # Wait, template codegen calls compile() on text_content of nodes.
220
- # If parse() returned literal text for CSS, then compile() will
221
- # see the curly braces!
222
- # And compile() iterates braces independently.
223
- # So compile() MUST also respect the validity check!)
224
-
225
- expr = text[i + 1 : j - 1]
226
- if self._is_valid_python(expr):
227
- # Prepend self. to simple identifiers
228
- # For simple identifiers, add self.
229
- # For complex expressions, leave as is (they reference self.* already)
230
- if re.match(r"^\w+$", expr):
231
- result.append(f"{{self.{expr}}}")
232
- else:
233
- # Complex expression - assume it references self correctly
234
- # Replace standalone identifiers with self. references
235
- # This is simplistic but works for common cases
236
- modified_expr = re.sub(
237
- r"\b([a-zA-Z_]\w*)\b(?!\s*[(\[])",
238
- lambda m: f"self.{str(m.group(1))}"
239
- if m.group(1)
240
- not in (
241
- "if",
242
- "else",
243
- "and",
244
- "or",
245
- "not",
246
- "in",
247
- "is",
248
- "True",
249
- "False",
250
- "None",
251
- )
252
- else m.group(1),
253
- expr,
254
- )
255
- result.append(f"{{{modified_expr}}}")
256
- else:
257
- # Literal (CSS etc)
258
- result.append(text[i:j])
259
-
260
- last_end = j
261
- i = j
262
- else:
263
- i += 1
264
- else:
265
- i += 1
266
-
267
- # Add remaining text
268
- if last_end < len(text):
269
- result.append(text[last_end:])
270
-
271
- compiled = "".join(result)
272
- return f"f{repr(compiled)}"