mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
6b53cabe03
* Add pickle support to dynamically created models * Add created model module check * Use globals() to retrieve model * Use single quotes * use mocker instead of mock Co-authored-by: PrettyWood <em.jolibois@gmail.com> * remove unused import * add test for dynamic forward ref * move generic test to test_generics.py * fix test_generic_model_pickle test, add additional check for <locals> in create_model * fix code style * resolve issues with global/local models * make ensure_picklable return given model * remove ensure_picklable, use test_is_call_from_module only in generics * reformat code after updating black==20.8b1 * move get_caller_module_name and is_call_from_module to generics * apply suggestions from @samuelcolvin * add tests for get_caller_module and is_call_from_module called from module * fix linting * fix path to modules in test_module fixture, capture stderr and stdout * fix broken test module * fix subprocess call for windows * enhance create_module fixture, add run_as_module fixture this will allow run modules in subprocess and define module code in functions-containers * add test for redefined concrete model without cache * add changes file * remove unused variable * rewrite tests with pytest.raises * fix linting * rewrite test_create_model_pickle and test_forward_ref_with_create_model with module definition in function * Update create_model docstring Co-authored-by: PrettyWood <em.jolibois@gmail.com> Co-authored-by: Samuel Colvin <s@muelcolvin.com>
93 lines
2.6 KiB
Python
93 lines
2.6 KiB
Python
import inspect
|
|
import os
|
|
import secrets
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
from importlib.machinery import SourceFileLoader
|
|
from types import FunctionType
|
|
|
|
import pytest
|
|
|
|
|
|
def _extract_source_code_from_function(function):
|
|
if function.__code__.co_argcount:
|
|
raise RuntimeError(f'function {function.__qualname__} cannot have any arguments')
|
|
|
|
code_lines = ''
|
|
body_started = False
|
|
for line in textwrap.dedent(inspect.getsource(function)).split('\n'):
|
|
if line.startswith('def '):
|
|
body_started = True
|
|
continue
|
|
elif body_started:
|
|
code_lines += f'{line}\n'
|
|
|
|
return textwrap.dedent(code_lines)
|
|
|
|
|
|
def _create_module(code, tmp_path, name):
|
|
name = f'{name}_{secrets.token_hex(5)}'
|
|
path = tmp_path / f'{name}.py'
|
|
path.write_text(code)
|
|
return name, path
|
|
|
|
|
|
class SetEnv:
|
|
def __init__(self):
|
|
self.envars = set()
|
|
|
|
def set(self, name, value):
|
|
self.envars.add(name)
|
|
os.environ[name] = value
|
|
|
|
def clear(self):
|
|
for n in self.envars:
|
|
os.environ.pop(n)
|
|
|
|
|
|
@pytest.yield_fixture
|
|
def env():
|
|
setenv = SetEnv()
|
|
|
|
yield setenv
|
|
|
|
setenv.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def create_module(tmp_path, request):
|
|
def run(module_source_code=None):
|
|
"""
|
|
Creates module and loads it with SourceFileLoader().load_module()
|
|
"""
|
|
if isinstance(module_source_code, FunctionType):
|
|
module_source_code = _extract_source_code_from_function(module_source_code)
|
|
name, path = _create_module(module_source_code, tmp_path, request.node.name)
|
|
return SourceFileLoader(name, str(path)).load_module()
|
|
|
|
return run
|
|
|
|
|
|
@pytest.fixture
|
|
def run_as_module(tmp_path, request):
|
|
def run(module_source_code=None):
|
|
"""
|
|
Creates module source and runs it in subprocess
|
|
|
|
This way is much slower than SourceFileLoader().load_module(),
|
|
but executes module as __main__ with a clear stack (https://docs.python.org/3/library/__main__.html)
|
|
"""
|
|
if isinstance(module_source_code, FunctionType):
|
|
module_source_code = _extract_source_code_from_function(module_source_code)
|
|
_, path = _create_module(module_source_code, tmp_path, request.node.name)
|
|
result = subprocess.run([sys.executable, str(path)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
|
|
if result.returncode != 0:
|
|
pytest.fail(
|
|
f'Running {path} failed with non-zero return code: {result.returncode}\n'
|
|
f'Captured stdout:\n{result.stdout.decode()}\n'
|
|
f'Captured stderr:\n{result.stderr.decode()}'
|
|
)
|
|
|
|
return run
|