Repository: sanic-org/sanic

Issue:
Sanic enables writing of DeprecationWarning to the log
**Describe the bug**
Sanic changes the default DeprecationWarning behaviour to be written to the log.
The code line that cause it is in app.py line 104:
`filterwarnings("once", category=DeprecationWarning)`

This change makes all of the DeprecationWarning, *in every 3rd party library* we use, to be written to the log.


**Expected behavior**
I would expect that this will not happen in production 😅 


**Environment (please complete the following information):**
<!-- Please provide the information below. Instead, you can copy and paste the message that Sanic shows on startup. If you do, please remember to format it with ``` -->
 - OS: ubuntu & macOS
 - Sanic Version: 22.6



Discussion:
Good point. At the very least that should probably be by config. Will think about how we can make that a nicer experience and still make sure we provide good warnings.
Yeah, I guess an ENABLE_DEPRECATION_WARNINGS config var would be best. I can submit a PR if you like
If you have a chance to add this PR this week, LMK. Otherwise I will tackle this so we can get it into the upcoming release.
Unfortunately I probably wont be able to get to this before the next release

בתאריך יום ד׳, 14 בספט׳ 2022 ב-14:31 מאת Adam Hopkins <
***@***.***>:

> If you have a chance to add this PR this week, LMK. Otherwise I will
> tackle this so we can get it into the upcoming release.
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/sanic-org/sanic/issues/2538#issuecomment-1246630468>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AC2SEHHHK5FNNA4TFFSZJ53V6GZQFANCNFSM6AAAAAAQAJWFOI>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>

> Unfortunately I probably wont be able to get to this before the next release בתאריך יום ד׳, 14 בספט׳ 2022 ב-14:31 מאת Adam Hopkins < ***@***.***>:

No worries. I will add it. יום טוב

Resolving patch:
diff --git a/sanic/app.py b/sanic/app.py
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -42,7 +42,6 @@
     Union,
 )
 from urllib.parse import urlencode, urlunparse
-from warnings import filterwarnings
 
 from sanic_routing.exceptions import FinalizationError, NotFound
 from sanic_routing.route import Route
@@ -105,8 +104,6 @@
 if OS_IS_WINDOWS:  # no cov
     enable_windows_color_support()
 
-filterwarnings("once", category=DeprecationWarning)
-
 
 class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta):
     """
diff --git a/sanic/config.py b/sanic/config.py
--- a/sanic/config.py
+++ b/sanic/config.py
@@ -1,9 +1,12 @@
 from __future__ import annotations
 
+import sys
+
 from inspect import getmembers, isclass, isdatadescriptor
 from os import environ
 from pathlib import Path
 from typing import Any, Callable, Dict, Optional, Sequence, Union
+from warnings import filterwarnings
 
 from sanic.constants import LocalCertCreator
 from sanic.errorpages import DEFAULT_FORMAT, check_error_format
@@ -13,6 +16,20 @@
 from sanic.utils import load_module_from_file_location, str_to_bool
 
 
+if sys.version_info >= (3, 8):
+    from typing import Literal
+
+    FilterWarningType = Union[
+        Literal["default"],
+        Literal["error"],
+        Literal["ignore"],
+        Literal["always"],
+        Literal["module"],
+        Literal["once"],
+    ]
+else:
+    FilterWarningType = str
+
 SANIC_PREFIX = "SANIC_"
 
 
@@ -22,6 +39,7 @@
     "AUTO_EXTEND": True,
     "AUTO_RELOAD": False,
     "EVENT_AUTOREGISTER": False,
+    "DEPRECATION_FILTER": "once",
     "FORWARDED_FOR_HEADER": "X-Forwarded-For",
     "FORWARDED_SECRET": None,
     "GRACEFUL_SHUTDOWN_TIMEOUT": 15.0,  # 15 sec
@@ -72,6 +90,7 @@ class Config(dict, metaclass=DescriptorMeta):
     AUTO_EXTEND: bool
     AUTO_RELOAD: bool
     EVENT_AUTOREGISTER: bool
+    DEPRECATION_FILTER: FilterWarningType
     FORWARDED_FOR_HEADER: str
     FORWARDED_SECRET: Optional[str]
     GRACEFUL_SHUTDOWN_TIMEOUT: float
@@ -130,6 +149,7 @@ def __init__(
             self.load_environment_vars(SANIC_PREFIX)
 
         self._configure_header_size()
+        self._configure_warnings()
         self._check_error_format()
         self._init = True
 
@@ -178,6 +198,8 @@ def _post_set(self, attr, value) -> None:
             self.LOCAL_CERT_CREATOR = LocalCertCreator[
                 self.LOCAL_CERT_CREATOR.upper()
             ]
+        elif attr == "DEPRECATION_FILTER":
+            self._configure_warnings()
 
     @property
     def FALLBACK_ERROR_FORMAT(self) -> str:
@@ -205,6 +227,13 @@ def _configure_header_size(self):
             self.REQUEST_MAX_SIZE,
         )
 
+    def _configure_warnings(self):
+        filterwarnings(
+            self.DEPRECATION_FILTER,
+            category=DeprecationWarning,
+            module=r"sanic.*",
+        )
+
     def _check_error_format(self, format: Optional[str] = None):
         check_error_format(format or self.FALLBACK_ERROR_FORMAT)
 


Test patch:
diff --git a/tests/test_deprecation.py b/tests/test_deprecation.py
--- a/tests/test_deprecation.py
+++ b/tests/test_deprecation.py
@@ -1,5 +1,6 @@
 import pytest
 
+from sanic import Sanic
 from sanic.log import deprecation
 
 
@@ -7,3 +8,13 @@ def test_deprecation():
     message = r"\[DEPRECATION v9\.9\] hello"
     with pytest.warns(DeprecationWarning, match=message):
         deprecation("hello", 9.9)
+
+
+@pytest.mark.parametrize(
+    "filter,expected",
+    (("default", 1), ("once", 1), ("ignore", 0)),
+)
+def test_deprecation_filter(app: Sanic, filter, expected, recwarn):
+    app.config.DEPRECATION_FILTER = filter
+    deprecation("hello", 9.9)
+    assert len(recwarn) == expected