sqlspec 0.12.1__py3-none-any.whl → 0.13.0__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.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (113) hide show
  1. sqlspec/_sql.py +21 -180
  2. sqlspec/adapters/adbc/config.py +10 -12
  3. sqlspec/adapters/adbc/driver.py +120 -118
  4. sqlspec/adapters/aiosqlite/config.py +3 -3
  5. sqlspec/adapters/aiosqlite/driver.py +116 -141
  6. sqlspec/adapters/asyncmy/config.py +3 -4
  7. sqlspec/adapters/asyncmy/driver.py +123 -135
  8. sqlspec/adapters/asyncpg/config.py +3 -7
  9. sqlspec/adapters/asyncpg/driver.py +98 -140
  10. sqlspec/adapters/bigquery/config.py +4 -5
  11. sqlspec/adapters/bigquery/driver.py +231 -181
  12. sqlspec/adapters/duckdb/config.py +3 -6
  13. sqlspec/adapters/duckdb/driver.py +132 -124
  14. sqlspec/adapters/oracledb/config.py +6 -5
  15. sqlspec/adapters/oracledb/driver.py +242 -259
  16. sqlspec/adapters/psqlpy/config.py +3 -7
  17. sqlspec/adapters/psqlpy/driver.py +118 -93
  18. sqlspec/adapters/psycopg/config.py +34 -30
  19. sqlspec/adapters/psycopg/driver.py +342 -214
  20. sqlspec/adapters/sqlite/config.py +3 -3
  21. sqlspec/adapters/sqlite/driver.py +150 -104
  22. sqlspec/config.py +0 -4
  23. sqlspec/driver/_async.py +89 -98
  24. sqlspec/driver/_common.py +52 -17
  25. sqlspec/driver/_sync.py +81 -105
  26. sqlspec/driver/connection.py +207 -0
  27. sqlspec/driver/mixins/_csv_writer.py +91 -0
  28. sqlspec/driver/mixins/_pipeline.py +38 -49
  29. sqlspec/driver/mixins/_result_utils.py +27 -9
  30. sqlspec/driver/mixins/_storage.py +149 -216
  31. sqlspec/driver/mixins/_type_coercion.py +3 -4
  32. sqlspec/driver/parameters.py +138 -0
  33. sqlspec/exceptions.py +10 -2
  34. sqlspec/extensions/aiosql/adapter.py +0 -10
  35. sqlspec/extensions/litestar/handlers.py +0 -1
  36. sqlspec/extensions/litestar/plugin.py +0 -3
  37. sqlspec/extensions/litestar/providers.py +0 -14
  38. sqlspec/loader.py +31 -118
  39. sqlspec/protocols.py +542 -0
  40. sqlspec/service/__init__.py +3 -2
  41. sqlspec/service/_util.py +147 -0
  42. sqlspec/service/base.py +1116 -9
  43. sqlspec/statement/builder/__init__.py +42 -32
  44. sqlspec/statement/builder/_ddl_utils.py +0 -10
  45. sqlspec/statement/builder/_parsing_utils.py +10 -4
  46. sqlspec/statement/builder/base.py +70 -23
  47. sqlspec/statement/builder/column.py +283 -0
  48. sqlspec/statement/builder/ddl.py +102 -65
  49. sqlspec/statement/builder/delete.py +23 -7
  50. sqlspec/statement/builder/insert.py +29 -15
  51. sqlspec/statement/builder/merge.py +4 -4
  52. sqlspec/statement/builder/mixins/_aggregate_functions.py +113 -14
  53. sqlspec/statement/builder/mixins/_common_table_expr.py +0 -1
  54. sqlspec/statement/builder/mixins/_delete_from.py +1 -1
  55. sqlspec/statement/builder/mixins/_from.py +10 -8
  56. sqlspec/statement/builder/mixins/_group_by.py +0 -1
  57. sqlspec/statement/builder/mixins/_insert_from_select.py +0 -1
  58. sqlspec/statement/builder/mixins/_insert_values.py +0 -2
  59. sqlspec/statement/builder/mixins/_join.py +20 -13
  60. sqlspec/statement/builder/mixins/_limit_offset.py +3 -3
  61. sqlspec/statement/builder/mixins/_merge_clauses.py +3 -4
  62. sqlspec/statement/builder/mixins/_order_by.py +2 -2
  63. sqlspec/statement/builder/mixins/_pivot.py +4 -7
  64. sqlspec/statement/builder/mixins/_select_columns.py +6 -5
  65. sqlspec/statement/builder/mixins/_unpivot.py +6 -9
  66. sqlspec/statement/builder/mixins/_update_from.py +2 -1
  67. sqlspec/statement/builder/mixins/_update_set.py +11 -8
  68. sqlspec/statement/builder/mixins/_where.py +61 -34
  69. sqlspec/statement/builder/select.py +32 -17
  70. sqlspec/statement/builder/update.py +25 -11
  71. sqlspec/statement/filters.py +39 -14
  72. sqlspec/statement/parameter_manager.py +220 -0
  73. sqlspec/statement/parameters.py +210 -79
  74. sqlspec/statement/pipelines/__init__.py +166 -23
  75. sqlspec/statement/pipelines/analyzers/_analyzer.py +22 -25
  76. sqlspec/statement/pipelines/context.py +35 -39
  77. sqlspec/statement/pipelines/transformers/__init__.py +2 -3
  78. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +19 -187
  79. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +667 -43
  80. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +76 -0
  81. sqlspec/statement/pipelines/validators/_dml_safety.py +33 -18
  82. sqlspec/statement/pipelines/validators/_parameter_style.py +87 -14
  83. sqlspec/statement/pipelines/validators/_performance.py +38 -23
  84. sqlspec/statement/pipelines/validators/_security.py +39 -62
  85. sqlspec/statement/result.py +37 -129
  86. sqlspec/statement/splitter.py +0 -12
  87. sqlspec/statement/sql.py +885 -379
  88. sqlspec/statement/sql_compiler.py +140 -0
  89. sqlspec/storage/__init__.py +10 -2
  90. sqlspec/storage/backends/fsspec.py +82 -35
  91. sqlspec/storage/backends/obstore.py +66 -49
  92. sqlspec/storage/capabilities.py +101 -0
  93. sqlspec/storage/registry.py +56 -83
  94. sqlspec/typing.py +6 -434
  95. sqlspec/utils/cached_property.py +25 -0
  96. sqlspec/utils/correlation.py +0 -2
  97. sqlspec/utils/logging.py +0 -6
  98. sqlspec/utils/sync_tools.py +0 -4
  99. sqlspec/utils/text.py +0 -5
  100. sqlspec/utils/type_guards.py +892 -0
  101. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/METADATA +1 -1
  102. sqlspec-0.13.0.dist-info/RECORD +150 -0
  103. sqlspec/statement/builder/protocols.py +0 -20
  104. sqlspec/statement/pipelines/base.py +0 -315
  105. sqlspec/statement/pipelines/result_types.py +0 -41
  106. sqlspec/statement/pipelines/transformers/_remove_comments.py +0 -66
  107. sqlspec/statement/pipelines/transformers/_remove_hints.py +0 -81
  108. sqlspec/statement/pipelines/validators/base.py +0 -67
  109. sqlspec/storage/protocol.py +0 -170
  110. sqlspec-0.12.1.dist-info/RECORD +0 -145
  111. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/WHEEL +0 -0
  112. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/licenses/LICENSE +0 -0
  113. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/licenses/NOTICE +0 -0
@@ -17,6 +17,7 @@ if TYPE_CHECKING:
17
17
  __all__ = (
18
18
  "AnyCollectionFilter",
19
19
  "BeforeAfterFilter",
20
+ "FilterTypeT",
20
21
  "FilterTypes",
21
22
  "InAnyFilter",
22
23
  "InCollectionFilter",
@@ -112,9 +113,7 @@ class BeforeAfterFilter(StatementFilter):
112
113
  final_condition = conditions[0]
113
114
  for cond in conditions[1:]:
114
115
  final_condition = exp.And(this=final_condition, expression=cond)
115
- # Use the SQL object's where method which handles all cases
116
116
  result = statement.where(final_condition)
117
- # Add the filter's parameters to the result
118
117
  _, named_params = self.extract_parameters()
119
118
  for name, value in named_params.items():
120
119
  result = result.add_named_parameter(name, value)
@@ -171,7 +170,6 @@ class OnBeforeAfterFilter(StatementFilter):
171
170
  for cond in conditions[1:]:
172
171
  final_condition = exp.And(this=final_condition, expression=cond)
173
172
  result = statement.where(final_condition)
174
- # Add the filter's parameters to the result
175
173
  _, named_params = self.extract_parameters()
176
174
  for name, value in named_params.items():
177
175
  result = result.add_named_parameter(name, value)
@@ -229,7 +227,6 @@ class InCollectionFilter(InAnyFilter[T]):
229
227
  ]
230
228
 
231
229
  result = statement.where(exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
232
- # Add the filter's parameters to the result
233
230
  _, named_params = self.extract_parameters()
234
231
  for name, value in named_params.items():
235
232
  result = result.add_named_parameter(name, value)
@@ -273,7 +270,6 @@ class NotInCollectionFilter(InAnyFilter[T]):
273
270
  result = statement.where(
274
271
  exp.Not(this=exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
275
272
  )
276
- # Add the filter's parameters to the result
277
273
  _, named_params = self.extract_parameters()
278
274
  for name, value in named_params.items():
279
275
  result = result.add_named_parameter(name, value)
@@ -323,7 +319,6 @@ class AnyCollectionFilter(InAnyFilter[T]):
323
319
  array_expr = exp.Array(expressions=placeholder_expressions)
324
320
  # Generates SQL like: self.field_name = ANY(ARRAY[?, ?, ...])
325
321
  result = statement.where(exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr)))
326
- # Add the filter's parameters to the result
327
322
  _, named_params = self.extract_parameters()
328
323
  for name, value in named_params.items():
329
324
  result = result.add_named_parameter(name, value)
@@ -372,7 +367,6 @@ class NotAnyCollectionFilter(InAnyFilter[T]):
372
367
  # Generates SQL like: NOT (self.field_name = ANY(ARRAY[?, ?, ...]))
373
368
  condition = exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr))
374
369
  result = statement.where(exp.Not(this=condition))
375
- # Add the filter's parameters to the result
376
370
  _, named_params = self.extract_parameters()
377
371
  for name, value in named_params.items():
378
372
  result = result.add_named_parameter(name, value)
@@ -396,13 +390,48 @@ class LimitOffsetFilter(PaginationFilter):
396
390
  offset: int
397
391
  """Value for ``OFFSET`` clause of query."""
398
392
 
393
+ def __post_init__(self) -> None:
394
+ """Initialize parameter names."""
395
+ # Generate unique parameter names to avoid conflicts
396
+ import uuid
397
+
398
+ unique_suffix = str(uuid.uuid4()).replace("-", "")[:8]
399
+ self._limit_param_name = f"limit_{unique_suffix}"
400
+ self._offset_param_name = f"offset_{unique_suffix}"
401
+
399
402
  def extract_parameters(self) -> tuple[list[Any], dict[str, Any]]:
400
403
  """Extract filter parameters."""
401
- # Return the limit and offset values as named parameters
402
- return [], {"limit": self.limit, "offset": self.offset}
404
+ return [], {self._limit_param_name: self.limit, self._offset_param_name: self.offset}
403
405
 
404
406
  def append_to_statement(self, statement: "SQL") -> "SQL":
405
- return statement.limit(self.limit, use_parameter=True).offset(self.offset, use_parameter=True)
407
+ # Create limit and offset expressions using our pre-generated parameter names
408
+ from sqlglot import exp
409
+
410
+ limit_placeholder = exp.Placeholder(this=self._limit_param_name)
411
+ offset_placeholder = exp.Placeholder(this=self._offset_param_name)
412
+
413
+ # Apply LIMIT and OFFSET to the statement
414
+ result = statement
415
+
416
+ # Check if the statement supports LIMIT directly
417
+ if isinstance(result._statement, exp.Select):
418
+ new_statement = result._statement.limit(limit_placeholder)
419
+ else:
420
+ # Wrap in a SELECT if the statement doesn't support LIMIT directly
421
+ new_statement = exp.Select().from_(result._statement).limit(limit_placeholder)
422
+
423
+ # Add OFFSET
424
+ if isinstance(new_statement, exp.Select):
425
+ new_statement = new_statement.offset(offset_placeholder)
426
+
427
+ result = result.copy(statement=new_statement)
428
+
429
+ # Add the parameters to the result
430
+ _, named_params = self.extract_parameters()
431
+ for name, value in named_params.items():
432
+ result = result.add_named_parameter(name, value)
433
+
434
+ return result
406
435
 
407
436
 
408
437
  @dataclass
@@ -450,7 +479,6 @@ class SearchFilter(StatementFilter):
450
479
  if isinstance(self.field_name, str):
451
480
  self._param_name = f"{self.field_name}_search"
452
481
  else:
453
- # For multiple fields, use a generic search parameter name
454
482
  self._param_name = "search_value"
455
483
 
456
484
  def extract_parameters(self) -> tuple[list[Any], dict[str, Any]]:
@@ -484,7 +512,6 @@ class SearchFilter(StatementFilter):
484
512
  final_condition = exp.Or(this=final_condition, expression=cond)
485
513
  result = statement.where(final_condition)
486
514
 
487
- # Add the filter's parameters to the result
488
515
  _, named_params = self.extract_parameters()
489
516
  for name, value in named_params.items():
490
517
  result = result.add_named_parameter(name, value)
@@ -502,7 +529,6 @@ class NotInSearchFilter(SearchFilter):
502
529
  if isinstance(self.field_name, str):
503
530
  self._param_name = f"{self.field_name}_not_search"
504
531
  else:
505
- # For multiple fields, use a generic search parameter name
506
532
  self._param_name = "not_search_value"
507
533
 
508
534
  def extract_parameters(self) -> tuple[list[Any], dict[str, Any]]:
@@ -536,7 +562,6 @@ class NotInSearchFilter(SearchFilter):
536
562
  final_condition = exp.And(this=final_condition, expression=cond)
537
563
  result = statement.where(final_condition)
538
564
 
539
- # Add the filter's parameters to the result
540
565
  _, named_params = self.extract_parameters()
541
566
  for name, value in named_params.items():
542
567
  result = result.add_named_parameter(name, value)
@@ -0,0 +1,220 @@
1
+ """Parameter management for SQL objects."""
2
+
3
+ from typing import Any, Optional
4
+
5
+ from sqlspec.statement.filters import StatementFilter
6
+ from sqlspec.statement.parameters import ParameterConverter, ParameterStyle
7
+
8
+ __all__ = ("ParameterManager",)
9
+
10
+
11
+ class ParameterManager:
12
+ """Manages parameter processing and conversion for SQL objects."""
13
+
14
+ def __init__(
15
+ self,
16
+ parameters: "Optional[tuple[Any, ...]]" = None,
17
+ kwargs: "Optional[dict[str, Any]]" = None,
18
+ converter: "Optional[ParameterConverter]" = None,
19
+ ) -> None:
20
+ self.converter = converter or ParameterConverter()
21
+ self.named_params: dict[str, Any] = {}
22
+ self.filters: list[StatementFilter] = []
23
+ self._positional_parameters = parameters or ()
24
+ self._named_parameters = kwargs or {}
25
+ if parameters:
26
+ for i, param in enumerate(parameters):
27
+ self.named_params[f"pos_param_{i}"] = param
28
+ if kwargs:
29
+ self.process_parameters(**kwargs)
30
+
31
+ def process_parameters(self, *parameters: Any, **kwargs: Any) -> None:
32
+ """Process positional parameters and kwargs into named parameters."""
33
+ for i, param in enumerate(parameters):
34
+ if isinstance(param, StatementFilter):
35
+ self.filters.append(param)
36
+ pos_params, named_params = param.extract_parameters()
37
+ for j, p_param in enumerate(pos_params):
38
+ self.named_params[f"pos_param_{i}_{j}"] = p_param
39
+ self.named_params.update(named_params)
40
+ elif isinstance(param, (list, tuple)):
41
+ for j, p_param in enumerate(param):
42
+ self.named_params[f"pos_param_{i}_{j}"] = p_param
43
+ elif isinstance(param, dict):
44
+ self.named_params.update(param)
45
+ else:
46
+ self.named_params[f"pos_param_{i}"] = param
47
+ if "parameters" in kwargs:
48
+ param_value = kwargs.pop("parameters")
49
+ if isinstance(param_value, (list, tuple)):
50
+ for i, p_param in enumerate(param_value):
51
+ self.named_params[f"kw_pos_param_{i}"] = p_param
52
+ elif isinstance(param_value, dict):
53
+ self.named_params.update(param_value)
54
+ else:
55
+ self.named_params["kw_single_param"] = param_value
56
+
57
+ for key, value in kwargs.items():
58
+ if not key.startswith("_"):
59
+ self.named_params[key] = value
60
+
61
+ def get_compiled_parameters(self, param_info: list[Any], target_style: ParameterStyle) -> Any:
62
+ """Compile internal named parameters into the target style."""
63
+ if target_style == ParameterStyle.POSITIONAL_COLON:
64
+ return self._convert_to_positional_colon_format(self.named_params, param_info)
65
+ if target_style in {ParameterStyle.QMARK, ParameterStyle.NUMERIC, ParameterStyle.POSITIONAL_PYFORMAT}:
66
+ return self._convert_to_positional_format(self.named_params, param_info)
67
+ if target_style == ParameterStyle.NAMED_COLON:
68
+ return self._convert_to_named_colon_format(self.named_params, param_info)
69
+ if target_style == ParameterStyle.NAMED_PYFORMAT:
70
+ return self._convert_to_named_pyformat_format(self.named_params, param_info)
71
+ return self.named_params
72
+
73
+ def copy_from(self, other: "ParameterManager") -> None:
74
+ """Copy parameters and filters from another parameter manager."""
75
+ self.named_params.update(other.named_params)
76
+ self.filters.extend(other.filters)
77
+
78
+ def add_named_parameter(self, name: str, value: Any) -> None:
79
+ """Add a named parameter."""
80
+ self.named_params[name] = value
81
+ self._named_parameters[name] = value
82
+
83
+ def get_unique_parameter_name(
84
+ self, base_name: str, namespace: Optional[str] = None, preserve_original: bool = False
85
+ ) -> str:
86
+ """Generate a unique parameter name."""
87
+ all_param_names = set(self.named_params.keys())
88
+ candidate = f"{namespace}_{base_name}" if namespace else base_name
89
+
90
+ if preserve_original and candidate not in all_param_names:
91
+ return candidate
92
+
93
+ if candidate not in all_param_names:
94
+ return candidate
95
+
96
+ counter = 1
97
+ while True:
98
+ new_candidate = f"{candidate}_{counter}"
99
+ if new_candidate not in all_param_names:
100
+ return new_candidate
101
+ counter += 1
102
+
103
+ def _convert_to_positional_format(self, params: dict[str, Any], param_info: list[Any]) -> list[Any]:
104
+ """Convert to positional format (list).
105
+
106
+ This is used for parameter styles like QMARK (?), NUMERIC ($1), and POSITIONAL_PYFORMAT (%s).
107
+ """
108
+ if not param_info:
109
+ return list(params.values())
110
+
111
+ result = []
112
+ for i, info in enumerate(param_info):
113
+ if info.name and info.name in params:
114
+ result.append(params[info.name])
115
+ elif f"pos_param_{i}" in params:
116
+ result.append(params[f"pos_param_{i}"])
117
+ elif f"kw_pos_param_{i}" in params:
118
+ result.append(params[f"kw_pos_param_{i}"])
119
+ elif f"arg_{i}" in params:
120
+ result.append(params[f"arg_{i}"])
121
+ else:
122
+ result.append(None)
123
+ return result
124
+
125
+ def _convert_to_positional_colon_format(self, params: dict[str, Any], param_info: list[Any]) -> dict[str, Any]:
126
+ """Convert to positional colon format (Oracle :1, :2 style).
127
+
128
+ Oracle's positional parameters are 1-indexed and are accessed by string keys.
129
+ Returns a dict with string keys "1", "2", etc.
130
+ """
131
+ digit_keys = {k: v for k, v in params.items() if k.isdigit()}
132
+ if (
133
+ digit_keys
134
+ and param_info
135
+ and all(hasattr(info, "style") and info.style == ParameterStyle.POSITIONAL_COLON for info in param_info)
136
+ ):
137
+ required_nums = {info.name for info in param_info if hasattr(info, "name")}
138
+ if required_nums.issubset(digit_keys.keys()):
139
+ return digit_keys
140
+
141
+ # This handles cases like :0, :1, :3 (with gaps) where we should preserve the actual numbers
142
+ if param_info and all(
143
+ hasattr(info, "style")
144
+ and info.style == ParameterStyle.POSITIONAL_COLON
145
+ and hasattr(info, "name")
146
+ and info.name.isdigit()
147
+ for info in param_info
148
+ ):
149
+ result = {}
150
+ positional_values = self._convert_to_positional_format(params, param_info)
151
+ for i, value in enumerate(positional_values):
152
+ if value is not None:
153
+ numeric_key = str(i)
154
+ if any(info.name == numeric_key for info in param_info):
155
+ result[numeric_key] = value
156
+ else:
157
+ result[str(i + 1)] = value
158
+
159
+ return result
160
+
161
+ positional_list = self._convert_to_positional_format(params, param_info)
162
+ return {str(i + 1): value for i, value in enumerate(positional_list)}
163
+
164
+ def _convert_to_named_colon_format(self, params: dict[str, Any], param_info: list[Any]) -> dict[str, Any]:
165
+ """Convert to named colon format (:name style).
166
+
167
+ This format expects a dictionary with parameter names as keys.
168
+ We need to ensure all placeholders have corresponding values.
169
+ """
170
+ result = {}
171
+ for info in param_info:
172
+ if info.name:
173
+ if info.name in params:
174
+ result[info.name] = params[info.name]
175
+ else:
176
+ for key, value in params.items():
177
+ if key.endswith(f"_{info.ordinal}") or key == f"arg_{info.ordinal}":
178
+ result[info.name] = value
179
+ break
180
+ else:
181
+ gen_name = f"arg_{info.ordinal}"
182
+ if f"pos_param_{info.ordinal}" in params:
183
+ result[gen_name] = params[f"pos_param_{info.ordinal}"]
184
+ elif f"kw_pos_param_{info.ordinal}" in params:
185
+ result[gen_name] = params[f"kw_pos_param_{info.ordinal}"]
186
+ elif gen_name in params:
187
+ result[gen_name] = params[gen_name]
188
+ for key, value in params.items():
189
+ if not key.startswith(("pos_param_", "kw_pos_param_", "arg_")) and key not in result:
190
+ result[key] = value
191
+
192
+ return result
193
+
194
+ def _convert_to_named_pyformat_format(self, params: dict[str, Any], param_info: list[Any]) -> dict[str, Any]:
195
+ """Convert to named pyformat format (%(name)s style).
196
+
197
+ This is similar to named colon format but uses Python string formatting syntax.
198
+ """
199
+ return self._convert_to_named_colon_format(params, param_info)
200
+
201
+ @property
202
+ def positional_parameters(self) -> tuple[Any, ...]:
203
+ """Get the original positional parameters."""
204
+ return self._positional_parameters
205
+
206
+ @property
207
+ def named_parameters(self) -> dict[str, Any]:
208
+ """Get the combined named parameters."""
209
+ return self.named_params
210
+
211
+ def get_parameter_info(self) -> tuple[tuple[Any, ...], dict[str, Any]]:
212
+ """Get parameter information in the legacy format.
213
+
214
+ This method provides backward compatibility for code expecting
215
+ the old parameter_info format.
216
+
217
+ Returns:
218
+ Tuple of (positional_parameters, named_parameters)
219
+ """
220
+ return (self._positional_parameters, self._named_parameters)