Pycharm type error: Expected type literal got list["Model"]

128 Views Asked by At

I write fastapi sqlmodel sqlalchemy project. I got type error in PyCharm:

expected type 'Literal["*"] | QueryableAttribute', got 'list[Account] | None' instead

error was caused by this code:

from sqlalchemy.orm import selectinload


class CRUDCounterPartyCommon(
    CRUDBase[CounterParty, CounterpartyCommonCreateUpdateSchema, CounterpartyCommonCreateUpdateSchema]
):
    async def get_by_uuid(self, db: AsyncSession, *, _uuid: uuid.UUID) -> Optional[CounterParty]:
        counterparty = select(CounterParty).where(CounterParty.id == _uuid).options(selectinload(CounterParty.accounts))

Warning was caused by selectinload. What should I do to solve this problem?

Update

mypy says:

error: Argument 1 to "selectinload" has incompatible type "list[Account] | None"; expected "Literal['*'] | QueryableAttribute[Any]"  [arg-type]

Base CRUD class that is ok according to PyCharm linter when we are talking about selectinload:

class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
    async def get_by_field_name(
        self,
        db: AsyncSession,
        field_name: str,
        field_value: str,
        _select: ModelType,
        selection_load_options: Optional[List[Tuple]] = None,
        conditions: Optional[List[Tuple]] = None,
        joins: Optional[List[Tuple]] = None,
    ) -> ModelType:
        query = select(_select)
        if joins:
            for table, condition in joins:
                query = query.join(table, condition)
        if selection_load_options:
            for options_tuple in selection_load_options:
                options_obj = None
                for option in options_tuple:
                    if options_obj is None:
                        options_obj = selectinload(option)
                    else:
                        options_obj = options_obj.options(selectinload(option))
                if options_obj:
                    query = query.options(options_obj)
        if conditions:
            for key, value, comparison in conditions:
                query = query.where(comparison(key, value))
        query = query.where(field_name == field_value)
        try:
            query = query.group_by(self.model.id)
        except Exception:
            pass
1

There are 1 best solutions below

5
J_H On

There is more to this puzzle which you choose not to tell us about.

        ... select(CounterParty)... options(selectinload(CounterParty.accounts))

It seems perfectly reasonable to me that accounts would be an optional list[Account] type. Consider imposing a non-NULL constraint, so it must at least be the empty list.

It's unclear where the Literal["*"] | QueryableAttribute type came from, but an MRO that mentions CounterpartyCommonCreateUpdateSchema seems a likely candidate. At least for debugging, try removing it to see if that changes what mypy says. Also, I can't imagine why you chose to list it twice. The second occurrence produces no useful effect.

There's more than one way to accomplish the aim of this call:

    ... .options(selectinload(CounterParty.accounts))

Somewhere you define a relationship(). We could tack on a , lazy="selectin" parameter there, for the same effect. Then mypy would have less visibility into what's happening and less reason to complain.

You might also choose to use .awaitable_attrs.