Skip to content

Commit

Permalink
Update type annotations for Python 3.10
Browse files Browse the repository at this point in the history
This commit will update type annotation syntax for Python 3.10. The
project currently also supports Python 3.8 and 3.9, so the annotations
are imported with `from __future__ import annotations`.

The Python 3.10 union operator (the pipe, like `str | None`) will not be
used on pydantic models. If running Python 3.9 or below, pydantic is not
compatible with the union operator, even if annotations are imported
with `from __future__ import annotations`.

https://peps.python.org/pep-0604/
https://docs.python.org/3/whatsnew/3.10.html
pydantic/pydantic#2597 (comment)
pydantic/pydantic#2609 (comment)
pydantic/pydantic#3300 (comment)
  • Loading branch information
br3ndonland committed Apr 2, 2022
1 parent 760c071 commit f3fd95d
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 25 deletions.
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

0 comments on commit f3fd95d

Please sign in to comment.