From 999332ef77196cdfedd7e1af03dc89010b5ec787 Mon Sep 17 00:00:00 2001 From: Eric Masseran Date: Tue, 18 Mar 2025 00:30:50 +0100 Subject: [PATCH] Dataclass transform change init False --- jedi/inference/value/klass.py | 25 ++++++++++++++++++++++++- jedi/plugins/stdlib.py | 12 ++++++++++-- test/test_inference/test_signature.py | 27 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index e6750614..7158eee4 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -296,7 +296,9 @@ class ClassMixin: continue # All inherited behave like dataclass - if is_dataclass_transform_with_init: + if is_dataclass_transform_with_init and ( + isinstance(cls, ClassValue) and not cls._has_init_false() + ): param_names.extend( get_dataclass_param_names(cls) ) @@ -557,6 +559,27 @@ class ClassValue(ClassMixin, FunctionAndClassBase, metaclass=CachedMetaClass): return values return NO_VALUES + def _has_init_false(self) -> bool: + """ + It returns ``True`` if ``class X(init=False):`` else ``False``. + """ + bases_arguments = self._get_bases_arguments() + + if bases_arguments.argument_node.type != "arglist": + # If it is not inheriting from the base model and having + # extra parameters, then init behavior is not changed. + return False + + for arg in bases_arguments.argument_node.children: + if ( + arg.type == "argument" + and arg.children[0].value == "init" + and arg.children[2].value == "False" + ): + return True + + return False + @plugin_manager.decorate() def get_metaclass_signatures(self, metaclasses): return [] diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 491a3b61..618965ca 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -625,9 +625,17 @@ def _dataclass(value, arguments, callback): ] ) elif c.is_function(): - # dataclass_transform on a decorator equivalent of @dataclass + # @dataclass_transform + # def create_model(): pass return ValueSet([value]) - elif value.name.string_name != "dataclass_transform": + elif ( + # @dataclass(...) + value.name.string_name != "dataclass_transform" + # @dataclass_transform + # def create_model(): pass + # @create_model(...) + or isinstance(value, Decoratee) + ): # dataclass (or like) decorator customization return ValueSet( [ diff --git a/test/test_inference/test_signature.py b/test/test_inference/test_signature.py index 47c9f032..b31b277d 100644 --- a/test/test_inference/test_signature.py +++ b/test/test_inference/test_signature.py @@ -482,6 +482,30 @@ dataclass_transform_cases = [ def __init__(self, toto: str): pass '''), ["toto"], False], + # Class based init=false + [dedent(''' + @dataclass_transform + class Y(): + y: int + z = 5 + class X(Y, init=False):'''), [], False], + # Decorator based init=false + [dedent(''' + @dataclass_transform + def create_model(): + pass + @create_model(init=False) + class X:'''), [], False], + # Metaclass based init=false + [dedent(''' + @dataclass_transform + class ModelMeta(): + y: int + z = 5 + class ModelBase(metaclass=ModelMeta): + t: int + p = 5 + class X(ModelBase, init=False):'''), [], False], ] ids = [ @@ -493,6 +517,9 @@ ids = [ "decorator_transformed", "metaclass_transformed", "custom_init", + "base_transformed_init_false", + "decorator_transformed_init_false", + "metaclass_transformed_init_false", ]