pyoq-sql 1.0.2__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 (267) hide show
  1. pyoq/__init__.py +10 -0
  2. pyoq/__main__.py +5 -0
  3. pyoq/_native.pyi +5 -0
  4. pyoq/cli/__init__.py +5 -0
  5. pyoq/cli/commands.py +270 -0
  6. pyoq/cli/defaults.py +98 -0
  7. pyoq/cli/services.py +97 -0
  8. pyoq/config/__init__.py +31 -0
  9. pyoq/config/connection.py +161 -0
  10. pyoq/config/loader.py +289 -0
  11. pyoq/config/models.py +245 -0
  12. pyoq/config/values.py +142 -0
  13. pyoq/descriptors.py +165 -0
  14. pyoq/diagnostics/__init__.py +68 -0
  15. pyoq/diagnostics/budget.py +136 -0
  16. pyoq/diagnostics/events.py +137 -0
  17. pyoq/diagnostics/fingerprint.py +267 -0
  18. pyoq/diagnostics/instrumented.py +237 -0
  19. pyoq/diagnostics/metrics.py +61 -0
  20. pyoq/diagnostics/observation.py +227 -0
  21. pyoq/diagnostics/scoped.py +103 -0
  22. pyoq/django/__init__.py +15 -0
  23. pyoq/django/apps.py +17 -0
  24. pyoq/django/execution.py +317 -0
  25. pyoq/django/generation.py +59 -0
  26. pyoq/django/management/__init__.py +0 -0
  27. pyoq/django/management/commands/__init__.py +0 -0
  28. pyoq/django/management/commands/makemigrations.py +53 -0
  29. pyoq/django/management/commands/pyoq_codegen.py +75 -0
  30. pyoq/django/parameters.py +101 -0
  31. pyoq/django/schema.py +379 -0
  32. pyoq/django/settings.py +87 -0
  33. pyoq/django/timeouts.py +105 -0
  34. pyoq/dsl/__init__.py +64 -0
  35. pyoq/dsl/aio/__init__.py +31 -0
  36. pyoq/dsl/aio/context.py +295 -0
  37. pyoq/dsl/aio/queries.py +335 -0
  38. pyoq/dsl/aio/writes.py +368 -0
  39. pyoq/dsl/context.py +326 -0
  40. pyoq/dsl/entry.py +37 -0
  41. pyoq/dsl/labels.py +36 -0
  42. pyoq/dsl/queries.py +339 -0
  43. pyoq/dsl/result.py +164 -0
  44. pyoq/dsl/writes.py +360 -0
  45. pyoq/errors.py +317 -0
  46. pyoq/fastapi/__init__.py +32 -0
  47. pyoq/fastapi/dependencies.py +167 -0
  48. pyoq/fastapi/lifespan.py +119 -0
  49. pyoq/fetching/__init__.py +55 -0
  50. pyoq/fetching/collections.py +136 -0
  51. pyoq/fetching/execution.py +587 -0
  52. pyoq/fetching/joined.py +79 -0
  53. pyoq/fetching/nesting.py +183 -0
  54. pyoq/fetching/plans.py +541 -0
  55. pyoq/fetching/select_in.py +149 -0
  56. pyoq/fetching/tables.py +110 -0
  57. pyoq/generation/__init__.py +54 -0
  58. pyoq/generation/cleanup.py +44 -0
  59. pyoq/generation/contracts.py +248 -0
  60. pyoq/generation/drift.py +169 -0
  61. pyoq/generation/lock.py +33 -0
  62. pyoq/generation/manifest.py +114 -0
  63. pyoq/generation/model.py +1001 -0
  64. pyoq/generation/pipeline.py +119 -0
  65. pyoq/generation/rendering/__init__.py +5 -0
  66. pyoq/generation/rendering/domains.py +51 -0
  67. pyoq/generation/rendering/enums.py +29 -0
  68. pyoq/generation/rendering/exports.py +70 -0
  69. pyoq/generation/rendering/imports.py +63 -0
  70. pyoq/generation/rendering/package.py +56 -0
  71. pyoq/generation/rendering/relations.py +133 -0
  72. pyoq/generation/rendering/routines.py +396 -0
  73. pyoq/generation/rendering/rows.py +79 -0
  74. pyoq/generation/rendering/source.py +121 -0
  75. pyoq/generation/rendering/tables.py +300 -0
  76. pyoq/generation/rendering/writes.py +514 -0
  77. pyoq/generation/validation.py +27 -0
  78. pyoq/generation/writer.py +184 -0
  79. pyoq/hydration/__init__.py +24 -0
  80. pyoq/hydration/engine.py +155 -0
  81. pyoq/hydration/identity.py +194 -0
  82. pyoq/hydration/plan.py +116 -0
  83. pyoq/migrations/__init__.py +9 -0
  84. pyoq/migrations/alembic.py +106 -0
  85. pyoq/migrations/hooks.py +75 -0
  86. pyoq/naming.py +261 -0
  87. pyoq/policies/__init__.py +47 -0
  88. pyoq/policies/bypass.py +122 -0
  89. pyoq/policies/governed.py +430 -0
  90. pyoq/policies/model.py +242 -0
  91. pyoq/policies/rewriting.py +263 -0
  92. pyoq/py.typed +1 -0
  93. pyoq/query/__init__.py +312 -0
  94. pyoq/query/aggregates.py +172 -0
  95. pyoq/query/arrays.py +65 -0
  96. pyoq/query/binding.py +52 -0
  97. pyoq/query/capabilities.py +317 -0
  98. pyoq/query/casts.py +73 -0
  99. pyoq/query/choices.py +185 -0
  100. pyoq/query/decoding.py +360 -0
  101. pyoq/query/documents.py +56 -0
  102. pyoq/query/execution/__init__.py +63 -0
  103. pyoq/query/execution/aio/__init__.py +31 -0
  104. pyoq/query/execution/aio/operations.py +228 -0
  105. pyoq/query/execution/aio/pooling.py +233 -0
  106. pyoq/query/execution/aio/streaming.py +161 -0
  107. pyoq/query/execution/aio/transactions.py +105 -0
  108. pyoq/query/execution/batch.py +96 -0
  109. pyoq/query/execution/binding_style.py +30 -0
  110. pyoq/query/execution/compilation.py +48 -0
  111. pyoq/query/execution/context.py +61 -0
  112. pyoq/query/execution/control.py +50 -0
  113. pyoq/query/execution/operations.py +224 -0
  114. pyoq/query/execution/planning.py +107 -0
  115. pyoq/query/execution/pooling.py +279 -0
  116. pyoq/query/execution/results.py +36 -0
  117. pyoq/query/execution/streaming.py +178 -0
  118. pyoq/query/execution/transactions.py +95 -0
  119. pyoq/query/expressions.py +1200 -0
  120. pyoq/query/fields.py +60 -0
  121. pyoq/query/mysql/__init__.py +59 -0
  122. pyoq/query/mysql/aio/__init__.py +38 -0
  123. pyoq/query/mysql/aio/commands.py +389 -0
  124. pyoq/query/mysql/aio/driver.py +196 -0
  125. pyoq/query/mysql/aio/executor.py +123 -0
  126. pyoq/query/mysql/aio/factory.py +26 -0
  127. pyoq/query/mysql/aio/operations.py +38 -0
  128. pyoq/query/mysql/aio/pool.py +53 -0
  129. pyoq/query/mysql/aio/transactions.py +313 -0
  130. pyoq/query/mysql/commands.py +354 -0
  131. pyoq/query/mysql/compiler.py +134 -0
  132. pyoq/query/mysql/context.py +20 -0
  133. pyoq/query/mysql/executor.py +126 -0
  134. pyoq/query/mysql/expressions.py +244 -0
  135. pyoq/query/mysql/factory.py +46 -0
  136. pyoq/query/mysql/health.py +66 -0
  137. pyoq/query/mysql/identifiers.py +9 -0
  138. pyoq/query/mysql/model.py +79 -0
  139. pyoq/query/mysql/operations.py +43 -0
  140. pyoq/query/mysql/parameters.py +69 -0
  141. pyoq/query/mysql/planning.py +20 -0
  142. pyoq/query/mysql/pool.py +67 -0
  143. pyoq/query/mysql/transactions.py +331 -0
  144. pyoq/query/mysql/writes.py +73 -0
  145. pyoq/query/nodes.py +750 -0
  146. pyoq/query/postgres/__init__.py +48 -0
  147. pyoq/query/postgres/aio/__init__.py +25 -0
  148. pyoq/query/postgres/aio/bulk.py +56 -0
  149. pyoq/query/postgres/aio/commands.py +264 -0
  150. pyoq/query/postgres/aio/executor.py +152 -0
  151. pyoq/query/postgres/aio/factory.py +26 -0
  152. pyoq/query/postgres/aio/operations.py +26 -0
  153. pyoq/query/postgres/aio/pool.py +40 -0
  154. pyoq/query/postgres/aio/transactions.py +295 -0
  155. pyoq/query/postgres/bulk.py +62 -0
  156. pyoq/query/postgres/commands.py +238 -0
  157. pyoq/query/postgres/compiler.py +114 -0
  158. pyoq/query/postgres/context.py +20 -0
  159. pyoq/query/postgres/executor.py +147 -0
  160. pyoq/query/postgres/expressions.py +311 -0
  161. pyoq/query/postgres/factory.py +24 -0
  162. pyoq/query/postgres/health.py +24 -0
  163. pyoq/query/postgres/identifiers.py +9 -0
  164. pyoq/query/postgres/model.py +81 -0
  165. pyoq/query/postgres/operations.py +25 -0
  166. pyoq/query/postgres/parameters.py +71 -0
  167. pyoq/query/postgres/planning.py +20 -0
  168. pyoq/query/postgres/pool.py +52 -0
  169. pyoq/query/postgres/transactions.py +295 -0
  170. pyoq/query/postgres/writes.py +37 -0
  171. pyoq/query/projections.py +105 -0
  172. pyoq/query/raw.py +90 -0
  173. pyoq/query/recursion.py +265 -0
  174. pyoq/query/rendering/__init__.py +1 -0
  175. pyoq/query/rendering/expressions.py +913 -0
  176. pyoq/query/rendering/identifiers.py +40 -0
  177. pyoq/query/rendering/projections.py +63 -0
  178. pyoq/query/rendering/queries.py +334 -0
  179. pyoq/query/rendering/sources.py +66 -0
  180. pyoq/query/rendering/writes.py +176 -0
  181. pyoq/query/results.py +459 -0
  182. pyoq/query/routines.py +196 -0
  183. pyoq/query/rows.py +156 -0
  184. pyoq/query/select.py +793 -0
  185. pyoq/query/select_nodes.py +277 -0
  186. pyoq/query/sources.py +236 -0
  187. pyoq/query/sqlite/__init__.py +43 -0
  188. pyoq/query/sqlite/commands.py +201 -0
  189. pyoq/query/sqlite/compiler.py +139 -0
  190. pyoq/query/sqlite/context.py +20 -0
  191. pyoq/query/sqlite/executor.py +119 -0
  192. pyoq/query/sqlite/expressions.py +224 -0
  193. pyoq/query/sqlite/factory.py +32 -0
  194. pyoq/query/sqlite/health.py +28 -0
  195. pyoq/query/sqlite/identifiers.py +9 -0
  196. pyoq/query/sqlite/model.py +73 -0
  197. pyoq/query/sqlite/operations.py +36 -0
  198. pyoq/query/sqlite/parameters.py +50 -0
  199. pyoq/query/sqlite/planning.py +20 -0
  200. pyoq/query/sqlite/pool.py +50 -0
  201. pyoq/query/sqlite/streaming.py +13 -0
  202. pyoq/query/sqlite/transactions.py +274 -0
  203. pyoq/query/sqlite/writes.py +35 -0
  204. pyoq/query/statements.py +27 -0
  205. pyoq/query/values.py +23 -0
  206. pyoq/query/vendor.py +162 -0
  207. pyoq/query/windows.py +424 -0
  208. pyoq/query/write_nodes.py +174 -0
  209. pyoq/query/writes.py +628 -0
  210. pyoq/relations/__init__.py +66 -0
  211. pyoq/relations/batching.py +219 -0
  212. pyoq/relations/derivation.py +111 -0
  213. pyoq/relations/fetching.py +355 -0
  214. pyoq/relations/graph.py +245 -0
  215. pyoq/relations/loading.py +74 -0
  216. pyoq/relations/model.py +75 -0
  217. pyoq/relations/planning.py +206 -0
  218. pyoq/runtime/__init__.py +9 -0
  219. pyoq/runtime/kernels.py +25 -0
  220. pyoq/runtime/python.py +43 -0
  221. pyoq/runtime/selection.py +73 -0
  222. pyoq/sanic/__init__.py +32 -0
  223. pyoq/sanic/scope.py +197 -0
  224. pyoq/sanic/workers.py +129 -0
  225. pyoq/schema/__init__.py +108 -0
  226. pyoq/schema/codec.py +711 -0
  227. pyoq/schema/models.py +604 -0
  228. pyoq/schema/mysql/__init__.py +16 -0
  229. pyoq/schema/mysql/connection.py +73 -0
  230. pyoq/schema/mysql/dsn.py +72 -0
  231. pyoq/schema/mysql/records.py +354 -0
  232. pyoq/schema/mysql/reflection.py +309 -0
  233. pyoq/schema/mysql/source.py +30 -0
  234. pyoq/schema/mysql/sql.py +128 -0
  235. pyoq/schema/mysql/types.py +105 -0
  236. pyoq/schema/postgres/__init__.py +13 -0
  237. pyoq/schema/postgres/connection.py +63 -0
  238. pyoq/schema/postgres/records.py +384 -0
  239. pyoq/schema/postgres/reflection.py +466 -0
  240. pyoq/schema/postgres/source.py +30 -0
  241. pyoq/schema/postgres/sql.py +246 -0
  242. pyoq/schema/postgres/types.py +98 -0
  243. pyoq/schema/registry.py +45 -0
  244. pyoq/schema/source.py +15 -0
  245. pyoq/schema/sqlite/__init__.py +6 -0
  246. pyoq/schema/sqlite/connection.py +54 -0
  247. pyoq/schema/sqlite/records.py +167 -0
  248. pyoq/schema/sqlite/reflection.py +393 -0
  249. pyoq/schema/sqlite/source.py +30 -0
  250. pyoq/schema/sqlite/sql.py +254 -0
  251. pyoq/schema/sqlite/types.py +74 -0
  252. pyoq/serving/__init__.py +23 -0
  253. pyoq/serving/databases.py +107 -0
  254. pyoq/serving/opening.py +331 -0
  255. pyoq/snapshots/__init__.py +20 -0
  256. pyoq/snapshots/drift.py +312 -0
  257. pyoq/snapshots/files.py +96 -0
  258. pyoq/snapshots/routing.py +40 -0
  259. pyoq/snapshots/source.py +33 -0
  260. pyoq/tracing/__init__.py +5 -0
  261. pyoq/tracing/spans.py +89 -0
  262. pyoq/unset.py +14 -0
  263. pyoq_sql-1.0.2.dist-info/METADATA +3050 -0
  264. pyoq_sql-1.0.2.dist-info/RECORD +267 -0
  265. pyoq_sql-1.0.2.dist-info/WHEEL +4 -0
  266. pyoq_sql-1.0.2.dist-info/entry_points.txt +3 -0
  267. pyoq_sql-1.0.2.dist-info/licenses/LICENSE +373 -0
@@ -0,0 +1,300 @@
1
+ """Generated table and column descriptor source rendering."""
2
+
3
+ from pathlib import PurePosixPath
4
+
5
+ from pyoq.generation.contracts import RenderedFile
6
+ from pyoq.generation.model import GeneratedColumn, GeneratedPackage, GeneratedTable
7
+ from pyoq.generation.rendering.imports import plan_annotation_imports
8
+ from pyoq.generation.rendering.source import (
9
+ LINE_LIMIT,
10
+ PythonSource,
11
+ literal,
12
+ render_all,
13
+ render_from_import,
14
+ render_string_tuple,
15
+ render_subscript,
16
+ )
17
+ from pyoq.query.results import family_for_kind
18
+ from pyoq.schema import TypeKind
19
+
20
+
21
+ def render_tables(package: GeneratedPackage) -> RenderedFile:
22
+ source = PythonSource()
23
+ source.line('"""Generated table and column descriptors."""')
24
+ source.line()
25
+ source.line("from __future__ import annotations")
26
+ if package.tables:
27
+ _render_imports(source, package)
28
+ for table in package.tables:
29
+ _render_table(source, table)
30
+ source.line()
31
+ source.line()
32
+ source.lines(render_all(_exported_names(package)))
33
+ return RenderedFile(PurePosixPath("tables.py"), source.render())
34
+
35
+
36
+ def _render_imports(source: PythonSource, package: GeneratedPackage) -> None:
37
+ annotations = plan_annotation_imports(_column_annotations(package), package)
38
+ source.line()
39
+ source.lines(tuple(sorted((*annotations.standard, "from typing import ClassVar"))))
40
+ source.line()
41
+ descriptor_names = ["ColumnDescriptor", "TableDescriptor"]
42
+ if any(table.keys for table in package.tables):
43
+ descriptor_names.append("KeyDescriptor")
44
+ if any(table.required_columns for table in package.tables):
45
+ descriptor_names.append("Missing")
46
+ descriptor_names.extend(annotations.descriptor_names)
47
+ source.lines(
48
+ render_from_import(
49
+ "pyoq.descriptors",
50
+ tuple(descriptor_names),
51
+ )
52
+ )
53
+ source.line("from pyoq.query import ScalarFamily")
54
+ if any(table.keys for table in package.tables):
55
+ source.line("from pyoq.schema import KeyKind")
56
+ source.line()
57
+ if annotations.enum_names:
58
+ source.lines(render_from_import(".enums", annotations.enum_names))
59
+ if annotations.domain_names:
60
+ source.lines(render_from_import(".domains", annotations.domain_names))
61
+ row_names = tuple(
62
+ name
63
+ for table in package.tables
64
+ for name in (table.row_name, *(key.type_name for key in table.keys))
65
+ )
66
+ source.lines(render_from_import(".rows", row_names))
67
+ write_names = tuple(
68
+ name for table in package.tables for name in _write_names(table)
69
+ )
70
+ source.lines(render_from_import(".writes", write_names))
71
+
72
+
73
+ def _write_names(table: GeneratedTable) -> tuple[str, ...]:
74
+ return (
75
+ table.insert_name,
76
+ table.update_name,
77
+ table.update_builder_name,
78
+ _returned_builder_name(table),
79
+ )
80
+
81
+
82
+ def _returned_builder_name(table: GeneratedTable) -> str:
83
+ if table.required_columns:
84
+ return table.builder_name
85
+ return table.complete_builder_name
86
+
87
+
88
+ def _column_annotations(package: GeneratedPackage) -> tuple[str, ...]:
89
+ return tuple(
90
+ column.value_annotation for table in package.tables for column in table.columns
91
+ )
92
+
93
+
94
+ def _class_declaration(table: GeneratedTable) -> tuple[str, ...]:
95
+ """Long enough table names push the base past the line limit."""
96
+ inline = (
97
+ f"class {table.class_name}(TableDescriptor["
98
+ f"{table.row_name}, {table.insert_name}, {table.update_name}]):"
99
+ )
100
+ if len(inline) <= 88:
101
+ return (inline,)
102
+ return (
103
+ f"class {table.class_name}(",
104
+ *render_subscript(
105
+ " ",
106
+ "TableDescriptor",
107
+ (table.row_name, table.insert_name, table.update_name),
108
+ argument_indent=" ",
109
+ closing_indent=" ",
110
+ suffix="",
111
+ ),
112
+ "):",
113
+ )
114
+
115
+
116
+ def _render_table(source: PythonSource, table: GeneratedTable) -> None:
117
+ required_states = tuple("Missing" for _ in table.required_columns)
118
+ source.line()
119
+ source.line()
120
+ source.lines(_class_declaration(table))
121
+ for column in table.columns:
122
+ _render_column(source, table, column)
123
+ _render_column_tuple(source, table)
124
+ for key in table.keys:
125
+ database_name = literal(
126
+ key.source.name.value if key.source.name is not None else None
127
+ )
128
+ column_names = render_string_tuple(
129
+ tuple(column.source.name.value for column in key.columns)
130
+ )
131
+ _render_descriptor_assignment(
132
+ source,
133
+ key.constant_name,
134
+ "KeyDescriptor",
135
+ key.type_name,
136
+ "KeyDescriptor",
137
+ (
138
+ f"KeyKind.{key.source.kind.name}",
139
+ column_names,
140
+ database_name,
141
+ ),
142
+ )
143
+ source.line()
144
+ source.line(" @staticmethod")
145
+ if required_states:
146
+ source.lines(
147
+ render_subscript(
148
+ " def builder() -> ",
149
+ table.builder_name,
150
+ required_states,
151
+ argument_indent=" ",
152
+ closing_indent=" ",
153
+ suffix=":",
154
+ )
155
+ )
156
+ else:
157
+ source.line(f" def builder() -> {table.complete_builder_name}:")
158
+ source.line(
159
+ f" return {table.complete_builder_name}()"
160
+ if not table.required_columns
161
+ else f" return {table.builder_name}()"
162
+ )
163
+ source.line()
164
+ source.line(" @staticmethod")
165
+ source.line(f" def update_builder() -> {table.update_builder_name}:")
166
+ source.line(f" return {table.update_builder_name}()")
167
+ source.line()
168
+ source.line()
169
+ inline = f"{table.constant_name} = {table.class_name}("
170
+ wrapped = len(inline) > LINE_LIMIT
171
+ indent = " " if wrapped else " "
172
+ source.lines(_constant_head(table))
173
+ source.line(f"{indent}{literal(table.source.name.value)},")
174
+ source.line(f"{indent}{literal(table.schema_name)},")
175
+ source.line(f"{indent}{literal(table.catalog_name)},")
176
+ source.line(f"{indent}{table.row_name},")
177
+ source.lines((" )", ")") if wrapped else (")",))
178
+
179
+
180
+ def _render_column_tuple(source: PythonSource, table: GeneratedTable) -> None:
181
+ """A table has at least one column, which the schema model enforces."""
182
+ names = tuple(column.constant_name for column in table.columns)
183
+ inline = f" COLUMNS: ClassVar[tuple[object, ...]] = ({names[0]},)"
184
+ if len(names) == 1 and len(inline) <= LINE_LIMIT:
185
+ source.line(inline)
186
+ return
187
+ source.line(" COLUMNS: ClassVar[tuple[object, ...]] = (")
188
+ for name in names:
189
+ source.line(f" {name},")
190
+ source.line(" )")
191
+
192
+
193
+ def _render_column(
194
+ source: PythonSource,
195
+ table: GeneratedTable,
196
+ generated_column: GeneratedColumn,
197
+ ) -> None:
198
+ _render_descriptor_assignment(
199
+ source,
200
+ generated_column.constant_name,
201
+ "ColumnDescriptor",
202
+ generated_column.value_annotation,
203
+ "ColumnDescriptor",
204
+ (
205
+ literal(generated_column.source.name.value),
206
+ f"nullable={generated_column.source.nullable!r}",
207
+ f"writable={generated_column.writable!r}",
208
+ f"has_default={generated_column.source.default is not None!r}",
209
+ f"generated={generated_column.source.generated is not None!r}",
210
+ f"scalar_family=ScalarFamily.{_scalar_family(generated_column.source.data_type.kind)}",
211
+ f"table_name={literal(table.source.name.value)}",
212
+ f"schema_name={literal(table.schema_name)}",
213
+ f"catalog_name={literal(table.catalog_name)}",
214
+ f"field_name={literal(generated_column.field_name)}",
215
+ f"value_type={_value_type(generated_column)}",
216
+ ),
217
+ )
218
+
219
+
220
+ def _value_type(generated_column: GeneratedColumn) -> str:
221
+ """The class this column reads back as, where one class can name it.
222
+
223
+ Any JSON is a union of everything JSON can be, which is no class at all.
224
+ The scalar family says JSON on its own, and reading the column back asks
225
+ the family when the declared type names nothing.
226
+ """
227
+ if generated_column.source.data_type.kind is TypeKind.JSON:
228
+ return "None"
229
+ return generated_column.annotation
230
+
231
+
232
+ def _scalar_family(kind: TypeKind) -> str:
233
+ """The name of the family this kind holds, asked of the one authority.
234
+
235
+ Rendered rather than restated, so generated code and the code that reads
236
+ it back cannot disagree about what a column holds.
237
+ """
238
+ return family_for_kind(kind).name
239
+
240
+
241
+ def _constant_head(table: GeneratedTable) -> tuple[str, ...]:
242
+ """Wrap in parentheses when the name and the class will not share a line."""
243
+ inline = f"{table.constant_name} = {table.class_name}("
244
+ if len(inline) <= LINE_LIMIT:
245
+ return (inline,)
246
+ return (f"{table.constant_name} = (", f" {table.class_name}(")
247
+
248
+
249
+ def _render_descriptor_assignment(
250
+ source: PythonSource,
251
+ name: str,
252
+ descriptor: str,
253
+ parameter: str,
254
+ constructor: str,
255
+ arguments: tuple[str, ...],
256
+ ) -> None:
257
+ """Break where a formatter would break, in the order it prefers.
258
+
259
+ The value is parenthesized first and the annotation split only when even
260
+ that does not fit, because generated code a formatter would rewrite is
261
+ generated code a project cannot check.
262
+ """
263
+ annotated = f"{descriptor}[{parameter}]"
264
+ declaration = f" {name}: ClassVar[{annotated}]"
265
+ argument_indent = " "
266
+ closing_lines: tuple[str, ...] = (" )",)
267
+ if len(f"{declaration} = {constructor}(") <= 88:
268
+ source.line(f"{declaration} = {constructor}(")
269
+ elif len(f"{declaration} = (") <= 88:
270
+ source.line(f"{declaration} = (")
271
+ source.line(f" {constructor}(")
272
+ argument_indent = " "
273
+ closing_lines = (" )", " )")
274
+ else:
275
+ source.line(f" {name}: ClassVar[")
276
+ source.lines(
277
+ render_subscript(
278
+ " ",
279
+ descriptor,
280
+ (parameter,),
281
+ argument_indent=" ",
282
+ closing_indent=" ",
283
+ suffix="",
284
+ )
285
+ )
286
+ source.line(f" ] = {constructor}(")
287
+ for argument in arguments:
288
+ source.line(f"{argument_indent}{argument},")
289
+ source.lines(closing_lines)
290
+
291
+
292
+ def _exported_names(package: GeneratedPackage) -> tuple[str, ...]:
293
+ return tuple(
294
+ name
295
+ for table in package.tables
296
+ for name in (table.class_name, table.constant_name)
297
+ )
298
+
299
+
300
+ __all__ = ("render_tables",)