GithubHelp home page GithubHelp logo

sanic-org / sanic-ext Goto Github PK

View Code? Open in Web Editor NEW
47.0 47.0 30.0 2.77 MB

Extended Sanic functionality

Home Page: https://sanic.dev/en/plugins/sanic-ext/getting-started.html

License: MIT License

Python 99.08% HTML 0.83% Makefile 0.09%
cors dependency-injection openapi plugin sanic

sanic-ext's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

sanic-ext's Issues

[Bug]Parameter description not included in openapi.json

Describe the bug
When using Parameter class to describe a method parameter, the description is not included in the resulting openapi.json

To reproduce
The following code tests 3 ways of documenting parameters, the output is almost identical except that test2 is missing the description in the output

@app.route('/test1/<val1>')
async def get_test1(request, val1: int):
    """
    openapi:
    ---
    operationId: get.test1
    parameters:
      - name: val1
        in: path
        description: val1 path param
        required: true
        schema:
          type: integer
          format: int32
    """
    return HTTPResponse("ok")

@app.route('/test2/<val1>')
@openapi.parameter(parameter=Parameter(name='val1', schema=int, location='path', description='val1 path param'))
async def get_test2(request, val1: int):
    return HTTPResponse("ok")

@app.route('/test3/<val1>')
@openapi.parameter('val1', description='val1 path param', required=True, schema=int)
async def get_test3(request, val1: int):
    return HTTPResponse("ok")

Expected behavior
The description part of Parameter should be included in the openapi.json output

Environment (please complete the following information):
OS: Windows 10
Python: 3.10
Sanic: 21.9.3
sanic-ext: 21.9.3

[Bug] Static and Class methods are added to the example request bodies

Describe the bug
Class / static methods are included in the example request body.

To Reproduce
Create a dataclass add a class/static method to it and set it to a requestbody with @openapi.body({"application/json": Class})

Expected behavior
We pass an instance of a class so class and static methods should not be in the documentation.

Environment (please complete the following information):

  • OS: Pop OS 22.04
  • Browser curl
  • Version 22.3.1

Swagger UI's stylesheets aren't loaded

Describe the bug
When accessing the /docs/swagger route, a blank page is loaded. It seems to be due to the defined stylesheets no longer existing.

Screenshots
Swagger UI blank page

To Reproduce
Load sanic-ext into your Sanic app and access the Swagger UI route.

Expected behavior
The Swagger UI is displayed.

Environment (please complete the following information):

  • OS: Windows 10 Home 21H1
  • Browser Mozilla Firefox
  • Version: 94.0.1 64-bit

[Bug] Dataclass Optional List fails

Dataclass Optional List fails with TypeError: Value '<factory>' must be a typing.List[str]

#! /usr/bin/env python3
import json
from dataclasses import field, dataclass
from http import HTTPStatus
from typing import Optional, List

from sanic import Sanic
from sanic.response import empty
from sanic_ext import validate

app = Sanic(name='name')


@dataclass
class SearchRequest:
    names: Optional[List[str]] = field(default_factory=list)


# Proving that the dataclass is valid.
SearchRequest()
SearchRequest(names=['foo', 'bar'])


@app.post('/search')
@validate(json=SearchRequest)
def search(_, body: SearchRequest):
    print(body.names)
    return empty()


# This request succeeds with 204.
_, response = app.test_client.post('/search', content=json.dumps({'names': ['foo', 'bar']}))
assert response.status_code == HTTPStatus.NO_CONTENT, response.status
# This request fails with 400.
_, response = app.test_client.post('/search', content=json.dumps({}))
assert response.status_code == HTTPStatus.NO_CONTENT, response.status
[2022-01-13 09:19:30 -0700] [8488] [INFO] 
  ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
  │                                          Sanic v21.12.1                                          │
  │                           Goin' Fast @ ('127.0.0.1', 50961) http://...                           │
  ├───────────────────────┬──────────────────────────────────────────────────────────────────────────┤
  │                       │     mode: production, single worker                                      │
  │     ▄███ █████ ██     │   server: sanic                                                          │
  │    ██                 │   python: 3.9.5                                                          │
  │     ▀███████ ███▄     │ platform: Linux-4.19.0-18-amd64-x86_64-with-glibc2.28                    │
  │                 ██    │ packages: sanic-routing==0.7.2, sanic-testing==0.8.2, sanic-ext==21.12.1 │
  │    ████ ████████▀     │                                                                          │
  │                       │                                                                          │
  │ Build Fast. Run Fast. │                                                                          │
  └───────────────────────┴──────────────────────────────────────────────────────────────────────────┘

[2022-01-13 09:19:30 -0700] [8488] [WARNING] Sanic is running in PRODUCTION mode. Consider using '--debug' or '--dev' while actively developing your application.
[2022-01-13 09:19:30 -0700] [8488] [INFO] Sanic Extensions:
[2022-01-13 09:19:30 -0700] [8488] [INFO]   > http 
[2022-01-13 09:19:30 -0700] [8488] [INFO]   > openapi [http://('127.0.0.1', 50961) http://.../docs]
[2022-01-13 09:19:30 -0700] [8488] [INFO]   > injection [0]
[2022-01-13 09:19:30 -0700] [8488] [INFO] http://127.0.0.1:50961/search
['foo', 'bar']
[2022-01-13 09:19:30 -0700] - (sanic.access)[INFO][127.0.0.1:59530]: POST http://127.0.0.1:50961/search  204 0
[2022-01-13 09:19:30 -0700] [8488] [INFO] Starting worker [8488]
[2022-01-13 09:19:30 -0700] [8488] [INFO] Stopping worker [8488]
[2022-01-13 09:19:30 -0700] [8488] [INFO] Server Stopped
[2022-01-13 09:19:30 -0700] [8488] [INFO] 
  ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
  │                                          Sanic v21.12.1                                          │
  │                               Goin' Fast @ http://127.0.0.1:50961                                │
  ├───────────────────────┬──────────────────────────────────────────────────────────────────────────┤
  │                       │     mode: production, single worker                                      │
  │     ▄███ █████ ██     │   server: sanic                                                          │
  │    ██                 │   python: 3.9.5                                                          │
  │     ▀███████ ███▄     │ platform: Linux-4.19.0-18-amd64-x86_64-with-glibc2.28                    │
  │                 ██    │ packages: sanic-routing==0.7.2, sanic-testing==0.8.2, sanic-ext==21.12.1 │
  │    ████ ████████▀     │                                                                          │
  │                       │                                                                          │
  │ Build Fast. Run Fast. │                                                                          │
  └───────────────────────┴──────────────────────────────────────────────────────────────────────────┘

[2022-01-13 09:19:30 -0700] [8488] [WARNING] Sanic is running in PRODUCTION mode. Consider using '--debug' or '--dev' while actively developing your application.
[2022-01-13 09:19:30 -0700] [8488] [INFO] Sanic Extensions:
[2022-01-13 09:19:30 -0700] [8488] [INFO]   > http 
[2022-01-13 09:19:30 -0700] [8488] [INFO]   > openapi [http://127.0.0.1:50961/docs]
[2022-01-13 09:19:30 -0700] [8488] [INFO]   > injection [0]
[2022-01-13 09:19:30 -0700] [8488] [INFO] http://127.0.0.1:50961/search
[2022-01-13 09:19:30 -0700] [8488] [ERROR] Exception occurred while handling uri: 'http://127.0.0.1:50961/search'
Traceback (most recent call last):
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/check.py", line 93, in check_data
    hydration_values[key] = hint.validate(
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/check.py", line 46, in validate
    _check_nullability(value, self.nullable, self.allowed, schema)
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/check.py", line 120, in _check_nullability
    allowed[0].validate(value, schema)
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/check.py", line 58, in validate
    value = _check_list(
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/check.py", line 145, in _check_list
    raise ValueError(f"Value '{value}' must be a {hint}")
ValueError: Value '<factory>' must be a typing.List[str]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/validators.py", line 24, in validate_body
    return validator(model, body)
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/validators.py", line 36, in _validate_annotations
    return check_data(model, body, schema, allow_multiple, allow_coerce)
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/check.py", line 100, in check_data
    raise TypeError(e)
TypeError: Value '<factory>' must be a typing.List[str]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "handle_request", line 83, in handle_request
    )
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/decorator.py", line 37, in decorated_function
    await do_validation(
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/setup.py", line 41, in do_validation
    validation = validate_body(validator, model, data)
  File "/home/user/projects/project/venv/lib/python3.9/site-packages/sanic_ext/extras/validation/validators.py", line 26, in validate_body
    raise ValidationError(
sanic_ext.exceptions.ValidationError: Invalid request body: SearchRequest. Error: Value '<factory>' must be a typing.List[str]
[2022-01-13 09:19:30 -0700] - (sanic.access)[INFO][127.0.0.1:59532]: POST http://127.0.0.1:50961/search  400 796
[2022-01-13 09:19:30 -0700] [8488] [INFO] Starting worker [8488]
[2022-01-13 09:19:30 -0700] [8488] [INFO] Stopping worker [8488]
[2022-01-13 09:19:30 -0700] [8488] [INFO] Server Stopped
Traceback (most recent call last):
  File "/home/user/.config/JetBrains/PyCharm2021.3/scratches/scratch_1.py", line 36, in <module>
    assert response.status_code == HTTPStatus.NO_CONTENT, response.status
AssertionError: 400

To Reproduce
Run the script provided above.

Expected behavior
I would expect that the body param would be a None or maybe an empty dict.

Environment (please complete the following information):

  • OS: Debian
  • Browser N/A
  • Version sanic==21.12.1 ext==21.12.1

[Bug] Typing errors when using `openapi` types

Describe the bug
When defining a parameter and using a type such as openapi.Integer(), typing checks fail because there's no available overload that matches.

Screenshots
Typing check error

To Reproduce

from sanic_ext import openapi
 
...

@app.route("/")
@openapi.parameter("scope", openapi.Integer())
async def do_stuff(*_, **_):
    ...

Expected behavior
There's no typing errors.

Environment (please complete the following information):

  • OS: Windows 10 Home 21H1
  • Browser: N/A
  • Version: sanic-ext 22.1.2

Additional context
N/A

Typing a response attribute as optional marks it as `object`

Describe the bug
When setting a TypedDict as a response value, if it has fields marked as Optional, they will be displayed as type object.

Screenshots
ReDoc screenshot showing error

To Reproduce

from typing import TypedDict

class AutonomousSystem(TypedDict):
    asn: str
    description: Optional[str]
    organisation: Optional[str]
    company: Optional[str]
    scope: int
@openapi.response(200, {"application/json": List[AutonomousSystem]})

Expected behavior
The field is shown as nullable, not object.

Environment (please complete the following information):

  • Python 3.9, sanic-ext 21.9.1

[Bug] `sanic-ext` seems to be instantiated twice when running in debug mode

Describe the bug
When the Sanic app is ran with debug=True, it seems like sanic-ext is being instantiated twice (maybe due to the reloader?).

Screenshots
Debug mode:
Debug mode on

Non-debug mode:
Debug mode off

To Reproduce
Create a Sanic app, load sanic-ext into it and run it using debug mode.

Expected behavior
Only one instance of sanic-ext is instantiated, or at least it's only logged once

Environment (please complete the following information):

  • OS: Windows 10 Home 21H1
  • Browser: N/A
  • Version: Sanic-Ext 21.9.3; Sanic 21.9.3; Routing 0.7.2

Additional context
N/A

[Bug] direct set app.ext.config not working

Describe the bug
set template search pathes via

app.config.TEMPLATING_PATH_TO_TEMPLATES = xxx
# OR
app.ext.config.TEMPLATING_PATH_TO_TEMPLATES = xxx

not working, sanic just use default templates dir
exception log

Traceback (most recent call last):
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 843, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/chendx/xxx/server/__init__.py", line 1, in <module>
    from .app import PigaiZiApp
  File "/Users/chendx/xxx/server/app.py", line 2, in <module>
    from .demo_handler import *
  File "/Users/chendx/xxx/server/demo_handler.py", line 8, in <module>
    @app.ext.template('demo.jinja2')
  File "/Users/chendx/anaconda3/envs/pigai/lib/python3.8/site-packages/sanic_ext/bootstrap.py", line 151, in template
    return self.templating.template(template_name, **kwargs)
  File "/Users/chendx/anaconda3/envs/pigai/lib/python3.8/site-packages/sanic_ext/extensions/templating/engine.py", line 30, in template
    template = self.environment.get_template(file_name)
  File "/Users/chendx/anaconda3/envs/pigai/lib/python3.8/site-packages/jinja2/environment.py", line 883, in get_template
    return self._load_template(name, self.make_globals(globals))
  File "/Users/chendx/anaconda3/envs/pigai/lib/python3.8/site-packages/jinja2/environment.py", line 857, in _load_template
    template = self.loader.load(self, name, globals)
  File "/Users/chendx/anaconda3/envs/pigai/lib/python3.8/site-packages/jinja2/loaders.py", line 115, in load
    source, filename, uptodate = self.get_source(environment, name)
  File "/Users/chendx/anaconda3/envs/pigai/lib/python3.8/site-packages/jinja2/loaders.py", line 197, in get_source
    raise TemplateNotFound(template)
jinja2.exceptions.TemplateNotFound: demo.jinja2

Additional context
I check the sanic_ext source code, before custom config been set, the template extend has been inited.

[Feature Request] OpenAPI support custom openapi file

Is your feature request related to a problem? Please describe your use case.

I want to give my openapi.yaml to swagger ui and find that i can't do this by modify config options(UI | Sanic Framework).

I find in

url: "__URL_PREFIX__/openapi.json",

it only support modify prefix and can't modify suffix openapi.json.

Describe the solution you'd like

Maybe in

url: "__URL_PREFIX__/openapi.json",
can be

url: "__URL_PREFIX__/__OPENAPI_FILE__",

And i can set OAS_OPENAPI_FILE=src/resource/openapi.yaml to make swagger ui default load my openapi.yaml

Additional context

Broken circular injection detection

The current circular detection algorithm is flawed in that it does not account for the following, which should be allowable.

class A:
    ...


class B:
    def __init__(self, a: A):
        self.a = a


class C:
    def __init__(self, a: A):
        self.a = a


class D:
    def __init__(self, b: B, c: C):
        self.b = b
        self.c = c


def make_a(_):
    return A()


def make_b(_, a: A):
    return B(a)


def make_c(_, a: A):
    return C(a)


def make_d(_, b: B, c: C):
    return D(b, c)

app.ext.add_dependency(A, make_a)
app.ext.add_dependency(B, make_b)
app.ext.add_dependency(C, make_c)
app.ext.add_dependency(D, make_d)

[Bug] [openapi] incorrect behaviour with tortoise-generated pydantic models in documentation

Describe the bug
Pydantic models have a config member that is obviously not meant included in a request.
As of sanic-ext 22.6.1 the vanilla for manually defined pydantic models it is left out. However when using tortoise-orm's pydantic_model_creator to generate the models, the Config field gets left in and special objects like enums are ignored.

Screenshots
pydantic_model_creator
image

manual model
image

To Reproduce
pydantic model creator

from tortoise.contrib.pydantic import pydantic_model_creator
from models import Customer

model = pydantic_model_creator(Customer, name="customer_in", exclude_readonly=True) 
from pydantic import BaseModel
from models import Gender

class CustomerIn(BaseModel):
    name: str
    email: str
    gender: Gender
    ....

model = CustomerIn

Attach them both to a route and see the differences.

Expected behavior
These two methods need to provide the same outcome.

Environment (please complete the following information):

  • OS: Fedora 36
  • Browser firefox
  • Version 22.6.1

Additional context
I'm using the latest stable pydantic and tortoise-orm versions from pypi.

[Bug] OpenAPI ExternalDocumentation Object Bug

app = Sanic("test")

@app.route("/test1")
@openapi.document(
    ExternalDocumentation("http://example.com/mor1111", "Find more info her111e")
)
async def handler1(request: Request):
    return text("ok")

@app.route("/test2")
async def handler2(request: Request):
    """
    openapi:
    ---
    summary: This is a summary.
    externalDocs:
        description: Find more info here
        url: http://example.com/more
    """
    return text("ok")

app.run()

The externalDocs field of path /test1 is incorrect:

{
    "openapi": "3.0.0",
    "info": {
        "title": "API",
        "version": "1.0.0",
        "contact": {}
    },
    "paths": {
        "\/test1": {
            "get": {
                "operationId": "get~handler1",
                "summary": "Handler1",
                "externalDocs": {
                    "url": {
                        "url": "http:\/\/example.com\/mor1111",
                        "description": "Find more info her111e"
                    },
                    "description": null
                },
                "responses": {
                    "default": {
                        "description": "OK"
                    }
                }
            }
        },
        "\/test2": {
            "get": {
                "operationId": "get~handler2",
                "summary": "This is a summary.",
                "externalDocs": {
                    "description": "Find more info here",
                    "url": "http:\/\/example.com\/more"
                },
                "responses": {
                    "default": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "tags": [],
    "servers": [],
    "security": []
}

[Bug] Dependency injection fails when multiple routes share the same name

Describe the bug

If multiple route handlers have the same name, sanic-ext will treat them as one route during dependency injection, causing a HTTP 500 because passed kwargs does not correspond to the function signature.

Screenshots

image

To Reproduce

Consider this code:

from sanic import Sanic, response

app = Sanic('test')

class Object:
    pass
    
app.ext.add_dependency(Object, lambda req: None)

@app.route('/foo')
def handler(req, foo: Object):
    return response.text('foo')
    
@app.route('/bar')
def handler(req, bar: Object):
    return response.text('bar')
    
app.run(debug=True)

Then visit /foo. It shows a HTTP 500 error:

Traceback (most recent call last):
  File "handle_request", line 81, in handle_request
    error_logger,
TypeError: handler() got an unexpected keyword argument 'bar'

Expected behavior

I'm not sure whether it is discouraged to use a same name (handler in the example above) across different route handlers.

I believe sanic-ext should either support this or warn users about this caveat.

Environment (please complete the following information):

  • OS: Windows 10 (19044.1466)
  • Browser: not related
  • Version: sanic 21.12.1, sanic-ext 22.1.2

Additional context

Maybe the signature_registry should use something else as the key.

[Bug] How to document sanic_jwt routes using swagger?

Hello Sanic Developers. I'm creating an API using sanic_jwt to make a user authentication. I've already documented some custom routes, but I want to write the /auth routes provided by sanic_jwt. Is it possible doing that using an external document? If so, how can I do that?

Remove external dependency

The swagger page generated by openapi extension depends on pagecdn.io/lib/swagger-ui/latest/. My application will be deployed in an environment where it will not be possible to resolve this dependency at runtime.

Please make the source for these resources configurable with pagecdn.io/lib/swagger-ui/latest/ as default. This will allow me to serve them from the application itself or a server nearby.

[Feature Request] If `API_SCHEMES` is defined in config and is not a list, try to convert it into one

Is your feature request related to a problem? Please describe your use case.
Currently the config value for API_SCHEMES is interpreted as-is but is used as an iterable, which makes it hard to use when it is defined using environment variables, as these are always strings.

for scheme in getattr(app.config, "API_SCHEMES", ["http"]):

Describe the solution you'd like
It would be nice if, in cases in which it is a string, it would split the string automatically (maybe in ,) to convert it into a list.

Additional context
N/A.

[Bug] Templating library has not been initialized when first requests are being served.

Describe the bug
Templating middleware initializes request context in after_server_start handler however
at this point the server is already serving requests.

Screenshots

Traceback (most recent call last):
  File "handle_request", line 62, in handle_request
    SanicException,
  File "_run_request_middleware", line 28, in _run_request_middleware
    AnyStr,
  File "/app/.venv/lib/python3.10/site-packages/sanic_ext/extensions/templating/extension.py", line 50, in attach_request
    request.app.ctx.__request__.set(request)
AttributeError: 'types.SimpleNamespace' object has no attribute '__request__'

To Reproduce
Start the Sanic server and immediately make a request.

Expected behavior
When the server is ready to handle requests the context variable should already be set.

Environment
Google Cloud Platform / Cloud Run / docker image: python:3.10-slim-bullseye
Dependencies handled by Poetry

Jinja2 = "^3.1.2"
sanic = { version = "^22.3.2", extras = ["ext"] }

Additional context
__request__ is not a good name for this, it implies that it is a magic variable typically belonging to Python itself, PEP8 suggests never inventing new ones.
I am guessing the intent was name mangling, but that only works on class attributes when prefixed with two underscores.

Load the context with before_server_start handler instead?
On closer inspection it does not appear that __request__ is used at all and this whole initialization chain can be stripped out?

Mitigations

# I need templates: Reach into the `app._future_middleware` and fish out the broken function.
>>> app._future_middleware[0].middleware.__module__
'sanic_ext.extensions.templating.extension'

# I don't need templates: Initializing extensions manually without builtins, then picking the modules used.
>>> app.extend(built_in_extensions=False, extensions=[HTTPExtension, OpenAPIExtension])

The openapi.definition decorator struggles in a sync context

The openapi.definition decorator with a definitions.RequestBody instance or a non-empty dictionary as the body parameter transforms the target function to a coroutine.

For example, in my case, I'm using the decorator in a sync view...

@bp.get("/top/tracks")
@openapi.definition(
    body=oa.RequestBody(serializers.TopTracksRequest, required=True),
    summary="Fetch top tracks",
    response=[
        oa.Response(serializers.TopTracksResponse),
        oa.Response(
            Error, status=404, description="not found. note: sync is required."
        ),
        oa.Response(Error, status=400, description="bad request"),
    ],
)
@authenticate
def fetch_top_tracks(request):
    ...

... that uses the Django ORM to retrieve data, and the only way I have to avoid the Django async safety check is to remove the definitions.RequestBody definition as a body (or perform an ugly workaround).

image
The code block that triggers the view actually detects the function as a coroutine and calls it as such

When I remove the body parameter, everything works as expected and the view is not triggered as a coroutine.

@bp.get("/top/tracks")
@openapi.definition(
    #body=oa.RequestBody(serializers.TopTracksRequest, required=True),
    summary="Fetch top tracks",
    response=[
        oa.Response(serializers.TopTracksResponse),
        oa.Response(
            Error, status=404, description="not found. note: sync is required."
        ),
        oa.Response(Error, status=400, description="bad request"),
    ],
)
@authenticate
def fetch_top_tracks(request):
    ...

I don't know if this behaviour is expected, my guess is that it has something to do with this check. Maybe it could be useful to add fully sync context support to this decorator, or add a flag to avoid explicitly this kind of behaviour.

Maybe it's totally expected, I'm missing the reasoning behind it and the body parameter is not expected to be used in a sync context (or needs some unknown -by me- configuration).

Anyway, if the behaviour is expected, I would love to know the reasoning behind it. Also, it could be useful to have it in the documentation since I couldn't understand what was happening easily. If it's already in the documentation and I couldn't find it; Send me a link! 😉

Thanks a lot!
Loving Sanic ❤️

[Bug] Not correct openapi json for ReDoc with dynamically generated classes

Hello. I make class views dynamically from SA models for REST api. And in ReDoc all summary for methods like last class.

bug_2022-04-27_10-18-48

Class view generate like this:

....................
views.append(type(f"VI_{name}",
                               (HTTPMethodView,),
                               {'model': model, 
                               '_url': f'/{name}/<id:int>',
                               'get': op.summary(f"Get obj {name}")(viewItem_get),
                               'put': op.summary(f"Update obj {name}")(viewItem_put),
                               'patch': op.summary(f"Change obj {name}")(viewItem_patch),
                               'delete': op.summary(f"Delete obj {name}")(viewItem_delete)}))

......................... 
for view in views:
    blueprint.add_route(view.as_view(), view._url)

I find way to generate custom summary and all openapi fields for every my dynamic class-view.

I use Manjaro + firefox. Sanic v22.3.1 and sanic-ext==22.3.1

I try deepcopy, copy all objects and copy func-methods for set unique names for every gen but it not help all.

[Bug] Authorization header specification using @openapi.parameter is not included Swagger UI request

Describe the bug
Authorization header specification using @openapi.parameter is not included Swagger UI request.

The UI shows the Input field, but the CURL example does not include the header and the request made does not include the header either.

Screenshots
The screenshots of Swagger UI if this bug is related to it.
image

To Reproduce

from sanic.response import text
from sanic_ext import Extend
from sanic_ext.extensions.openapi import openapi

app = Sanic("MyHelloWorldApp")
Extend(app)

@app.get("/")
@openapi.parameter("Authorization", str, "header", required=True)
async def hello_world(request):
    return text("Hello, world.")


if __name__ == "__main__":
    app.run(
        host="0.0.0.0",
        port=8002,
        auto_reload=True,
    )

Expected behavior
I expect receive

curl -X 'GET' \
  'http://localhost:8002/ \
  -H 'accept: application/json' \
  -H 'Authorization: token123'

Environment:

  • OS: MacOS
  • Browser chrome
  • Version sanic==21.9.3, sanic-ext==21.9.3

[Feature Request]

Hi,

Just testing templating module, it looks great, but one feature is terrible, by default autoescape of Jinja2 is ON and lots of symbols is escaped. solution is simple:
app.ext.environment.autoescape = False

I use url_for() function in Flask and it' will be good to add it to templating

Thanks

`openapi.Parameter` location is ignored when defined from the `openapi.definition` decorator

Describe the bug
When a parameter has a defined location such as "path" or "header" and is defined using the openapi.definition decorator, the location seems to be ignored and is shown as "query".

Screenshots
SwaggerUI example

To Reproduce
Use the decorator like so:

@openapi.definition(
    parameter=[
        openapi.Parameter("enterprise", str, "path"),
    ],
)

Expected behavior
The parameter is marked as whatever type it's set to, and isn't overriden by the fallback value.

Environment (please complete the following information):

  • Version 21.9.1

[Feature Request] Please allow validator decorator to use attrs class for parameter validation

Is your feature request related to a problem? Please describe your use case.
I am actively using attrs across my applications and I believe that with the speed, versatility, and functionality, attrs is a must-have addition to the validator decorator. Currently, validator supports pydantic, dataclasses, and custom implementation, but it would be really great to see attrs on this list.

Thank you very much.

Here's a simple example of how the class with attrs would look like:

import attrs

@attrs.define
class AttrsParameterValidator:
    parameter_a: str
    parameter_b: int
    parameter_c: list

Please find documentation for attrs here: https://www.attrs.org/en/stable/

Describe the solution you'd like
Same behavior as pydantic or dataclasses, but for attrs.

Additional context
N/A

[Bug] Dataclass optional not working in validate query

Describe the bug
I'm following this tutorial https://sanic.dev/en/plugins/sanic-ext/validation.html#implementation .
with define dataclass attribute with optional paramater,
it will return 400 , if the query params q have value ex q=123123
without value it will work without problem

I also found the same issue when using pydantic

Screenshots
Screen Shot 2022-07-05 at 16 21 59

To Reproduce

from dataclasses import dataclass, asdict
from typing import Optional

from sanic import Sanic
from sanic.response import json
from sanic_ext import validate


app = Sanic("Sanic-APP")


@dataclass
class SearchParams:
    q: Optional[str] = None


@app.route("/")
@validate(query=SearchParams)
async def handler(request, query: SearchParams):
    return json(asdict(query))

Expected behavior
when the dataclass attribute is Optional, it should not error when we send the query params with value ex ?q=123456

Environment (please complete the following information):

  • OS: Mac Catalina v10.15.7
  • Browser Chrome
  • Version 103.0.5060.53

[Bug] @validate decorator doesn't work for Class-Based Views #2483

Describe the bug
While using the @validate decorator on a class-based view, I get the following exception:

[...]python/site-packages/sanic_ext/extras/validation/decorator.py", line 39, in decorated_function
    data=request.json,
AttributeError: 'SomeResource' object has no attribute 'json'

And this is how my Class Based view look like:

class SomeResource(HTTPMethodView):
    @validate(json=ResourceSchema)
    async def post(self, request):
        pass

Additional Notes
Diving quickly into the code of the decorator, it seems like we are not handling the class case of the views, where the first argument of the method is the class instance and not the request instance (like in the function-based view):

async def decorated_function(request: Request, *args, **kwargs):

I think a solution for that can be to use the kwargs rather than the position, but I am not too familiar with the codebase just yet, so there might be a more suitable solution for this repo.

OpenAPI extension parses dataclass validators as body

#from pydantic.dataclasses import dataclass
from pydantic.dataclasses import dataclass

@dataclass
class RegisterBody:
    name: str
    email: str
    password: str

    @validator('name')
    def validate_name(cls, v):
        if len(v) < 5:
            raise ValueError('name must be at leasth 5 characters')

@app.post("/register")
@openapi.body({"application/json": RegisterBody})
@validate(json=RegisterBody)
async def register(request, body):
     return json({})

The code above produces the OpenAPI /docs page to populate a validator_name field in the Swagger UI, while for the actual mode it is not a field. Tried with the builting python dataclasses and the Pydantic wrapper too.

image

Sanic v22.3.2
Sanic-ext v22.3.2
Arch Linux x86_64
Python 3.10.4

[Bug] Dependency injection not works with inherited request class

Describe the bug
Injector does not recognize the inherited request class as Sanic request class.

Screenshots
Not applicable

To Reproduce

from sanic import Sanic, Request as SanicRequest


class InheritedRequest(SanicRequest):
    ...


app = Sanic("TEST_SERVER", request_class=InheritedRequest)


class Something:
    async def execute(self):
        ...

async def get_something(request: InheritedRequest):
    return Something


app.ext.add_dependency(Something, get_something)

app.run(port=5000, dev=True)

Expected behavior
The injector should skip the request parameter, so that the error not occurs.

Environment (please complete the following information):

  • OS: Windows 10
  • Version: Python 3.10 / Sanic 22.3.2 / Sanic-ext 22.3.2

Additional context

  File "C:\Users\<truncated>\lib\site-packages\sanic_ext\extensions\injection\injector.py", line 31, in finalize_injections
    injection_registry.finalize(router_types)
  File "C:\Users\<truncated>\lib\site-packages\sanic_ext\extensions\injection\registry.py", line 32, in finalize
    constructor.prepare(self, allowed_types)
  File "C:\Users\<truncated>\lib\site-packages\sanic_ext\extensions\injection\constructor.py", line 81, in prepare
    raise InitError(
sanic_ext.exceptions.InitError: Unable to resolve dependencies for 'get_something'. Could not find the following dependencies:
  - request: <class '__main__.InheritedRequest'>.
Make sure the dependencies are declared using ext.injection. See https://sanicframework.org/en/plugins/sanic-ext/injection.html#injecting-services for more details.

[Bug] Can't use Parameter as an object anymore ?

Describe the bug
In my Sanic application, I declare all my parameters once as they are used accross many endpoints. With last sanic-ext, I have this error:
AttributeError: module 'sanic_ext.extensions.openapi.openapi' has no attribute 'Parameter'. Did you mean: 'parameter'?

To Reproduce
Simple code to reproduce

from sanic import Sanic
from sanic.response import text
from sanic_ext import openapi

app = Sanic("MyHelloWorldApp")
my_parameter = openapi.Parameter()


@app.get("/test1")
@openapi.parameter(my_parameter)
async def test1(request):
    return text("test1")


@app.get("/test2")
@openapi.parameter(my_parameter)
async def test2(request):
    return text("test2")

Additional context
If this is expected behaviour, please fix the doc also as this page states

@openapi.parameter(parameter=Parameter("foobar", deprecated=True))

Edit: broken change was introduced w/ 22.1.0

[Bug] JSON validation with dataclass model does not works with list

Describe the bug
I tried to use the sanic-ext validation to validate a complex json body without pydantic, and occour error. Using the pydantic model works.

To Reproduce
Validate like this example of models, using built-in list keyword:

@dataclass
class User:
    id: int
    name: str
    friends: list[Friend]

@dataclass
class Friend:
    name: str

Expected behavior
Validate normally.

Environment (please complete the following information):

  • OS: Endeavour OS Linux

Additional context
Like @ahopkins said, maybe this is a bug related to 'list' keyword that is builtin in more recent Python versions. Using the List type imported of typing module appears to work as expected.
https://discord.com/channels/812221182594121728/872787982741032971/898279553288523807

Running multiple instance of in one heap causes OPENAPI specification to build properly [Bug]

Describe the bug
Starting two instances of sanic (on different ports) using create_server with different routes assigned to them. The API specification will merge all routes together

Expected behavior
The specification builder should use the request.app to determine which routes are applicable for the specification to be used, and only show those routes.

Additional context
As seen here The specification is built from a singleton, if this is in the same heap as the main application then all routes are merged together.

[Bug] Openapi <str> dynamic path parameters displayed as <object>

Describe the bug
Both of the following path setting give the same result on the screenshot
app.add_route(get_one_point_from_tif, "/tif/<method:str>/<bands:int>/<x:int>/<y:int>", ["GET"])
app.add_route(get_one_point_from_tif, "/tif/<method>/<bands:int>/<x:int>/<y:int>", ["GET"])

Screenshots
image

[Question] optional fields in openapi body from dataclass vs validate

Description

Not sure if it's related to sanic-ext at all or just missing understanding of dataclasses on my side ;)

I'm trying to describe and validate a json body w/ optional parameters.

from dataclasses import dataclass
from typing import List, Optional
from sanic import Sanic
from sanic.response import json
from sanic_ext import openapi, validate


app = Sanic("Test")


@dataclass
class Test:
    foo: str
    bar: int
    foobar: Optional[List[str]]


@app.post("/")
@openapi.body({"application/json": Test})
@validate(Test)                                                                                                                                               
async def test(request, body: Test):
    return json({"message": "Hello world!"})

This renders as request body describing foobar as object or nullable.

Screenshot from 2022-01-12 09-50-17

Validation now fails when foobar is omitted (correct so far)

$ curl -d '{"foo": "string", "bar": 1337}' http://127.0.0.1:8000/
{"description":"Bad Request","status":400,"message":"Invalid request body: Test. Error: missing a required argument: 'foobar'"}

Now, if i set a default for foobar like

@dataclass
class Test:
    foo: str
    bar: int
    foobar: Optional[List[str]] = None

the request works as expected:

$ curl -d '{"foo": "string", "bar": 1337}' http://127.0.0.1:8000/
{"message":"Hello world!"}

But now API docs renders as object without nullable:

Screenshot from 2022-01-12 09-59-56

Question

How can i combine the two? Show foobar as nullable in docs and pass validation w/ parameter omitted?

Do i need 2 seperate dataclasses?

Also, is it possible to show foobar as list of strings in the docs instead of object (derives from Optional, i guess).

Versions

  • sanic 21.12.0
  • sanic-ext 21.12.3

[Bug] Can't change the openapi endpoint url due to hard coding in html

Describe the bug
Setting OAS_URL_PREFIX to anything other than /docs causes redoc and swagger to fail, because the html looks for /docs/openapi.json (and swagger-config for swagger)

Screenshots
image
image

To Reproduce
Initialize Sanic-ext like this:

cfg = Config(oas_ui_default="swagger", oas_url_prefix='/swagger')
Extend(app, config=cfg)

Expected behavior
Swagger UI functions at hostname/swagger

Environment (please complete the following information):

  • OS: Windows
  • Browser Chrome
  • Version 21.9.3 (really pip install -e /main from this repo)

Additional context
This is fixed in a branch in my fork. Let me know if you want a PR. I also fixed / enhanced with the ability to use the root as the openapi endpoint. Tested with redoc and swagger with the default of no prefix set (so default of /docs) and url prefixes ['/docs', '/swagger', '']. I only tested with the base of my installed sanic, which is 21.9.1
rocknet@ec1f277

`TypeError` when setting `tag` with `openapi.definition` decorator

Describe the bug
Using the openapi.definition decorator to set tag raises an exception.

Screenshots
Traceback

To Reproduce
Use the decorator like so:

@openapi.definition(
    tag="Enterprises",
)

Expected behavior
The tag is set for the route just like when using @openapi.tag.

Environment (please complete the following information):

  • Version 21.9.1

[Bug] Dependency injection not working for websocket handlers

Describe the bug

Sanic-Ext fails to inject dependencies to @app.websocket() handlers.

Screenshots

image

To Reproduce

Try this code:

from sanic import Sanic

app = Sanic('test')

class Foo:
    pass
    
app.ext.add_dependency(Foo, lambda req: None)

@app.websocket('/foo')
async def handler(req, ws, foo: Foo):
    pass
    
app.run(port=4321, debug=True)

Visit http://127.0.0.1:4321/, then execute new WebSocket('ws://127.0.0.1:4321/foo') in browser console.

This error will be thrown:

Traceback (most recent call last):
  File "handle_request", line 83, in handle_request
    )
  File "C:\Users\xmcp\AppData\Roaming\Python\Python38\site-packages\sanic\app.py", line 1002, in _websocket_handler
    fut = ensure_future(handler(request, ws, *args, **kwargs))
TypeError: handler() missing 1 required positional argument: 'foo'

Expected behavior

It should not throw an error.

Environment (please complete the following information):

  • OS: Windows 10 (19044.1466)
  • Browser: not related
  • Version: sanic 21.12.1, sanic-ext 22.1.2

Additional context

Sanic wraps a functools.partial to the websocket handler, effectively erasing the type signature of the handler function. Therefore sanic_ext.extensions.injection.injector (screenshot below) cannot get its type hint:

image

[Bug] No CORS information if Origin set in the request

Describe the bug
On an OPTIONS HTTP request, no Access-Control headers is returned if the Origin is set

To Reproduce
Simple "Hello world" server from the documentation pages

from sanic import Sanic
from sanic.response import text
from sanic_ext import Extend
from sanic_cors import CORS

app = Sanic("MyHelloWorldApp")
Extend(app)
app.config.CORS_ORIGINS = "*"


@app.get("/")
async def hello_world(request):
    return text("Hello, world.")


app.run()

On the client side:

[dalexandre@dalexandre ~]$ http OPTIONS :8000 Access-Control-Request-Method:GET 
HTTP/1.1 204 No Content
access-control-allow-headers: *
access-control-allow-methods: HEAD,OPTIONS,GET
access-control-allow-origin: *
access-control-max-age: 5
allow: HEAD,GET,OPTIONS
connection: keep-alive
[dalexandre@dalexandre ~]$ http OPTIONS :8000 Access-Control-Request-Method:GET Origin:test
HTTP/1.1 204 No Content
allow: HEAD,GET,OPTIONS
connection: keep-alive

Expected behavior
Headers have to be returned wether or not the Origin header is set

Environment (please complete the following information):
Sanic 21.9.3 + Sanic-ext 22.1.2

Enabling ext breaks static routing

Describe the bug

I previously used app.static to configure our static files directory. However, after installing ext, GET requests for static files throw 405s and log the following

[2022-01-13 12:04:35 -0500] [11337] [DEBUG] Dispatching signal: http.routing.before
[2022-01-13 12:04:35 -0500] - (sanic.access)[INFO][127.0.0.1:60821]: GET http://0.0.0.0:8000/static/pickadate/picker.js  405 777

Screenshots
N/A

To Reproduce

APP = Sanic(...)

static_directory = os.path.abspath("frontend")
APP.static("/static", static_directory)
APP.static("/", os.path.join(static_directory, "index.html"))

APP.extend(config=Config(oas=False)) 

Expected behavior
GET requests to the /static/ routes return 200s.

Environment (please complete the following information):

  • OS macOS-10.16-x86_64-i386-64bit
  • Browser chrome, firefox
  • Version python==3.9.4, sanic==21.12.1, sanic-routing==0.7.0, sanic-ext==21.12.3

Thank you!

AttributeError: 'getset_descriptor' object has no attribute 'items'

Describe the bug

  • Getting TypeError: build_spec() missing 1 required positional argument: 'loop' upon running the server
  • sanic-ext is installed

Code snippet

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/sanic/app.py", line 1140, in _listener
    maybe_coro = listener(app)  # type: ignore
TypeError: build_spec() missing 1 required positional argument: 'loop'

Additonal Logs

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/usr/local/lib/python3.9/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.9/site-packages/sanic/server/runners.py", line 130, in serve
    loop.run_until_complete(app._server_event("init", "before"))
  File "uvloop/loop.pyx", line 1501, in uvloop.loop.Loop.run_until_complete
  File "/usr/local/lib/python3.9/site-packages/sanic/app.py", line 1581, in _server_event
    await self.dispatch(
  File "/usr/local/lib/python3.9/site-packages/sanic/signals.py", line 193, in dispatch
    return await dispatch
  File "/usr/local/lib/python3.9/site-packages/sanic/signals.py", line 163, in _dispatch
    retval = await maybe_coroutine
  File "/usr/local/lib/python3.9/site-packages/sanic/app.py", line 1142, in _listener
    maybe_coro = listener(app, loop)  # type: ignore
  File "/usr/local/lib/python3.9/site-packages/sanic_ext/extensions/openapi/blueprint.py", line 142, in build_spec
    operation.parameter(
  File "/usr/local/lib/python3.9/site-packages/sanic_ext/extensions/openapi/builders.py", line 99, in parameter
    Parameter.make(name, schema, location, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/sanic_ext/extensions/openapi/definitions.py", line 236, in make
    return Parameter(name, Schema.make(schema), location, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/sanic_ext/extensions/openapi/types.py", line 182, in make
    return Object.make(value, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/sanic_ext/extensions/openapi/types.py", line 279, in make
    {k: Schema.make(v) for k, v in _properties(value).items()},
  File "/usr/local/lib/python3.9/site-packages/sanic_ext/extensions/openapi/types.py", line 319, in _properties
    for k, v in {**get_type_hints(cls), **fields}.items()
  File "/usr/local/lib/python3.9/typing.py", line 1454, in get_type_hints
    for name, value in ann.items():
AttributeError: 'getset_descriptor' object has no attribute 'items'

Environment (please complete the following information):

  • Version [e.g. 22.3.1]

Additional context
Without sanic-ext package, it works fine but then I have CORS issues.

[Bug] Dataclass with Py 3.10 Optional field unable to be fed to @validate()

Describe the bug
When making a dataclass with py 3.10 optional/union syntax, I get a stack trace on server start. I did work around the issue by converting to Optional[str].

To Reproduce

@dataclass
class Comment:
    name: str
    # Note py 3.10 syntax here
    comment: str | None


@api_bp.post("/foo")
@validate(json=Comment)
async def stock_buy(request: Request):
    pass
  File "$ENV/lib/python3.10/site-packages/sanic_ext/extras/validation/decorator.py", line 20, in validate
    schemas = {
  File "$ENV/lib/python3.10/site-packages/sanic_ext/extras/validation/decorator.py", line 21, in <dictcomp>
    key: generate_schema(param)
  File "$ENV/lib/python3.10/site-packages/sanic_ext/extras/validation/setup.py", line 60, in generate_schema
    return make_schema({}, param) if isclass(param) else param
  File "$ENV/lib/python3.10/site-packages/sanic_ext/extras/validation/schema.py", line 37, in make_schema
    make_schema(agg, hint.hint)
  File "$ENV/lib/python3.10/site-packages/sanic_ext/extras/validation/schema.py", line 27, in make_schema
    elif item.__name__ not in agg and is_dataclass(item):
AttributeError: 'types.UnionType' object has no attribute '__name__'. Did you mean: '__ne__'?

Expected behavior
No stacky boi 😄

Environment (please complete the following information):

  • OS: Arch
  • Browser None
  • Version
sanic[ext]==21.12.1
   # via -r requirements.in
sanic-ext==22.1.2
   # via sanic
sanic-routing==0.7.2
   # via sanic

Additional context
Optional[str] works for now!

Allow for a more flexible/customisable injection time throughout a request's lifetime

Is your feature request related to a problem? Please describe your use case.
Currently when a parameter is defined for injection, the injection happens before anything else. This makes it hard for usecases such as ratelimiting or authentication middlewares, as these are executed after the injection. In cases where handlers should be ratelimited to reduce load (on a database, for example) this makes ratelimiting useless, as the database is always hit before the user has a chance to stop the request. Another usecase would be checking for a valid authentication before injecting any parameters.

Describe the solution you'd like
A few possibilities considered:

  • Ability to determine signal at which parameters are injected (new signals?)
  • Add a configuration value to determine whether injection should happen automatically or not (+@no_auto_inject and @inject decorators to determine where injection should occur)
  • Some other solution that you (the reader) can come up with? 🙃

Additional context
N/A

[Bug][openapi] "query" and "path" type parameters can not display properly in the Redoc documentation

Describe the bug

Sorry my english may be very poor.

I have created an API that accepts both "path" and "query" type parameters, then I added OpenAPI YAML docstring under my API function, but the "query" type parameters were not displayed on the Redoc properly.

I also tried to use the openapi.parameter decorators but didn't work too.

Screenshots

image

To Reproduce

this is my route file:

userbp = Blueprint("usermgr")

# 配置url
userbp.add_route(UserView.as_view(), "/user/<pk:strorempty>")

this is my view function's docstring:

openapi:
---
summary: 查看用户信息
description: 查看个人信息, 参数id在path中为可选项, 不传id是查询全部用户, 传id查看指定用户的简要信息
operationId: userget
tags:
  - 标签1
  - 标签2
parameters:
  - pkparameter:
    name: pk
    in: path
    description: 用户唯一标识
    required: false
  - queryparameter:
    name: query
    in: query
    description: 用户对象的查询条件。
    schema:
      type: object
      properties:
        username:
          type: string
          description: 用户名, 使用in查询
  - pageparameter:
    name: page
    in: query
    description: 页码, 默认为1
  - limitparameter:
    name: limit
    in: query
    description: 每页数量限制
  - orderparameter:
    name: order
    in: query
    description: 排序规则
responses:
  '200':
    description: 用户信息
    content:
      application/json:
        schema:
          type: object
          properties:
            code:
              type: string
              description: 返回码
            info:
              type: string
              description: 说明
            data:
              type: array
              items:
                type: object
                properties:
                  pk:
                    type: string
                    description: 用户唯一标识
                  username:
                    type: string
                    description: 用户名
                  headimg:
                    type: string
                    description: 用户头像的base64信息
                  level:
                    type: string
                    description: 用户等级
              description: 用户数据

Environment (please complete the following information):

  • OS: Windows 10
  • Browser firefox
  • Version Sanic v22.3.2, sanic-routing==22.3.0, sanic-ext==22.3.2

Add OpenAPI example factories

Nowadays, the Sanic OpenAPI extension does not permit to add example requests and responses to your documented views.
I suggest to add a field example to definitions.Response, definitions.RequestBody and definitions.Parameter to support OpenAPI examples.


The class definitions.Components references to OpenAPI examples -I guess-, but is unused. Maybe it's a WIP.

I don't know if it's on the roadmap or actually documented (since I read carefully the documentation and the code and I didn't find any traces of this feature, besides in this class) but it could be a cool feature. The thing that, personally, I miss the most about the OpenAPI standard.

I could open a PR too, if need. I'd be glad to contribute to Sanic Extensions. Just need an approval that what I suggest makes sense!

Thanks ❤️

[Feature Request] Render template from string

Is your feature request related to a problem? Please describe your use case.
I'm migrating from Flask to Sanic, and currently I'm using Flask's render_template_string to render a jinja template from a string.
If I understand correctly, Sanic only supports rendering from a file.

Describe the solution you'd like
I would like something like this:

TEAMPLATE = '''
<!DOCTYPE html>
<html>
<head>
  <style>
    
  </style>
</head>
<body>
  <script>
    
  </script>
</body>
</html>'''

rendered = render_from_string(TEMPLATE, context)

Complete OpenAPI Spec in signatures

Describe the bug
Missed the possibility to point out allowed values of path or query string parameters in sanic-ext openapi decorator.
I remember it was possible when openapi docs were settled with @doc decorator. But in @openapi.parameter I don't see any possible keys to set such values. Key kwargs decieves me in this case.

Expected behavior
For example I have a parameter which shall accept an int in range from 0 to 2.
I'd like to point it out in swagger and to make this field accept not any input, but only preset choices: 0, 1 and 2.
So I expect to find in @openapi.parameter a key like allowed_values or something like that:

@openapi.parameter(..., allowed_values=list(range(3)))

Additional question
Is it possible to make swagger fields to be dependant from the value set in other fields?
For example I have a field device with allowed values screen, phone, tablet and a field size.
Each kind of device has its correspondent range of sizes, e.g.: screens: 15''-40'', phone: 5''-8'', tablets: 9''-15''.
Depending on the device value field size shall allow in swagger to choose any size from the correspondent range.

Is it possible?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.