Skip to content

Commit

Permalink
Remove unnecessary warning for config in plugin (#9039)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmontagu committed Mar 18, 2024
1 parent 26a2f06 commit 09f4456
Show file tree
Hide file tree
Showing 16 changed files with 25 additions and 82 deletions.
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'),
('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

0 comments on commit 09f4456

Please sign in to comment.