Skip to content

Commit

Permalink
Merge pull request #36 from br3ndonland/python-3.10
Browse files Browse the repository at this point in the history
Add Python 3.10 support
  • Loading branch information
br3ndonland authored Apr 2, 2022
2 parents 97e35b7 + f3fd95d commit 7b2fdf6
Show file tree
Hide file tree
Showing 12 changed files with 43 additions and 33 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
Expand Down Expand Up @@ -67,12 +67,13 @@ jobs:
- name: Run unit tests
run: poetry run pytest --cov-report=xml
- name: Upload test coverage report to Codecov
if: matrix.python-version == '3.10'
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
flags: unit
- name: Build Python package with latest Python version and publish to PyPI
if: startsWith(github.ref, 'refs/tags/') && matrix.python-version == 3.9
if: startsWith(github.ref, 'refs/tags/') && matrix.python-version == '3.10'
run: |
PACKAGE_VERSION=$(poetry version -s)
GIT_TAG_VERSION=$(echo ${{ github.ref }} | cut -d / -f 3)
Expand All @@ -91,7 +92,7 @@ jobs:
fail-fast: false
matrix:
linux-version: ["", "alpine", "slim"]
python-version: [3.8, 3.9]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
Expand Down Expand Up @@ -231,7 +232,7 @@ jobs:
-u ${{ github.actor }} --password-stdin
- name: Tag and push Docker images with latest tags
if: >
matrix.python-version == 3.9 &&
matrix.python-version == '3.10' &&
(
startsWith(github.ref, 'refs/tags/') ||
github.ref == 'refs/heads/develop' ||
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.x"
python-version: "3.10"
- name: Set up Poetry cache for Python dependencies
uses: actions/cache@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/hooks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
Expand Down Expand Up @@ -62,6 +62,7 @@ jobs:
- name: Run unit tests
run: poetry run pytest --cov-report=xml
- name: Upload test coverage report to Codecov
if: matrix.python-version == '3.10'
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG PYTHON_VERSION=3.9 LINUX_VERSION=
ARG PYTHON_VERSION=3.10 LINUX_VERSION=
FROM python:${PYTHON_VERSION}${LINUX_VERSION:+-$LINUX_VERSION} AS base
LABEL org.opencontainers.image.authors="Brendon Smith <bws@bws.bio>"
LABEL org.opencontainers.image.description="Docker images and utilities to power your Python APIs and help you ship faster."
Expand Down
12 changes: 7 additions & 5 deletions inboard/app/main_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import os
import sys
from typing import Awaitable, Callable, Dict
from typing import Awaitable, Callable


class App:
Expand All @@ -9,13 +11,13 @@ class App:
https://www.uvicorn.org/
"""

def __init__(self, scope: Dict) -> None:
def __init__(self, scope: dict) -> None:
assert scope["type"] == "http"
self.scope = scope

async def __call__(
self, receive: Dict, send: Callable[[Dict], Awaitable]
) -> Dict[str, str]:
self, receive: dict, send: Callable[[dict], Awaitable]
) -> dict[str, str]:
await send(
{
"type": "http.response.start",
Expand All @@ -32,7 +34,7 @@ async def __call__(
raise NameError("Process manager needs to be either uvicorn or gunicorn.")
server = "Uvicorn" if process_manager == "uvicorn" else "Uvicorn, Gunicorn,"
message = f"Hello World, from {server} and Python {version}!"
response: Dict = {"type": "http.response.body", "body": message.encode("utf-8")}
response: dict = {"type": "http.response.body", "body": message.encode("utf-8")}
await send(response)
return response

Expand Down
5 changes: 3 additions & 2 deletions inboard/app/utilities_starlette.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

import base64
import os
import secrets
from typing import Optional, Tuple

from starlette.authentication import (
AuthCredentials,
Expand All @@ -17,7 +18,7 @@ class BasicAuth(AuthenticationBackend):

async def authenticate(
self, request: HTTPConnection
) -> Optional[Tuple[AuthCredentials, SimpleUser]]:
) -> tuple[AuthCredentials, SimpleUser] | None:
"""Authenticate a Starlette request with HTTP Basic auth."""
if "Authorization" not in request.headers:
return None
Expand Down
7 changes: 4 additions & 3 deletions inboard/gunicorn_conf.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from __future__ import annotations

import multiprocessing
import os
from typing import Optional

from inboard.logging_conf import configure_logging


def calculate_workers(
max_workers: Optional[str] = None,
total_workers: Optional[str] = None,
max_workers: str | None = None,
total_workers: str | None = None,
workers_per_core: str = "1",
) -> int:
"""Calculate the number of Gunicorn worker processes."""
Expand Down
9 changes: 5 additions & 4 deletions inboard/logging_conf.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

import importlib.util
import logging
import logging.config
import os
import sys
from pathlib import Path
from typing import Optional, Set


def find_and_load_logging_conf(logging_conf: str) -> dict:
Expand All @@ -30,7 +31,7 @@ def find_and_load_logging_conf(logging_conf: str) -> dict:

def configure_logging(
logger: logging.Logger = logging.getLogger(),
logging_conf: Optional[str] = os.getenv("LOGGING_CONF"),
logging_conf: str | None = os.getenv("LOGGING_CONF"),
) -> dict:
"""Configure Python logging given the name of a logging module or file."""
try:
Expand Down Expand Up @@ -67,7 +68,7 @@ class LogFilter(logging.Filter):
def __init__(
self,
name: str = "",
filters: Optional[Set[str]] = None,
filters: set[str] | None = None,
) -> None:
"""Initialize a filter."""
self.name = name
Expand All @@ -85,7 +86,7 @@ def filter(self, record: logging.LogRecord) -> bool:
return all(match not in message for match in self.filters)

@staticmethod
def set_filters(input_filters: Optional[str] = None) -> Optional[Set[str]]:
def set_filters(input_filters: str | None = None) -> set[str] | None:
"""Set log message filters.
Filters identify log messages to filter out, so that the logger does not
Expand Down
9 changes: 5 additions & 4 deletions inboard/start.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/usr/bin/env python3
from __future__ import annotations

import importlib.util
import json
import logging
import os
import subprocess
from pathlib import Path
from typing import Optional

import uvicorn # type: ignore

Expand Down Expand Up @@ -52,7 +53,7 @@ def set_gunicorn_options(app_module: str) -> list:
return ["gunicorn", "-k", worker_class, "-c", gunicorn_conf_path, app_module]


def _split_uvicorn_option(option: str) -> Optional[list]:
def _split_uvicorn_option(option: str) -> list | None:
return (
[option_item.strip() for option_item in str(option_value).split(sep=",")]
if (option_value := os.getenv(option.upper()))
Expand All @@ -77,7 +78,7 @@ def _update_uvicorn_config_options(uvicorn_config_options: dict) -> dict:
return uvicorn_config_options


def set_uvicorn_options(log_config: Optional[dict] = None) -> dict:
def set_uvicorn_options(log_config: dict | None = None) -> dict:
"""Set options for running the Uvicorn server."""
host = os.getenv("HOST", "0.0.0.0")
port = int(os.getenv("PORT", "80"))
Expand All @@ -99,7 +100,7 @@ def start_server(
process_manager: str,
app_module: str,
logger: logging.Logger = logging.getLogger(),
logging_conf_dict: Optional[dict] = None,
logging_conf_dict: dict | None = None,
) -> None:
"""Start the Uvicorn or Gunicorn server."""
try:
Expand Down
9 changes: 5 additions & 4 deletions tests/app/test_main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
from typing import Dict, List

import pytest
from fastapi import FastAPI
Expand All @@ -14,7 +15,7 @@ class TestCors:
[Starlette CORS docs](https://www.starlette.io/middleware/#corsmiddleware).
"""

origins: Dict[str, List[str]] = {
origins: dict[str, list[str]] = {
"allowed": [
"http://br3ndon.land",
"https://br3ndon.land",
Expand All @@ -38,7 +39,7 @@ def test_cors_preflight_response_allowed(
self, allowed_origin: str, client: TestClient
) -> None:
"""Test pre-flight response to cross-origin request from allowed origin."""
headers: Dict[str, str] = {
headers: dict[str, str] = {
"Origin": allowed_origin,
"Access-Control-Request-Method": "GET",
"Access-Control-Request-Headers": "X-Example",
Expand All @@ -54,7 +55,7 @@ def test_cors_preflight_response_disallowed(
self, disallowed_origin: str, client: TestClient
) -> None:
"""Test pre-flight response to cross-origin request from disallowed origin."""
headers: Dict[str, str] = {
headers: dict[str, str] = {
"Origin": disallowed_origin,
"Access-Control-Request-Method": "GET",
"Access-Control-Request-Headers": "X-Example",
Expand Down
7 changes: 4 additions & 3 deletions tests/test_gunicorn_conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

import multiprocessing
import subprocess
from pathlib import Path
from typing import Optional

import pytest

Expand All @@ -20,7 +21,7 @@ def test_calculate_workers_default(self) -> None:
assert gunicorn_conf.workers == max(cores, 2)

@pytest.mark.parametrize("max_workers", (None, "1", "2", "5", "10"))
def test_calculate_workers_max(self, max_workers: Optional[str]) -> None:
def test_calculate_workers_max(self, max_workers: str | None) -> None:
"""Test Gunicorn worker process calculation with custom maximum."""
cores = multiprocessing.cpu_count()
default = max(cores, 2)
Expand All @@ -31,7 +32,7 @@ def test_calculate_workers_max(self, max_workers: Optional[str]) -> None:
assert result == default

@pytest.mark.parametrize("total_workers", (None, "1", "2", "5", "10"))
def test_calculate_workers_total(self, total_workers: Optional[str]) -> None:
def test_calculate_workers_total(self, total_workers: str | None) -> None:
"""Test Gunicorn worker process calculation with custom total."""
cores = multiprocessing.cpu_count()
result = gunicorn_conf.calculate_workers(None, total_workers)
Expand Down

1 comment on commit 7b2fdf6

@vercel
Copy link

@vercel vercel bot commented on 7b2fdf6 Apr 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.