Skip to content

Commit dc2363c

Browse files
authored
0.0.4-rc: multi auto-imports; generate mode (#5)
1 parent 117bc25 commit dc2363c

File tree

5 files changed

+62
-23
lines changed

5 files changed

+62
-23
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ f00
3939
f00bar
4040
```
4141

42+
## generate
43+
If the `_` variable is not used in the expression, its value is outputed:
44+
```bash
45+
pythoned '"\n".join(map(str, range(5)))'
46+
```
47+
output:
48+
```
49+
0
50+
1
51+
2
52+
3
53+
4
54+
```
55+
4256
## modules
4357

4458
Modules are auto-imported, example with `re`:

pythoned/__init__.py

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,56 @@
1+
import ast
12
import importlib
23
import os
34
import re
45
from types import ModuleType
5-
from typing import Dict, Iterator
6-
6+
from typing import Any, Dict, Iterator, Set
77

8+
LINE_IDENTIFIER = "_"
89
_TYPE_ERROR_MSG = "The provided expression must be an str (editing) or a bool (filtering), but got {}."
910

1011

12+
def iter_identifiers(expr: str) -> Iterator[str]:
13+
for node in iter_asts(ast.parse(expr, mode="eval").body):
14+
if isinstance(node, ast.Name):
15+
yield node.id
16+
17+
18+
def iter_asts(node: ast.AST) -> Iterator[ast.AST]:
19+
"""
20+
Depth-first traversal of nodes
21+
"""
22+
yield node
23+
yield from (
24+
name for child in ast.iter_child_nodes(node) for name in iter_asts(child)
25+
)
26+
27+
28+
def auto_import_eval(expression: str, globals: Dict[str, Any]) -> Any:
29+
globals = globals.copy()
30+
encountered_name_errors: Set[str] = set()
31+
while True:
32+
try:
33+
return eval(expression, globals)
34+
except NameError as name_error:
35+
if str(name_error) in encountered_name_errors:
36+
raise
37+
encountered_name_errors.add(str(name_error))
38+
match = re.match(r"name '([A-Za-z]+)'.*", str(name_error))
39+
if match:
40+
module = match.group(1)
41+
globals[module] = importlib.import_module(module)
42+
continue
43+
44+
1145
def edit(lines: Iterator[str], expression) -> Iterator[str]:
1246
modules: Dict[str, ModuleType] = {}
47+
1348
for line in lines:
1449
linesep = ""
1550
if line.endswith(os.linesep):
1651
linesep, line = os.linesep, line[: -len(os.linesep)]
17-
globals = {"_": line, **modules}
18-
try:
19-
value = eval(expression, globals)
20-
except NameError as name_error:
21-
match = re.match(r"name '([A-Za-z]+)'.*", str(name_error))
22-
if match:
23-
module = match.group(1)
24-
else:
25-
raise name_error
26-
try:
27-
modules[module] = importlib.import_module(module)
28-
globals = {"_": line, **modules}
29-
except:
30-
raise name_error
31-
value = eval(expression, globals)
52+
globals = {LINE_IDENTIFIER: line, **modules}
53+
value = auto_import_eval(expression, globals)
3254
if isinstance(value, str):
3355
yield value + linesep
3456
elif isinstance(value, bool):

pythoned/__main__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import argparse
22
import sys
33

4-
from pythoned import edit
4+
from pythoned import LINE_IDENTIFIER, auto_import_eval, edit, iter_identifiers
55

66

77
def main() -> int:
@@ -10,8 +10,11 @@ def main() -> int:
1010

1111
args = arg_parser.parse_args()
1212
expression: str = args.expression
13-
for output_line in edit(sys.stdin, expression):
14-
print(output_line, end="")
13+
if not LINE_IDENTIFIER in iter_identifiers(expression):
14+
print(auto_import_eval(expression, {}))
15+
else:
16+
for output_line in edit(sys.stdin, expression):
17+
print(output_line, end="")
1518
return 0
1619

1720

tests/test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_edit(self) -> None:
3131
msg="_ must exclude linesep",
3232
)
3333
self.assertEqual(
34-
list(edit(lines(), "str(int(math.pow(10, len(_))))")),
35-
["1000\n", "1000\n", "1000000"],
34+
list(edit(lines(), "re.sub('[0]', 'O', str(int(math.pow(10, len(_)))))")),
35+
["1OOO\n", "1OOO\n", "1OOOOOO"],
3636
msg="modules should be auto-imported",
3737
)

version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# to show the CHANGELOG: git log -- version.py
2-
__version__ = "0.0.3"
2+
__version__ = "0.0.4-rc"

0 commit comments

Comments
 (0)