Repository: sanic-org/sanic

Issue:
[Feature Request] Warn when multiple route names are the same
**Is your feature request related to a problem? Please describe your use case.**
Just spent a good amount of time trying to figure why injections were not working on certain routes, after digging the code a bit more, it seemed like when the route names are the same, the injection will overwrite the value in the `SignatureRegistry`

**Describe the solution you'd like**
Something like this, showing which route names are already in the registry and maybe also mentioning which route have injections (although that last one would be for debugging purposes, maybe have a toggle?):

```py
[2022-08-08 21:17:46 -0400] [80724] [DEBUG] Dispatching signal: server.init.after
[2022-08-08 21:23:19 -0400] [81202] [DEBUG] Injecting into route 'manager.cases.get_cases' {'user': (<class 'src.utils.User'>, <Constructor(func=create)>)} 
[2022-08-08 21:23:19 -0400] [81202] [WARNING] Following route has already been injected: 'ioc_manager.templates.get_rule_filters'
[2022-08-08 21:23:19 -0400] [81202] [WARNING] Following route has already been injected: 'ioc_manager.cases.get_case_iocs'
[2022-08-08 21:23:19 -0400] [81202] [WARNING] Following route has already been injected: 'ioc_manager.cases.get_case_iocs'
[2022-08-08 21:17:46 -0400] [80724] [INFO] Starting worker [80724]
```

For the warning, I've just added a simple check here:

https://github.com/sanic-org/sanic-ext/blob/b65cd9f28c8d1e6ee0aad91685bfb84fc4925ac6/sanic_ext/extensions/injection/registry.py#L54-L59

With

```py
    def register(
        self,
        route_name: str,
        injections: Dict[str, Tuple[Type, Optional[Callable[..., Any]]]],
    ) -> None:
        if route_name in self._registry:
            logger.warning(f"Following route has already been injected: '{route_name}'")
        self._registry[route_name] = injections
```

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/sanic-org/sanic/pull/2525?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) Report
> Merging [#2525](https://codecov.io/gh/sanic-org/sanic/pull/2525?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) (6f39cee) into [main](https://codecov.io/gh/sanic-org/sanic/commit/2f6f2bfa7660e9583694a55fdce4a4bb73b81819?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) (2f6f2bf) will **increase** coverage by `0.065%`.
> The diff coverage is `100.000%`.

```diff
@@              Coverage Diff              @@
##              main     #2525       +/-   ##
=============================================
+ Coverage   87.392%   87.457%   +0.065%     
=============================================
  Files           69        69               
  Lines         5560      5565        +5     
  Branches       966       969        +3     
=============================================
+ Hits          4859      4867        +8     
+ Misses         509       507        -2     
+ Partials       192       191        -1     
```


| [Impacted Files](https://codecov.io/gh/sanic-org/sanic/pull/2525?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) | Coverage Δ | |
|---|---|---|
| [sanic/app.py](https://codecov.io/gh/sanic-org/sanic/pull/2525/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org#diff-c2FuaWMvYXBwLnB5) | `88.825% <100.000%> (+0.106%)` | :arrow_up: |
| [sanic/server/protocols/http\_protocol.py](https://codecov.io/gh/sanic-org/sanic/pull/2525/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org#diff-c2FuaWMvc2VydmVyL3Byb3RvY29scy9odHRwX3Byb3RvY29sLnB5) | `83.823% <0.000%> (+2.205%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org)


Patch:
diff --git a/sanic/app.py b/sanic/app.py
index 59056eb2..fa0548c7 100644
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -1521,6 +1521,18 @@ class Sanic(BaseSanic, RunnerMixin, metaclass=TouchUpMeta):
         self.signalize(self.config.TOUCHUP)
         self.finalize()
 
+        route_names = [route.name for route in self.router.routes]
+        duplicates = {
+            name for name in route_names if route_names.count(name) > 1
+        }
+        if duplicates:
+            names = ", ".join(duplicates)
+            deprecation(
+                f"Duplicate route names detected: {names}. In the future, "
+                "Sanic will enforce uniqueness in route naming.",
+                23.3,
+            )
+
         # TODO: Replace in v22.6 to check against apps in app registry
         if (
             self.__class__._uvloop_setting is not None


Test patch:
diff --git a/tests/test_routes.py b/tests/test_routes.py
index 6fdf97d5..4ff5d9fd 100644
--- a/tests/test_routes.py
+++ b/tests/test_routes.py
@@ -1266,3 +1266,22 @@ async def test_added_callable_route_ctx_kwargs(app):
 
     assert request.route.ctx.foo() == "foo"
     assert await request.route.ctx.bar() == 99
+
+
+@pytest.mark.asyncio
+async def test_duplicate_route_deprecation(app):
+    @app.route("/foo", name="duped")
+    async def handler_foo(request):
+        return text("...")
+
+    @app.route("/bar", name="duped")
+    async def handler_bar(request):
+        return text("...")
+
+    message = (
+        r"\[DEPRECATION v23\.3\] Duplicate route names detected: "
+        r"test_duplicate_route_deprecation\.duped\. In the future, "
+        r"Sanic will enforce uniqueness in route naming\."
+    )
+    with pytest.warns(DeprecationWarning, match=message):
+        await app._startup()