Repository: aio-libs/aiohttp

Issue:
Add debug argument to web.run_app()
```
500 Internal Server Error
Traceback:
Traceback (most recent call last):
   ...
NameError: name '...' is not defined
```
need a simple debug=False in web.run_app() like flask for security?


Discussion:
@NewUserHa sorry.
The tracker is not the helpdesk but a site for discussing bugs and future improvements.

Please stop using the bug tracker as Q&A resource like StackOverflow.
I respect your self-learning but the issue definitely is not a bug report or feature request.

I apologize if my response is not very friendly but please respect the project maintainers time.

P.S.
The same rule is applied for Python itself.
https://bugs.python.org/ exists for *working on Python*, not for asking *Python usage questions*.
sorry, I might not explain well.
I was not asking a question, but try to open a future improvement issue. Is my English so crap?

Or, did the issue tracker already have a discussion on this?

shouldn't the traceback be hidden in a production environment like what flask does? no?
do you want to say 'go to use the `implement custom error pages`' next for every user from like flask?
Well, by default the traceback is printed in debug mode only.
Application debug mode is inherited from asyncio debug mode: https://docs.python.org/3/library/asyncio-dev.html#debug-mode
it might be worth documenting this better, currently the only thing I can see in the docs is that the `debug` kwarg is deprecated.
Please create an issue.
Pull Request would be even better!
ok. Please let me reclaim my issue.
might you maintainers consider that aiohttp's web.run_app() might need a `debug=True` to control some settings easily, like the switch of `Flask` to give the users `friendly` convenient.

by the way, aiohttp-dev-tools will force changing `loop.set_debug()` without notifying users, that's what makes me open this issue. thanks for it
1. that's an issue with aiohttp-devtools, so please create the issue there.
2. aiohttp-devtools as the name clearly describes is for development where you always want tracebacks.
In asyncio code usually you need debug mode on when development to see useful warnings (they are most likely signs of programming errors).
Debug mode is relative slow, that's why these checks cannot be switched on permanently.
Thus, aiohttp debug mode is usually used in conjunction with asyncio debug mode.
Hint: in python 3.7 there is very useful `python -X dev prog.py` option which is highly recommended for development.

Is there a reason for using asyncio and aiohttp debug modes separately?
I don't think so, but maybe I missed some use case?
'aiohttp's web.run_app() might need a debug=True to control some settings easily', just a parameter of `web.run_app()` to set `loop.set_debug(True)`, even `logging.basicConfig(level=logging.DEBUG)` that's what I meant.
as for me(maybe others well), that the times use `debug=True` flag is more ofen than other parameters since the only time use the other seems is once putting/moving the final version on the server.

I never say `using asyncio and aiohttp debug modes separately`.
This also is a small suggest, not a big deal. for convenient. even not accepted if you don't think it is needed, the document can also have space to improve. of course, after you would like
Are you talking about `web.run_app()` only, not `web.Application`?
That makes sense, especially after having `debug=False` parameter in `asyncio.run()`: https://docs.python.org/3/library/asyncio-task.html#asyncio.run

I think `web.run_app()` should operate with loop, debug mode and exceptions in exactly the same manner as `asyncio.run()` does.
Now they are pretty similar but still different.
@NewUserHa would you prepare a pull request for adding `debug` keyword argument to `web.run_app()`?
no problem.
what about 'add debug into run_app() as the last one parameter and apply loop.set_debug(True) just in run_app() as well'?
need pytest for it? I don't think it need
Rather I propose to add `debug` as the first keyword-only argument (but honestly I don't care too much about the parameter position unless it is keyword-only).

Agree with `loop.set_debug()`
Also I'm asking to replace `get_event_loop()` with `new_event_loop()` as `asyncio.run()` does.

Regarding tests: there is `test_run_app.py`.  Adding a new test is not a big deal.
Documentation should be updated as well.
I'll help you on review if needed.

Resolving patch:
diff --git a/aiohttp/web.py b/aiohttp/web.py
--- a/aiohttp/web.py
+++ b/aiohttp/web.py
@@ -401,6 +401,7 @@ def _cancel_tasks(to_cancel: Set['asyncio.Task[Any]'],
 
 
 def run_app(app: Union[Application, Awaitable[Application]], *,
+            debug: bool=False,
             host: Optional[str]=None,
             port: Optional[int]=None,
             path: Optional[str]=None,
@@ -417,6 +418,7 @@ def run_app(app: Union[Application, Awaitable[Application]], *,
             reuse_port: Optional[bool]=None) -> None:
     """Run an app locally"""
     loop = asyncio.get_event_loop()
+    loop.set_debug(debug)
 
     # Configure if and only if in debugging mode and using the default logger
     if loop.get_debug() and access_log and access_log.name == 'aiohttp.access':
@@ -451,6 +453,7 @@ def run_app(app: Union[Application, Awaitable[Application]], *,
         if sys.version_info >= (3, 6):  # don't use PY_36 to pass mypy
             loop.run_until_complete(loop.shutdown_asyncgens())
         loop.close()
+        asyncio.set_event_loop(None)
 
 
 def main(argv: List[str]) -> None:


Test patch:
diff --git a/tests/test_run_app.py b/tests/test_run_app.py
--- a/tests/test_run_app.py
+++ b/tests/test_run_app.py
@@ -543,7 +543,6 @@ async def make_app():
 
 
 def test_run_app_default_logger(monkeypatch, patched_loop):
-    patched_loop.set_debug(True)
     logger = web.access_logger
     attrs = {
         'hasHandlers.return_value': False,
@@ -554,7 +553,7 @@ def test_run_app_default_logger(monkeypatch, patched_loop):
     mock_logger.configure_mock(**attrs)
 
     app = web.Application()
-    web.run_app(app,
+    web.run_app(app, debug=True,
                 print=stopper(patched_loop),
                 access_log=mock_logger)
     mock_logger.setLevel.assert_any_call(logging.DEBUG)
@@ -564,7 +563,6 @@ def test_run_app_default_logger(monkeypatch, patched_loop):
 
 
 def test_run_app_default_logger_setup_requires_debug(patched_loop):
-    patched_loop.set_debug(False)
     logger = web.access_logger
     attrs = {
         'hasHandlers.return_value': False,
@@ -575,7 +573,7 @@ def test_run_app_default_logger_setup_requires_debug(patched_loop):
     mock_logger.configure_mock(**attrs)
 
     app = web.Application()
-    web.run_app(app,
+    web.run_app(app, debug=False,
                 print=stopper(patched_loop),
                 access_log=mock_logger)
     mock_logger.setLevel.assert_not_called()
@@ -584,7 +582,6 @@ def test_run_app_default_logger_setup_requires_debug(patched_loop):
 
 
 def test_run_app_default_logger_setup_requires_default_logger(patched_loop):
-    patched_loop.set_debug(True)
     logger = web.access_logger
     attrs = {
         'hasHandlers.return_value': False,
@@ -595,7 +592,7 @@ def test_run_app_default_logger_setup_requires_default_logger(patched_loop):
     mock_logger.configure_mock(**attrs)
 
     app = web.Application()
-    web.run_app(app,
+    web.run_app(app, debug=True,
                 print=stopper(patched_loop),
                 access_log=mock_logger)
     mock_logger.setLevel.assert_not_called()
@@ -604,7 +601,6 @@ def test_run_app_default_logger_setup_requires_default_logger(patched_loop):
 
 
 def test_run_app_default_logger_setup_only_if_unconfigured(patched_loop):
-    patched_loop.set_debug(True)
     logger = web.access_logger
     attrs = {
         'hasHandlers.return_value': True,
@@ -615,7 +611,7 @@ def test_run_app_default_logger_setup_only_if_unconfigured(patched_loop):
     mock_logger.configure_mock(**attrs)
 
     app = web.Application()
-    web.run_app(app,
+    web.run_app(app, debug=True,
                 print=stopper(patched_loop),
                 access_log=mock_logger)
     mock_logger.setLevel.assert_not_called()