コード例 #1
0
def _attributes_from_assignment(ctx: 'mypy.plugin.ClassDefContext',
                                stmt: AssignmentStmt, auto_attribs: bool,
                                kw_only: bool) -> Iterable[Attribute]:
    """Return Attribute objects that are created by this assignment.

    The assignments can look like this:
        x = attr.ib()
        x = y = attr.ib()
        x, y = attr.ib(), attr.ib()
    or if auto_attribs is enabled also like this:
        x: type
        x: type = default_value
    """
    for lvalue in stmt.lvalues:
        lvalues, rvalues = _parse_assignments(lvalue, stmt)

        if len(lvalues) != len(rvalues):
            # This means we have some assignment that isn't 1 to 1.
            # It can't be an attrib.
            continue

        for lhs, rvalue in zip(lvalues, rvalues):
            # Check if the right hand side is a call to an attribute maker.
            if (isinstance(rvalue, CallExpr)
                    and isinstance(rvalue.callee, RefExpr)
                    and rvalue.callee.fullname in attr_attrib_makers):
                attr = _attribute_from_attrib_maker(ctx, auto_attribs, kw_only, lhs, rvalue, stmt)
                if attr:
                    yield attr
            elif auto_attribs and stmt.type and stmt.new_syntax and not is_class_var(lhs):
                yield _attribute_from_auto_attrib(ctx, kw_only, lhs, rvalue, stmt)
コード例 #2
0
ファイル: attrs.py プロジェクト: sixolet/mypy
def _attributes_from_assignment(ctx: 'mypy.plugin.ClassDefContext',
                                stmt: AssignmentStmt, auto_attribs: bool) -> Iterable[Attribute]:
    """Return Attribute objects that are created by this assignment.

    The assignments can look like this:
        x = attr.ib()
        x = y = attr.ib()
        x, y = attr.ib(), attr.ib()
    or if auto_attribs is enabled also like this:
        x: type
        x: type = default_value
    """
    for lvalue in stmt.lvalues:
        lvalues, rvalues = _parse_assignments(lvalue, stmt)

        if len(lvalues) != len(rvalues):
            # This means we have some assignment that isn't 1 to 1.
            # It can't be an attrib.
            continue

        for lhs, rvalue in zip(lvalues, rvalues):
            # Check if the right hand side is a call to an attribute maker.
            if (isinstance(rvalue, CallExpr)
                    and isinstance(rvalue.callee, RefExpr)
                    and rvalue.callee.fullname in attr_attrib_makers):
                attr = _attribute_from_attrib_maker(ctx, auto_attribs, lhs, rvalue, stmt)
                if attr:
                    yield attr
            elif auto_attribs and stmt.type and stmt.new_syntax and not is_class_var(lhs):
                yield _attribute_from_auto_attrib(ctx, lhs, rvalue, stmt)
コード例 #3
0
ファイル: classdef.py プロジェクト: alanhdu/mypy
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None:
    """Generate an initialization method for default attr values (from class vars)."""
    cls = builder.mapper.type_to_ir[cdef.info]
    if cls.builtin_base:
        return

    # Pull out all assignments in classes in the mro so we can initialize them
    # TODO: Support nested statements
    default_assignments = []
    for info in reversed(cdef.info.mro):
        if info not in builder.mapper.type_to_ir:
            continue
        for stmt in info.defn.defs.body:
            if (isinstance(stmt, AssignmentStmt)
                    and isinstance(stmt.lvalues[0], NameExpr)
                    and not is_class_var(stmt.lvalues[0])
                    and not isinstance(stmt.rvalue, TempNode)):
                name = stmt.lvalues[0].name
                if name == '__slots__':
                    continue

                if name == '__deletable__':
                    check_deletable_declaration(builder, cls, stmt.line)
                    continue

                # Skip type annotated assignments in dataclasses
                if is_dataclass(cdef) and stmt.type:
                    continue

                default_assignments.append(stmt)

    if not default_assignments:
        return

    builder.enter_method(cls, '__mypyc_defaults_setup', bool_rprimitive)

    self_var = builder.self()
    for stmt in default_assignments:
        lvalue = stmt.lvalues[0]
        assert isinstance(lvalue, NameExpr)
        if not stmt.is_final_def and not is_constant(stmt.rvalue):
            builder.warning('Unsupported default attribute value',
                            stmt.rvalue.line)

        # If the attribute is initialized to None and type isn't optional,
        # don't initialize it to anything.
        attr_type = cls.attr_type(lvalue.name)
        if isinstance(stmt.rvalue,
                      RefExpr) and stmt.rvalue.fullname == 'builtins.None':
            if (not is_optional_type(attr_type)
                    and not is_object_rprimitive(attr_type)
                    and not is_none_rprimitive(attr_type)):
                continue
        val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line)
        builder.add(SetAttr(self_var, lvalue.name, val, -1))

    builder.add(Return(builder.true()))

    builder.leave_method()
コード例 #4
0
ファイル: classdef.py プロジェクト: pranavrajpal/mypy
def find_attr_initializers(
    builder: IRBuilder,
    cdef: ClassDef,
    skip: Optional[Callable[[str, AssignmentStmt], bool]] = None,
) -> Tuple[Set[str], List[AssignmentStmt]]:
    """Find initializers of attributes in a class body.

    If provided, the skip arg should be a callable which will return whether
    to skip generating a default for an attribute.  It will be passed the name of
    the attribute and the corresponding AssignmentStmt.
    """
    cls = builder.mapper.type_to_ir[cdef.info]
    if cls.builtin_base:
        return set(), []

    attrs_with_defaults = set()

    # Pull out all assignments in classes in the mro so we can initialize them
    # TODO: Support nested statements
    default_assignments = []
    for info in reversed(cdef.info.mro):
        if info not in builder.mapper.type_to_ir:
            continue
        for stmt in info.defn.defs.body:
            if (isinstance(stmt, AssignmentStmt)
                    and isinstance(stmt.lvalues[0], NameExpr)
                    and not is_class_var(stmt.lvalues[0])
                    and not isinstance(stmt.rvalue, TempNode)):
                name = stmt.lvalues[0].name
                if name == '__slots__':
                    continue

                if name == '__deletable__':
                    check_deletable_declaration(builder, cls, stmt.line)
                    continue

                if skip is not None and skip(name, stmt):
                    continue

                attr_type = cls.attr_type(name)

                # If the attribute is initialized to None and type isn't optional,
                # doesn't initialize it to anything (special case for "# type:" comments).
                if isinstance(
                        stmt.rvalue,
                        RefExpr) and stmt.rvalue.fullname == 'builtins.None':
                    if (not is_optional_type(attr_type)
                            and not is_object_rprimitive(attr_type)
                            and not is_none_rprimitive(attr_type)):
                        continue

                attrs_with_defaults.add(name)
                default_assignments.append(stmt)

    return attrs_with_defaults, default_assignments
コード例 #5
0
ファイル: classdef.py プロジェクト: srittau/mypy
 def add_attr(self, lvalue: NameExpr, stmt: AssignmentStmt) -> None:
     # Variable declaration with no body
     if isinstance(stmt.rvalue, TempNode):
         return
     # Only treat marked class variables as class variables.
     if not (is_class_var(lvalue) or stmt.is_final_def):
         return
     typ = self.builder.load_native_type_object(self.cdef.fullname)
     value = self.builder.accept(stmt.rvalue)
     self.builder.call_c(
         py_setattr_op, [typ, self.builder.load_str(lvalue.name), value], stmt.line)
     if self.builder.non_function_scope() and stmt.is_final_def:
         self.builder.init_final_static(lvalue, value, self.cdef.name)
コード例 #6
0
def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
    """Create IR for a class definition.

    This can generate both extension (native) and non-extension
    classes.  These are generated in very different ways. In the
    latter case we construct a Python type object at runtime by doing
    the equivalent of "type(name, bases, dict)" in IR. Extension
    classes are defined via C structs that are generated later in
    mypyc.codegen.emitclass.

    This is the main entry point to this module.
    """
    ir = builder.mapper.type_to_ir[cdef.info]

    # We do this check here because the base field of parent
    # classes aren't necessarily populated yet at
    # prepare_class_def time.
    if any(ir.base_mro[i].base != ir. base_mro[i + 1] for i in range(len(ir.base_mro) - 1)):
        builder.error("Non-trait MRO must be linear", cdef.line)

    if ir.allow_interpreted_subclasses:
        for parent in ir.mro:
            if not parent.allow_interpreted_subclasses:
                builder.error(
                    'Base class "{}" does not allow interpreted subclasses'.format(
                        parent.fullname), cdef.line)

    # Currently, we only create non-extension classes for classes that are
    # decorated or inherit from Enum. Classes decorated with @trait do not
    # apply here, and are handled in a different way.
    if ir.is_ext_class:
        # If the class is not decorated, generate an extension class for it.
        type_obj = allocate_class(builder, cdef)  # type: Optional[Value]
        non_ext = None  # type: Optional[NonExtClassInfo]
        dataclass_non_ext = dataclass_non_ext_info(builder, cdef)
    else:
        non_ext_bases = populate_non_ext_bases(builder, cdef)
        non_ext_metaclass = find_non_ext_metaclass(builder, cdef, non_ext_bases)
        non_ext_dict = setup_non_ext_dict(builder, cdef, non_ext_metaclass, non_ext_bases)
        # We populate __annotations__ for non-extension classes
        # because dataclasses uses it to determine which attributes to compute on.
        # TODO: Maybe generate more precise types for annotations
        non_ext_anns = builder.call_c(dict_new_op, [], cdef.line)
        non_ext = NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass)
        dataclass_non_ext = None
        type_obj = None

    attrs_to_cache = []  # type: List[Tuple[Lvalue, RType]]

    for stmt in cdef.defs.body:
        if isinstance(stmt, OverloadedFuncDef) and stmt.is_property:
            if not ir.is_ext_class:
                # properties with both getters and setters in non_extension
                # classes not supported
                builder.error("Property setters not supported in non-extension classes",
                           stmt.line)
            for item in stmt.items:
                with builder.catch_errors(stmt.line):
                    transform_method(builder, cdef, non_ext, get_func_def(item))
        elif isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef)):
            # Ignore plugin generated methods (since they have no
            # bodies to compile and will need to have the bodies
            # provided by some other mechanism.)
            if cdef.info.names[stmt.name].plugin_generated:
                continue
            with builder.catch_errors(stmt.line):
                transform_method(builder, cdef, non_ext, get_func_def(stmt))
        elif isinstance(stmt, PassStmt):
            continue
        elif isinstance(stmt, AssignmentStmt):
            if len(stmt.lvalues) != 1:
                builder.error("Multiple assignment in class bodies not supported", stmt.line)
                continue
            lvalue = stmt.lvalues[0]
            if not isinstance(lvalue, NameExpr):
                builder.error("Only assignment to variables is supported in class bodies",
                           stmt.line)
                continue
            # We want to collect class variables in a dictionary for both real
            # non-extension classes and fake dataclass ones.
            var_non_ext = non_ext or dataclass_non_ext
            if var_non_ext:
                add_non_ext_class_attr(builder, var_non_ext, lvalue, stmt, cdef, attrs_to_cache)
                if non_ext:
                    continue
            # Variable declaration with no body
            if isinstance(stmt.rvalue, TempNode):
                continue
            # Only treat marked class variables as class variables.
            if not (is_class_var(lvalue) or stmt.is_final_def):
                continue
            typ = builder.load_native_type_object(cdef.fullname)
            value = builder.accept(stmt.rvalue)
            builder.call_c(
                py_setattr_op, [typ, builder.load_str(lvalue.name), value], stmt.line)
            if builder.non_function_scope() and stmt.is_final_def:
                builder.init_final_static(lvalue, value, cdef.name)
        elif isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr):
            # Docstring. Ignore
            pass
        else:
            builder.error("Unsupported statement in class body", stmt.line)

    if not non_ext:  # That is, an extension class
        generate_attr_defaults(builder, cdef)
        create_ne_from_eq(builder, cdef)
        if dataclass_non_ext:
            assert type_obj
            dataclass_finalize(builder, cdef, dataclass_non_ext, type_obj)
    else:
        # Dynamically create the class via the type constructor
        non_ext_class = load_non_ext_class(builder, ir, non_ext, cdef.line)
        non_ext_class = load_decorated_class(builder, cdef, non_ext_class)

        # Save the decorated class
        builder.add(InitStatic(non_ext_class, cdef.name, builder.module_name, NAMESPACE_TYPE))

        # Add the non-extension class to the dict
        builder.call_c(dict_set_item_op,
                       [
                           builder.load_globals_dict(),
                           builder.load_str(cdef.name),
                           non_ext_class
                       ], cdef.line)

        # Cache any cachable class attributes
        cache_class_attrs(builder, attrs_to_cache, cdef)
コード例 #7
0
ファイル: genclass.py プロジェクト: anygitdata/ubn-nltest
    def generate_attr_defaults(self, cdef: ClassDef) -> None:
        """Generate an initialization method for default attr values (from class vars)"""
        cls = self.mapper.type_to_ir[cdef.info]
        if cls.builtin_base:
            return

        # Pull out all assignments in classes in the mro so we can initialize them
        # TODO: Support nested statements
        default_assignments = []
        for info in reversed(cdef.info.mro):
            if info not in self.mapper.type_to_ir:
                continue
            for stmt in info.defn.defs.body:
                if (isinstance(stmt, AssignmentStmt)
                        and isinstance(stmt.lvalues[0], NameExpr)
                        and not is_class_var(stmt.lvalues[0])
                        and not isinstance(stmt.rvalue, TempNode)):
                    if stmt.lvalues[0].name == '__slots__':
                        continue

                    # Skip type annotated assignments in dataclasses
                    if is_dataclass(cdef) and stmt.type:
                        continue

                    default_assignments.append(stmt)

        if not default_assignments:
            return

        self.builder.enter()
        self.builder.ret_types[-1] = bool_rprimitive

        rt_args = (RuntimeArg(SELF_NAME, RInstance(cls)),)
        self_var = self.builder.read(add_self_to_env(self.builder.environment, cls), -1)

        for stmt in default_assignments:
            lvalue = stmt.lvalues[0]
            assert isinstance(lvalue, NameExpr)
            if not stmt.is_final_def and not is_constant(stmt.rvalue):
                self.builder.warning('Unsupported default attribute value', stmt.rvalue.line)

            # If the attribute is initialized to None and type isn't optional,
            # don't initialize it to anything.
            attr_type = cls.attr_type(lvalue.name)
            if isinstance(stmt.rvalue, RefExpr) and stmt.rvalue.fullname == 'builtins.None':
                if (not is_optional_type(attr_type) and not is_object_rprimitive(attr_type)
                        and not is_none_rprimitive(attr_type)):
                    continue
            val = self.builder.coerce(self.accept(stmt.rvalue), attr_type, stmt.line)
            self.add(SetAttr(self_var, lvalue.name, val, -1))

        self.add(Return(self.primitive_op(true_op, [], -1)))

        blocks, env, ret_type, _ = self.builder.leave()
        ir = FuncIR(
            FuncDecl('__mypyc_defaults_setup',
                     cls.name, self.module_name,
                     FuncSignature(rt_args, ret_type)),
            blocks, env)
        self.builder.functions.append(ir)
        cls.methods[ir.name] = ir
コード例 #8
0
def attr_class_maker_callback(attr_classes: Dict[TypeInfo, List[Attribute]],
                              ctx: ClassDefContext,
                              auto_attribs_default: bool = False) -> None:
    """Add necessary dunder methods to classes decorated with attr.s.

    attrs is a package that lets you define classes without writing dull boilerplate code.

    At a quick glance, the decorator searches the class body for assignments of `attr.ib`s (or
    annotated variables if auto_attribs=True), then depending on how the decorator is called,
    it will add an __init__ or all the __cmp__ methods.  For frozen=True it will turn the attrs
    into properties.

    See http://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works.
    """
    info = ctx.cls.info

    # auto_attribs means we also generate Attributes from annotated variables.
    auto_attribs = _attrs_get_decorator_bool_argument(ctx, 'auto_attribs',
                                                      auto_attribs_default)

    if ctx.api.options.python_version[0] < 3:
        if auto_attribs:
            ctx.api.fail("auto_attribs is not supported in Python 2",
                         ctx.reason)
            return
        if not info.defn.base_type_exprs:
            # Note: This does not catch subclassing old-style classes.
            ctx.api.fail("attrs only works with new-style classes", info.defn)
            return

    # First, walk the body looking for attribute definitions.
    # They will look like this:
    #     x = attr.ib()
    #     x = y = attr.ib()
    #     x, y = attr.ib(), attr.ib()
    # or if auto_attribs is enabled also like this:
    #     x: type
    #     x: type = default_value
    own_attrs = OrderedDict()  # type: OrderedDict[str, Attribute]
    for stmt in ctx.cls.defs.body:
        if isinstance(stmt, AssignmentStmt):
            for lvalue in stmt.lvalues:
                # To handle all types of assignments we just convert everything
                # to a matching lists of lefts and rights.
                lhss = []  # type: List[NameExpr]
                rvalues = []  # type: List[Expression]
                if isinstance(lvalue, (TupleExpr, ListExpr)):
                    if all(
                            isinstance(item, NameExpr)
                            for item in lvalue.items):
                        lhss = cast(List[NameExpr], lvalue.items)
                    if isinstance(stmt.rvalue, (TupleExpr, ListExpr)):
                        rvalues = stmt.rvalue.items
                elif isinstance(lvalue, NameExpr):
                    lhss = [lvalue]
                    rvalues = [stmt.rvalue]

                if len(lhss) != len(rvalues):
                    # This means we have some assignment that isn't 1 to 1.
                    # It can't be an attrib.
                    continue

                for lhs, rvalue in zip(lhss, rvalues):
                    typ = stmt.type
                    name = lhs.name

                    # Check if the right hand side is a call to an attribute maker.
                    if (isinstance(rvalue, CallExpr)
                            and isinstance(rvalue.callee, RefExpr)
                            and rvalue.callee.fullname in attr_attrib_makers):
                        if auto_attribs and not stmt.new_syntax:
                            # auto_attribs requires annotation on every attr.ib.
                            ctx.api.fail(messages.NEED_ANNOTATION_FOR_VAR,
                                         stmt)
                            continue

                        if len(stmt.lvalues) > 1:
                            ctx.api.fail("Too many names for one attribute",
                                         stmt)
                            continue

                        # Look for default=<something> in the call.
                        # TODO: Check for attr.NOTHING
                        attr_has_default = bool(
                            _attrs_get_argument(rvalue, 'default'))

                        # If the type isn't set through annotation but it is passed through type=
                        # use that.
                        type_arg = _attrs_get_argument(rvalue, 'type')
                        if type_arg and not typ:
                            try:
                                un_type = expr_to_unanalyzed_type(type_arg)
                            except TypeTranslationError:
                                ctx.api.fail('Invalid argument to type',
                                             type_arg)
                            else:
                                typ = ctx.api.anal_type(un_type)
                                if typ and isinstance(
                                        lhs.node, Var) and not lhs.node.type:
                                    # If there is no annotation, add one.
                                    lhs.node.type = typ
                                    lhs.is_inferred_def = False

                        # If the attrib has a converter function take the type of the first
                        # argument as the init type.
                        # Note: convert is deprecated but works the same as converter.
                        converter = _attrs_get_argument(rvalue, 'converter')
                        convert = _attrs_get_argument(rvalue, 'convert')
                        if convert and converter:
                            ctx.api.fail(
                                "Can't pass both `convert` and `converter`.",
                                rvalue)
                        elif convert:
                            converter = convert
                        if (converter and isinstance(converter, RefExpr)
                                and converter.node
                                and isinstance(converter.node, FuncBase)
                                and converter.node.type and isinstance(
                                    converter.node.type, CallableType)
                                and converter.node.type.arg_types):
                            typ = converter.node.type.arg_types[0]

                        # Does this even have to go in init.
                        init = _attrs_get_bool_argument(
                            ctx, rvalue, 'init', True)

                        # When attrs are defined twice in the same body we want to use
                        # the 2nd definition in the 2nd location. So remove it from the
                        # OrderedDict.  auto_attribs doesn't work that way.
                        if not auto_attribs and name in own_attrs:
                            del own_attrs[name]
                        own_attrs[name] = Attribute(name, typ,
                                                    attr_has_default, init,
                                                    stmt)
                    elif auto_attribs and typ and stmt.new_syntax and not is_class_var(
                            lhs):
                        # `x: int` (without equal sign) assigns rvalue to TempNode(AnyType())
                        has_rhs = not isinstance(rvalue, TempNode)
                        own_attrs[name] = Attribute(name, typ, has_rhs, True,
                                                    stmt)

        elif isinstance(stmt, Decorator):
            # Look for attr specific decorators.  ('x.default' and 'x.validator')
            remove_me = []
            for func_decorator in stmt.decorators:
                if (isinstance(func_decorator, MemberExpr)
                        and isinstance(func_decorator.expr, NameExpr)
                        and func_decorator.expr.name in own_attrs):

                    if func_decorator.name == 'default':
                        # This decorator lets you set a default after the fact.
                        own_attrs[func_decorator.expr.name].has_default = True

                    if func_decorator.name in ('default', 'validator'):
                        # These are decorators on the attrib object that only exist during
                        # class creation time.  In order to not trigger a type error later we
                        # just remove them.  This might leave us with a Decorator with no
                        # decorators (Emperor's new clothes?)
                        # TODO: It would be nice to type-check these rather than remove them.
                        #       default should be Callable[[], T]
                        #       validator should be Callable[[Any, 'Attribute', T], Any]
                        #       where T is the type of the attribute.
                        remove_me.append(func_decorator)

            for dec in remove_me:
                stmt.decorators.remove(dec)

    taken_attr_names = set(own_attrs)
    super_attrs = []

    # Traverse the MRO and collect attributes from the parents.
    for super_info in info.mro[1:-1]:
        if super_info in attr_classes:
            for a in attr_classes[super_info]:
                # Only add an attribute if it hasn't been defined before.  This
                # allows for overwriting attribute definitions by subclassing.
                if a.name not in taken_attr_names:
                    super_attrs.append(a)
                    taken_attr_names.add(a.name)

    attributes = super_attrs + list(own_attrs.values())
    # Save the attributes so that subclasses can reuse them.
    # TODO: This doesn't work with incremental mode if the parent class is in a different file.
    attr_classes[info] = attributes

    if ctx.api.options.disallow_untyped_defs:
        for attribute in attributes:
            if attribute.type is None:
                # This is a compromise.  If you don't have a type here then the __init__ will
                # be untyped. But since the __init__ is added it's pointing at the decorator.
                # So instead we just show the error in the assignment, which is where you
                # would fix the issue.
                ctx.api.fail(messages.NEED_ANNOTATION_FOR_VAR,
                             attribute.context)

    # Check the init args for correct default-ness.  Note: This has to be done after all the
    # attributes for all classes have been read, because subclasses can override parents.
    last_default = False
    for attribute in attributes:
        if not attribute.has_default and last_default:
            ctx.api.fail(
                "Non-default attributes not allowed after default attributes.",
                attribute.context)
        last_default = attribute.has_default

    adder = MethodAdder(info, ctx.api.named_type('__builtins__.function'))

    if _attrs_get_decorator_bool_argument(ctx, 'init', True):
        # Generate the __init__ method.
        adder.add_method('__init__', [
            attribute.argument() for attribute in attributes if attribute.init
        ], NoneTyp())

        for stmt in ctx.cls.defs.body:
            # The type of classmethods will be wrong because it's based on the parent's __init__.
            # Set it correctly.
            if isinstance(stmt, Decorator) and stmt.func.is_class:
                func_type = stmt.func.type
                if isinstance(func_type, CallableType):
                    func_type.arg_types[0] = ctx.api.class_type(info)

    if _attrs_get_decorator_bool_argument(ctx, 'frozen', False):
        # If the class is frozen then all the attributes need to be turned into properties.
        for attribute in attributes:
            node = info.names[attribute.name].node
            assert isinstance(node, Var)
            node.is_initialized_in_class = False
            node.is_property = True

    if _attrs_get_decorator_bool_argument(ctx, 'cmp', True):
        # For __ne__ and __eq__ the type is:
        #     def __ne__(self, other: object) -> bool
        bool_type = ctx.api.named_type('__builtins__.bool')
        object_type = ctx.api.named_type('__builtins__.object')

        args = [
            Argument(Var('other', object_type), object_type, None, ARG_POS)
        ]
        for method in ['__ne__', '__eq__']:
            adder.add_method(method, args, bool_type)

        # For the rest we use:
        #    AT = TypeVar('AT')
        #    def __lt__(self: AT, other: AT) -> bool
        # This way comparisons with subclasses will work correctly.
        tvd = TypeVarDef('AT', 'AT', 1, [], object_type)
        tvd_type = TypeVarType(tvd)
        args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)]
        for method in ['__lt__', '__le__', '__gt__', '__ge__']:
            adder.add_method(method,
                             args,
                             bool_type,
                             self_type=tvd_type,
                             tvd=tvd)