From 50778c390f539ca160338ac74fe733c6f4521b76 Mon Sep 17 00:00:00 2001 From: Eric Masseran Date: Sat, 15 Mar 2025 16:23:32 +0100 Subject: [PATCH] Fix init=false for transform and exclude fields on base transform --- jedi/inference/value/klass.py | 35 ++++++++++++++++++--------- jedi/plugins/stdlib.py | 21 ++++++++++------ test/test_inference/test_signature.py | 9 ------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index c8c77703..e6750614 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -259,11 +259,12 @@ class ClassMixin: for meta in self.get_metaclasses(): # type: ignore[attr-defined] if ( # Not sure if necessary - isinstance(meta, DataclassWrapper) + (isinstance(meta, DataclassWrapper) and meta.dataclass_init) or ( isinstance(meta, Decoratee) # Internal leakage :| and isinstance(meta._wrapped_value, DataclassWrapper) + and meta._wrapped_value.dataclass_init ) ): return True @@ -276,31 +277,31 @@ class ClassMixin: empty list. """ param_names = [] - is_dataclass_transform = False + is_dataclass_transform_with_init = False for cls in reversed(list(self.py__mro__())): - if not is_dataclass_transform and ( + if not is_dataclass_transform_with_init and ( # If dataclass_transform is applied to a class, dataclass-like semantics # will be assumed for any class that directly or indirectly derives from # the decorated class or uses the decorated class as a metaclass. - isinstance(cls, DataclassWrapper) + (isinstance(cls, DataclassWrapper) and cls.dataclass_init) or ( # Some object like CompiledValues would not be compatible isinstance(cls, ClassMixin) and cls._has_dataclass_transform_metaclasses() ) ): - is_dataclass_transform = True + is_dataclass_transform_with_init = True # Attributes on the decorated class and its base classes are not # considered to be fields. continue # All inherited behave like dataclass - if is_dataclass_transform: + if is_dataclass_transform_with_init: param_names.extend( get_dataclass_param_names(cls) ) - if is_dataclass_transform: + if is_dataclass_transform_with_init: return [DataclassSignature(cls, param_names)] else: [] @@ -468,13 +469,25 @@ class DataclassDecorator(ValueWrapper, FunctionMixin): class DataclassWrapper(ValueWrapper, ClassMixin): + + def __init__( + self, wrapped_value, dataclass_init: bool, is_dataclass_transform: bool = False + ): + super().__init__(wrapped_value) + self.dataclass_init = dataclass_init + self.is_dataclass_transform = is_dataclass_transform + def get_signatures(self): param_names = [] for cls in reversed(list(self.py__mro__())): - if isinstance(cls, DataclassWrapper): - param_names.extend( - get_dataclass_param_names(cls) - ) + if ( + isinstance(cls, DataclassWrapper) + and cls.dataclass_init + # Attributes on the decorated class and its base classes are not + # considered to be fields. + and not cls.is_dataclass_transform + ): + param_names.extend(get_dataclass_param_names(cls)) return [DataclassSignature(cls, param_names)] diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 0ed2f24c..6d83d2c6 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -597,19 +597,26 @@ def _dataclass(value, arguments, callback): """ for c in _follow_param(value.inference_state, arguments, 0): if c.is_class(): - # Decorate the class + # Decorate a class dataclass_init = ( - # Customized decorator + # Customized decorator, init may be disabled not value.has_dataclass_init_false if isinstance(value, DataclassDecorator) - # Bare dataclass decorator + # Bare dataclass decorator, always with init else True ) - if dataclass_init: - return ValueSet([DataclassWrapper(c)]) - else: - return ValueSet([c]) + return ValueSet( + [ + DataclassWrapper( + c, + dataclass_init, + is_dataclass_transform=value.name.string_name + == "dataclass_transform", + ) + ] + ) + else: # Decorator customization return ValueSet( diff --git a/test/test_inference/test_signature.py b/test/test_inference/test_signature.py index cf4ef436..1b7e2d4d 100644 --- a/test/test_inference/test_signature.py +++ b/test/test_inference/test_signature.py @@ -439,14 +439,6 @@ dataclass_transform_cases = [ y: int @dataclass_transform class X(Y):'''), [], False], - # Both classes - [dedent(''' - @dataclass_transform - class Y(): - y: int - z = 5 - @dataclass_transform - class X(Y):'''), [], False], # 2/ Declare dataclass transformed # Class based [dedent(''' @@ -502,7 +494,6 @@ ids = [ "direct_transformer", "transformer_with_params", "subclass_transformer", - "both_transformer", "base_transformed", "decorator_transformed", "metaclass_transformed",