5353 reason = f"Installed black ({ black .__version__ } ) doesn't support Python 3.14" ,
5454)
5555
56+ CURRENT_PYTHON_VERSION = f"{ sys .version_info [0 ]} .{ sys .version_info [1 ]} "
57+ """Current Python version as string (e.g., '3.13')."""
58+
5659DATA_PATH : Path = Path (__file__ ).parent .parent / "data"
5760EXPECTED_MAIN_PATH : Path = DATA_PATH / "expected" / "main"
5861
@@ -102,6 +105,21 @@ def output_dir(tmp_path: Path) -> Path:
102105 return tmp_path / "model"
103106
104107
108+ def get_current_version_args (* extra_args : str ) -> list [str ]:
109+ """Create CLI args list with --target-python-version set to current version.
110+
111+ This is a convenience function for tests that want to use the current
112+ Python version to enable exec() validation.
113+
114+ Example:
115+ run_main_and_assert(
116+ ...,
117+ extra_args=get_current_version_args("--use-field-description"),
118+ )
119+ """
120+ return ["--target-python-version" , CURRENT_PYTHON_VERSION , * extra_args ]
121+
122+
105123def _copy_files (copy_files : CopyFilesMapping | None ) -> None :
106124 """Copy files from source to destination paths."""
107125 if copy_files is not None :
@@ -223,6 +241,7 @@ def run_main_and_assert( # noqa: PLR0912
223241 monkeypatch : pytest .MonkeyPatch | None = None ,
224242 # Code validation options
225243 skip_code_validation : bool = False ,
244+ force_exec_validation : bool = False ,
226245) -> None :
227246 """Execute main() and assert output.
228247
@@ -259,6 +278,13 @@ def run_main_and_assert( # noqa: PLR0912
259278 expected_stderr: Assert exact stderr match
260279 expected_stderr_contains: Assert stderr contains string
261280 assert_no_stderr: Assert stderr is empty
281+
282+ Code validation options:
283+ skip_code_validation: Skip all code validation (compile and exec)
284+ force_exec_validation: Run exec() even when target Python version differs from
285+ the test environment (only effective when target <= runtime). This catches
286+ runtime errors that would otherwise be missed. Has no effect when target >
287+ runtime since compile is skipped in that case.
262288 """
263289 __tracebackhide__ = True
264290
@@ -349,7 +375,7 @@ def run_main_and_assert( # noqa: PLR0912
349375 assert_func (output_path , expected_file , transform = transform )
350376
351377 if output_path is not None and not skip_code_validation :
352- _validate_output_files (output_path , extra_args )
378+ _validate_output_files (output_path , extra_args , force_exec_validation = force_exec_validation )
353379
354380
355381def _get_argument_value (arguments : Sequence [str ] | None , argument_name : str ) -> str | None :
@@ -380,8 +406,15 @@ def _should_skip_compile(extra_arguments: Sequence[str] | None) -> bool:
380406 return target_version > sys .version_info [:2 ]
381407
382408
383- def _should_skip_exec (extra_arguments : Sequence [str ] | None ) -> bool :
384- """Check if exec should be skipped based on model type, pydantic version, and Python version."""
409+ def _should_skip_exec (extra_arguments : Sequence [str ] | None , * , force_exec : bool = False ) -> bool :
410+ """Check if exec should be skipped based on model type, pydantic version, and Python version.
411+
412+ Args:
413+ extra_arguments: CLI arguments passed to the test.
414+ force_exec: If True, skip version mismatch check and allow exec on current Python version.
415+ This only works when target version <= runtime version (older target on newer runtime).
416+ When target > runtime, compile will be skipped entirely regardless of this flag.
417+ """
385418 output_model_type = _get_argument_value (extra_arguments , "--output-model-type" )
386419 is_pydantic_v1 = output_model_type is None or output_model_type == DataModelType .PydanticBaseModel .value
387420 if (is_pydantic_v1 and PYDANTIC_V2 ) or (
@@ -390,16 +423,30 @@ def _should_skip_exec(extra_arguments: Sequence[str] | None) -> bool:
390423 return True
391424 if (target_version := _parse_target_version (extra_arguments )) is None :
392425 return True
393- if target_version != sys .version_info [:2 ]:
426+ if not force_exec and target_version != sys .version_info [:2 ]:
394427 return True
395428 return _get_argument_value (extra_arguments , "--base-class" ) is not None
396429
397430
398- def _validate_output_files (output_path : Path , extra_arguments : Sequence [str ] | None = None ) -> None :
399- """Validate generated Python files by compiling/executing them."""
431+ def _validate_output_files (
432+ output_path : Path ,
433+ extra_arguments : Sequence [str ] | None = None ,
434+ * ,
435+ force_exec_validation : bool = False ,
436+ ) -> None :
437+ """Validate generated Python files by compiling/executing them.
438+
439+ Args:
440+ output_path: Path to output file or directory to validate.
441+ extra_arguments: CLI arguments passed to the test.
442+ force_exec_validation: If True, run exec even when target Python version differs from
443+ the test environment (only when target <= runtime). This helps catch runtime errors
444+ that would otherwise be missed. Has no effect when target > runtime since compile
445+ is skipped in that case.
446+ """
400447 if _should_skip_compile (extra_arguments ):
401448 return
402- should_exec = not _should_skip_exec (extra_arguments )
449+ should_exec = not _should_skip_exec (extra_arguments , force_exec = force_exec_validation )
403450 if output_path .is_file () and output_path .suffix == ".py" :
404451 validate_generated_code (output_path .read_text (encoding = "utf-8" ), str (output_path ), do_exec = should_exec )
405452 elif output_path .is_dir (): # pragma: no cover
@@ -471,6 +518,7 @@ def run_main_url_and_assert(
471518 expected_file : str | Path ,
472519 extra_args : Sequence [str ] | None = None ,
473520 transform : Callable [[str ], str ] | None = None ,
521+ force_exec_validation : bool = False ,
474522) -> None :
475523 """Execute main() with URL input and assert output.
476524
@@ -482,10 +530,12 @@ def run_main_url_and_assert(
482530 expected_file: Expected output filename
483531 extra_args: Additional CLI arguments
484532 transform: Optional function to transform output before comparison
533+ force_exec_validation: Run exec() even when target Python version differs from
534+ the test environment (only effective when target <= runtime).
485535 """
486536 __tracebackhide__ = True
487537 return_code = _run_main_url (url , output_path , input_file_type , extra_args = extra_args )
488538 _assert_exit_code (return_code , Exit .OK , f"URL: { url } " )
489539 assert_func (output_path , expected_file , transform = transform )
490540
491- _validate_output_files (output_path , extra_args )
541+ _validate_output_files (output_path , extra_args , force_exec_validation = force_exec_validation )
0 commit comments