Skip to content

“Decorator @X can only be used on methods” is too strict #2610

@grievejia

Description

@grievejia

Describe the Bug

Pyrefly reports "Decorator @x can only be used on methods" when @property, @classmethod, or @staticmethod are applied to functions outside a class body. This is valid Python -- these descriptors work correctly when the resulting objects are later assigned to class attributes.

Real-world: Django uses this pattern extensively in ModelAdmin and FormSet classes. Libraries like attrs and SQLAlchemy also create properties outside class bodies via factory functions.

# Pattern 1: @property factory function
#
# A helper creates a property descriptor, which is later assigned to a class.
# This is common when multiple classes share the same computed attributes.
def make_property(attr_name: str):
    @property
    def prop(self):
        return getattr(self, "_" + attr_name)

    return prop


class Point:
    def __init__(self, x: int, y: int):
        self._x = x
        self._y = y

    x = make_property("x")
    y = make_property("y")


p = Point(1, 2)
assert p.x == 1
assert p.y == 2


# Pattern 2: @staticmethod outside class body
#
# A utility function decorated with @staticmethod at module level,
# then assigned as a class attribute.
@staticmethod
def utility(x: int) -> int:
    return x * 2


class Helper:
    compute = utility


result = Helper.compute(5)


# Pattern 3: @classmethod outside class body
#
# A classmethod created at module level, then assigned to a class.
@classmethod
def from_value(cls, val: int) -> "Container":
    obj = cls()
    obj.value = val
    return obj


class Container:
    value: int = 0
    from_value = from_value  # type: ignore[assignment]


c = Container.from_value(42)

The easiest way to address this issue might be to lower the default severity of the problem?

Sandbox Link

No response

(Only applicable for extension issues) IDE Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-discussionAn issue where it's not clear whether there is a bug or we are behaving as expected.typecheckingusabilityUsability & readiness issues identified with running Pyrefly on top OSS projectsv1-verifiedIn both V1 milestone and top-ranked (verified by ranking pipeline)

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions