Comments (8)
@tomwojcik sorry for the delayed reply. Yep i have just tested the issue is solved in 0.3.5.
Thank you so much! @hhamana
from starlette-context.
I tried it as well, and it looks like I overlooked the exception handling interaction with Depends indeed.
As it currently stands, the context is reset in a try/finally clause, as part of the yield, which causes it to be executed before exception handlers, leading your scenario of invalidated context once it gets there.
It looks like not running it as a try/finally, but resetting it after the yield pushes the execution after the exception handlers, and after background tasks as well, which sounds desirable.
I'll need to investigate a bit further to make sure that kind of fix doesn't introduce unwanted side-effects.
thank you for raising the issue
from starlette-context.
@mancioshell chances are @hhamana figured it out. Their fix has been released in 0.3.5. Could you please confirm the issue has been resolved?
from starlette-context.
How exactly are you "try[ing] to access context inside the custom exception handler"?
Exceptions within a request cycle are still handled with the Depends, so in a regular situation, this should be ok. It will break if, as the message says, you are not actually within a request-response cycle.
Are you by any chance trying to test the exception handler alone without initiating a request?
from starlette-context.
@hhamana thank you for your reply.
This is an example, which reproduce my issue.
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from typing import Dict, Any
from fastapi import Request
from starlette_context import context
from fastapi import FastAPI, Depends, Header
from starlette_context import request_cycle_context
async def my_context_dependency(x_plt_user_id: str = Header(None)) -> Any:
data = {"user-id": x_plt_user_id}
with request_cycle_context(data):
# yield allows it to pass along to the rest of the request
yield
app = FastAPI(
dependencies=[Depends(my_context_dependency)],
title="Test App",
)
@app.exception_handler(Exception)
async def unicorn_exception_handler(request: Request, exc: Exception) -> JSONResponse:
user_id = context.get("user-id", default=None)
print(user_id)
return JSONResponse(
status_code=400,
content={"message": "Bad Request"},
)
@app.get("/")
async def root() -> Dict[str, str]:
raise Exception("Test")
return {"message": "Hello World"}
if __name__ == "__main__":
# Use this for debugging purposes only
import uvicorn
uvicorn.run(
app,
host="0.0.0.0",
port=8080,
log_level="debug",
)
If you run this server and try to do a HTTP GET request on localhost:8080 you should receive the following error:
$ poetry run python src/test.py
INFO: Started server process [13584]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\middleware\errors.py", line 162, in call
await self.app(scope, receive, _send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\exceptions.py", line 93, in call
raise exc
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\exceptions.py", line 82, in call
await self.app(scope, receive, sender)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in call
raise e
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in call
await self.app(scope, receive, send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\routing.py", line 670, in call
await route.handle(scope, receive, send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\routing.py", line 266, in handle
await self.app(scope, receive, send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\routing.py", line 65, in app
response = await func(request)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\fastapi\routing.py", line 227, in app
raw_response = await run_endpoint_function(
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\fastapi\routing.py", line 160, in run_endpoint_function
return await dependant.call(**values)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity\src\pltm021\test.py", line 39, in root
raise Exception("Test")
Exception: Test
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette_context\ctx.py", line 33, in data
return _request_scope_context_storage.get()
LookupError: <ContextVar name='starlette_context' at 0x000002BBC3084CC0>
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 404, in run_asgi
result = await app( # type: ignore[func-returns-value]
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 78, in call
return await self.app(scope, receive, send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\fastapi\applications.py", line 269, in call
await super().call(scope, receive, send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\applications.py", line 124, in call
await self.middleware_stack(scope, receive, send)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette\middleware\errors.py", line 174, in call
response = await self.handler(request, exc)
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity\src\pltm021\test.py", line 27, in unicorn_exception_handler
user_id = context.get("user-id", default=None)
File "C:\Python310\lib_collections_abc.py", line 819, in get
return self[key]
File "C:\Python310\lib\collections_init_.py", line 1102, in getitem
if key in self.data:
File "C:\Users\A409089\Desktop\Git\Room Core\dl00001\glin-pltm021-lan-tm021\repo_structure\src\docker_virtualentity.venv\lib\site-packages\starlette_context\ctx.py", line 35, in data
raise ContextDoesNotExistError
starlette_context.errors.ContextDoesNotExistError: You didn't use the required middleware or you're trying to access context
object outside of the request-response cycle.
INFO: 127.0.0.1:50577 - "GET / HTTP/1.1" 500 Internal Server Error
Python version: 3.10
fastapi = "^0.80.0"
starlette-context = "^0.3.4"
from starlette-context.
I don't really have time to check it myself right now, but IIRC handlers for any custom exception and a generic Exception
won't work the same. Please try to reproduce with another Exception
type.
Refer to
previous ticket about a similar issue #9 (comment)
starlette-context docs https://starlette-context.readthedocs.io/en/latest/middleware.html#errors-and-middlewares-in-starlette
starlette docs https://www.starlette.io/exceptions/#errors-and-handled-exceptions
starlette source that might prevent you from achieving this https://github.com/encode/starlette/blob/040d8c86b09f34be49e8c253d97a588973bc7308/starlette/applications.py#L91-L99
related fastapi ticket tiangolo/fastapi#2750
related fastapi ticket tiangolo/fastapi#2683
I think it'd also make sense to first reproduce with Starlette instead of FastAPI, even though we want to support both. If you can't make it work using Starlette, it won't work with FastAPI.
Again, I assume the reason for missing context there is that this exception handler is run in the outermost middleware and it's not really possible to "make it work differently" (refer to Starlette source), so the previous middleware (starlette-context middleware) already cleaned up after itself. At least that's the case for 500 / Exception.
from starlette-context.
I have also tried with a custom exception, with the same error. I'll reproduce it in the afternoon.
Anyway i''m using starlette context to save specific header values in the context, because i have to log these informations.
My aim is to raise an exception anywhere in my code when something went wrong, and intercept it in fastapi customs exception handler, log the reason of the exception with the current headers informations and fail safe with an appropriate http response.
If are there any other methods to achieve this purpose, wuold be ok for me.
I wouldn't fill my code with a lot of try except and repetitive log methods.
from starlette-context.
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from typing import Dict, Any
from fastapi import Request
from starlette_context import context
from fastapi import FastAPI, Depends, Header
from starlette_context import request_cycle_context
class CustomException(Exception):
def __init__(self, message: str):
self.message = message
super().__init__(self.message)
def __str__(self) -> str:
return f'{self.message}'
async def my_context_dependency(x_plt_user_id: str = Header(None)) -> Any:
data = {"user-id": x_plt_user_id}
with request_cycle_context(data):
# yield allows it to pass along to the rest of the request
yield
app = FastAPI(
dependencies=[Depends(my_context_dependency)],
title="Test App",
)
@app.exception_handler(CustomException)
async def unicorn_exception_handler(request: Request, exc: CustomException) -> JSONResponse:
user_id = context.get("user-id", default=None)
print(user_id)
return JSONResponse(
status_code=400,
content={"message": "Bad Request"},
)
@app.get("/")
async def root() -> Dict[str, str]:
raise CustomException("Test")
return {"message": "Hello World"}
if __name__ == "__main__":
# Use this for debugging purposes only
import uvicorn
uvicorn.run(
app,
host="0.0.0.0",
port=8080,
log_level="debug",
)
Same issue
from starlette-context.
Related Issues (20)
- Possible to have multiple RawContextMiddlewares? HOT 3
- codecov report not being uploaded HOT 1
- Question: Can I add request-id into uvicorn.access log? HOT 2
- Can't write new plugin - plugin is not iterable HOT 4
- Testing code that relies on context vars without a full test client / app HOT 8
- Consider removing `RawContextMiddleware` and keeping only `ContextMiddleware` after resolving memory usage issue HOT 8
- How to use context in Jinja (or any other template engine)? HOT 1
- No module named '_contextvars' HOT 1
- How to get request.body from starlette-context plugin? HOT 2
- async pytests fail when using logging middleware from the examples HOT 3
- Intended behavior on BackgroundTasks HOT 2
- Improved logging HOT 4
- Can't use http middleware HOT 1
- Improving the __repr__ method of _Context HOT 2
- Properly use pyproject.toml HOT 2
- 0.3.3 does not contain the testing helper request_cycle_context HOT 2
- Docs are outdated HOT 1
- Exceptions should inherit from Exception, not BaseException HOT 2
- bump starlette 0.23.1 -> 0.25 and other deps
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from starlette-context.