From cc4a5983797b582d36b5a79f39098373f346216b Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Tue, 8 Aug 2023 01:58:35 +0900 Subject: [PATCH 01/23] support URLRouter with include --- channels/routing.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/channels/routing.py b/channels/routing.py index efb428ac..63d22030 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -81,7 +81,21 @@ class URLRouter: _path_routing = True def __init__(self, routes): - self.routes = routes + new_routes = [] + for route in routes: + if not route.callback and isinstance(route, URLResolver): + for url_pattern in route.url_patterns: + url_pattern: URLPattern + # concatenate parent's url and child's url + regex = ''.join(x.pattern for x in [route.pattern.regex, url_pattern.pattern.regex]) + regex = re.sub(r'(/)\1+', r'\1', regex) + name = f"{route.app_name}:{url_pattern.name}" if url_pattern.name else None + pattern = RegexPattern(regex, name=name, is_endpoint=True) + new_routes.append(URLPattern(pattern, url_pattern.callback, route.default_kwargs, name)) + else: + new_routes.append(route) + + self.routes = new_routes for route in self.routes: # The inner ASGI app wants to do additional routing, route From 5309e68c93b0ff7be4d3cfa446c538b9d558c3e8 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Wed, 5 Jun 2024 15:02:30 +0900 Subject: [PATCH 02/23] fixed indentation error --- channels/routing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 1b975713..9927056b 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -1,9 +1,10 @@ import importlib +import re from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.urls.exceptions import Resolver404 -from django.urls.resolvers import RegexPattern, RoutePattern, URLResolver +from django.urls.resolvers import RegexPattern, RoutePattern, URLResolver, URLPattern """ All Routing instances inside this file are also valid ASGI applications - with @@ -81,7 +82,7 @@ class URLRouter: _path_routing = True def __init__(self, routes): - new_routes = [] + new_routes = [] for route in routes: if not route.callback and isinstance(route, URLResolver): for url_pattern in route.url_patterns: From b9b0c76fbbb44de099145e81b48b42fb40d624f5 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Wed, 5 Jun 2024 15:02:51 +0900 Subject: [PATCH 03/23] remove test_invalid_routes --- tests/test_routing.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/test_routing.py b/tests/test_routing.py index 99c76790..0037ee31 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -301,14 +301,3 @@ async def test_path_remaining(): None, ) - -def test_invalid_routes(): - """ - Test URLRouter route validation - """ - from django.urls import include - - with pytest.raises(ImproperlyConfigured) as exc: - URLRouter([path("", include([]))]) - - assert "include() is not supported in URLRouter." in str(exc) From 436d38a7a489784f8218402092741401a2cf5408 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Wed, 5 Jun 2024 15:29:14 +0900 Subject: [PATCH 04/23] added test_url_router_nesting_by_include in test_rouiting.py and passed --- tests/test_routing.py | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/test_routing.py b/tests/test_routing.py index 0037ee31..48a52f36 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -301,3 +301,70 @@ async def test_path_remaining(): None, ) + +@pytest.mark.asyncio +async def test_url_router_nesting_by_include(): + """ + Tests that nested URLRouters is constructed by include function. + """ + import sys + from django.urls import include + from django.urls.resolvers import URLResolver + + test_app = MockApplication(return_value=1) + + # mocking the universe module following the directory structure; + # ├── universe + # │ └── routings.py + # └── routings.py (parent) + # + # + # in routings.py + # ====================== + # ... + # urlpatterns = [ + # re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), + # re_path(r"test/(\d+)/$", test_app), + # ] + # ====================== + module_routings = type(sys)('routings') + module_routings.urlpatterns = [ + re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), + re_path(r"test/(\d+)/$", test_app), + ] + module = type(sys)('universe') + module.routings = module_routings + sys.modules['universe'] = module + sys.modules['universe.routings'] = module.routings + + # parent routings.py + outer_router = URLRouter( + [ + path( + "universe/", include('universe.routings'), name='universe' + ), + ] + ) + assert ( + await outer_router( + { + "type": "http", + "path": "/universe/book/channels-guide/page/10/", + }, + None, + None, + ) + == 1 + ) + + assert ( + await outer_router( + { + "type": "http", + "path": "/universe/test/10/", + }, + None, + None, + ) + == 1 + ) From 4b4798145b60793ebf0ebe64a94f43991393d827 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Wed, 10 Jul 2024 23:06:57 +0900 Subject: [PATCH 05/23] fixed lint errors --- channels/routing.py | 19 +++++++++++++++---- tests/test_routing.py | 22 +++++++++------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 9927056b..0a850e5c 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -88,11 +88,22 @@ def __init__(self, routes): for url_pattern in route.url_patterns: url_pattern: URLPattern # concatenate parent's url and child's url - regex = ''.join(x.pattern for x in [route.pattern.regex, url_pattern.pattern.regex]) - regex = re.sub(r'(/)\1+', r'\1', regex) - name = f"{route.app_name}:{url_pattern.name}" if url_pattern.name else None + regex = "".join( + x.pattern + for x in [route.pattern.regex, url_pattern.pattern.regex] + ) + regex = re.sub(r"(/)\1+", r"\1", regex) + name = ( + f"{route.app_name}:{url_pattern.name}" + if url_pattern.name + else None + ) pattern = RegexPattern(regex, name=name, is_endpoint=True) - new_routes.append(URLPattern(pattern, url_pattern.callback, route.default_kwargs, name)) + new_routes.append( + URLPattern( + pattern, url_pattern.callback, route.default_kwargs, name + ) + ) else: new_routes.append(route) diff --git a/tests/test_routing.py b/tests/test_routing.py index 48a52f36..92fef66a 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1,5 +1,4 @@ import pytest -from django.core.exceptions import ImproperlyConfigured from django.urls import path, re_path from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter @@ -309,10 +308,9 @@ async def test_url_router_nesting_by_include(): """ import sys from django.urls import include - from django.urls.resolvers import URLResolver - + test_app = MockApplication(return_value=1) - + # mocking the universe module following the directory structure; # ├── universe # │ └── routings.py @@ -327,22 +325,20 @@ async def test_url_router_nesting_by_include(): # re_path(r"test/(\d+)/$", test_app), # ] # ====================== - module_routings = type(sys)('routings') + module_routings = type(sys)("routings") module_routings.urlpatterns = [ re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), re_path(r"test/(\d+)/$", test_app), ] - module = type(sys)('universe') + module = type(sys)("universe") module.routings = module_routings - sys.modules['universe'] = module - sys.modules['universe.routings'] = module.routings - + sys.modules["universe"] = module + sys.modules["universe.routings"] = module.routings + # parent routings.py outer_router = URLRouter( [ - path( - "universe/", include('universe.routings'), name='universe' - ), + path("universe/", include("universe.routings"), name="universe"), ] ) assert ( @@ -356,7 +352,7 @@ async def test_url_router_nesting_by_include(): ) == 1 ) - + assert ( await outer_router( { From 504bd3c12931ec581f3a65002be0121dec129601 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 12:51:18 +0900 Subject: [PATCH 06/23] modified URLPattern's arguments (#2110) --- channels/routing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channels/routing.py b/channels/routing.py index 8d283b82..b556f690 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -87,7 +87,7 @@ def __init__(self, routes): pattern = RegexPattern(regex, name=name, is_endpoint=True) new_routes.append( URLPattern( - pattern, url_pattern.callback, route.default_kwargs, name + pattern, url_pattern.callback, url_pattern.default_args, name ) ) else: From 76260f7176dfb77a7e2ee875a6dc7ec39d528d2d Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 12:52:10 +0900 Subject: [PATCH 07/23] removed type hint (#2110) --- channels/routing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/channels/routing.py b/channels/routing.py index b556f690..d031d5d0 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -72,7 +72,6 @@ def __init__(self, routes): for route in routes: if not route.callback and isinstance(route, URLResolver): for url_pattern in route.url_patterns: - url_pattern: URLPattern # concatenate parent's url and child's url regex = "".join( x.pattern From 7ca0037b2cd05f03506736a2c4541eb8680da1c1 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 13:04:16 +0900 Subject: [PATCH 08/23] add comment for regex (#2110) --- channels/routing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/channels/routing.py b/channels/routing.py index d031d5d0..a42746fd 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -77,6 +77,7 @@ def __init__(self, routes): x.pattern for x in [route.pattern.regex, url_pattern.pattern.regex] ) + # Remove the sequential '/' regex = re.sub(r"(/)\1+", r"\1", regex) name = ( f"{route.app_name}:{url_pattern.name}" From 4df1d5fbade21effaece5efea4871bbb20626ec0 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 14:47:56 +0900 Subject: [PATCH 09/23] add path in test (#2110) --- channels/routing.py | 7 ++++++- tests/test_routing.py | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index a42746fd..32ec17cd 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -71,14 +71,18 @@ def __init__(self, routes): new_routes = [] for route in routes: if not route.callback and isinstance(route, URLResolver): + # parse the urls resolved by django's `include` function for url_pattern in route.url_patterns: - # concatenate parent's url and child's url + # concatenate parent's url (route) and child's url (url_pattern) regex = "".join( x.pattern for x in [route.pattern.regex, url_pattern.pattern.regex] ) + # Remove the redundant caret ^ which is appended by `path` function + regex = re.sub(r"(?[\w\-]+)/page/(?P\d+)/$", test_app), re_path(r"test/(\d+)/$", test_app), + path('/home/', test_app) ] module = type(sys)("universe") module.routings = module_routings @@ -364,3 +365,15 @@ async def test_url_router_nesting_by_include(): ) == 1 ) + + assert ( + await outer_router( + { + "type": "http", + "path": "/universe/home/", + }, + None, + None, + ) + == 1 + ) From cdc8c6cf0b9add0de2b608d27afd5800d79e1fdd Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 14:49:55 +0900 Subject: [PATCH 10/23] formatting (#2110) --- channels/routing.py | 7 +++++-- tests/test_routing.py | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 32ec17cd..62eaf8bc 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -82,7 +82,7 @@ def __init__(self, routes): regex = re.sub(r"(?[\w\-]+)/page/(?P\d+)/$", test_app), re_path(r"test/(\d+)/$", test_app), - path('/home/', test_app) + path("/home/", test_app), ] module = type(sys)("universe") module.routings = module_routings @@ -365,7 +365,7 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) - + assert ( await outer_router( { From e35c2d57987705517e1437f523bcf7c891c8d79e Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 15:49:58 +0900 Subject: [PATCH 11/23] support deeper include (#2110) --- channels/routing.py | 93 +++++++++++++++++++++++++++++----------- tests/test_routing.py | 99 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 158 insertions(+), 34 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 62eaf8bc..9922ab25 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -53,6 +53,72 @@ async def __call__(self, scope, receive, send): ) +def _parse_resolver(child_url_pattern, parent_resolver, parent_regex, routes): + """Parse resolver (returned by `include`) recurrsively + + Parameters + ---------- + child_url_pattern : URLResolver | + The child url pattern + parent_resolver : URLResolver + The parent resolver + parent_regex : Pattern + The parent regex pattern + routes : list[URLPattern] + The URLPattern's list that stores the routes + + Returns + ------- + list[URLPattern] + The URLPattern's list that stores the routes + """ + if not child_url_pattern.callback and isinstance(child_url_pattern, URLResolver): + # parse the urls resolved by django's `include` function + for url_pattern in child_url_pattern.url_patterns: + # call _parse_resolver recurrsively to parse nested URLResolver + routes.extend( + _parse_resolver( + url_pattern, + child_url_pattern, + parent_resolver.pattern.regex, + routes, + ) + ) + else: + # concatenate parent's url (route) and child's url (url_pattern) + regex = "".join( + x.pattern + for x in [ + parent_regex, + parent_resolver.pattern.regex, + child_url_pattern.pattern.regex, + ] + ) + print([parent_resolver.pattern.regex, child_url_pattern.pattern.regex]) + + # Remove the redundant caret ^ which is appended by `path` function + regex = re.sub(r"(?[\w\-]+)/page/(?P\d+)/$", test_app), # re_path(r"test/(\d+)/$", test_app), + # path("/home/", test_app), # ] # ====================== - module_routings = type(sys)("routings") - module_routings.urlpatterns = [ - re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), - re_path(r"test/(\d+)/$", test_app), - path("/home/", test_app), + universe_routings = type(sys)("routings") + universe_routings.urlpatterns = [ + re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app, name="book"), + re_path(r"test/(\d+)/$", test_app, name="test"), + path("/home/", test_app, name="home"), ] - module = type(sys)("universe") - module.routings = module_routings - sys.modules["universe"] = module - sys.modules["universe.routings"] = module.routings + universe = type(sys)("universe") + universe.routings = universe_routings + sys.modules["universe"] = universe + sys.modules["universe.routings"] = universe.routings # parent routings.py outer_router = URLRouter( @@ -377,3 +378,83 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) + + +@pytest.mark.asyncio +async def test_url_router_deep_nesting_by_include(root_urlconf): + """ + Tests that deep nested URLRouters is constructed by include function. + """ + import sys + from django.urls import include + + test_app = MockApplication(return_value=1) + + # mocking the universe module following the directory structure; + # ├── universe + # │ ├── routings.py (use include) + # │ └── earth + # │ └── routings.py + # └── routings.py (parent; use include) + + earth_routings = type(sys)("routings") + earth_routings.urlpatterns = [ + re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app, name="book"), + re_path(r"test/(\d+)/$", test_app, name="test"), + path("/home/", test_app, name="home"), + ] + earth = type(sys)("earth") + earth.routings = earth_routings + sys.modules["earth"] = earth + sys.modules["earth.routings"] = earth.routings + + universe_routings = type(sys)("routings") + universe_routings.urlpatterns = [ + path("earth/", include("earth.routings"), name="earth"), + ] + universe = type(sys)("universe") + universe.routings = universe_routings + sys.modules["universe"] = universe + sys.modules["universe.routings"] = universe.routings + + # parent routings.py + outer_router = URLRouter( + [ + path("universe/", include("universe.routings"), name="universe"), + ] + ) + assert ( + await outer_router( + { + "type": "http", + "path": "/universe/earth/book/channels-guide/page/10/", + }, + None, + None, + ) + == 1 + ) + + assert ( + await outer_router( + { + "type": "http", + "path": "/universe/earth/test/10/", + }, + None, + None, + ) + == 1 + ) + + assert ( + await outer_router( + { + "type": "http", + "path": "/universe/earth/home/", + }, + None, + None, + ) + == 1 + ) From e3e411f73a7485802c9b17839def36da9503dc50 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 3 Aug 2024 16:41:21 +0900 Subject: [PATCH 12/23] add usual case with include (#2110) --- channels/routing.py | 1 - tests/test_routing.py | 128 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/channels/routing.py b/channels/routing.py index 9922ab25..0c774323 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -94,7 +94,6 @@ def _parse_resolver(child_url_pattern, parent_resolver, parent_regex, routes): child_url_pattern.pattern.regex, ] ) - print([parent_resolver.pattern.regex, child_url_pattern.pattern.regex]) # Remove the redundant caret ^ which is appended by `path` function regex = re.sub(r"(?[\w\-]+)/page/(?P\d+)/$", test_app), +# # re_path(r"test/(\d+)/$", test_app), +# # ] +# # ====================== +# module_routings = type(sys)("routings") +# module_routings.urlpatterns = [ +# re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), +# re_path(r"test/(\d+)/$", test_app), +# path("home/", test_app, name='home'), +# ] +# module = type(sys)("universe") +# module.routings = module_routings +# sys.modules["src"] = type(sys)("routings") +# sys.modules["src.universe"] = module +# sys.modules["src.universe.routings"] = module.routings + +# # parent routings.py +# outer_router = URLRouter( +# [ +# path("universe/", include("src.universe.routings"), name="universe"), +# ] +# ) +# assert ( +# await outer_router( +# { +# "type": "http", +# "path": "/universe/book/channels-guide/page/10/", +# }, +# None, +# None, +# ) +# == 1 +# ) + +# assert ( +# await outer_router( +# { +# "type": "http", +# "path": "/universe/test/10/", +# }, +# None, +# None, +# ) +# == 1 +# ) + +# assert ( +# await outer_router( +# { +# "type": "http", +# "path": reverse('universe:home'), +# }, +# None, +# None, +# ) +# == 1 +# ) + + @pytest.mark.asyncio async def test_url_router_nesting_by_include(root_urlconf): """ @@ -341,8 +420,33 @@ async def test_url_router_nesting_by_include(root_urlconf): outer_router = URLRouter( [ path("universe/", include("universe.routings"), name="universe"), + path("moon/", test_app, name="moon"), + re_path(r"mars/(\d+)/$", test_app, name="mars"), ] ) + assert ( + await outer_router( + { + "type": "http", + "path": "/moon/", + }, + None, + None, + ) + == 1 + ) + assert ( + await outer_router( + { + "type": "http", + "path": "/mars/5/", + }, + None, + None, + ) + == 1 + ) + assert ( await outer_router( { @@ -421,8 +525,32 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): outer_router = URLRouter( [ path("universe/", include("universe.routings"), name="universe"), + path("moon/", test_app, name="moon"), + re_path(r"mars/(\d+)/$", test_app, name="mars"), ] ) + assert ( + await outer_router( + { + "type": "http", + "path": "/moon/", + }, + None, + None, + ) + == 1 + ) + assert ( + await outer_router( + { + "type": "http", + "path": "/mars/5/", + }, + None, + None, + ) + == 1 + ) assert ( await outer_router( { From 3376bb2b53c2f2b25308ded1e7fc34071b92079b Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Mon, 5 Aug 2024 12:42:36 +0900 Subject: [PATCH 13/23] add reverse (#2110) --- channels/routing.py | 22 ++++ tests/conftest.py | 18 +++ tests/test_routing.py | 254 +++++++++++++++++++++++------------------- 3 files changed, 182 insertions(+), 112 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 0c774323..ba4f89cb 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -5,6 +5,7 @@ from django.core.exceptions import ImproperlyConfigured from django.urls.exceptions import Resolver404 from django.urls.resolvers import RegexPattern, RoutePattern, URLResolver, URLPattern +from django.urls import reverse as django_reverse """ All Routing instances inside this file are also valid ASGI applications - with @@ -234,3 +235,24 @@ async def __call__(self, scope, receive, send): raise ValueError( "No application configured for channel name %r" % scope["channel"] ) + + +def reverse(*args, urlconf=None, **kwargs): + """reverse wrapper for django's reverse function + + Parameters + ---------- + urlconf : str, optional + The root path of the routings, by default None + + See the django's [reverse](https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#reverse) + for more details of the other arguments + + Returns + ------- + str + The reversed url + """ + if urlconf is None: + urlconf = settings.ROOT_WEBSOCKET_URLCONF + return django_reverse(*args, urlconf=urlconf, **kwargs) diff --git a/tests/conftest.py b/tests/conftest.py index 94c9803a..7c8f9667 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import pytest +import sys from django.conf import settings @@ -38,3 +39,20 @@ def samesite(request, settings): def samesite_invalid(settings): """Set samesite flag to strict.""" settings.SESSION_COOKIE_SAMESITE = "Hello" + + +@pytest.fixture +def root_urlconf(settings): + """Set ROOT_WEBSOCKET_URLCONF.""" + settings.ROOT_WEBSOCKET_URLCONF = "__src.routings" + return settings.ROOT_WEBSOCKET_URLCONF + + +@pytest.fixture(autouse=True) +def mock_modules(): + """Save original modules for each test and clear a cache""" + original_modules = sys.modules.copy() + yield + sys.modules = original_modules + from django.urls.base import _get_cached_resolver + _get_cached_resolver.cache_clear() diff --git a/tests/test_routing.py b/tests/test_routing.py index a7a46e38..0310f436 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1,7 +1,7 @@ import pytest -from django.urls import path, re_path, reverse +from django.urls import path, re_path -from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter +from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter, reverse class MockApplication: @@ -301,85 +301,6 @@ async def test_path_remaining(): ) -# @pytest.mark.asyncio -# async def test_url_router_nesting_by_include(root_urlconf): -# """ -# Tests that nested URLRouters is constructed by include function. -# """ -# import sys -# from django.urls import include - -# test_app = MockApplication(return_value=1) - -# # mocking the universe module following the directory structure; -# # ├── universe -# # │ └── routings.py -# # └── routings.py (parent) -# # -# # -# # in routings.py -# # ====================== -# # ... -# # urlpatterns = [ -# # re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), -# # re_path(r"test/(\d+)/$", test_app), -# # ] -# # ====================== -# module_routings = type(sys)("routings") -# module_routings.urlpatterns = [ -# re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), -# re_path(r"test/(\d+)/$", test_app), -# path("home/", test_app, name='home'), -# ] -# module = type(sys)("universe") -# module.routings = module_routings -# sys.modules["src"] = type(sys)("routings") -# sys.modules["src.universe"] = module -# sys.modules["src.universe.routings"] = module.routings - -# # parent routings.py -# outer_router = URLRouter( -# [ -# path("universe/", include("src.universe.routings"), name="universe"), -# ] -# ) -# assert ( -# await outer_router( -# { -# "type": "http", -# "path": "/universe/book/channels-guide/page/10/", -# }, -# None, -# None, -# ) -# == 1 -# ) - -# assert ( -# await outer_router( -# { -# "type": "http", -# "path": "/universe/test/10/", -# }, -# None, -# None, -# ) -# == 1 -# ) - -# assert ( -# await outer_router( -# { -# "type": "http", -# "path": reverse('universe:home'), -# }, -# None, -# None, -# ) -# == 1 -# ) - - @pytest.mark.asyncio async def test_url_router_nesting_by_include(root_urlconf): """ @@ -387,16 +308,17 @@ async def test_url_router_nesting_by_include(root_urlconf): """ import sys from django.urls import include + from django.urls import reverse as django_reverse test_app = MockApplication(return_value=1) # mocking the universe module following the directory structure; + # __src # ├── universe # │ └── routings.py - # └── routings.py (parent) - # - # - # in routings.py + # └── routings.py (root) + + # in __src/universe/routings.py # ====================== # ... # urlpatterns = [ @@ -405,25 +327,44 @@ async def test_url_router_nesting_by_include(root_urlconf): # path("/home/", test_app), # ] # ====================== + universe_routings = type(sys)("routings") + universe_routings.app_name = "universe" universe_routings.urlpatterns = [ re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app, name="book"), re_path(r"test/(\d+)/$", test_app, name="test"), - path("/home/", test_app, name="home"), + path("home/", test_app, name="home"), ] universe = type(sys)("universe") universe.routings = universe_routings - sys.modules["universe"] = universe - sys.modules["universe.routings"] = universe.routings + sys.modules["__src.universe"] = universe + sys.modules["__src.universe.routings"] = universe.routings + + # in __src/routings.py (root) + # ====================== + # ... + # urlpatterns = [ + # path("universe/", include("__src.universe.routings"), name="universe"), + # path("moon/", test_app, name="moon"), + # re_path(r"mars/(\d+)/$", test_app, name="mars"), + # ] + # + # outer_router = URLRouter(urlpatterns) + # ====================== + urlpatterns = [ + path("universe/", include("__src.universe.routings"), name="universe"), + path("moon/", test_app, name="moon"), + re_path(r"mars/(\d+)/$", test_app, name="mars"), + ] + outer_router = URLRouter(urlpatterns) + + src = type(sys)("__src") + src.routings = type(sys)("routings") + src.routings.urlpatterns = urlpatterns + src.routings.outer_router = outer_router + sys.modules["__src"] = src + sys.modules["__src.routings"] = src.routings - # parent routings.py - outer_router = URLRouter( - [ - path("universe/", include("universe.routings"), name="universe"), - path("moon/", test_app, name="moon"), - re_path(r"mars/(\d+)/$", test_app, name="mars"), - ] - ) assert ( await outer_router( { @@ -435,6 +376,9 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) + assert django_reverse("moon", urlconf=root_urlconf) == "/moon/" + assert reverse("moon") == "/moon/" + assert ( await outer_router( { @@ -446,6 +390,8 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) + assert django_reverse("mars", urlconf=root_urlconf, args=(5,)) == "/mars/5/" + assert reverse("mars", args=(5,)) == "/mars/5/" assert ( await outer_router( @@ -458,6 +404,18 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) + assert ( + django_reverse( + "universe:book", + urlconf=root_urlconf, + kwargs=dict(book="channels-guide", page=10), + ) + == "/universe/book/channels-guide/page/10/" + ) + assert ( + reverse("universe:book", kwargs=dict(book="channels-guide", page=10)) + == "/universe/book/channels-guide/page/10/" + ) assert ( await outer_router( @@ -470,6 +428,11 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) + assert ( + django_reverse("universe:test", urlconf=root_urlconf, args=(10,)) + == "/universe/test/10/" + ) + assert reverse("universe:test", args=(10,)) == "/universe/test/10/" assert ( await outer_router( @@ -482,6 +445,8 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == 1 ) + assert django_reverse("universe:home", urlconf=root_urlconf) == "/universe/home/" + assert reverse("universe:home") == "/universe/home/" @pytest.mark.asyncio @@ -491,44 +456,81 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): """ import sys from django.urls import include - + from django.urls import reverse as django_reverse + test_app = MockApplication(return_value=1) # mocking the universe module following the directory structure; + # __src # ├── universe # │ ├── routings.py (use include) # │ └── earth # │ └── routings.py - # └── routings.py (parent; use include) + # └── routings.py (root; use include) + # in __src/universe/earth/routings.py + # ====================== + # ... + # app_name = "earth" + # urlpatterns = [ + # re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app), + # re_path(r"test/(\d+)/$", test_app), + # path("/home/", test_app), + # ] + # ====================== earth_routings = type(sys)("routings") + earth_routings.app_name = "earth" earth_routings.urlpatterns = [ re_path(r"book/(?P[\w\-]+)/page/(?P\d+)/$", test_app, name="book"), re_path(r"test/(\d+)/$", test_app, name="test"), - path("/home/", test_app, name="home"), + path("home/", test_app, name="home"), ] earth = type(sys)("earth") earth.routings = earth_routings - sys.modules["earth"] = earth - sys.modules["earth.routings"] = earth.routings + sys.modules["__src.universe.earth"] = earth + sys.modules["__src.universe.earth.routings"] = earth.routings + # in __src/universe/routings.py + # ====================== + # ... + # app_name = "earth" + # urlpatterns = [ + # path("earth/", include("__src.universe.earth.routings"), name="earth"), + # ] + # ====================== universe_routings = type(sys)("routings") + universe_routings.app_name = "universe" universe_routings.urlpatterns = [ - path("earth/", include("earth.routings"), name="earth"), + path("earth/", include("__src.universe.earth.routings"), name="earth"), ] universe = type(sys)("universe") universe.routings = universe_routings - sys.modules["universe"] = universe - sys.modules["universe.routings"] = universe.routings + sys.modules["__src.universe"] = universe + sys.modules["__src.universe.routings"] = universe.routings + + # in __src/routings.py (root) + # ====================== + # ... + # urlpatterns = [ + # path("universe/", include("__src.universe.routings"), name="universe"), + # path("moon/", test_app, name="moon"), + # re_path(r"mars/(\d+)/$", test_app, name="mars"), + # ] + # outer_router = URLRouter(urlpatterns) + # ====================== + urlpatterns = [ + path("universe/", include("__src.universe.routings"), name="universe"), + path("moon/", test_app, name="moon"), + re_path(r"mars/(\d+)/$", test_app, name="mars"), + ] + outer_router = URLRouter(urlpatterns) + src = type(sys)("__src") + src.routings = type(sys)("routings") + src.routings.urlpatterns = urlpatterns + src.routings.outer_router = outer_router + sys.modules["__src"] = src + sys.modules["__src.routings"] = src.routings - # parent routings.py - outer_router = URLRouter( - [ - path("universe/", include("universe.routings"), name="universe"), - path("moon/", test_app, name="moon"), - re_path(r"mars/(\d+)/$", test_app, name="mars"), - ] - ) assert ( await outer_router( { @@ -540,6 +542,9 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): ) == 1 ) + assert django_reverse("moon", urlconf=root_urlconf) == "/moon/" + assert reverse("moon") == "/moon/" + assert ( await outer_router( { @@ -551,6 +556,9 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): ) == 1 ) + assert django_reverse("mars", urlconf=root_urlconf, args=(5,)) == "/mars/5/" + assert reverse("mars", args=(5,)) == "/mars/5/" + assert ( await outer_router( { @@ -562,6 +570,18 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): ) == 1 ) + assert ( + django_reverse( + "universe:earth:book", + urlconf=root_urlconf, + kwargs=dict(book="channels-guide", page=10), + ) + == "/universe/earth/book/channels-guide/page/10/" + ) + assert ( + reverse("universe:earth:book", kwargs=dict(book="channels-guide", page=10)) + == "/universe/earth/book/channels-guide/page/10/" + ) assert ( await outer_router( @@ -574,6 +594,11 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): ) == 1 ) + assert ( + django_reverse("universe:earth:test", urlconf=root_urlconf, args=(10,)) + == "/universe/earth/test/10/" + ) + assert reverse("universe:earth:test", args=(10,)) == "/universe/earth/test/10/" assert ( await outer_router( @@ -586,3 +611,8 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): ) == 1 ) + assert ( + django_reverse("universe:earth:home", urlconf=root_urlconf) + == "/universe/earth/home/" + ) + assert reverse("universe:earth:home") == "/universe/earth/home/" From 88ddd77878cc5b1d2854e6717129a76158aa07a2 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 10 Aug 2024 00:43:44 +0900 Subject: [PATCH 14/23] fixed linter errors (#2110) --- channels/routing.py | 3 ++- tests/test_routing.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index ba4f89cb..44bd5e80 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -245,7 +245,8 @@ def reverse(*args, urlconf=None, **kwargs): urlconf : str, optional The root path of the routings, by default None - See the django's [reverse](https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#reverse) + See the django's + [reverse](https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#reverse) for more details of the other arguments Returns diff --git a/tests/test_routing.py b/tests/test_routing.py index 0310f436..04c422a0 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -457,7 +457,7 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): import sys from django.urls import include from django.urls import reverse as django_reverse - + test_app = MockApplication(return_value=1) # mocking the universe module following the directory structure; From 758a205eff5400a14f05a62193ed84ff18dcc2d2 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 10 Aug 2024 00:47:19 +0900 Subject: [PATCH 15/23] removed handiling the sequential / (#2110) --- channels/routing.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 44bd5e80..b64c0529 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -98,8 +98,7 @@ def _parse_resolver(child_url_pattern, parent_resolver, parent_regex, routes): # Remove the redundant caret ^ which is appended by `path` function regex = re.sub(r"(? Date: Sat, 10 Aug 2024 00:48:53 +0900 Subject: [PATCH 16/23] removed not route.callback (#2110) --- channels/routing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channels/routing.py b/channels/routing.py index b64c0529..5918de0d 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -73,7 +73,7 @@ def _parse_resolver(child_url_pattern, parent_resolver, parent_regex, routes): list[URLPattern] The URLPattern's list that stores the routes """ - if not child_url_pattern.callback and isinstance(child_url_pattern, URLResolver): + if isinstance(child_url_pattern, URLResolver): # parse the urls resolved by django's `include` function for url_pattern in child_url_pattern.url_patterns: # call _parse_resolver recurrsively to parse nested URLResolver From 29c74027d1b43a0b44c4c81904ce1151920572b4 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 10 Aug 2024 11:51:15 +0900 Subject: [PATCH 17/23] fix linter error (#2110) --- tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/conftest.py b/tests/conftest.py index 7c8f9667..1045194a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,4 +55,5 @@ def mock_modules(): yield sys.modules = original_modules from django.urls.base import _get_cached_resolver + _get_cached_resolver.cache_clear() From 74e5ee2ca71b8d579329afba5d8381a425aeed7c Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 10 Aug 2024 11:53:31 +0900 Subject: [PATCH 18/23] removed reverse function (#2110) --- channels/routing.py | 23 ----------------------- tests/test_routing.py | 18 +----------------- 2 files changed, 1 insertion(+), 40 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 5918de0d..27fdeaba 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -5,7 +5,6 @@ from django.core.exceptions import ImproperlyConfigured from django.urls.exceptions import Resolver404 from django.urls.resolvers import RegexPattern, RoutePattern, URLResolver, URLPattern -from django.urls import reverse as django_reverse """ All Routing instances inside this file are also valid ASGI applications - with @@ -234,25 +233,3 @@ async def __call__(self, scope, receive, send): raise ValueError( "No application configured for channel name %r" % scope["channel"] ) - - -def reverse(*args, urlconf=None, **kwargs): - """reverse wrapper for django's reverse function - - Parameters - ---------- - urlconf : str, optional - The root path of the routings, by default None - - See the django's - [reverse](https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#reverse) - for more details of the other arguments - - Returns - ------- - str - The reversed url - """ - if urlconf is None: - urlconf = settings.ROOT_WEBSOCKET_URLCONF - return django_reverse(*args, urlconf=urlconf, **kwargs) diff --git a/tests/test_routing.py b/tests/test_routing.py index 04c422a0..a5462b88 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1,7 +1,7 @@ import pytest from django.urls import path, re_path -from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter, reverse +from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter class MockApplication: @@ -377,7 +377,6 @@ async def test_url_router_nesting_by_include(root_urlconf): == 1 ) assert django_reverse("moon", urlconf=root_urlconf) == "/moon/" - assert reverse("moon") == "/moon/" assert ( await outer_router( @@ -391,7 +390,6 @@ async def test_url_router_nesting_by_include(root_urlconf): == 1 ) assert django_reverse("mars", urlconf=root_urlconf, args=(5,)) == "/mars/5/" - assert reverse("mars", args=(5,)) == "/mars/5/" assert ( await outer_router( @@ -412,10 +410,6 @@ async def test_url_router_nesting_by_include(root_urlconf): ) == "/universe/book/channels-guide/page/10/" ) - assert ( - reverse("universe:book", kwargs=dict(book="channels-guide", page=10)) - == "/universe/book/channels-guide/page/10/" - ) assert ( await outer_router( @@ -432,7 +426,6 @@ async def test_url_router_nesting_by_include(root_urlconf): django_reverse("universe:test", urlconf=root_urlconf, args=(10,)) == "/universe/test/10/" ) - assert reverse("universe:test", args=(10,)) == "/universe/test/10/" assert ( await outer_router( @@ -446,7 +439,6 @@ async def test_url_router_nesting_by_include(root_urlconf): == 1 ) assert django_reverse("universe:home", urlconf=root_urlconf) == "/universe/home/" - assert reverse("universe:home") == "/universe/home/" @pytest.mark.asyncio @@ -543,7 +535,6 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): == 1 ) assert django_reverse("moon", urlconf=root_urlconf) == "/moon/" - assert reverse("moon") == "/moon/" assert ( await outer_router( @@ -557,7 +548,6 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): == 1 ) assert django_reverse("mars", urlconf=root_urlconf, args=(5,)) == "/mars/5/" - assert reverse("mars", args=(5,)) == "/mars/5/" assert ( await outer_router( @@ -578,10 +568,6 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): ) == "/universe/earth/book/channels-guide/page/10/" ) - assert ( - reverse("universe:earth:book", kwargs=dict(book="channels-guide", page=10)) - == "/universe/earth/book/channels-guide/page/10/" - ) assert ( await outer_router( @@ -598,7 +584,6 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): django_reverse("universe:earth:test", urlconf=root_urlconf, args=(10,)) == "/universe/earth/test/10/" ) - assert reverse("universe:earth:test", args=(10,)) == "/universe/earth/test/10/" assert ( await outer_router( @@ -615,4 +600,3 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): django_reverse("universe:earth:home", urlconf=root_urlconf) == "/universe/earth/home/" ) - assert reverse("universe:earth:home") == "/universe/earth/home/" From 05a869abf4d74f0ec9fdd8da58e6e66f7affdaba Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 10 Aug 2024 11:57:43 +0900 Subject: [PATCH 19/23] removed root_urlconf in conftest (#2110) --- tests/conftest.py | 7 ------- tests/test_routing.py | 8 ++++++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1045194a..3579749e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -41,13 +41,6 @@ def samesite_invalid(settings): settings.SESSION_COOKIE_SAMESITE = "Hello" -@pytest.fixture -def root_urlconf(settings): - """Set ROOT_WEBSOCKET_URLCONF.""" - settings.ROOT_WEBSOCKET_URLCONF = "__src.routings" - return settings.ROOT_WEBSOCKET_URLCONF - - @pytest.fixture(autouse=True) def mock_modules(): """Save original modules for each test and clear a cache""" diff --git a/tests/test_routing.py b/tests/test_routing.py index a5462b88..e79444d3 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -302,7 +302,7 @@ async def test_path_remaining(): @pytest.mark.asyncio -async def test_url_router_nesting_by_include(root_urlconf): +async def test_url_router_nesting_by_include(): """ Tests that nested URLRouters is constructed by include function. """ @@ -310,6 +310,8 @@ async def test_url_router_nesting_by_include(root_urlconf): from django.urls import include from django.urls import reverse as django_reverse + root_urlconf = "__src.routings" + test_app = MockApplication(return_value=1) # mocking the universe module following the directory structure; @@ -442,7 +444,7 @@ async def test_url_router_nesting_by_include(root_urlconf): @pytest.mark.asyncio -async def test_url_router_deep_nesting_by_include(root_urlconf): +async def test_url_router_deep_nesting_by_include(): """ Tests that deep nested URLRouters is constructed by include function. """ @@ -450,6 +452,8 @@ async def test_url_router_deep_nesting_by_include(root_urlconf): from django.urls import include from django.urls import reverse as django_reverse + root_urlconf = "__src.routings" + test_app = MockApplication(return_value=1) # mocking the universe module following the directory structure; From 6773674696cfd0d402528b00ab86fed0673293c6 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Sat, 10 Aug 2024 12:25:24 +0900 Subject: [PATCH 20/23] sorted by isort (#2110) --- channels/routing.py | 2 +- tests/conftest.py | 3 ++- tests/test_routing.py | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 27fdeaba..75c8c42e 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -4,7 +4,7 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.urls.exceptions import Resolver404 -from django.urls.resolvers import RegexPattern, RoutePattern, URLResolver, URLPattern +from django.urls.resolvers import RegexPattern, RoutePattern, URLPattern, URLResolver """ All Routing instances inside this file are also valid ASGI applications - with diff --git a/tests/conftest.py b/tests/conftest.py index 3579749e..27f84603 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ -import pytest import sys + +import pytest from django.conf import settings diff --git a/tests/test_routing.py b/tests/test_routing.py index e79444d3..422f1363 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -307,6 +307,7 @@ async def test_url_router_nesting_by_include(): Tests that nested URLRouters is constructed by include function. """ import sys + from django.urls import include from django.urls import reverse as django_reverse @@ -449,6 +450,7 @@ async def test_url_router_deep_nesting_by_include(): Tests that deep nested URLRouters is constructed by include function. """ import sys + from django.urls import include from django.urls import reverse as django_reverse From a30357234342e700e51f376958825ce8c0d7b9ca Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Thu, 19 Sep 2024 22:26:45 +0900 Subject: [PATCH 21/23] add drafts for docs #2110 --- docs/topics/routing.rst | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/topics/routing.rst b/docs/topics/routing.rst index 983e7ef4..e265287d 100644 --- a/docs/topics/routing.rst +++ b/docs/topics/routing.rst @@ -103,10 +103,29 @@ would do this: stream = self.scope["url_route"]["kwargs"]["stream"] -Please note that ``URLRouter`` nesting will not work properly with -``path()`` routes if inner routers are wrapped by additional middleware. -See `Issue #1428 `__. +You can use [include](https://docs.djangoproject.com/en/5.1/ref/urls/#include) +function for nested routings. This is similar as Django's URL routing system. + +Here's an example for nested routings. When you configure the routings in parent ``routings.py``; + +.. code-block:: python + + urlpatterns = [ + path("app1/", include("src.app1.routings"), name="app1"), + ] + +and in child ``app1/routings.py``; + +.. code-block:: python + + app_name = 'app1' + + urlpatterns = [ + re_path(r"chats/(\d+)/$", test_app, name="chats"), + ] + +you can establish the connection via the path such like ``/app1/chats/5/``. ChannelNameRouter ----------------- From c2b5ee0cd90880bf94939ca3c1c050e4312c8fe3 Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Tue, 8 Oct 2024 21:59:43 +0900 Subject: [PATCH 22/23] modified routing.rst (#2110) --- docs/topics/routing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/routing.rst b/docs/topics/routing.rst index e265287d..f4c78925 100644 --- a/docs/topics/routing.rst +++ b/docs/topics/routing.rst @@ -112,7 +112,7 @@ Here's an example for nested routings. When you configure the routings in parent .. code-block:: python urlpatterns = [ - path("app1/", include("src.app1.routings"), name="app1"), + path("app1/", include("app1.routings"), name="app1"), ] and in child ``app1/routings.py``; @@ -125,7 +125,7 @@ and in child ``app1/routings.py``; re_path(r"chats/(\d+)/$", test_app, name="chats"), ] -you can establish the connection via the path such like ``/app1/chats/5/``. +This would resolve to a path such as ``/app1/chats/5/``. ChannelNameRouter ----------------- From 67d5240ea1e79078a0ebf11c2a88a9c4b9ca8a8b Mon Sep 17 00:00:00 2001 From: jjjkkkjjj Date: Tue, 8 Oct 2024 22:04:41 +0900 Subject: [PATCH 23/23] modified routing.py (#2110) --- channels/routing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/channels/routing.py b/channels/routing.py index 75c8c42e..66f5fd41 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -54,11 +54,12 @@ async def __call__(self, scope, receive, send): def _parse_resolver(child_url_pattern, parent_resolver, parent_regex, routes): - """Parse resolver (returned by `include`) recurrsively + """ + Parse resolver (returned by `include`) recurrsively Parameters ---------- - child_url_pattern : URLResolver | + child_url_pattern : URLResolver | Any The child url pattern parent_resolver : URLResolver The parent resolver