Skip to content

Commit

Permalink
Support latest fastapi >= 0.106.0 (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
k0t3n authored Feb 5, 2024
1 parent 77e56e9 commit d341ba4
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 116 deletions.
212 changes: 120 additions & 92 deletions cachepot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,99 +79,127 @@ def get_request_handler(
actual_response_class = response_class

async def app(request: Request) -> Response:
try:
body: Any = None
if body_field:
if is_body_form:
body = await request.form()
stack = request.scope.get('fastapi_astack')
assert isinstance(stack, AsyncExitStack)
stack.push_async_callback(body.close)
exception_to_reraise: Optional[Exception] = None
response: Union[Response, None] = None
async with AsyncExitStack() as async_exit_stack:
# TODO: remove this scope later, after a few releases
# This scope fastapi_astack is no longer used by FastAPI, kept for
# compatibility, just in case
request.scope['fastapi_astack'] = async_exit_stack
try:
body: Any = None
if body_field:
if is_body_form:
body = await request.form()
async_exit_stack.push_async_callback(body.close)
else:
body_bytes = await request.body()
if body_bytes:
json_body: Any = Undefined
content_type_value = request.headers.get('content-type')
if not content_type_value:
json_body = await request.json()
else:
message = email.message.Message()
message['content-type'] = content_type_value
if message.get_content_maintype() == 'application':
subtype = message.get_content_subtype()
if subtype == 'json' or subtype.endswith('+json'):
json_body = await request.json()
if json_body != Undefined:
body = json_body
else:
body = body_bytes
except json.JSONDecodeError as e:
validation_error = RequestValidationError(
[
{
'type': 'json_invalid',
'loc': ('body', e.pos),
'msg': 'JSON decode error',
'input': {},
'ctx': {'error': e.msg},
}
],
body=e.doc,
)
exception_to_reraise = validation_error
raise validation_error from e
except HTTPException as e:
exception_to_reraise = e
raise
except Exception as e:
http_error = HTTPException(
status_code=400, detail='There was an error parsing the body'
)
exception_to_reraise = http_error
raise http_error from e
try:
solved_result = await solve_dependencies(
request=request,
dependant=dependant,
body=body,
dependency_overrides_provider=dependency_overrides_provider,
async_exit_stack=async_exit_stack,
)
values, errors, background_tasks, sub_response, _ = solved_result
except Exception as e:
exception_to_reraise = e
raise e
if errors:
validation_error = RequestValidationError(
_normalize_errors(errors), body=body
)
exception_to_reraise = validation_error
raise validation_error
else:
if response := await get_cached_response(request, cache_policy):
return response

try:
raw_response = await run_endpoint_function(
dependant=dependant, values=values, is_coroutine=is_coroutine
)
except Exception as e:
exception_to_reraise = e
raise e
if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = background_tasks
response = raw_response
else:
body_bytes = await request.body()
if body_bytes:
json_body: Any = Undefined
content_type_value = request.headers.get('content-type')
if not content_type_value:
json_body = await request.json()
else:
message = email.message.Message()
message['content-type'] = content_type_value
if message.get_content_maintype() == 'application':
subtype = message.get_content_subtype()
if subtype == 'json' or subtype.endswith('+json'):
json_body = await request.json()
if json_body != Undefined:
body = json_body
else:
body = body_bytes
except json.JSONDecodeError as e:
raise RequestValidationError(
[
{
'type': 'json_invalid',
'loc': ('body', e.pos),
'msg': 'JSON decode error',
'input': {},
'ctx': {'error': e.msg},
}
],
body=e.doc,
) from e
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=400, detail='There was an error parsing the body'
) from e
solved_result = await solve_dependencies(
request=request,
dependant=dependant,
body=body,
dependency_overrides_provider=dependency_overrides_provider,
)
values, errors, background_tasks, sub_response, _ = solved_result
if errors:
raise RequestValidationError(_normalize_errors(errors), body=body)

if response := await get_cached_response(request, cache_policy):
return response

# route runtime
raw_response = await run_endpoint_function(
dependant=dependant, values=values, is_coroutine=is_coroutine
)

if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = background_tasks
return raw_response
response_args: Dict[str, Any] = {'background': background_tasks}
# If status_code was set, use it, otherwise use the default from the
# response class, in the case of redirect it's 307
current_status_code = (
status_code if status_code else sub_response.status_code
)
if current_status_code is not None:
response_args['status_code'] = current_status_code
if sub_response.status_code:
response_args['status_code'] = sub_response.status_code
content = await serialize_response(
field=response_field,
response_content=raw_response,
include=response_model_include,
exclude=response_model_exclude,
by_alias=response_model_by_alias,
exclude_unset=response_model_exclude_unset,
exclude_defaults=response_model_exclude_defaults,
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
)
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b''
response.headers.raw.extend(sub_response.headers.raw)

response_args: Dict[str, Any] = {'background': background_tasks}
# If status_code was set, use it, otherwise use the default from the
# response class, in the case of redirect it's 307
current_status_code = (
status_code if status_code else sub_response.status_code
)
if current_status_code is not None:
response_args['status_code'] = current_status_code
if sub_response.status_code:
response_args['status_code'] = sub_response.status_code
content = await serialize_response(
field=response_field,
response_content=raw_response,
include=response_model_include,
exclude=response_model_exclude,
by_alias=response_model_by_alias,
exclude_unset=response_model_exclude_unset,
exclude_defaults=response_model_exclude_defaults,
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
)
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b''
response.headers.raw.extend(sub_response.headers.raw)
# This exception was possibly handled by the dependency but it should
# still bubble up so that the ServerErrorMiddleware can return a 500
# or the ExceptionMiddleware can catch and handle any other exceptions
if exception_to_reraise:
raise exception_to_reraise
assert response is not None, 'An error occurred while generating the request'
return await cache_response(request, response, cache_policy)

return app

45 changes: 22 additions & 23 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ packages = [

[tool.poetry.dependencies]
python = "^3.9"
fastapi = "<1.0.0"
fastapi = ">= 0.106.0"
pydantic = "^2.0"
redis = { version = "^4.0.0", optional = true }

Expand Down

0 comments on commit d341ba4

Please sign in to comment.