Skip to content

Commit b1ab0de

Browse files
authored
Bump supported fastapi versions (#1110)
* Bump supported fastapi version to <=0.97, change all fastapi tests from starlette client to httpx.AsyncClient * Add lifecycle manager to fastapi tests * Fix coverage * Add python 3.11 to test suite, bump version
1 parent e72e40d commit b1ab0de

27 files changed

Lines changed: 733 additions & 587 deletions

.github/workflows/test-package.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'collerek/ormar'
1818
strategy:
1919
matrix:
20-
python-version: [3.7, 3.8, 3.9, "3.10"]
20+
python-version: [3.7, 3.8, 3.9, "3.10", 3.11]
2121
fail-fast: false
2222
services:
2323
mysql:
@@ -49,7 +49,7 @@ jobs:
4949
python-version: ${{ matrix.python-version }}
5050
- name: Install dependencies
5151
run: |
52-
python -m pip install poetry==1.4.1
52+
python -m pip install poetry==1.4.2
5353
poetry install --extras "all"
5454
env:
5555
POETRY_VIRTUALENVS_CREATE: false

benchmarks/test_benchmark_init.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ async def initialize_models(num_models: int):
2121
]
2222
assert len(authors) == num_models
2323

24-
initialize_models(num_models)
24+
await initialize_models(num_models)
2525

2626

2727
@pytest.mark.parametrize("num_models", [10, 20, 40])
@@ -30,7 +30,7 @@ async def test_initializing_models_with_related_models(aio_benchmark, num_models
3030
async def initialize_models_with_related_models(
3131
author: Author, publisher: Publisher, num_models: int
3232
):
33-
books = [
33+
_ = [
3434
Book(
3535
author=author,
3636
publisher=publisher,
@@ -43,6 +43,6 @@ async def initialize_models_with_related_models(
4343
author = await Author(name="Author", score=10).save()
4444
publisher = await Publisher(name="Publisher", prestige=random.randint(0, 10)).save()
4545

46-
ids = initialize_models_with_related_models(
46+
_ = initialize_models_with_related_models(
4747
author=author, publisher=publisher, num_models=num_models
4848
)

benchmarks/test_benchmark_update.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async def test_updating_models_individually(
1818
@aio_benchmark
1919
async def update(authors: List[Author]):
2020
for author in authors:
21-
a = await author.update(
21+
_ = await author.update(
2222
name="".join(random.sample(string.ascii_letters, 5))
2323
)
2424

ormar/models/newbasemodel.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
Union,
1919
cast,
2020
)
21-
import functools
2221

2322
import databases
2423
import pydantic
@@ -242,14 +241,18 @@ def _update_relation_cache(self, prev_hash: int, new_hash: int) -> None:
242241
:param new_hash: The hash to update to
243242
:type new_hash: int
244243
"""
244+
245245
def _update_cache(relations: List[Relation], recurse: bool = True) -> None:
246246
for relation in relations:
247247
relation_proxy = relation.get()
248248

249249
if hasattr(relation_proxy, "update_cache"):
250250
relation_proxy.update_cache(prev_hash, new_hash) # type: ignore
251251
elif recurse and hasattr(relation_proxy, "_orm"):
252-
_update_cache(relation_proxy._orm._relations.values(), recurse=False) # type: ignore
252+
_update_cache(
253+
relation_proxy._orm._relations.values(), # type: ignore
254+
recurse=False,
255+
)
253256

254257
_update_cache(list(self._orm._relations.values()))
255258

ormar/relations/relation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ def _find_existing(
133133
return None
134134
else:
135135
# We need to clear the weakrefs that don't point to anything anymore
136-
# There's an assumption here that if some of the related models went out of scope,
137-
# then they all did, so we can just check the first one
136+
# There's an assumption here that if some of the related models
137+
# went out of scope, then they all did, so we can just check the first one
138138
try:
139139
self.related_models[0].__repr__.__self__
140140
return self.related_models.index(child)

ormar/relations/relation_proxy.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def pop(self, index: SupportsIndex = 0) -> T:
145145
"""
146146
item = self[index]
147147

148-
# Try to delete it, but do it the long way if weakly-referenced thing doesn't exist
148+
# Try to delete it, but do it a long way
149+
# if weakly-referenced thing doesn't exist
149150
try:
150151
self._relation_cache.pop(item.__hash__())
151152
except ReferenceError:

poetry.lock

Lines changed: 448 additions & 364 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name = "ormar"
33

44
[tool.poetry]
55
name = "ormar"
6-
version = "0.12.1"
7-
description = "A simple async ORM with fastapi in mind and pydantic validation."
6+
version = "0.12.2"
7+
description = "An async ORM with fastapi in mind and pydantic validation."
88
authors = ["Radosław Drążkiewicz <collerek@gmail.com>"]
99
license = "MIT"
1010
readme = "README.md"
@@ -56,6 +56,7 @@ psycopg2-binary = { version = "^2.9.1", optional = true }
5656
mysqlclient = { version = "^2.1.0", optional = true }
5757
PyMySQL = { version = ">=0.9", optional = true }
5858

59+
5960
[tool.poetry.dependencies.orjson]
6061
version = ">=3.6.4"
6162
optional = true
@@ -75,7 +76,7 @@ pytest = "^7.3.1"
7576
pytest-cov = "^4.0.0"
7677
codecov = "^2.1.13"
7778
pytest-asyncio = "^0.21.0"
78-
fastapi = ">=0.70.1,<0.86"
79+
fastapi = ">=0.70.1,<=0.97"
7980
flake8 = "^3.9.2"
8081
flake8-black = "^0.3.6"
8182
flake8-bugbear = "^23.3.12"
@@ -137,6 +138,10 @@ all = [
137138
"cryptography",
138139
]
139140

141+
[tool.poetry.group.dev.dependencies]
142+
httpx = "^0.24.1"
143+
asgi-lifespan = "^2.1.0"
144+
140145
[build-system]
141146
requires = ["poetry-core>=1.0.0"]
142147
build-backend = "poetry.core.masonry.api"

tests/test_exclude_include_dict/test_excluding_fields_in_fastapi.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import pydantic
77
import pytest
88
import sqlalchemy
9+
from asgi_lifespan import LifespanManager
910
from fastapi import FastAPI
10-
from starlette.testclient import TestClient
11+
from httpx import AsyncClient
1112

1213
import ormar
1314
from ormar import post_save, property_field
@@ -153,34 +154,35 @@ async def create_user7(user: RandomModel):
153154
return await user.save()
154155

155156

156-
def test_excluding_fields_in_endpoints():
157-
client = TestClient(app)
158-
with client as client:
157+
@pytest.mark.asyncio
158+
async def test_excluding_fields_in_endpoints():
159+
client = AsyncClient(app=app, base_url="http://testserver")
160+
async with client as client, LifespanManager(app):
159161
user = {
160162
"email": "test@domain.com",
161163
"password": "^*^%A*DA*IAAA",
162164
"first_name": "John",
163165
"last_name": "Doe",
164166
}
165-
response = client.post("/users/", json=user)
167+
response = await client.post("/users/", json=user)
166168
created_user = User(**response.json())
167169
assert created_user.pk is not None
168170
assert created_user.password is None
169171

170172
user2 = {"email": "test@domain.com", "first_name": "John", "last_name": "Doe"}
171173

172-
response = client.post("/users/", json=user2)
174+
response = await client.post("/users/", json=user2)
173175
created_user = User(**response.json())
174176
assert created_user.pk is not None
175177
assert created_user.password is None
176178

177-
response = client.post("/users2/", json=user)
179+
response = await client.post("/users2/", json=user)
178180
created_user2 = User(**response.json())
179181
assert created_user2.pk is not None
180182
assert created_user2.password is None
181183

182184
# response has only 3 fields from UserBase
183-
response = client.post("/users3/", json=user)
185+
response = await client.post("/users3/", json=user)
184186
assert list(response.json().keys()) == ["email", "first_name", "last_name"]
185187

186188
timestamp = datetime.datetime.now()
@@ -192,7 +194,7 @@ def test_excluding_fields_in_endpoints():
192194
"last_name": "Doe",
193195
"timestamp": str(timestamp),
194196
}
195-
response = client.post("/users4/", json=user3)
197+
response = await client.post("/users4/", json=user3)
196198
assert list(response.json().keys()) == [
197199
"id",
198200
"email",
@@ -209,7 +211,7 @@ def test_excluding_fields_in_endpoints():
209211
assert isinstance(user_instance.timestamp, datetime.datetime)
210212
assert user_instance.timestamp == timestamp
211213

212-
response = client.post("/users4/", json=user3)
214+
response = await client.post("/users4/", json=user3)
213215
assert list(response.json().keys()) == [
214216
"id",
215217
"email",
@@ -226,11 +228,12 @@ def test_excluding_fields_in_endpoints():
226228
)
227229

228230

229-
def test_adding_fields_in_endpoints():
230-
client = TestClient(app)
231-
with client as client:
231+
@pytest.mark.asyncio
232+
async def test_adding_fields_in_endpoints():
233+
client = AsyncClient(app=app, base_url="http://testserver")
234+
async with client as client, LifespanManager(app):
232235
user3 = {"last_name": "Test", "full_name": "deleted"}
233-
response = client.post("/random/", json=user3)
236+
response = await client.post("/random/", json=user3)
234237
assert list(response.json().keys()) == [
235238
"id",
236239
"password",
@@ -242,7 +245,7 @@ def test_adding_fields_in_endpoints():
242245
assert response.json().get("full_name") == "John Test"
243246

244247
user3 = {"last_name": "Test"}
245-
response = client.post("/random/", json=user3)
248+
response = await client.post("/random/", json=user3)
246249
assert list(response.json().keys()) == [
247250
"id",
248251
"password",
@@ -254,11 +257,12 @@ def test_adding_fields_in_endpoints():
254257
assert response.json().get("full_name") == "John Test"
255258

256259

257-
def test_adding_fields_in_endpoints2():
258-
client = TestClient(app)
259-
with client as client:
260+
@pytest.mark.asyncio
261+
async def test_adding_fields_in_endpoints2():
262+
client = AsyncClient(app=app, base_url="http://testserver")
263+
async with client as client, LifespanManager(app):
260264
user3 = {"last_name": "Test"}
261-
response = client.post("/random2/", json=user3)
265+
response = await client.post("/random2/", json=user3)
262266
assert list(response.json().keys()) == [
263267
"id",
264268
"password",
@@ -270,18 +274,19 @@ def test_adding_fields_in_endpoints2():
270274
assert response.json().get("full_name") == "John Test"
271275

272276

273-
def test_excluding_property_field_in_endpoints2():
277+
@pytest.mark.asyncio
278+
async def test_excluding_property_field_in_endpoints2():
274279

275280
dummy_registry = {}
276281

277282
@post_save(RandomModel)
278283
async def after_save(sender, instance, **kwargs):
279284
dummy_registry[instance.pk] = instance.dict()
280285

281-
client = TestClient(app)
282-
with client as client:
286+
client = AsyncClient(app=app, base_url="http://testserver")
287+
async with client as client, LifespanManager(app):
283288
user3 = {"last_name": "Test"}
284-
response = client.post("/random3/", json=user3)
289+
response = await client.post("/random3/", json=user3)
285290
assert list(response.json().keys()) == [
286291
"id",
287292
"password",

tests/test_fastapi/test_binary_fields.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import databases
77
import pytest
88
import sqlalchemy
9+
from asgi_lifespan import LifespanManager
910
from fastapi import FastAPI
10-
from starlette.testclient import TestClient
11+
from httpx import AsyncClient
1112

1213
import ormar
1314
from tests.settings import DATABASE_URL
@@ -78,16 +79,17 @@ def create_test_database():
7879
metadata.drop_all(engine)
7980

8081

81-
def test_read_main():
82-
client = TestClient(app)
83-
with client as client:
84-
response = client.post(
82+
@pytest.mark.asyncio
83+
async def test_read_main():
84+
client = AsyncClient(app=app, base_url="http://testserver")
85+
async with client as client, LifespanManager(app):
86+
response = await client.post(
8587
"/things",
86-
data=json.dumps({"bt": base64.b64encode(blob3).decode()}),
88+
json={"bt": base64.b64encode(blob3).decode()},
8789
headers=headers,
8890
)
8991
assert response.status_code == 200
90-
response = client.get("/things")
92+
response = await client.get("/things")
9193
assert response.json()[0]["bt"] == base64.b64encode(blob3).decode()
9294
thing = BinaryThing(**response.json()[0])
9395
assert thing.__dict__["bt"] == blob3

0 commit comments

Comments
 (0)