Skip to content

Commit

Permalink
ensure cythonized functions are left untouched2 (#2228)
Browse files Browse the repository at this point in the history
* ensure cythonized functions are left untouched2

* add change
  • Loading branch information
samuelcolvin committed Feb 13, 2021
1 parent 78934db commit b87e249
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 2 deletions.
1 change: 1 addition & 0 deletions changes/2228-samuelcolvin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ensure cythonized functions are left untouched when creating models, based on #1944 by @kollmats
9 changes: 7 additions & 2 deletions pydantic/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901

prepare_config(config, name)

untouched_types = ANNOTATED_FIELD_UNTOUCHED_TYPES

def is_untouched(v: Any) -> bool:
return isinstance(v, untouched_types) or v.__class__.__name__ == 'cython_function_or_method'

if (namespace.get('__module__'), namespace.get('__qualname__')) != ('pydantic.main', 'BaseModel'):
annotations = resolve_annotations(namespace.get('__annotations__', {}), namespace.get('__module__', None))
# annotation only fields need to come first in fields
Expand All @@ -270,7 +275,7 @@ def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901
value = namespace.get(ann_name, Undefined)
allowed_types = get_args(ann_type) if get_origin(ann_type) is Union else (ann_type,)
if (
isinstance(value, ANNOTATED_FIELD_UNTOUCHED_TYPES)
is_untouched(value)
and ann_type != PyObject
and not any(
lenient_issubclass(get_origin(allowed_type), Type) for allowed_type in allowed_types
Expand All @@ -289,7 +294,7 @@ def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901

untouched_types = UNTOUCHED_TYPES + config.keep_untouched
for var_name, value in namespace.items():
can_be_changed = var_name not in class_vars and not isinstance(value, untouched_types)
can_be_changed = var_name not in class_vars and not is_untouched(value)
if isinstance(value, ModelPrivateAttr):
if not is_valid_private_name(var_name):
raise NameError(
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ addopts = -p no:hypothesispytest
filterwarnings =
error
ignore::DeprecationWarning:distutils
ignore::DeprecationWarning:Cython

[flake8]
max-line-length = 120
Expand Down
28 changes: 28 additions & 0 deletions tests/test_edge_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
)
from pydantic.fields import Field, Schema

try:
import cython
except ImportError:
cython = None


def test_str_bytes():
class Model(BaseModel):
Expand Down Expand Up @@ -1745,3 +1750,26 @@ class Child(Parent):
pass

assert Child(foo=['a', 'b']).foo == ['a-1', 'b-1']


@pytest.mark.skipif(cython is None, reason='cython not installed')
def test_cython_function_untouched():
Model = cython.inline(
# language=Python
"""
from pydantic import BaseModel
class Model(BaseModel):
a = 0.0
b = 10
def get_double_a(self) -> float:
return self.a + self.b
return Model
"""
)
model = Model(a=10.2)
assert model.a == 10.2
assert model.b == 10
return model.get_double_a() == 20.2

0 comments on commit b87e249

Please sign in to comment.