Skip to content

Commit 8682e2c

Browse files
authored
Merge pull request #18 from django-components/jo-safe-eval-allow-multiline-and-comments
2 parents 5e31d14 + 13b9106 commit 8682e2c

File tree

9 files changed

+977
-87
lines changed

9 files changed

+977
-87
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/djc-safe-eval/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ ruff_python_ast = { workspace = true }
1212
ruff_python_codegen = { workspace = true }
1313
ruff_source_file = { workspace = true }
1414
ruff_text_size = { workspace = true }
15+
regex = { workspace = true }

crates/djc-safe-eval/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ expression, but also collects metadata for the linter about:
3333

3434
1. What variables were used in the expression
3535
2. What variables were introduced in the expression using walrus operator `x := 1`
36+
3. What comments were found in the expression (including their positions and text)
3637

3738
## Usage
3839

@@ -249,6 +250,39 @@ The entire python code must be a SINGLE [expression](https://docs.python.org/3/l
249250

250251
For simplicity we don't allow async features like async comprehensions.
251252

253+
### Comments
254+
255+
Python comments (`# ...`) are supported and are captured during parsing. Comments are preserved with their positions and text content, allowing linters and other tools to analyze them.
256+
257+
```python
258+
compiled = safe_eval("x + 1 # Add one to x")
259+
```
260+
261+
### Multiline expressions
262+
263+
Multiline expressions are supported. When an expression spans multiple lines, it is automatically wrapped in parentheses with newlines: `(\n{}\n)`. This wrapping serves two purposes:
264+
265+
1. **Enables multiline syntax**: In Python, when you're inside parentheses `(...)`, square brackets `[...]`, or curly braces `{...}`, Python ignores indentation and allows expressions to span multiple lines. This means you can write:
266+
267+
```python
268+
[
269+
1,
270+
2,
271+
3,
272+
]
273+
```
274+
275+
Without the wrapping, Python would require proper indentation.
276+
277+
2. **Allows trailing comments**: So we wrap the original expression in `(...)`. If used decided to add a comment after the expression, the comment would consume the closing `)`. Hence, we also add newlines so that `(` and `)` are on separate lines:
278+
279+
```
280+
(2 + 2 # comment) ❌ Comment consumes the ')'
281+
(\n2 + 2 # comment\n) ✅ Comment is on separate line
282+
```
283+
284+
The wrapping is transparent to users - all token positions (variables, comments, etc.) are automatically adjusted to reference the original unwrapped source positions.
285+
252286
### Supported syntax
253287

254288
Almost anything that is a valid Python expression is allowed:

0 commit comments

Comments
 (0)