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

Remove unnecessary warning for config in plugin #9039

Merged
merged 2 commits into from Mar 18, 2024
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
15 changes: 12 additions & 3 deletions pydantic/mypy.py
Expand Up @@ -550,7 +550,7 @@ def collect_config(self) -> ModelConfigData: # noqa: C901 (ignore complexity)
for arg_name, arg in zip(stmt.rvalue.arg_names, stmt.rvalue.args):
if arg_name is None:
continue
config.update(self.get_config_update(arg_name, arg))
config.update(self.get_config_update(arg_name, arg, lax_extra=True))
elif isinstance(stmt.rvalue, DictExpr): # dict literals
for key_expr, value_expr in stmt.rvalue.items:
if not isinstance(key_expr, StrExpr):
Expand Down Expand Up @@ -944,7 +944,7 @@ def set_frozen(self, fields: list[PydanticModelField], api: SemanticAnalyzerPlug
var._fullname = info.fullname + '.' + var.name
info.names[var.name] = SymbolTableNode(MDEF, var)

def get_config_update(self, name: str, arg: Expression) -> ModelConfigData | None:
def get_config_update(self, name: str, arg: Expression, lax_extra: bool = False) -> ModelConfigData | None:
"""Determines the config update due to a single kwarg in the ConfigDict definition.

Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int)
Expand All @@ -957,7 +957,16 @@ def get_config_update(self, name: str, arg: Expression) -> ModelConfigData | Non
elif isinstance(arg, MemberExpr):
forbid_extra = arg.name == 'forbid'
else:
error_invalid_config_value(name, self._api, arg)
if not lax_extra:
# Only emit an error for other types of `arg` (e.g., `NameExpr`, `ConditionalExpr`, etc.) when
# reading from a config class, etc. If a ConfigDict is used, then we don't want to emit an error
# because you'll get type checking from the ConfigDict itself.
#
# It would be nice if we could introspect the types better otherwise, but I don't know what the API
# is to evaluate an expr into its type and then check if that type is compatible with the expected
# type. Note that you can still get proper type checking via: `model_config = ConfigDict(...)`, just
# if you don't use an explicit string, the plugin won't be able to infer whether extra is forbidden.
error_invalid_config_value(name, self._api, arg)
return None
return ModelConfigData(forbid_extra=forbid_extra)
if name == 'alias_generator':
Expand Down
9 changes: 9 additions & 0 deletions tests/mypy/modules/config_conditional_extra.py
@@ -0,0 +1,9 @@
from pydantic import BaseModel, ConfigDict


def condition() -> bool:
return True


class MyModel(BaseModel):
model_config = ConfigDict(extra='ignore' if condition() else 'forbid')
4 changes: 0 additions & 4 deletions tests/mypy/modules/plugin_fail.py
Expand Up @@ -51,10 +51,6 @@ class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
pass

Expand Down
Expand Up @@ -57,12 +57,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
6 changes: 0 additions & 6 deletions tests/mypy/outputs/1.0.1/mypy-plugin_ini/plugin_fail.py
Expand Up @@ -57,12 +57,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
Expand Up @@ -57,12 +57,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
6 changes: 0 additions & 6 deletions tests/mypy/outputs/1.0.1/pyproject-plugin_toml/plugin_fail.py
Expand Up @@ -57,12 +57,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
6 changes: 0 additions & 6 deletions tests/mypy/outputs/1.2.0/mypy-plugin_ini/plugin_fail.py
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
6 changes: 0 additions & 6 deletions tests/mypy/outputs/1.2.0/pyproject-plugin_toml/plugin_fail.py
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
6 changes: 0 additions & 6 deletions tests/mypy/outputs/1.4.1/mypy-plugin_ini/plugin_fail.py
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
6 changes: 0 additions & 6 deletions tests/mypy/outputs/1.4.1/pyproject-plugin_toml/plugin_fail.py
Expand Up @@ -59,12 +59,6 @@ class KwargsForbidExtraModel(BaseModel, extra='forbid'):

class BadExtraModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item]
# MYPY: error: Invalid value for "Config.extra" [pydantic-config]
# MYPY: note: Error code "pydantic-config" not covered by "type: ignore" comment


class BadExtraButIgnoredModel(BaseModel):
model_config = ConfigDict(extra=1) # type: ignore[typeddict-item,pydantic-config]


class KwargsBadExtraModel(BaseModel, extra=1):
Expand Down
7 changes: 4 additions & 3 deletions tests/mypy/test_mypy.py
Expand Up @@ -87,7 +87,6 @@ def build(self) -> List[Union[Tuple[str, str], Any]]:
'plugin_fail.py',
'plugin_success_baseConfig.py',
'plugin_fail_baseConfig.py',
'plugin_optional_inheritance.py',
'pydantic_settings.py',
],
).build()
Expand All @@ -105,6 +104,8 @@ def build(self) -> List[Union[Tuple[str, str], Any]]:
# One-off cases
+ [
('mypy-plugin.ini', 'custom_constructor.py'),
('mypy-plugin.ini', 'config_conditional_extra.py'),
('mypy-plugin.ini', 'plugin_optional_inheritance.py'),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just moved this down here to reduce the number of mypy tests by 1, since I added this yesterday and forgot I could add it down here instead of above to reduce the number of runs.

('mypy-plugin.ini', 'generics.py'),
('mypy-plugin.ini', 'root_models.py'),
('mypy-plugin-strict.ini', 'plugin_default_factory.py'),
Expand Down Expand Up @@ -195,11 +196,11 @@ def test_mypy_results(config_filename: str, python_filename: str, request: pytes
existing_output_code = test_config.existing.output_path.read_text()
print(f'Comparing output with {test_config.existing.output_path}')
else:
print('Expecting no mypy errors')
print(f'Comparing output with {input_path} (expecting no mypy errors)')

merged_output = merge_python_and_mypy_output(input_code, mypy_out)

if merged_output == existing_output_code:
if merged_output == (existing_output_code or input_code):
# Test passed, no changes needed
pass
elif request.config.getoption('update_mypy'):
Expand Down