[BUG] Variables with annotation of 'typing.Literal' causes a panic

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the SQLModel documentation, with the integrated search.
  • I already searched in Google "How to X in SQLModel" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to SQLModel but to Pydantic.
  • I already checked if it is not related to SQLModel but to SQLAlchemy.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from sqlmodel import SQLModel
from typing import Literal


class Cool(SQLModel, table=True):
    name: Literal["fastapi", "sqlmodel"]


# This code outputs the following to stderr

#   File "main.py", line 5, in <module>
#     class Cool(SQLModel, table=True):
#   File "sqlmodel/main.py", line 292, in __new__
#     col = get_column_from_field(v)
#   File "sqlmodel/main.py", line 415, in get_column_from_field
#     sa_type = get_sqlachemy_type(field)
#   File "sqlmodel/main.py", line 373, in get_sqlachemy_type
#     if issubclass(field.type_, str):
# TypeError: issubclass() arg 1 must be a class

Description

  • Create a sub-class of sqlmodel.main.SQLModel with table=True
  • Add a variable and annotate it with typing.Literal[...]
  • Python raises a TypeError exception at sqlmodel/main:get_sqlachemy_type

This happens because typing.Literal[...] is a function and since SQLModel uses issubclass to get the variable's SQLAlchemy type; issubclass just throws a TypeError since it expects a class.

Operating System

Linux

Operating System Details

No response

SQLModel Version

0.0.4

Python Version

Python 3.9.6

Additional Context

Funny enough Pydantic had quite the same issue pydantic/pydantic#1026 so it can be as easy as importing pydantic.typing.is_literal_type and running an if statement at the top of get_sqlachemy_type

I think the real issue is that typing.Literal is an 'Union' type it receive one or more values but since it's often used for one type (e.g. Literal["r", "w", ...]) we can return the type of all the passed values if they are all the same else None. The values can be retrieved with typing.get_args as a tuple.