Skip to content

Commit a8f4c2a

Browse files
authored
Adopt aiohttp 3 (#160)
* Convert to new test fixtures * Fix flake8 * Make tests passed * Drop old resource adapter * Update requirements
1 parent 6425463 commit a8f4c2a

6 files changed

Lines changed: 107 additions & 287 deletions

File tree

aiohttp_cors/cors_config.py

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
from aiohttp import hdrs, web
2323

24-
from .urldispatcher_router_adapter import OldRoutesUrlDispatcherRouterAdapter
2524
from .urldispatcher_router_adapter import ResourcesUrlDispatcherRouterAdapter
2625
from .abc import AbstractRouterAdapter
2726
from .resource_options import ResourceOptions
@@ -233,23 +232,11 @@ def __init__(self, app: web.Application, *,
233232

234233
self._old_routes_cors_impl = None
235234

236-
if router_adapter is not None:
237-
self._cors_impl = _CorsConfigImpl(app, router_adapter)
238-
239-
elif isinstance(app.router, web.UrlDispatcher):
240-
self._resources_router_adapter = \
235+
if router_adapter is None:
236+
router_adapter = \
241237
ResourcesUrlDispatcherRouterAdapter(app.router, self.defaults)
242-
self._resources_cors_impl = _CorsConfigImpl(
243-
app,
244-
self._resources_router_adapter)
245-
self._old_routes_cors_impl = _CorsConfigImpl(
246-
app,
247-
OldRoutesUrlDispatcherRouterAdapter(app.router, self.defaults))
248-
else:
249-
raise RuntimeError(
250-
"Router adapter is not specified. "
251-
"Routers other than aiohttp.web.UrlDispatcher requires"
252-
"custom router adapter.")
238+
239+
self._cors_impl = _CorsConfigImpl(app, router_adapter)
253240

254241
def add(self,
255242
routing_entity,
@@ -273,30 +260,4 @@ def add(self,
273260
DeprecationWarning,
274261
stacklevel=2)
275262

276-
if self._cors_impl is not None:
277-
# Custom router adapter.
278-
return self._cors_impl.add(routing_entity, config)
279-
280-
else:
281-
# UrlDispatcher.
282-
283-
if isinstance(routing_entity, (web.Resource, web.StaticResource)):
284-
# New Resource - use new router adapter.
285-
return self._resources_cors_impl.add(routing_entity, config)
286-
287-
elif isinstance(routing_entity, web.AbstractRoute):
288-
if self._resources_router_adapter.is_cors_for_resource(
289-
routing_entity.resource):
290-
# Route which resource has CORS configuration in
291-
# new-style router adapter.
292-
return self._resources_cors_impl.add(
293-
routing_entity, config)
294-
else:
295-
# Route which resource has no CORS configuration, i.e.
296-
# old-style route.
297-
return self._old_routes_cors_impl.add(
298-
routing_entity, config)
299-
300-
else:
301-
raise ValueError(
302-
"Unknown resource/route type: {!r}".format(routing_entity))
263+
return self._cors_impl.add(routing_entity, config)

aiohttp_cors/urldispatcher_router_adapter.py

Lines changed: 39 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"""AbstractRouterAdapter for aiohttp.web.UrlDispatcher.
1616
"""
1717
import collections
18-
import re
1918

2019
from typing import Union
2120

@@ -89,16 +88,19 @@ def __init__(self, default_config):
8988
self.method_config = {}
9089

9190

92-
def _is_web_view(entity):
91+
def _is_web_view(entity, strict=True):
9392
webview = False
9493
if isinstance(entity, web.AbstractRoute):
9594
handler = entity.handler
9695
if isinstance(handler, type) and issubclass(handler, web.View):
9796
webview = True
9897
if not issubclass(handler, CorsViewMixin):
99-
raise ValueError("web view should be derivad from "
100-
"aiohttp_cors.WebViewMixig for working "
101-
"with the library")
98+
if strict:
99+
raise ValueError("web view should be derived from "
100+
"aiohttp_cors.WebViewMixig for working "
101+
"with the library")
102+
else:
103+
return False
102104
return webview
103105

104106

@@ -143,10 +145,6 @@ def add_preflight_handler(
143145
Should fail if there are conflicting user-defined OPTIONS handlers.
144146
"""
145147

146-
webview = _is_web_view(routing_entity)
147-
if webview:
148-
raise ValueError("WebView should use Route based dispatcher.")
149-
150148
if isinstance(routing_entity, web.Resource):
151149
resource = routing_entity
152150

@@ -155,6 +153,22 @@ def add_preflight_handler(
155153
if resource in self._resources_with_preflight_handlers:
156154
# Preflight handler already added for this resource.
157155
return
156+
for route_obj in resource:
157+
if route_obj.method == hdrs.METH_OPTIONS:
158+
if route_obj.handler is handler:
159+
return # already added
160+
else:
161+
raise ValueError(
162+
"{!r} already has OPTIONS handler {!r}"
163+
.format(resource, route_obj.handler))
164+
elif route_obj.method == hdrs.METH_ANY:
165+
if _is_web_view(route_obj):
166+
self._preflight_routes.add(route_obj)
167+
self._resources_with_preflight_handlers.add(resource)
168+
return
169+
else:
170+
raise ValueError("{!r} already has a '*' handler "
171+
"for all methods".format(resource))
158172

159173
preflight_route = resource.add_route(hdrs.METH_OPTIONS, handler)
160174
self._preflight_routes.add(preflight_route)
@@ -177,13 +191,8 @@ def add_preflight_handler(
177191
elif isinstance(routing_entity, web.ResourceRoute):
178192
route = routing_entity
179193

180-
# Preflight handler for Route's Resource already must be
181-
# configured.
182194
if not self.is_cors_for_resource(route.resource):
183-
raise ValueError(
184-
"Can't setup CORS for {!r} request, "
185-
"CORS must be enabled for route's resource first.".format(
186-
route))
195+
self.add_preflight_handler(route.resource, handler)
187196

188197
else:
189198
raise ValueError(
@@ -204,8 +213,10 @@ def _request_resource(self, request: web.Request) -> web.Resource:
204213

205214
def is_preflight_request(self, request: web.Request) -> bool:
206215
"""Is `request` is a CORS preflight request."""
207-
208-
return self._request_route(request) in self._preflight_routes
216+
route = self._request_route(request)
217+
if _is_web_view(route, strict=False):
218+
return request.method == 'OPTIONS'
219+
return route in self._preflight_routes
209220

210221
def is_cors_enabled_on_request(self, request: web.Request) -> bool:
211222
"""Is `request` is a request for CORS-enabled resource."""
@@ -219,8 +230,6 @@ def set_config_for_routing_entity(
219230
config):
220231
"""Record configuration for resource or it's route."""
221232

222-
_is_web_view(routing_entity)
223-
224233
if isinstance(routing_entity, (web.Resource, web.StaticResource)):
225234
resource = routing_entity
226235

@@ -237,6 +246,9 @@ def set_config_for_routing_entity(
237246
route = routing_entity
238247

239248
# Add resource's route configuration or fail if it's already added.
249+
if route.resource not in self._resource_config:
250+
self.set_config_for_routing_entity(route.resource, config)
251+
240252
if route.resource not in self._resource_config:
241253
raise ValueError(
242254
"Can't setup CORS for {!r} request, "
@@ -297,168 +309,16 @@ def get_non_preflight_request_config(self, request: web.Request):
297309
resource_config = self._resource_config[resource]
298310
# Take Route config (if any) with defaults from Resource CORS
299311
# configuration and global defaults.
312+
route = request.match_info.route
313+
if _is_web_view(route, strict=False):
314+
method_config = request.match_info.handler.get_request_config(
315+
request, request.method)
316+
else:
317+
method_config = resource_config.method_config.get(request.method,
318+
{})
300319
defaulted_config = collections.ChainMap(
301-
resource_config.method_config.get(request.method, {}),
320+
method_config,
302321
resource_config.default_config,
303322
self._default_config)
304323

305324
return defaulted_config
306-
307-
308-
class OldRoutesUrlDispatcherRouterAdapter(AbstractRouterAdapter):
309-
"""Adapter for `UrlDispatcher` for old-style routing only.
310-
311-
In all use cases when Resource is not explicitly used,
312-
Resource will automatically allocated for old route.
313-
In this case all routes will have it's own resource, and to find
314-
related routes (routes that shares same path) we need to iterate over
315-
all routes with enabled CORS and check is they handle specific path.
316-
317-
This whole class should go away when user will migrate to proper
318-
Resource/Route usage scheme.
319-
"""
320-
321-
def __init__(self,
322-
router: web.UrlDispatcher,
323-
defaults):
324-
"""
325-
:param defaults:
326-
Default CORS configuration.
327-
"""
328-
self._router = router
329-
330-
# Default configuration for all routes.
331-
self._default_config = defaults
332-
333-
# Mapping from route to config.
334-
self._route_config = collections.OrderedDict()
335-
336-
self._preflight_routes = set()
337-
338-
def add_preflight_handler(
339-
self,
340-
route: web.AbstractRoute,
341-
handler):
342-
"""Add OPTIONS handler for same paths that `route` handles."""
343-
344-
assert isinstance(route, web.AbstractRoute)
345-
webview = _is_web_view(route)
346-
347-
if webview:
348-
self._preflight_routes.add(route)
349-
return
350-
351-
if isinstance(route, web.ResourceRoute):
352-
# New-style route (which Resource is not used explicitly,
353-
# otherwise it would be handled by other adapter).
354-
preflight_route = route.resource.add_route(
355-
hdrs.METH_OPTIONS, handler)
356-
357-
elif isinstance(route, web.Route):
358-
# Old-style route.
359-
360-
if isinstance(route, web.StaticRoute):
361-
# TODO: Use custom matches that uses `str.startswith()`
362-
# if regexp performance is not enough.
363-
pattern = re.compile("^" + re.escape(route._prefix))
364-
preflight_route = web.DynamicRoute(
365-
hdrs.METH_OPTIONS, handler, None, pattern, "")
366-
self._router.register_route(preflight_route)
367-
368-
elif isinstance(route, web.PlainRoute):
369-
# May occur only if user manually creates PlainRoute.
370-
preflight_route = self._router.add_route(
371-
hdrs.METH_OPTIONS, route._path, handler)
372-
373-
elif isinstance(route, web.DynamicRoute):
374-
# May occur only if user manually creates DynamicRoute.
375-
preflight_route = web.DynamicRoute(
376-
hdrs.METH_OPTIONS, handler, None,
377-
route._pattern, route._formatter)
378-
self._router.register_route(preflight_route)
379-
380-
else:
381-
raise RuntimeError(
382-
"Unhandled deprecated route type {!r}".format(route))
383-
384-
else:
385-
raise RuntimeError("Unhandled route type {!r}".format(route))
386-
387-
self._preflight_routes.add(preflight_route)
388-
389-
def _request_route(self, request: web.Request) -> web.ResourceRoute:
390-
match_info = request.match_info
391-
assert isinstance(match_info, web.UrlMappingMatchInfo)
392-
return match_info.route
393-
394-
def is_preflight_request(self, request: web.Request) -> bool:
395-
"""Is `request` is a CORS preflight request."""
396-
397-
return (self._request_route(request) in self._preflight_routes and
398-
request.method == hdrs.METH_OPTIONS)
399-
400-
def is_cors_enabled_on_request(self, request: web.Request) -> bool:
401-
"""Is `request` is a request for CORS-enabled resource."""
402-
403-
return self._request_route(request) in self._route_config
404-
405-
def set_config_for_routing_entity(
406-
self,
407-
route: web.AbstractRoute,
408-
config):
409-
"""Record CORS configuration for route."""
410-
411-
assert isinstance(route, web.AbstractRoute)
412-
webview = _is_web_view(route)
413-
414-
if any(options.allow_methods is not None
415-
for options in config.values()):
416-
raise ValueError(
417-
"'allow_methods' parameter is not supported on old-style "
418-
"routes. You specified {!r} for {!r}. "
419-
"Use Resources to configure CORS.".format(
420-
config, route))
421-
422-
if route in self._route_config:
423-
raise ValueError(
424-
"CORS is already configured for {!r} route.".format(
425-
route))
426-
427-
if webview:
428-
config = {'webview': True}
429-
430-
self._route_config[route] = config
431-
432-
async def get_preflight_request_config(
433-
self,
434-
preflight_request: web.Request,
435-
origin: str,
436-
requested_method: str):
437-
assert self.is_preflight_request(preflight_request)
438-
439-
request = preflight_request.clone(method=requested_method)
440-
for route, config in self._route_config.items():
441-
match_info, allowed_methods = await route.resource.resolve(
442-
request)
443-
if match_info is not None:
444-
return collections.ChainMap(config, self._default_config)
445-
else:
446-
raise KeyError
447-
448-
def get_non_preflight_request_config(self, request: web.Request):
449-
"""Get stored CORS configuration for routing entity that handles
450-
specified request."""
451-
452-
assert self.is_cors_enabled_on_request(request)
453-
454-
route = self._request_route(request)
455-
route_config = self._route_config[route]
456-
457-
if route_config.get('webview', False):
458-
handler = self._request_route(request).handler
459-
route_config = handler.get_request_config(request, request.method)
460-
461-
defaulted_config = collections.ChainMap(
462-
route_config, self._default_config)
463-
464-
return defaulted_config

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
aiohttp==2.3.10
1+
aiohttp==3.0.5
22
tox==2.9.1
33
pytest==3.4.0
44
pytest-aiohttp==0.3.0

0 commit comments

Comments
 (0)