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

False invalid-overridden-method positive with nested property #4368

Closed
scop opened this issue Apr 16, 2021 · 1 comment · Fixed by #4710
Closed

False invalid-overridden-method positive with nested property #4368

scop opened this issue Apr 16, 2021 · 1 comment · Fixed by #4710
Assignees
Labels
Bug 🪲 False Positive 🦟 A message is emitted but nothing is wrong with the code
Milestone

Comments

@scop
Copy link
Contributor

scop commented Apr 16, 2021

Originally reported in #3150 (comment) which was closed as the initially reported issue was fixed, but the one I added still remains.

Steps to reproduce

$ cat t.py
def my_property(func):
    @property
    def _wrapper(self):
        pass

    return _wrapper


class A:
    @property
    def foo():
        return True


class B(A):
    @my_property
    def foo():
        return False

Current behavior

$ pylint ./t.py  | grep overridden
t.py:17:4: W0236: Method 'foo' was expected to be 'property', found it instead as 'method' (invalid-overridden-method)

Expected behavior

No invalid-overridden-method

pylint --version output

Result of pylint --version output:

pylint 2.7.4
astroid 2.5.3
Python 3.8.0 (default, Mar 30 2021, 18:43:10) 
[GCC 9.3.0]
@Pierre-Sassoulas Pierre-Sassoulas added the False Positive 🦟 A message is emitted but nothing is wrong with the code label Apr 16, 2021
@cdce8p cdce8p self-assigned this Jul 14, 2021
@cdce8p cdce8p added this to the 2.9.4 milestone Jul 14, 2021
@Gui-FernandesBR
Copy link

I'm still facing this problem, don't know if I'm doing something wrong here or if the old behavior is back.
I would appreciate if someone could give a double check in this issue.

-> in my .pylintrc file I wrote:

# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty,funcify_method

-> But I'm getting this error:

Method 'propellant_I_11' was expected to be 'property', found it instead as 'method'Pylint[W0236:invalid-overridden-method](https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/invalid-overridden-method.html)

I'm working on a public project so no problem if I share the code code. This are my functions, summarizing:

from abc import ABC, abstractmethod

class Motor(ABC):
    @property
    @abstractmethod
    def propellant_I_11(self):
        return 0 + 0 + 0  # just for example

class SolidMotor(Motor):
    @funcify_method("Time (s)", "Inertia I_11 (kg m²)")
    def propellant_I_11(self):
        return 0  # just for example

def funcify_method(*args, **kwargs):
    """Decorator factory to wrap methods as Function objects and save them as
    cached properties.

    Parameters
    ----------
    *args : list
        Positional arguments to be passed to rocketpy.Function.
    **kwargs : dict
        Keyword arguments to be passed to rocketpy.Function.

    Returns
    -------
    decorator : function
        Decorator function to wrap callables as Function objects.

    Examples
    --------
    There are 3 types of methods that this decorator supports:

    1. Method which returns a valid rocketpy.Function source argument.

    >>> from rocketpy.mathutils import funcify_method
    >>> class Example():
    ...     @funcify_method(inputs=['x'], outputs=['y'])
    ...     def f(self):
    ...         return lambda x: x**2
    >>> example = Example()
    >>> example.f
    'Function from R1 to R1 : (x) → (y)'

    Normal algebra can be performed afterwards:

    >>> g = 2*example.f + 3
    >>> g(2)
    11

    2. Method which returns a rocketpy.Function instance. An interesting use is
    to reset input and output names after algebraic operations.

    >>> class Example():
    ...     @funcify_method(inputs=['x'], outputs=['x**3'])
    ...     def cube(self):
    ...         f = Function(lambda x: x**2)
    ...         g = Function(lambda x: x**5)
    ...         return g / f
    >>> example = Example()
    >>> example.cube
    'Function from R1 to R1 : (x) → (x**3)'

    3. Method which is itself a valid rocketpy.Function source argument.

    >>> class Example():
    ...     @funcify_method('x', 'f(x)')
    ...     def f(self, x):
    ...         return x**2
    >>> example = Example()
    >>> example.f
    'Function from R1 to R1 : (x) → (f(x))'

    In order to reset the cache, just delete de attribute from the instance:

    >>> del example.f

    Once it is requested again, it will be re-created as a new Function object:

    >>> example.f
    'Function from R1 to R1 : (x) → (f(x))'
    """
    func = None
    if len(args) == 1 and callable(args[0]):
        func = args[0]
        args = []

    class funcify_method_decorator:
        """Decorator class to transform a cached property that is being defined
        inside a class to a Function object. This improves readability of the
        code since it will not require the user to directly invoke the Function
        class.
        """

        # pylint: disable=C0103
        def __init__(self, func):
            self.func = func
            self.attrname = None
            self.__doc__ = func.__doc__

        def __set_name__(self, owner, name):
            self.attrname = name

        def __get__(self, instance, owner=None):
            if instance is None:
                return self
            cache = instance.__dict__
            try:
                # If cache is ready, return it
                val = cache[self.attrname]
            except KeyError:
                # If cache is not ready, create it
                try:
                    # Handle methods which return Function instances
                    val = self.func(instance).reset(*args, **kwargs)
                except AttributeError:
                    # Handle methods which return a valid source
                    source = self.func(instance)
                    val = Function(source, *args, **kwargs)
                except TypeError:
                    # Handle methods which are the source themselves
                    source = lambda *_: self.func(instance, *_)
                    val = Function(source, *args, **kwargs)
                except Exception as exc:
                    raise Exception(
                        "Could not create Function object from method "
                        f"{self.func.__name__}."
                    ) from exc

                val.__doc__ = self.__doc__
                val.__cached__ = True
                cache[self.attrname] = val
            return val

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🪲 False Positive 🦟 A message is emitted but nothing is wrong with the code
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants