Skip to content

Commit

Permalink
Support for Python 3.12
Browse files Browse the repository at this point in the history
Plus a bunch of gardening:
* leaftovers cleanup from #220,
* update thelist of projects using pyramid_openapi3,
* update the dev env to latest packages,
  • Loading branch information
zupo committed Mar 7, 2024
1 parent fa817ca commit 3e63012
Show file tree
Hide file tree
Showing 11 changed files with 686 additions and 608 deletions.
2 changes: 0 additions & 2 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ ignore =
D202,
# D204: 1 blank line required after class docstring
D204,
# D101: Missing docstring in __init__
/__init__/* : D101,
# D107: Missing docstring in __init__
D107,
# W503: line break before binary operator
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ concurrency:

jobs:

test_311:
name: "Python 3.11 Tests"
test_312:
name: "Python 3.12 Tests"

runs-on: ubuntu-22.04

Expand Down Expand Up @@ -104,7 +104,7 @@ jobs:
name: "Release to PyPI"

if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
needs: [test_311, test_38]
needs: [test_312, test_39]
runs-on: ubuntu-22.04

# To test publishing to testpypi:
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
## Changelog

0.17.0 (unreleased)
-------------------

* Update the supported version of Python to 3.12, Pyramid to 2.0.2.
Drop support for Python 3.8.
[zupo]

* Update the supported version of `openapi-core` to 0.19.0, refs #220.
Drop support for all older versions of `openapi-core`.
[miketheman, Wim-De-Clercq, zupo]

* Update Swagger UI version to 4.18.3, refs #210.
[kskarthik]

* Add support for specifying the protocol and port for getting the openapi3
spec file, fixes running behind a reverse proxy, refs #176.
[vpet98, zupo]


0.16.0 (2023-03-22)
-------------------
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Convenience makefile to build the dev env and run common commands
# Based on https://github.com/niteoweb/Makefile

PYTHON ?= python3.11
PYTHON ?= python3.12

.PHONY: all
all: tests
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ The authors of pyramid_openapi3 believe that the approach of validating a manual

## Running tests

You need to have [poetry](https://python-poetry.org/) and Python 3.9 through 3.11 installed on your machine. All `Makefile` commands assume you have the Poetry environment activated, i.e. `poetry shell`.
You need to have [poetry](https://python-poetry.org/) and Python 3.9 through 3.12 installed on your machine. All `Makefile` commands assume you have the Poetry environment activated, i.e. `poetry shell`.

Alternatively, if you use [nix](https://nix.dev/tutorials/declarative-and-reproducible-developer-environments), run `nix-shell` to drop into a shell that has everything prepared for development.

Expand All @@ -201,16 +201,18 @@ These packages tackle the same problem-space:

We do our best to follow the rules below.

* Support the latest few releases of Python, currently Python 3.9 through 3.11.
* Support the latest few releases of Pyramid, currently 1.10.7 through 2.0.
* Support the latest few releases of `openapi-core`, currently 0.16.1 through 0.16.2.
* Support the latest few releases of Python, currently Python 3.9 through 3.12.
* Support the latest few releases of Pyramid, currently 1.10.7 through 2.0.2.
* Support the latest few releases of `openapi-core`, currently just 0.19.0.
* See `poetry.lock` for a frozen-in-time known-good-set of all dependencies.

## Use in the wild

A couple of projects that use pyramid_openapi3 in production:

- [WooCart API](https://app.woocart.com/api/v1) - User control panel for WooCart Managed WooCommerce service.
- [Pareto Security Team Dashboard API](https://dash.paretosecurity.com/api/v1) - Team Dashboard for Pareto Security macOS security app.
- [SEO Domain Finder API](https://app.seodomainfinder.com/api/v1) - A tool for finding expired domains with SEO value.
- [Rankalyzer.io](https://app.rankalyzer.io/api/v1) - Monitor SEO changes and strategies of competitors to improve your search traffic.
- [Kafkai API](https://app.kafkai.com/api/v1) - User control panel for Kafkai text generation service.
- [Open on-chain data API](https://tradingstrategy.ai/api/explorer/) - Decentralised exchange and blockchain trading data open API
- [Pareto Security Team Dashboard API](https://dash.paretosecurity.app/api/v1) - Team Dashboard for Pareto Security macOS security app
- [Open on-chain data API](https://tradingstrategy.ai/api/explorer/) - Decentralised exchange and blockchain trading data open API.
- [Mayet RX](https://app.mayetrx.com/api/v1) - Vendor management system for pharma/medical clinical trials.
1,160 changes: 603 additions & 557 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ pre-commit-hooks = "*"
pytest = "*"
pytest-cov = "*"
pytest-instafail = "*"
pytest-lazy-fixture = "*"
pytest-randomly = "*"
pytest-socket = "*"
pyupgrade = "*"
Expand Down
43 changes: 28 additions & 15 deletions pyramid_openapi3/tests/test_app_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from pyramid.request import Request
from pyramid.testing import testConfig
from pyramid_openapi3 import MissingEndpointsError
from pytest_lazyfixture import lazy_fixture

import logging
import os
Expand Down Expand Up @@ -181,19 +180,19 @@ def root_server_app_config(
yield simple_config


@pytest.fixture(
params=(
lazy_fixture("simple_app_config"),
lazy_fixture("split_file_app_config"),
)
app_config = pytest.mark.parametrize(
"app_config",
[
"simple_app_config",
"split_file_app_config",
],
)
def app_config(request: SubRequest) -> Configurator:
"""Parametrized fixture containing various app configs to test."""
return request.param


def test_all_routes(app_config: Configurator) -> None:
@app_config
def test_all_routes(app_config: Configurator, request: SubRequest) -> None:
"""Test case showing that an app can be created with all routes defined."""
app_config = request.getfixturevalue(app_config)
app_config.add_route(name="foo", pattern="/foo")
app_config.add_route(name="bar", pattern="/bar")
app_config.add_view(
Expand All @@ -206,8 +205,10 @@ def test_all_routes(app_config: Configurator) -> None:
app_config.make_wsgi_app()


def test_prefixed_routes(app_config: Configurator) -> None:
@app_config
def test_prefixed_routes(app_config: Configurator, request: SubRequest) -> None:
"""Test case for prefixed routes."""
app_config = request.getfixturevalue(app_config)
app_config.add_route(name="foo", pattern="/api/v1/foo")
app_config.add_route(name="bar", pattern="/api/v1/bar")
app_config.add_view(
Expand All @@ -220,8 +221,12 @@ def test_prefixed_routes(app_config: Configurator) -> None:
app_config.make_wsgi_app()


def test_pyramid_prefixed_context_routes(app_config: Configurator) -> None:
@app_config
def test_pyramid_prefixed_context_routes(
app_config: Configurator, request: SubRequest
) -> None:
"""Test case for prefixed routes using pyramid route_prefix_context."""
app_config = request.getfixturevalue(app_config)
with app_config.route_prefix_context("/api/v1"):
app_config.add_route(name="foo", pattern="/foo")
app_config.add_route(name="bar", pattern="/bar")
Expand All @@ -235,19 +240,23 @@ def test_pyramid_prefixed_context_routes(app_config: Configurator) -> None:
app_config.make_wsgi_app()


def test_missing_routes(app_config: Configurator) -> None:
@app_config
def test_missing_routes(app_config: Configurator, request: SubRequest) -> None:
"""Test case showing app creation fails, when defined routes are missing."""
app_config = request.getfixturevalue(app_config)
with pytest.raises(MissingEndpointsError) as ex:
app_config.make_wsgi_app()

assert str(ex.value) == "Unable to find routes for endpoints: /foo, /bar"


@app_config
def test_disable_endpoint_validation(
app_config: Configurator, caplog: LogCaptureFixture
app_config: Configurator, caplog: LogCaptureFixture, request: SubRequest
) -> None:
"""Test case showing app creation whilst disabling endpoint validation."""
caplog.set_level(logging.INFO)
app_config = request.getfixturevalue(app_config)
app_config.registry.settings["pyramid_openapi3.enable_endpoint_validation"] = False
app_config.add_route(name="foo", pattern="/foo")
app_config.add_view(
Expand All @@ -273,8 +282,12 @@ def test_unconfigured_app(
assert "pyramid_openapi3 settings not found" in caplog.text


def test_routes_setting_generation(app_config: Configurator) -> None:
@app_config
def test_routes_setting_generation(
app_config: Configurator, request: SubRequest
) -> None:
"""Test the `routes` setting is correctly created after app creation."""
app_config = request.getfixturevalue(app_config)

# Test that having multiple routes for a single route / pattern still works
app_config.add_route(name="get_foo", pattern="/foo", request_method="GET")
Expand Down
2 changes: 1 addition & 1 deletion pyramid_openapi3/tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
]


class DummyDefaultContext(object):
class DummyDefaultContext(object): # noqa: D101

__acl__ = DEFAULT_ACL

Expand Down
34 changes: 18 additions & 16 deletions pyramid_openapi3/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@


@dataclass
class DummyRoute:
class DummyRoute: # noqa: D101
name: str
pattern: str


class DummyStartResponse(object):
class DummyStartResponse(object): # noqa: D101
def __call__(self, status: str, headerlist: t.List[t.Tuple[str, str]]) -> None:
"""WSGI start_response protocol."""
self.status = status
self.headerlist = headerlist


class RequestValidationBase(TestCase):
class RequestValidationBase(TestCase): # noqa: D101
openapi_spec: bytes

def setUp(self) -> None:
Expand Down Expand Up @@ -63,7 +63,9 @@ def tearDown(self) -> None:
tearDown()
self.config = None

def _add_view(self, view_func: t.Callable = None, openapi: bool = True) -> None:
def _add_view(
self, view_func: t.Optional[t.Callable] = None, openapi: bool = True
) -> None:
"""Add a simple example view.
:param view_func: an optional view callable.
Expand All @@ -84,7 +86,7 @@ def _get_view(self) -> View:
)
return view

def _get_request(self, params: t.Dict = None) -> DummyRequest:
def _get_request(self, params: t.Optional[t.Dict] = None) -> DummyRequest:
"""Create a DummyRequest instance matching example view.
:param params: Query parameter dictionary
Expand All @@ -97,7 +99,7 @@ def _get_request(self, params: t.Dict = None) -> DummyRequest:
return request


class TestRequestValidation(RequestValidationBase):
class TestRequestValidation(RequestValidationBase): # noqa: D101

openapi_spec = (
b"openapi: '3.0.0'\n"
Expand Down Expand Up @@ -301,9 +303,9 @@ def test_request_validation_disabled(self) -> None:
self.assertEqual(start_response.status, "400 Bad Request")

# now let's disable it
self.config.registry.settings[
"pyramid_openapi3.enable_request_validation"
] = False
self.config.registry.settings["pyramid_openapi3.enable_request_validation"] = (
False
)
start_response = DummyStartResponse()
response = router(environ, start_response)
self.assertEqual(start_response.status, "200 OK")
Expand All @@ -329,9 +331,9 @@ def test_response_validation_disabled(self) -> None:
self.assertEqual(start_response.status, "500 Internal Server Error")

# now let's disable it
self.config.registry.settings[
"pyramid_openapi3.enable_response_validation"
] = False
self.config.registry.settings["pyramid_openapi3.enable_response_validation"] = (
False
)
start_response = DummyStartResponse()
response = router(environ, start_response)
self.assertEqual(start_response.status, "200 OK")
Expand All @@ -341,9 +343,9 @@ def test_request_validation_disabled_response_validation_enabled(self) -> None:
"""Test response validation still works if request validation is disabled."""
self._add_view(lambda *arg: "not-valid")

self.config.registry.settings[
"pyramid_openapi3.enable_request_validation"
] = False
self.config.registry.settings["pyramid_openapi3.enable_request_validation"] = (
False
)

# by default validation is enabled
router = Router(self.config.registry)
Expand All @@ -361,7 +363,7 @@ def test_request_validation_disabled_response_validation_enabled(self) -> None:
self.assertEqual(start_response.status, "500 Internal Server Error")


class TestImproperAPISpecValidation(RequestValidationBase):
class TestImproperAPISpecValidation(RequestValidationBase): # noqa: D101

openapi_spec = (
b'openapi: "3.0.0"\n'
Expand Down
10 changes: 5 additions & 5 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
let
nixpkgs = builtins.fetchTarball {
# https://github.com/NixOS/nixpkgs/tree/nixos-23.11 on 2024-03-07
url = "https://github.com/nixos/nixpkgs/archive/880992dcc006a5e00dd0591446fdf723e6a51a64.tar.gz";
sha256 = "1rsbjwbjgnrh0gm54w7nlshkkgp5718d5rk4x8kvklpfivlpfb5n";
url = "https://github.com/nixos/nixpkgs/archive/f945939fd679284d736112d3d5410eb867f3b31c.tar.gz";
sha256 = "06da1wf4w752spsm16kkckfhxx5m09lwcs8931gwh76yvclq7257";
};
poetry2nixsrc = builtins.fetchTarball {
# https://github.com/nix-community/poetry2nix/commits/master on 2024-03-07
Expand Down Expand Up @@ -33,8 +33,8 @@ let
});
};

devEnv_311 = poetry2nix.mkPoetryEnv (commonPoetryArgs // {
python = pkgs.python311;
devEnv_312 = poetry2nix.mkPoetryEnv (commonPoetryArgs // {
python = pkgs.python312;
});

devEnv_39 = poetry2nix.mkPoetryEnv (commonPoetryArgs // {
Expand All @@ -49,7 +49,7 @@ pkgs.mkShell {
name = "dev-shell";

buildInputs = with pkgs; [
devEnv_311
devEnv_312
devEnv_39
poetry
gitAndTools.pre-commit
Expand Down

0 comments on commit 3e63012

Please sign in to comment.