fluent-codegen 0.1.1__tar.gz → 0.3.0__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.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluent-codegen
3
- Version: 0.1.1
3
+ Version: 0.3.0
4
4
  Summary: A Python library for generating Python code via AST construction.
5
5
  Author-email: Luke Plant <luke@lukeplant.me.uk>
6
6
  License-Expression: Apache-2.0
7
7
  Project-URL: Repository, https://github.com/spookylukey/fluent-codegen
8
8
  Keywords: codegen,code-generation,ast,python,metaprogramming
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -76,7 +76,7 @@ fluent method-chaining for expressions:
76
76
  func, _ = module.create_function("fizzbuzz", args=["n"])
77
77
 
78
78
  # 2. A Name reference to the "n" parameter (Function *is* a Scope)
79
- n = codegen.Name("n", func)
79
+ n = func.name("n")
80
80
 
81
81
  # 3. Build an if / elif / else chain
82
82
  if_stmt = func.body.create_if()
@@ -94,7 +94,7 @@ fluent method-chaining for expressions:
94
94
  branch.create_return(codegen.String("Buzz"))
95
95
 
96
96
  # else: return str(n)
97
- if_stmt.else_block.create_return(codegen.function_call("str", [n], {}, func))
97
+ if_stmt.else_block.create_return(module.scope.name("str").call([n]))
98
98
 
99
99
  # 4. Inspect the generated source
100
100
  print(module.as_python_source())
@@ -54,7 +54,7 @@ fluent method-chaining for expressions:
54
54
  func, _ = module.create_function("fizzbuzz", args=["n"])
55
55
 
56
56
  # 2. A Name reference to the "n" parameter (Function *is* a Scope)
57
- n = codegen.Name("n", func)
57
+ n = func.name("n")
58
58
 
59
59
  # 3. Build an if / elif / else chain
60
60
  if_stmt = func.body.create_if()
@@ -72,7 +72,7 @@ fluent method-chaining for expressions:
72
72
  branch.create_return(codegen.String("Buzz"))
73
73
 
74
74
  # else: return str(n)
75
- if_stmt.else_block.create_return(codegen.function_call("str", [n], {}, func))
75
+ if_stmt.else_block.create_return(module.scope.name("str").call([n]))
76
76
 
77
77
  # 4. Inspect the generated source
78
78
  print(module.as_python_source())
@@ -14,7 +14,7 @@ keywords = [
14
14
  "metaprogramming",
15
15
  ]
16
16
  classifiers = [
17
- "Development Status :: 3 - Alpha",
17
+ "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
19
19
  "Operating System :: OS Independent",
20
20
  "Programming Language :: Python :: 3 :: Only",
@@ -24,7 +24,7 @@ classifiers = [
24
24
  "Topic :: Software Development :: Code Generators",
25
25
  "Topic :: Software Development :: Libraries :: Python Modules",
26
26
  ]
27
- version = "0.1.1"
27
+ version = "0.3.0"
28
28
  dependencies = []
29
29
 
30
30
  [project.urls]
@@ -74,7 +74,7 @@ dev = [
74
74
  "pyright==1.1.406",
75
75
  "pytest>=7.4.4",
76
76
  "pytest-cov>=7.0.0",
77
- "ruff>=0.4.0",
77
+ "ruff>=0.15.1",
78
78
  "tox>=4.36.0",
79
79
  "tox-uv>=1.29.0",
80
80
  ]
@@ -11,6 +11,7 @@ from typing import TypedDict
11
11
 
12
12
  Add = ast.Add
13
13
  And = ast.And
14
+ Assert = ast.Assert
14
15
  Assign = ast.Assign
15
16
  AnnAssign = ast.AnnAssign
16
17
  BoolOp = ast.BoolOp
@@ -77,6 +78,12 @@ alias = ast.alias
77
78
  # It's hard to get something sensible we can put for line/col numbers so we put arbitrary values.
78
79
 
79
80
 
81
+ try:
82
+ _Unparser = ast._Unparser # type: ignore
83
+ except AttributeError: # pragma: no cover
84
+ from _ast_unparse import Unparser as _Unparser # pragma: no cover # type: ignore
85
+
86
+
80
87
  class DefaultAstArgs(TypedDict):
81
88
  lineno: int
82
89
  col_offset: int
@@ -91,3 +98,31 @@ DEFAULT_AST_ARGS_ARGUMENTS: dict[str, object] = dict()
91
98
 
92
99
  def subscript_slice_object[T](value: T) -> T:
93
100
  return value
101
+
102
+
103
+ class CommentNode(ast.stmt):
104
+ """Custom AST statement node representing a comment.
105
+
106
+ This is not a standard Python AST node. It is ignored by ``compile()``
107
+ (callers must strip it first, which ``as_ast()`` does automatically),
108
+ but is rendered by :func:`unparse_with_comments`.
109
+ """
110
+
111
+ _fields = ("text",)
112
+
113
+ def __init__(self, text: str, **kwargs: int) -> None:
114
+ self.text = text
115
+ super().__init__(**kwargs) # type: ignore[reportCallIssue]
116
+
117
+
118
+ class _CommentUnparser(_Unparser): # type: ignore[reportAttributeAccessIssue]
119
+ """An unparser that knows how to render :class:`CommentNode`."""
120
+
121
+ def visit_CommentNode(self, node: CommentNode) -> None:
122
+ self.fill("# " + node.text) # type: ignore[reportAttributeAccessIssue]
123
+
124
+
125
+ def unparse_with_comments(node: ast.AST) -> str:
126
+ """Like :func:`ast.unparse`, but also renders :class:`CommentNode` nodes."""
127
+ unparser = _CommentUnparser()
128
+ return unparser.visit(node) # type: ignore[reportUnknownMemberType,reportUnknownVariableType]