Skip to content

Commit

Permalink
✨ Python 3.11 compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
perdy committed Jan 19, 2023
1 parent a31575a commit 9b74f46
Show file tree
Hide file tree
Showing 27 changed files with 463 additions and 352 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docker_push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

env:
DEFAULT_LINUX: "slim"
DEFAULT_PYTHON: "3.10"
DEFAULT_PYTHON: "3.11"

jobs:
docker_push:
Expand All @@ -19,7 +19,7 @@ jobs:
strategy:
matrix:
linux: ["slim"]
python: ["3.7", "3.8", "3.9", "3.10"]
python: ["3.7", "3.8", "3.9", "3.10", "3.11"]
steps:
- name: Check out the repo
uses: actions/checkout@master
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test_and_publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python: [ "3.7", "3.8", "3.9", "3.10" ]
python: [ "3.7", "3.8", "3.9", "3.10", "3.11"]
container:
image: python:${{ matrix.python }}
steps:
Expand Down Expand Up @@ -43,7 +43,7 @@ jobs:
run: ./scripts/flake8
- id: mypy
name: Static types check
run: ./scripts/mypy
run: ./scripts/mypy .
- id: tests
name: Tests
run: ./scripts/test
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test_pull_request_branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.7", "3.8", "3.9", "3.10"]
python: ["3.7", "3.8", "3.9", "3.10", "3.11"]
container:
image: python:${{ matrix.python }}
steps:
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:
run: ./scripts/flake8
- id: mypy
name: Static types check
run: ./scripts/mypy
run: ./scripts/mypy .
- id: tests
name: Tests
run: ./scripts/test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var/
*.egg
*dist/
pip-wheel-metadata/
.python-version

# Installer logs
pip-log.txt
Expand Down
1 change: 0 additions & 1 deletion .python-version

This file was deleted.

9 changes: 7 additions & 2 deletions flama/models/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
import json
import typing

import torch

from flama import exceptions
from flama.injection import Component
from flama.serialize import ModelFormat, loads

try:
import torch
except Exception: # pragma: no cover
torch = None # type: ignore

__all__ = ["Model", "PyTorchModel", "SKLearnModel", "TensorFlowModel", "ModelComponent", "ModelComponentBuilder"]


Expand All @@ -33,6 +36,8 @@ def inspect(self) -> typing.Any:
}

def predict(self, x: typing.List[typing.List[typing.Any]]) -> typing.Any:
assert torch is not None, "`torch` must be installed to use PyTorchModel."

try:
return self.model(torch.Tensor(x)).tolist()
except ValueError as e:
Expand Down
199 changes: 122 additions & 77 deletions poetry.lock

Large diffs are not rendered by default.

44 changes: 29 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ include = ["flama/py.typed", "flama/templates/**/*"]
exclude = []

[tool.poetry.dependencies]
python = "^3.7"
python = ">=3.7,<3.12"
starlette = ">=0.21.0,<1.0.0"
click = "^8.1"
uvicorn = "^0.18.3"
Expand Down Expand Up @@ -62,27 +62,41 @@ ipython = [
isort = "^5.1"
python-multipart = "^0.0.5"
flake8 = "^5.0"
black = { version = "^22.3", extras = ["d"]}
uvicorn = {version = ">=0.18.3,<1.0.0", extras = ["standard"]}
httpx = "^0.23.0"
black = { version = "^22.3", extras = ["d"] }
uvicorn = { version = ">=0.18.3,<1.0.0", extras = ["standard"] }
httpx = ">=0.23,<1.0.0"
aiosqlite = ">=0.11.0,<1.0.0"
requests = "^2.22.0"
Faker = "^8.2.0"
marshmallow = "^3.0"
requests = "^2.22"
Faker = "^8.2"
python-forge = "^18.6"
apispec = "^5.0"
typesystem = "^0.4.1"
SQLAlchemy = { version = "^1.4", extras = ["asyncio"] }
gevent = "^21.12.0"
gevent = "^22.10"
mypy = "^0.942"
pre-commit = "^2.19"
asyncmock = { version = "^0.4", python = ">=3.7,<3.8" }

[tool.poetry.group.schemas]
optional = true

[tool.poetry.group.schemas.dependencies]
marshmallow = "^3.0"
apispec = "^5.0"
typesystem = "^0.4"

[tool.poetry.group.ml]
optional = true

[tool.poetry.group.ml.dependencies]
numpy = [
{ version = "^1.21.0", python = ">=3.7,<3.8" },
{ version = "^1.23", python = ">=3.8" }
]
scikit-learn = [
{ version = "^1.0.0", python = ">=3.7,<3.8" },
{ version = "^1.1.0", python = ">=3.8" }
{ version = "^1.1", python = ">=3.8" }
]
tensorflow-cpu = { version = "^2.9", python = ">=3.7,<3.11" }
pre-commit = "^2.19.0"
torch = "^1.12.1"
asyncmock = { version = "^0.4.2", python = ">=3.7,<3.8" }
tensorflow-cpu = { version = "^2.10", python = ">=3.7,<3.11" }
torch = { version = "^1.12", python = ">=3.7,<3.11" }

[tool.black]
line-length = 120
Expand Down
2 changes: 1 addition & 1 deletion scripts/install
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
install_pkg()
{
echo "🔥 Install python requirements..."
poetry install --with dev "$@"
poetry install --with dev,schemas,ml "$@"
cd templates
echo "🔥 Install js requirements..."
npm i
Expand Down
28 changes: 28 additions & 0 deletions tests/asserts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from time import sleep


def assert_recursive_contains(first, second):
if isinstance(first, dict) and isinstance(second, dict):
assert first.keys() <= second.keys()

for k, v in first.items():
assert_recursive_contains(v, second[k])
elif isinstance(first, (list, set, tuple)) and isinstance(second, (list, set, tuple)):
assert len(first) <= len(second)

for i, _ in enumerate(first):
assert_recursive_contains(first[i], second[i])
else:
assert first == second


def assert_read_from_file(file_path, value, max_sleep=10):
read_value = None
i = 0
while not read_value and i < max_sleep:
sleep(i)
with open(file_path) as f:
read_value = f.read()
i += 0.1

assert read_value == value
99 changes: 65 additions & 34 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import asyncio
import sys
import typing
from contextlib import ExitStack
from time import sleep
from pathlib import Path

import marshmallow
import pytest
Expand All @@ -13,12 +12,29 @@
from flama import Flama
from flama.sqlalchemy import metadata
from flama.testclient import TestClient
from tests.utils import ExceptionContext, check_param_lib_installed

if sys.version_info >= (3, 8): # PORT: Remove when stop supporting 3.7 # pragma: no cover
from unittest.mock import AsyncMock
else: # pragma: no cover
from asyncmock import AsyncMock

try:
import sklearn
from sklearn.linear_model import LogisticRegression
except Exception:
sklearn = None

try:
import tensorflow as tf
except Exception:
tf = None

try:
import torch
except Exception:
torch = None

DATABASE_URL = "sqlite+aiosqlite://"


Expand All @@ -30,18 +46,6 @@ def event_loop():
loop.close()


class ExceptionContext:
def __init__(self, context, exception: typing.Optional[Exception] = None):
self.context = context
self.exception = exception

def __enter__(self):
return self.context.__enter__()

def __exit__(self, exc_type, exc_val, exc_tb):
return self.context.__exit__(exc_type, exc_val, exc_tb)


@pytest.fixture(scope="function")
def exception(request):
if request.param is None:
Expand Down Expand Up @@ -149,28 +153,55 @@ def asgi_send():
return AsyncMock()


def assert_recursive_contains(first, second):
if isinstance(first, dict) and isinstance(second, dict):
assert first.keys() <= second.keys()
def sklearn_model():
return LogisticRegression(), LogisticRegression

for k, v in first.items():
assert_recursive_contains(v, second[k])
elif isinstance(first, (list, set, tuple)) and isinstance(second, (list, set, tuple)):
assert len(first) <= len(second)

for i, _ in enumerate(first):
assert_recursive_contains(first[i], second[i])
else:
assert first == second
def tensorflow_model():
tf_model = tf.keras.models.Sequential(
[
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation="relu"),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation="softmax"),
]
)

tf_model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])

return tf_model, tf.keras.models.Sequential


def torch_model():
class Model(torch.nn.Module):
def forward(self, x):
return x + 10

return Model(), torch.jit.RecursiveScriptModule

def assert_read_from_file(file_path, value, max_tries=10):
read_value = None
i = 0
while not read_value and i < max_tries:
sleep(i)
with open(file_path) as f:
read_value = f.read()
i += 1

assert read_value == value
@pytest.fixture(scope="function")
@check_param_lib_installed
def model(request):
return {"sklearn": sklearn_model, "tensorflow": tensorflow_model, "torch": torch_model}[request.param]()[0]


@pytest.fixture(scope="function")
@check_param_lib_installed
def serialized_model_class(request):
return {"sklearn": sklearn_model, "tensorflow": tensorflow_model, "torch": torch_model}[request.param]()[1]


@pytest.fixture(scope="session")
def model_paths():
return {
"sklearn": Path("tests/data/sklearn_model.flm"),
"tensorflow": Path("tests/data/tensorflow_model.flm"),
"torch": Path("tests/data/pytorch_model.flm"),
}


@pytest.fixture(scope="function")
@check_param_lib_installed
def model_path(request, model_paths):
return model_paths[request.param]
File renamed without changes.
File renamed without changes.
File renamed without changes.
48 changes: 0 additions & 48 deletions tests/models/conftest.py

This file was deleted.

Loading

0 comments on commit 9b74f46

Please sign in to comment.