PyCharm doesn't recognize SqlAlchemy declarative subclass signature without redundant __init__

Viewed 350

I have an SqlAlchemy declarative base class from which more complex classes are derived but for which I also need instances that are "plain" instances of the base class. I understand that SqlAlchemy doesn't create __init__ methods by default, but the base class does have one. Nonetheless at least PyCharm's linter doesn't seem to grok that the way I would expect: it's complaining that it doesn't recognize subclass instance initialization parameters.

If I'm understanding/using polymorphic identity properly, when I query the base class table I see anything that derives from the base class that matches the query. Rather than distinguish the "plain" instances by checking type or something, it feels like I should be putting them in a separate simple derived class that only introduces a new class name, table name and polymorphic identity name.

That's all background to explain why I have declarative definitions like those below for the base class and the "plain" subclass.

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class GenericFoo(Base):
    __tablename__ = 'generic_foo'
    __mapper_args__ = {'polymorphic_identity': 'generic_foo'}

    def __init__(self, name, color):
        self.name = name
        self.color = color

class SpecificFoo(GenericFoo):
    __tablename__ = 'specific_foo'
    __mapper_args__ = {'polymorphic_identity': 'specific_foo'}

    # If I uncomment this the linter complaint goes away.
    # def __init__(self, name, color):
    #     super(SpecificFoo, self).__init__(name, color)

# PyCharm flags "Unexpected argument" on this statement.
foo = SpecificFoo(name='bob', color='blue')

# - - - - - - - - - - - - - - - - - - - - - - - -
# "Standard" definitions for comparison:
# - - - - - - - - - - - - - - - - - - - - - - - -
class GenericBar(object):
    def __init__(self, name, color):
        self.name = name
        self.color = color

class SpecificBar(GenericBar):
    pass

# This is of course fine.
bar = SpecificBar(name='bob', color='blue')

As noted in the commented line, if I omit a seemingly redundant __init__ method in SpecificFoo, PyCharm flags the foo instantiation for unrecognized arguments. If I add an __init__(self, name, color) method to SpecificFoo that just calls super(SpecificFoo, self).__init__(name, color), PyCharm is happy.

The code appears to execute without error, although I haven't tried anything that might exercise it much. I don't like adding a whole redundant method just to make the linter happy. But I'm concerned that PyCharm knows something I don't and there's an error here that will cause me grief later on. Any idea why PyCharm is flagging this, and if there's indeed a way to satisfy it (presumably without the redundant __init__ method)?

1 Answers

If you would like to solve without a lot of effort, you can use type hints on more recent versions of python.

When defining your class, simply place the type hint :db.Column directly into the declaration:

class GenericFoo(Base):
    __tablename__ = 'generic_foo'

    id: db.Column = db.Column(db.Integer, primary_key=True)
    name: db.Column = db.Column(db.String, unique=True, nullable=False)

If you do this for all tables, it does turn out a bit verbose, but it will also be fully functional for all linters, not just PyCharm.

Related