sqlmodel 0.0.8__tar.gz → 0.0.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sqlmodel
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness.
5
5
  Home-page: https://github.com/tiangolo/sqlmodel
6
6
  License: MIT
7
7
  Author: Sebastián Ramírez
8
8
  Author-email: tiangolo@gmail.com
9
- Requires-Python: >=3.6.1,<4.0.0
9
+ Requires-Python: >=3.7,<4.0
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Framework :: AsyncIO
12
12
  Classifier: Intended Audience :: Developers
@@ -18,19 +18,16 @@ Classifier: Programming Language :: Python :: 3.7
18
18
  Classifier: Programming Language :: Python :: 3.8
19
19
  Classifier: Programming Language :: Python :: 3.9
20
20
  Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
21
22
  Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Programming Language :: Python :: 3.6
23
- Classifier: Programming Language :: Python :: 3.7
24
- Classifier: Programming Language :: Python :: 3.8
25
- Classifier: Programming Language :: Python :: 3.9
26
23
  Classifier: Topic :: Database
27
24
  Classifier: Topic :: Database :: Database Engines/Servers
28
25
  Classifier: Topic :: Internet
29
26
  Classifier: Topic :: Internet :: WWW/HTTP
30
27
  Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
31
28
  Classifier: Typing :: Typed
32
- Requires-Dist: SQLAlchemy (>=1.4.17,<=1.4.41)
33
- Requires-Dist: pydantic (>=1.8.2,<2.0.0)
29
+ Requires-Dist: SQLAlchemy (>=1.4.36,<2.0.0)
30
+ Requires-Dist: pydantic (>=1.9.0,<2.0.0)
34
31
  Requires-Dist: sqlalchemy2-stubs
35
32
  Project-URL: Documentation, https://sqlmodel.tiangolo.com
36
33
  Project-URL: Repository, https://github.com/tiangolo/sqlmodel
@@ -49,9 +46,8 @@ Description-Content-Type: text/markdown
49
46
  <a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3APublish" target="_blank">
50
47
  <img src="https://github.com/tiangolo/sqlmodel/workflows/Publish/badge.svg" alt="Publish">
51
48
  </a>
52
- <a href="https://codecov.io/gh/tiangolo/sqlmodel" target="_blank">
53
- <img src="https://img.shields.io/codecov/c/github/tiangolo/sqlmodel?color=%2334D058" alt="Coverage">
54
- </a>
49
+ <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/sqlmodel" target="_blank">
50
+ <img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/sqlmodel.svg" alt="Coverage">
55
51
  <a href="https://pypi.org/project/sqlmodel" target="_blank">
56
52
  <img src="https://img.shields.io/pypi/v/sqlmodel?color=%2334D058&label=pypi%20package" alt="Package version">
57
53
  </a>
@@ -89,7 +85,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
89
85
 
90
86
  ## Requirements
91
87
 
92
- A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
88
+ A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python</a>.
93
89
 
94
90
  As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
95
91
 
@@ -11,9 +11,8 @@
11
11
  <a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3APublish" target="_blank">
12
12
  <img src="https://github.com/tiangolo/sqlmodel/workflows/Publish/badge.svg" alt="Publish">
13
13
  </a>
14
- <a href="https://codecov.io/gh/tiangolo/sqlmodel" target="_blank">
15
- <img src="https://img.shields.io/codecov/c/github/tiangolo/sqlmodel?color=%2334D058" alt="Coverage">
16
- </a>
14
+ <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/sqlmodel" target="_blank">
15
+ <img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/sqlmodel.svg" alt="Coverage">
17
16
  <a href="https://pypi.org/project/sqlmodel" target="_blank">
18
17
  <img src="https://img.shields.io/pypi/v/sqlmodel?color=%2334D058&label=pypi%20package" alt="Package version">
19
18
  </a>
@@ -51,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
51
50
 
52
51
  ## Requirements
53
52
 
54
- A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
53
+ A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python</a>.
55
54
 
56
55
  As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
57
56
 
@@ -17,10 +17,10 @@ classifiers = [
17
17
  "Intended Audience :: System Administrators",
18
18
  "License :: OSI Approved :: MIT License",
19
19
  "Programming Language :: Python :: 3 :: Only",
20
- "Programming Language :: Python :: 3.6",
21
20
  "Programming Language :: Python :: 3.7",
22
21
  "Programming Language :: Python :: 3.8",
23
22
  "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
24
  "Topic :: Database",
25
25
  "Topic :: Database :: Database Engines/Servers",
26
26
  "Topic :: Internet",
@@ -30,26 +30,24 @@ classifiers = [
30
30
  ]
31
31
 
32
32
  [tool.poetry.dependencies]
33
- python = "^3.6.1"
34
- SQLAlchemy = ">=1.4.17,<=1.4.41"
35
- pydantic = "^1.8.2"
33
+ python = "^3.7"
34
+ SQLAlchemy = ">=1.4.36,<2.0.0"
35
+ pydantic = "^1.9.0"
36
36
  sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
37
37
 
38
- [tool.poetry.dev-dependencies]
39
- pytest = "^6.2.4"
40
- mypy = "0.930"
41
- flake8 = "^3.9.2"
42
- black = {version = "^21.5-beta.1", python = "^3.7"}
43
- mkdocs = "^1.2.1"
44
- mkdocs-material = "^8.1.4"
38
+ [tool.poetry.group.dev.dependencies]
39
+ pytest = "^7.0.1"
40
+ mypy = "0.971"
41
+ # Needed by the code generator using templates
42
+ black = "^22.10.0"
43
+ mkdocs-material = "9.1.21"
44
+ pillow = "^9.3.0"
45
+ cairosvg = "^2.5.2"
45
46
  mdx-include = "^1.4.1"
46
- coverage = {extras = ["toml"], version = "^5.5"}
47
+ coverage = {extras = ["toml"], version = "^6.2"}
47
48
  fastapi = "^0.68.1"
48
49
  requests = "^2.26.0"
49
- autoflake = "^1.4"
50
- isort = "^5.9.3"
51
- async_generator = {version = "*", python = "~3.6"}
52
- async-exit-stack = {version = "*", python = "~3.6"}
50
+ ruff = "^0.1.2"
53
51
 
54
52
  [build-system]
55
53
  requires = ["poetry-core"]
@@ -65,6 +63,7 @@ source = [
65
63
  "tests",
66
64
  "sqlmodel"
67
65
  ]
66
+ context = '${CONTEXT}'
68
67
 
69
68
  [tool.coverage.report]
70
69
  exclude_lines = [
@@ -74,33 +73,36 @@ exclude_lines = [
74
73
  "if TYPE_CHECKING:",
75
74
  ]
76
75
 
77
- [tool.isort]
78
- profile = "black"
79
- known_third_party = ["sqlmodel"]
80
- skip_glob = [
81
- "sqlmodel/__init__.py",
82
- ]
83
-
84
-
85
76
  [tool.mypy]
86
- # --strict
87
- disallow_any_generics = true
88
- disallow_subclassing_any = true
89
- disallow_untyped_calls = true
90
- disallow_untyped_defs = true
91
- disallow_incomplete_defs = true
92
- check_untyped_defs = true
93
- disallow_untyped_decorators = true
94
- no_implicit_optional = true
95
- warn_redundant_casts = true
96
- warn_unused_ignores = true
97
- warn_return_any = true
98
- implicit_reexport = false
99
- strict_equality = true
100
- # --strict end
77
+ strict = true
101
78
 
102
79
  [[tool.mypy.overrides]]
103
80
  module = "sqlmodel.sql.expression"
104
81
  warn_unused_ignores = false
105
82
 
106
- # invalidate CI cache: 1
83
+ [tool.ruff]
84
+ select = [
85
+ "E", # pycodestyle errors
86
+ "W", # pycodestyle warnings
87
+ "F", # pyflakes
88
+ "I", # isort
89
+ "C", # flake8-comprehensions
90
+ "B", # flake8-bugbear
91
+ "UP", # pyupgrade
92
+ ]
93
+ ignore = [
94
+ "E501", # line too long, handled by black
95
+ "B008", # do not perform function calls in argument defaults
96
+ "C901", # too complex
97
+ "W191", # indentation contains tabs
98
+ ]
99
+
100
+ [tool.ruff.per-file-ignores]
101
+ # "__init__.py" = ["F401"]
102
+
103
+ [tool.ruff.isort]
104
+ known-third-party = ["sqlmodel", "sqlalchemy", "pydantic", "fastapi"]
105
+
106
+ [tool.ruff.pyupgrade]
107
+ # Preserve types, even if a file imports `from __future__ import annotations`.
108
+ keep-runtime-typing = true
@@ -1,16 +1,16 @@
1
- __version__ = "0.0.8"
1
+ __version__ = "0.0.10"
2
2
 
3
3
  # Re-export from SQLAlchemy
4
4
  from sqlalchemy.engine import create_mock_engine as create_mock_engine
5
5
  from sqlalchemy.engine import engine_from_config as engine_from_config
6
6
  from sqlalchemy.inspection import inspect as inspect
7
7
  from sqlalchemy.schema import BLANK_SCHEMA as BLANK_SCHEMA
8
+ from sqlalchemy.schema import DDL as DDL
8
9
  from sqlalchemy.schema import CheckConstraint as CheckConstraint
9
10
  from sqlalchemy.schema import Column as Column
10
11
  from sqlalchemy.schema import ColumnDefault as ColumnDefault
11
12
  from sqlalchemy.schema import Computed as Computed
12
13
  from sqlalchemy.schema import Constraint as Constraint
13
- from sqlalchemy.schema import DDL as DDL
14
14
  from sqlalchemy.schema import DefaultClause as DefaultClause
15
15
  from sqlalchemy.schema import FetchedValue as FetchedValue
16
16
  from sqlalchemy.schema import ForeignKey as ForeignKey
@@ -23,6 +23,14 @@ from sqlalchemy.schema import Sequence as Sequence
23
23
  from sqlalchemy.schema import Table as Table
24
24
  from sqlalchemy.schema import ThreadLocalMetaData as ThreadLocalMetaData
25
25
  from sqlalchemy.schema import UniqueConstraint as UniqueConstraint
26
+ from sqlalchemy.sql import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT
27
+ from sqlalchemy.sql import (
28
+ LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY,
29
+ )
30
+ from sqlalchemy.sql import LABEL_STYLE_NONE as LABEL_STYLE_NONE
31
+ from sqlalchemy.sql import (
32
+ LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL,
33
+ )
26
34
  from sqlalchemy.sql import alias as alias
27
35
  from sqlalchemy.sql import all_ as all_
28
36
  from sqlalchemy.sql import and_ as and_
@@ -48,14 +56,6 @@ from sqlalchemy.sql import insert as insert
48
56
  from sqlalchemy.sql import intersect as intersect
49
57
  from sqlalchemy.sql import intersect_all as intersect_all
50
58
  from sqlalchemy.sql import join as join
51
- from sqlalchemy.sql import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT
52
- from sqlalchemy.sql import (
53
- LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY,
54
- )
55
- from sqlalchemy.sql import LABEL_STYLE_NONE as LABEL_STYLE_NONE
56
- from sqlalchemy.sql import (
57
- LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL,
58
- )
59
59
  from sqlalchemy.sql import lambda_stmt as lambda_stmt
60
60
  from sqlalchemy.sql import lateral as lateral
61
61
  from sqlalchemy.sql import literal as literal
@@ -85,55 +85,53 @@ from sqlalchemy.sql import values as values
85
85
  from sqlalchemy.sql import within_group as within_group
86
86
  from sqlalchemy.types import ARRAY as ARRAY
87
87
  from sqlalchemy.types import BIGINT as BIGINT
88
- from sqlalchemy.types import BigInteger as BigInteger
89
88
  from sqlalchemy.types import BINARY as BINARY
90
89
  from sqlalchemy.types import BLOB as BLOB
91
90
  from sqlalchemy.types import BOOLEAN as BOOLEAN
92
- from sqlalchemy.types import Boolean as Boolean
93
91
  from sqlalchemy.types import CHAR as CHAR
94
92
  from sqlalchemy.types import CLOB as CLOB
95
93
  from sqlalchemy.types import DATE as DATE
96
- from sqlalchemy.types import Date as Date
97
94
  from sqlalchemy.types import DATETIME as DATETIME
98
- from sqlalchemy.types import DateTime as DateTime
99
95
  from sqlalchemy.types import DECIMAL as DECIMAL
100
- from sqlalchemy.types import Enum as Enum
101
96
  from sqlalchemy.types import FLOAT as FLOAT
102
- from sqlalchemy.types import Float as Float
103
97
  from sqlalchemy.types import INT as INT
104
98
  from sqlalchemy.types import INTEGER as INTEGER
105
- from sqlalchemy.types import Integer as Integer
106
- from sqlalchemy.types import Interval as Interval
107
99
  from sqlalchemy.types import JSON as JSON
108
- from sqlalchemy.types import LargeBinary as LargeBinary
109
100
  from sqlalchemy.types import NCHAR as NCHAR
110
101
  from sqlalchemy.types import NUMERIC as NUMERIC
111
- from sqlalchemy.types import Numeric as Numeric
112
102
  from sqlalchemy.types import NVARCHAR as NVARCHAR
113
- from sqlalchemy.types import PickleType as PickleType
114
103
  from sqlalchemy.types import REAL as REAL
115
104
  from sqlalchemy.types import SMALLINT as SMALLINT
105
+ from sqlalchemy.types import TEXT as TEXT
106
+ from sqlalchemy.types import TIME as TIME
107
+ from sqlalchemy.types import TIMESTAMP as TIMESTAMP
108
+ from sqlalchemy.types import VARBINARY as VARBINARY
109
+ from sqlalchemy.types import VARCHAR as VARCHAR
110
+ from sqlalchemy.types import BigInteger as BigInteger
111
+ from sqlalchemy.types import Boolean as Boolean
112
+ from sqlalchemy.types import Date as Date
113
+ from sqlalchemy.types import DateTime as DateTime
114
+ from sqlalchemy.types import Enum as Enum
115
+ from sqlalchemy.types import Float as Float
116
+ from sqlalchemy.types import Integer as Integer
117
+ from sqlalchemy.types import Interval as Interval
118
+ from sqlalchemy.types import LargeBinary as LargeBinary
119
+ from sqlalchemy.types import Numeric as Numeric
120
+ from sqlalchemy.types import PickleType as PickleType
116
121
  from sqlalchemy.types import SmallInteger as SmallInteger
117
122
  from sqlalchemy.types import String as String
118
- from sqlalchemy.types import TEXT as TEXT
119
123
  from sqlalchemy.types import Text as Text
120
- from sqlalchemy.types import TIME as TIME
121
124
  from sqlalchemy.types import Time as Time
122
- from sqlalchemy.types import TIMESTAMP as TIMESTAMP
123
125
  from sqlalchemy.types import TypeDecorator as TypeDecorator
124
126
  from sqlalchemy.types import Unicode as Unicode
125
127
  from sqlalchemy.types import UnicodeText as UnicodeText
126
- from sqlalchemy.types import VARBINARY as VARBINARY
127
- from sqlalchemy.types import VARCHAR as VARCHAR
128
128
 
129
- # Extensions and modifications of SQLAlchemy in SQLModel
129
+ # From SQLModel, modifications of SQLAlchemy or equivalents of Pydantic
130
130
  from .engine.create import create_engine as create_engine
131
+ from .main import Field as Field
132
+ from .main import Relationship as Relationship
133
+ from .main import SQLModel as SQLModel
131
134
  from .orm.session import Session as Session
132
- from .sql.expression import select as select
133
135
  from .sql.expression import col as col
136
+ from .sql.expression import select as select
134
137
  from .sql.sqltypes import AutoString as AutoString
135
-
136
- # Export SQLModel specifics (equivalent to Pydantic)
137
- from .main import SQLModel as SQLModel
138
- from .main import Field as Field
139
- from .main import Relationship as Relationship
@@ -6,7 +6,7 @@ class _DefaultPlaceholder:
6
6
  You shouldn't use this class directly.
7
7
 
8
8
  It's used internally to recognize when a default value has been overwritten, even
9
- if the overriden default value was truthy.
9
+ if the overridden default value was truthy.
10
10
  """
11
11
 
12
12
  def __init__(self, value: Any):
@@ -27,6 +27,6 @@ def Default(value: _TDefaultType) -> _TDefaultType:
27
27
  You shouldn't use this function directly.
28
28
 
29
29
  It's used internally to recognize when a default value has been overwritten, even
30
- if the overriden default value was truthy.
30
+ if the overridden default value was truthy.
31
31
  """
32
32
  return _DefaultPlaceholder(value) # type: ignore
@@ -1,17 +1,17 @@
1
- from typing import Any, Mapping, Optional, Sequence, TypeVar, Union
1
+ from typing import Any, Mapping, Optional, Sequence, TypeVar, Union, overload
2
2
 
3
3
  from sqlalchemy import util
4
4
  from sqlalchemy.ext.asyncio import AsyncSession as _AsyncSession
5
5
  from sqlalchemy.ext.asyncio import engine
6
6
  from sqlalchemy.ext.asyncio.engine import AsyncConnection, AsyncEngine
7
7
  from sqlalchemy.util.concurrency import greenlet_spawn
8
- from sqlmodel.sql.base import Executable
9
8
 
10
- from ...engine.result import ScalarResult
9
+ from ...engine.result import Result, ScalarResult
11
10
  from ...orm.session import Session
12
- from ...sql.expression import Select
11
+ from ...sql.base import Executable
12
+ from ...sql.expression import Select, SelectOfScalar
13
13
 
14
- _T = TypeVar("_T")
14
+ _TSelectParam = TypeVar("_TSelectParam")
15
15
 
16
16
 
17
17
  class AsyncSession(_AsyncSession):
@@ -40,14 +40,46 @@ class AsyncSession(_AsyncSession):
40
40
  Session(bind=bind, binds=binds, **kw) # type: ignore
41
41
  )
42
42
 
43
+ @overload
43
44
  async def exec(
44
45
  self,
45
- statement: Union[Select[_T], Executable[_T]],
46
+ statement: Select[_TSelectParam],
47
+ *,
48
+ params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
49
+ execution_options: Mapping[str, Any] = util.EMPTY_DICT,
50
+ bind_arguments: Optional[Mapping[str, Any]] = None,
51
+ _parent_execute_state: Optional[Any] = None,
52
+ _add_event: Optional[Any] = None,
53
+ **kw: Any,
54
+ ) -> Result[_TSelectParam]:
55
+ ...
56
+
57
+ @overload
58
+ async def exec(
59
+ self,
60
+ statement: SelectOfScalar[_TSelectParam],
61
+ *,
62
+ params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
63
+ execution_options: Mapping[str, Any] = util.EMPTY_DICT,
64
+ bind_arguments: Optional[Mapping[str, Any]] = None,
65
+ _parent_execute_state: Optional[Any] = None,
66
+ _add_event: Optional[Any] = None,
67
+ **kw: Any,
68
+ ) -> ScalarResult[_TSelectParam]:
69
+ ...
70
+
71
+ async def exec(
72
+ self,
73
+ statement: Union[
74
+ Select[_TSelectParam],
75
+ SelectOfScalar[_TSelectParam],
76
+ Executable[_TSelectParam],
77
+ ],
46
78
  params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
47
79
  execution_options: Mapping[Any, Any] = util.EMPTY_DICT,
48
80
  bind_arguments: Optional[Mapping[str, Any]] = None,
49
81
  **kw: Any,
50
- ) -> ScalarResult[_T]:
82
+ ) -> Union[Result[_TSelectParam], ScalarResult[_TSelectParam]]:
51
83
  # TODO: the documentation says execution_options accepts a dict, but only
52
84
  # util.immutabledict has the union() method. Is this a bug in SQLAlchemy?
53
85
  execution_options = execution_options.union({"prebuffer_rows": True}) # type: ignore
@@ -11,6 +11,7 @@ from typing import (
11
11
  Callable,
12
12
  ClassVar,
13
13
  Dict,
14
+ ForwardRef,
14
15
  List,
15
16
  Mapping,
16
17
  Optional,
@@ -25,15 +26,24 @@ from typing import (
25
26
 
26
27
  from pydantic import BaseConfig, BaseModel
27
28
  from pydantic.errors import ConfigError, DictError
28
- from pydantic.fields import SHAPE_SINGLETON
29
+ from pydantic.fields import SHAPE_SINGLETON, ModelField, Undefined, UndefinedType
29
30
  from pydantic.fields import FieldInfo as PydanticFieldInfo
30
- from pydantic.fields import ModelField, Undefined, UndefinedType
31
31
  from pydantic.main import ModelMetaclass, validate_model
32
- from pydantic.typing import ForwardRef, NoArgAnyCallable, resolve_annotations
32
+ from pydantic.typing import NoArgAnyCallable, resolve_annotations
33
33
  from pydantic.utils import ROOT_KEY, Representation
34
- from sqlalchemy import Boolean, Column, Date, DateTime
34
+ from sqlalchemy import (
35
+ Boolean,
36
+ Column,
37
+ Date,
38
+ DateTime,
39
+ Float,
40
+ ForeignKey,
41
+ Integer,
42
+ Interval,
43
+ Numeric,
44
+ inspect,
45
+ )
35
46
  from sqlalchemy import Enum as sa_Enum
36
- from sqlalchemy import Float, ForeignKey, Integer, Interval, Numeric, inspect
37
47
  from sqlalchemy.orm import RelationshipProperty, declared_attr, registry, relationship
38
48
  from sqlalchemy.orm.attributes import set_attribute
39
49
  from sqlalchemy.orm.decl_api import DeclarativeMeta
@@ -135,12 +145,17 @@ def Field(
135
145
  lt: Optional[float] = None,
136
146
  le: Optional[float] = None,
137
147
  multiple_of: Optional[float] = None,
148
+ max_digits: Optional[int] = None,
149
+ decimal_places: Optional[int] = None,
138
150
  min_items: Optional[int] = None,
139
151
  max_items: Optional[int] = None,
152
+ unique_items: Optional[bool] = None,
140
153
  min_length: Optional[int] = None,
141
154
  max_length: Optional[int] = None,
142
155
  allow_mutation: bool = True,
143
156
  regex: Optional[str] = None,
157
+ discriminator: Optional[str] = None,
158
+ repr: bool = True,
144
159
  primary_key: bool = False,
145
160
  foreign_key: Optional[Any] = None,
146
161
  unique: bool = False,
@@ -166,12 +181,17 @@ def Field(
166
181
  lt=lt,
167
182
  le=le,
168
183
  multiple_of=multiple_of,
184
+ max_digits=max_digits,
185
+ decimal_places=decimal_places,
169
186
  min_items=min_items,
170
187
  max_items=max_items,
188
+ unique_items=unique_items,
171
189
  min_length=min_length,
172
190
  max_length=max_length,
173
191
  allow_mutation=allow_mutation,
174
192
  regex=regex,
193
+ discriminator=discriminator,
194
+ repr=repr,
175
195
  primary_key=primary_key,
176
196
  foreign_key=foreign_key,
177
197
  unique=unique,
@@ -304,9 +324,9 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
304
324
  config_registry = cast(registry, config_registry)
305
325
  # If it was passed by kwargs, ensure it's also set in config
306
326
  new_cls.__config__.registry = config_table
307
- setattr(new_cls, "_sa_registry", config_registry)
308
- setattr(new_cls, "metadata", config_registry.metadata)
309
- setattr(new_cls, "__abstract__", True)
327
+ setattr(new_cls, "_sa_registry", config_registry) # noqa: B010
328
+ setattr(new_cls, "metadata", config_registry.metadata) # noqa: B010
329
+ setattr(new_cls, "__abstract__", True) # noqa: B010
310
330
  return new_cls
311
331
 
312
332
  # Override SQLAlchemy, allow both SQLAlchemy and plain Pydantic models
@@ -319,19 +339,16 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
319
339
  # triggers an error
320
340
  base_is_table = False
321
341
  for base in bases:
322
- config = getattr(base, "__config__")
342
+ config = getattr(base, "__config__") # noqa: B009
323
343
  if config and getattr(config, "table", False):
324
344
  base_is_table = True
325
345
  break
326
346
  if getattr(cls.__config__, "table", False) and not base_is_table:
327
- dict_used = dict_.copy()
328
- for field_name, field_value in cls.__fields__.items():
329
- dict_used[field_name] = get_column_from_field(field_value)
330
347
  for rel_name, rel_info in cls.__sqlmodel_relationships__.items():
331
348
  if rel_info.sa_relationship:
332
349
  # There's a SQLAlchemy relationship declared, that takes precedence
333
350
  # over anything else, use that and continue with the next attribute
334
- dict_used[rel_name] = rel_info.sa_relationship
351
+ setattr(cls, rel_name, rel_info.sa_relationship) # Fix #315
335
352
  continue
336
353
  ann = cls.__annotations__[rel_name]
337
354
  temp_field = ModelField.infer(
@@ -349,7 +366,7 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
349
366
  rel_kwargs["back_populates"] = rel_info.back_populates
350
367
  if rel_info.link_model:
351
368
  ins = inspect(rel_info.link_model)
352
- local_table = getattr(ins, "local_table")
369
+ local_table = getattr(ins, "local_table") # noqa: B009
353
370
  if local_table is None:
354
371
  raise RuntimeError(
355
372
  "Couldn't find the secondary table for "
@@ -364,53 +381,57 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
364
381
  rel_value: RelationshipProperty = relationship( # type: ignore
365
382
  relationship_to, *rel_args, **rel_kwargs
366
383
  )
367
- dict_used[rel_name] = rel_value
368
384
  setattr(cls, rel_name, rel_value) # Fix #315
369
- DeclarativeMeta.__init__(cls, classname, bases, dict_used, **kw)
385
+ # SQLAlchemy no longer uses dict_
386
+ # Ref: https://github.com/sqlalchemy/sqlalchemy/commit/428ea01f00a9cc7f85e435018565eb6da7af1b77
387
+ # Tag: 1.4.36
388
+ DeclarativeMeta.__init__(cls, classname, bases, dict_, **kw)
370
389
  else:
371
390
  ModelMetaclass.__init__(cls, classname, bases, dict_, **kw)
372
391
 
373
392
 
374
- def get_sqlachemy_type(field: ModelField) -> Any:
375
- if issubclass(field.type_, str):
376
- if field.field_info.max_length:
377
- return AutoString(length=field.field_info.max_length)
378
- return AutoString
379
- if issubclass(field.type_, float):
380
- return Float
381
- if issubclass(field.type_, bool):
382
- return Boolean
383
- if issubclass(field.type_, int):
384
- return Integer
385
- if issubclass(field.type_, datetime):
386
- return DateTime
387
- if issubclass(field.type_, date):
388
- return Date
389
- if issubclass(field.type_, timedelta):
390
- return Interval
391
- if issubclass(field.type_, time):
392
- return Time
393
- if issubclass(field.type_, Enum):
394
- return sa_Enum(field.type_)
395
- if issubclass(field.type_, bytes):
396
- return LargeBinary
397
- if issubclass(field.type_, Decimal):
398
- return Numeric(
399
- precision=getattr(field.type_, "max_digits", None),
400
- scale=getattr(field.type_, "decimal_places", None),
401
- )
402
- if issubclass(field.type_, ipaddress.IPv4Address):
403
- return AutoString
404
- if issubclass(field.type_, ipaddress.IPv4Network):
405
- return AutoString
406
- if issubclass(field.type_, ipaddress.IPv6Address):
407
- return AutoString
408
- if issubclass(field.type_, ipaddress.IPv6Network):
409
- return AutoString
410
- if issubclass(field.type_, Path):
411
- return AutoString
412
- if issubclass(field.type_, uuid.UUID):
413
- return GUID
393
+ def get_sqlalchemy_type(field: ModelField) -> Any:
394
+ if isinstance(field.type_, type) and field.shape == SHAPE_SINGLETON:
395
+ # Check enums first as an enum can also be a str, needed by Pydantic/FastAPI
396
+ if issubclass(field.type_, Enum):
397
+ return sa_Enum(field.type_)
398
+ if issubclass(field.type_, str):
399
+ if field.field_info.max_length:
400
+ return AutoString(length=field.field_info.max_length)
401
+ return AutoString
402
+ if issubclass(field.type_, float):
403
+ return Float
404
+ if issubclass(field.type_, bool):
405
+ return Boolean
406
+ if issubclass(field.type_, int):
407
+ return Integer
408
+ if issubclass(field.type_, datetime):
409
+ return DateTime
410
+ if issubclass(field.type_, date):
411
+ return Date
412
+ if issubclass(field.type_, timedelta):
413
+ return Interval
414
+ if issubclass(field.type_, time):
415
+ return Time
416
+ if issubclass(field.type_, bytes):
417
+ return LargeBinary
418
+ if issubclass(field.type_, Decimal):
419
+ return Numeric(
420
+ precision=getattr(field.type_, "max_digits", None),
421
+ scale=getattr(field.type_, "decimal_places", None),
422
+ )
423
+ if issubclass(field.type_, ipaddress.IPv4Address):
424
+ return AutoString
425
+ if issubclass(field.type_, ipaddress.IPv4Network):
426
+ return AutoString
427
+ if issubclass(field.type_, ipaddress.IPv6Address):
428
+ return AutoString
429
+ if issubclass(field.type_, ipaddress.IPv6Network):
430
+ return AutoString
431
+ if issubclass(field.type_, Path):
432
+ return AutoString
433
+ if issubclass(field.type_, uuid.UUID):
434
+ return GUID
414
435
  raise ValueError(f"The field {field.name} has no matching SQLAlchemy type")
415
436
 
416
437
 
@@ -418,7 +439,7 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore
418
439
  sa_column = getattr(field.field_info, "sa_column", Undefined)
419
440
  if isinstance(sa_column, Column):
420
441
  return sa_column
421
- sa_type = get_sqlachemy_type(field)
442
+ sa_type = get_sqlalchemy_type(field)
422
443
  primary_key = getattr(field.field_info, "primary_key", False)
423
444
  index = getattr(field.field_info, "index", Undefined)
424
445
  if index is Undefined:
@@ -427,7 +448,7 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore
427
448
  # Override derived nullability if the nullable property is set explicitly
428
449
  # on the field
429
450
  if hasattr(field.field_info, "nullable"):
430
- field_nullable = getattr(field.field_info, "nullable")
451
+ field_nullable = getattr(field.field_info, "nullable") # noqa: B009
431
452
  if field_nullable != Undefined:
432
453
  nullable = field_nullable
433
454
  args = []
@@ -576,7 +597,11 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
576
597
 
577
598
  def __repr_args__(self) -> Sequence[Tuple[Optional[str], Any]]:
578
599
  # Don't show SQLAlchemy private attributes
579
- return [(k, v) for k, v in self.__dict__.items() if not k.startswith("_sa_")]
600
+ return [
601
+ (k, v)
602
+ for k, v in super().__repr_args__()
603
+ if not (isinstance(k, str) and k.startswith("_sa_"))
604
+ ]
580
605
 
581
606
  # From Pydantic, override to enforce validation with dict
582
607
  @classmethod
@@ -4,11 +4,11 @@ from sqlalchemy import util
4
4
  from sqlalchemy.orm import Query as _Query
5
5
  from sqlalchemy.orm import Session as _Session
6
6
  from sqlalchemy.sql.base import Executable as _Executable
7
- from sqlmodel.sql.expression import Select, SelectOfScalar
8
7
  from typing_extensions import Literal
9
8
 
10
9
  from ..engine.result import Result, ScalarResult
11
10
  from ..sql.base import Executable
11
+ from ..sql.expression import Select, SelectOfScalar
12
12
 
13
13
  _TSelectParam = TypeVar("_TSelectParam")
14
14
 
@@ -1,6 +1,5 @@
1
1
  # WARNING: do not modify this code, it is generated by expression.py.jinja2
2
2
 
3
- import sys
4
3
  from datetime import datetime
5
4
  from typing import (
6
5
  TYPE_CHECKING,
@@ -12,7 +11,6 @@ from typing import (
12
11
  Type,
13
12
  TypeVar,
14
13
  Union,
15
- cast,
16
14
  overload,
17
15
  )
18
16
  from uuid import UUID
@@ -24,36 +22,17 @@ from sqlalchemy.sql.expression import Select as _Select
24
22
 
25
23
  _TSelect = TypeVar("_TSelect")
26
24
 
27
- # Workaround Generics incompatibility in Python 3.6
28
- # Ref: https://github.com/python/typing/issues/449#issuecomment-316061322
29
- if sys.version_info.minor >= 7:
30
25
 
31
- class Select(_Select, Generic[_TSelect]):
32
- inherit_cache = True
26
+ class Select(_Select, Generic[_TSelect]):
27
+ inherit_cache = True
33
28
 
34
- # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
35
- # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
36
- # entity, so the result will be converted to a scalar by default. This way writing
37
- # for loops on the results will feel natural.
38
- class SelectOfScalar(_Select, Generic[_TSelect]):
39
- inherit_cache = True
40
29
 
41
- else:
42
- from typing import GenericMeta # type: ignore
43
-
44
- class GenericSelectMeta(GenericMeta, _Select.__class__): # type: ignore
45
- pass
46
-
47
- class _Py36Select(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
48
- inherit_cache = True
49
-
50
- class _Py36SelectOfScalar(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
51
- inherit_cache = True
52
-
53
- # Cast them for editors to work correctly, from several tricks tried, this works
54
- # for both VS Code and PyCharm
55
- Select = cast("Select", _Py36Select) # type: ignore
56
- SelectOfScalar = cast("SelectOfScalar", _Py36SelectOfScalar) # type: ignore
30
+ # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
31
+ # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
32
+ # entity, so the result will be converted to a scalar by default. This way writing
33
+ # for loops on the results will feel natural.
34
+ class SelectOfScalar(_Select, Generic[_TSelect]):
35
+ inherit_cache = True
57
36
 
58
37
 
59
38
  if TYPE_CHECKING: # pragma: no cover
@@ -8,7 +8,6 @@ from sqlalchemy.sql.type_api import TypeEngine
8
8
 
9
9
 
10
10
  class AutoString(types.TypeDecorator): # type: ignore
11
-
12
11
  impl = types.String
13
12
  cache_ok = True
14
13
  mysql_default_length = 255
sqlmodel-0.0.8/setup.py DELETED
@@ -1,36 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- packages = \
5
- ['sqlmodel',
6
- 'sqlmodel.engine',
7
- 'sqlmodel.ext',
8
- 'sqlmodel.ext.asyncio',
9
- 'sqlmodel.orm',
10
- 'sqlmodel.pool',
11
- 'sqlmodel.sql']
12
-
13
- package_data = \
14
- {'': ['*']}
15
-
16
- install_requires = \
17
- ['SQLAlchemy>=1.4.17,<=1.4.41', 'pydantic>=1.8.2,<2.0.0', 'sqlalchemy2-stubs']
18
-
19
- setup_kwargs = {
20
- 'name': 'sqlmodel',
21
- 'version': '0.0.8',
22
- 'description': 'SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness.',
23
- 'long_description': '<p align="center">\n <a href="https://sqlmodel.tiangolo.com"><img src="https://sqlmodel.tiangolo.com/img/logo-margin/logo-margin-vector.svg" alt="SQLModel"></a>\n</p>\n<p align="center">\n <em>SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness.</em>\n</p>\n<p align="center">\n<a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3ATest" target="_blank">\n <img src="https://github.com/tiangolo/sqlmodel/workflows/Test/badge.svg" alt="Test">\n</a>\n<a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3APublish" target="_blank">\n <img src="https://github.com/tiangolo/sqlmodel/workflows/Publish/badge.svg" alt="Publish">\n</a>\n<a href="https://codecov.io/gh/tiangolo/sqlmodel" target="_blank">\n <img src="https://img.shields.io/codecov/c/github/tiangolo/sqlmodel?color=%2334D058" alt="Coverage">\n</a>\n<a href="https://pypi.org/project/sqlmodel" target="_blank">\n <img src="https://img.shields.io/pypi/v/sqlmodel?color=%2334D058&label=pypi%20package" alt="Package version">\n</a>\n</p>\n\n---\n\n**Documentation**: <a href="https://sqlmodel.tiangolo.com" target="_blank">https://sqlmodel.tiangolo.com</a>\n\n**Source Code**: <a href="https://github.com/tiangolo/sqlmodel" target="_blank">https://github.com/tiangolo/sqlmodel</a>\n\n---\n\nSQLModel is a library for interacting with <abbr title=\'Also called "Relational databases"\'>SQL databases</abbr> from Python code, with Python objects. It is designed to be intuitive, easy to use, highly compatible, and robust.\n\n**SQLModel** is based on Python type annotations, and powered by <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> and <a href="https://sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a>.\n\nThe key features are:\n\n* **Intuitive to write**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging. Designed to be easy to use and learn. Less time reading docs.\n* **Easy to use**: It has sensible defaults and does a lot of work underneath to simplify the code you write.\n* **Compatible**: It is designed to be compatible with **FastAPI**, Pydantic, and SQLAlchemy.\n* **Extensible**: You have all the power of SQLAlchemy and Pydantic underneath.\n* **Short**: Minimize code duplication. A single type annotation does a lot of work. No need to duplicate models in SQLAlchemy and Pydantic.\n\n## SQL Databases in FastAPI\n\n<a href="https://fastapi.tiangolo.com" target="_blank"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" style="width: 20%;"></a>\n\n**SQLModel** is designed to simplify interacting with SQL databases in <a href="https://fastapi.tiangolo.com" class="external-link" target="_blank">FastAPI</a> applications, it was created by the same <a href="https://tiangolo.com/" class="external-link" target="_blank">author</a>. 😁\n\nIt combines SQLAlchemy and Pydantic and tries to simplify the code you write as much as possible, allowing you to reduce the **code duplication to a minimum**, but while getting the **best developer experience** possible.\n\n**SQLModel** is, in fact, a thin layer on top of **Pydantic** and **SQLAlchemy**, carefully designed to be compatible with both.\n\n## Requirements\n\nA recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).\n\nAs **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.\n\n## Installation\n\n<div class="termy">\n\n```console\n$ pip install sqlmodel\n---> 100%\nSuccessfully installed sqlmodel\n```\n\n</div>\n\n## Example\n\nFor an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com" target="_blank">SQLModel documentation</a>.\n\nHere\'s a quick example. ✨\n\n### A SQL Table\n\nImagine you have a SQL table called `hero` with:\n\n* `id`\n* `name`\n* `secret_name`\n* `age`\n\nAnd you want it to have this data:\n\n| id | name | secret_name | age |\n-----|------|-------------|------|\n| 1 | Deadpond | Dive Wilson | null |\n| 2 | Spider-Boy | Pedro Parqueador | null |\n| 3 | Rusty-Man | Tommy Sharp | 48 |\n\n### Create a SQLModel Model\n\nThen you could create a **SQLModel** model like this:\n\n```Python\nfrom typing import Optional\n\nfrom sqlmodel import Field, SQLModel\n\n\nclass Hero(SQLModel, table=True):\n id: Optional[int] = Field(default=None, primary_key=True)\n name: str\n secret_name: str\n age: Optional[int] = None\n```\n\nThat class `Hero` is a **SQLModel** model, the equivalent of a SQL table in Python code.\n\nAnd each of those class attributes is equivalent to each **table column**.\n\n### Create Rows\n\nThen you could **create each row** of the table as an **instance** of the model:\n\n```Python\nhero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")\nhero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")\nhero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)\n```\n\nThis way, you can use conventional Python code with **classes** and **instances** that represent **tables** and **rows**, and that way communicate with the **SQL database**.\n\n### Editor Support\n\nEverything is designed for you to get the best developer experience possible, with the best editor support.\n\nIncluding **autocompletion**:\n\n<img class="shadow" src="https://sqlmodel.tiangolo.com/img/index/autocompletion01.png">\n\nAnd **inline errors**:\n\n<img class="shadow" src="https://sqlmodel.tiangolo.com/img/index/inline-errors01.png">\n\n### Write to the Database\n\nYou can learn a lot more about **SQLModel** by quickly following the **tutorial**, but if you need a taste right now of how to put all that together and save to the database, you can do this:\n\n```Python hl_lines="18 21 23-27"\nfrom typing import Optional\n\nfrom sqlmodel import Field, Session, SQLModel, create_engine\n\n\nclass Hero(SQLModel, table=True):\n id: Optional[int] = Field(default=None, primary_key=True)\n name: str\n secret_name: str\n age: Optional[int] = None\n\n\nhero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")\nhero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")\nhero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)\n\n\nengine = create_engine("sqlite:///database.db")\n\n\nSQLModel.metadata.create_all(engine)\n\nwith Session(engine) as session:\n session.add(hero_1)\n session.add(hero_2)\n session.add(hero_3)\n session.commit()\n```\n\nThat will save a **SQLite** database with the 3 heroes.\n\n### Select from the Database\n\nThen you could write queries to select from that same database, for example with:\n\n```Python hl_lines="15-18"\nfrom typing import Optional\n\nfrom sqlmodel import Field, Session, SQLModel, create_engine, select\n\n\nclass Hero(SQLModel, table=True):\n id: Optional[int] = Field(default=None, primary_key=True)\n name: str\n secret_name: str\n age: Optional[int] = None\n\n\nengine = create_engine("sqlite:///database.db")\n\nwith Session(engine) as session:\n statement = select(Hero).where(Hero.name == "Spider-Boy")\n hero = session.exec(statement).first()\n print(hero)\n```\n\n### Editor Support Everywhere\n\n**SQLModel** was carefully designed to give you the best developer experience and editor support, **even after selecting data** from the database:\n\n<img class="shadow" src="https://sqlmodel.tiangolo.com/img/index/autocompletion02.png">\n\n## SQLAlchemy and Pydantic\n\nThat class `Hero` is a **SQLModel** model.\n\nBut at the same time, ✨ it is a **SQLAlchemy** model ✨. So, you can combine it and use it with other SQLAlchemy models, or you could easily migrate applications with SQLAlchemy to **SQLModel**.\n\nAnd at the same time, ✨ it is also a **Pydantic** model ✨. You can use inheritance with it to define all your **data models** while avoiding code duplication. That makes it very easy to use with **FastAPI**.\n\n## License\n\nThis project is licensed under the terms of the [MIT license](https://github.com/tiangolo/sqlmodel/blob/main/LICENSE).\n',
24
- 'author': 'Sebastián Ramírez',
25
- 'author_email': 'tiangolo@gmail.com',
26
- 'maintainer': 'None',
27
- 'maintainer_email': 'None',
28
- 'url': 'https://github.com/tiangolo/sqlmodel',
29
- 'packages': packages,
30
- 'package_data': package_data,
31
- 'install_requires': install_requires,
32
- 'python_requires': '>=3.6.1,<4.0.0',
33
- }
34
-
35
-
36
- setup(**setup_kwargs)
File without changes
File without changes
File without changes