Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds reserved word check to signature generation logic. #4012

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/4011-strue36.md
@@ -0,0 +1 @@
Adds reserved word check to signature generation logic.
15 changes: 13 additions & 2 deletions pydantic/utils.py
@@ -1,3 +1,4 @@
import keyword
import warnings
import weakref
from collections import OrderedDict, defaultdict, deque
Expand Down Expand Up @@ -56,6 +57,7 @@
'lenient_isinstance',
'lenient_issubclass',
'in_ipython',
'is_valid_identifier',
'deep_update',
'update_not_none',
'almost_equal_floats',
Expand Down Expand Up @@ -192,6 +194,15 @@ def in_ipython() -> bool:
return True


def is_valid_identifier(identifier: str) -> bool:
"""
Checks that a string is a valid identifier and not a Python keyword.
:param identifier: The identifier to test.
:return: True if the identifier is valid.
"""
return identifier.isidentifier() and not keyword.iskeyword(identifier)


KeyType = TypeVar('KeyType')


Expand Down Expand Up @@ -244,8 +255,8 @@ def generate_model_signature(
param_name = field.alias
if field_name in merged_params or param_name in merged_params:
continue
elif not param_name.isidentifier():
if allow_names and field_name.isidentifier():
elif not is_valid_identifier(param_name):
if allow_names and is_valid_identifier(field_name):
param_name = field_name
else:
use_var_kw = True
Expand Down
10 changes: 10 additions & 0 deletions tests/test_model_signature.py
Expand Up @@ -84,6 +84,16 @@ class Config:
assert _equals(str(signature(Foo)), '(*, foo: str) -> None')


def test_does_not_use_reserved_word():
class Foo(BaseModel):
from_: str = Field(..., alias='from')

class Config:
allow_population_by_field_name = True

assert _equals(str(signature(Foo)), '(*, from_: str) -> None')


def test_extra_allow_no_conflict():
class Model(BaseModel):
spam: str
Expand Down