@@ -164,3 +164,137 @@ def test_init_special_chars_typeddict() -> None:
164164 method_filter = None ,
165165 protocol_version = "v2.0" ,
166166 )
167+
168+
169+ def test_python_keyword_field_names_basemodel () -> None :
170+ """Handles Python reserved keywords as field names for BaseModel."""
171+
172+ import ast
173+ import json
174+ from pathlib import Path
175+
176+ keyword_schema = {
177+ "services" : {
178+ "test_service" : {
179+ "procedures" : {
180+ "rpc_method" : {
181+ "input" : {
182+ "type" : "object" ,
183+ "properties" : {
184+ "from" : {"type" : "string" },
185+ "to" : {"type" : "string" },
186+ "class" : {"type" : "number" },
187+ "import" : {"type" : "boolean" },
188+ },
189+ "required" : ["from" , "to" ],
190+ },
191+ "output" : {
192+ "type" : "object" ,
193+ "properties" : {
194+ "from" : {"type" : "string" },
195+ "to" : {"type" : "string" },
196+ },
197+ "required" : ["from" , "to" ],
198+ },
199+ "errors" : {"not" : {}},
200+ "type" : "rpc" ,
201+ }
202+ }
203+ }
204+ }
205+ }
206+
207+ files : dict [Path , StringIO ] = {}
208+
209+ def file_opener (path : Path ) -> StringIO :
210+ buf = StringIO ()
211+ files [path ] = buf
212+ return buf
213+
214+ schema_to_river_client_codegen (
215+ read_schema = lambda : StringIO (json .dumps (keyword_schema )),
216+ target_path = "test_keyword_bm" ,
217+ client_name = "KeywordBMClient" ,
218+ typed_dict_inputs = False ,
219+ file_opener = file_opener ,
220+ method_filter = None ,
221+ protocol_version = "v1.1" ,
222+ )
223+
224+ # Verify all generated files are valid Python
225+ for path , buf in files .items ():
226+ buf .seek (0 )
227+ content = buf .read ()
228+ try :
229+ ast .parse (content )
230+ except SyntaxError as e :
231+ raise AssertionError (
232+ f"Generated file { path } has invalid syntax: { e } \n { content } "
233+ )
234+
235+
236+ def test_python_keyword_field_names_typeddict () -> None :
237+ """Handles Python reserved keywords as field names for TypedDict."""
238+
239+ import ast
240+ import json
241+ from pathlib import Path
242+
243+ keyword_schema = {
244+ "services" : {
245+ "test_service" : {
246+ "procedures" : {
247+ "rpc_method" : {
248+ "input" : {
249+ "type" : "object" ,
250+ "properties" : {
251+ "from" : {"type" : "string" },
252+ "to" : {"type" : "string" },
253+ "class" : {"type" : "number" },
254+ "import" : {"type" : "boolean" },
255+ },
256+ "required" : ["from" , "to" ],
257+ },
258+ "output" : {
259+ "type" : "object" ,
260+ "properties" : {
261+ "from" : {"type" : "string" },
262+ "to" : {"type" : "string" },
263+ },
264+ "required" : ["from" , "to" ],
265+ },
266+ "errors" : {"not" : {}},
267+ "type" : "rpc" ,
268+ }
269+ }
270+ }
271+ }
272+ }
273+
274+ files : dict [Path , StringIO ] = {}
275+
276+ def file_opener (path : Path ) -> StringIO :
277+ buf = StringIO ()
278+ files [path ] = buf
279+ return buf
280+
281+ schema_to_river_client_codegen (
282+ read_schema = lambda : StringIO (json .dumps (keyword_schema )),
283+ target_path = "test_keyword_td" ,
284+ client_name = "KeywordTDClient" ,
285+ typed_dict_inputs = True ,
286+ file_opener = file_opener ,
287+ method_filter = None ,
288+ protocol_version = "v1.1" ,
289+ )
290+
291+ # Verify all generated files are valid Python
292+ for path , buf in files .items ():
293+ buf .seek (0 )
294+ content = buf .read ()
295+ try :
296+ ast .parse (content )
297+ except SyntaxError as e :
298+ raise AssertionError (
299+ f"Generated file { path } has invalid syntax: { e } \n { content } "
300+ )
0 commit comments