We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Default responders aren't wrapped with global hooks
This includes the OPTIONS responder and 405 responder (method not allowed because no responder is defined for the method, for the requested resource).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `falcon/api_helpers.py`
Content:
```
1 """Includes private helpers for the API class.
2 
3 Copyright 2013 by Rackspace Hosting, Inc.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9    http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 """
18 
19 import inspect
20 import re
21 from functools import wraps
22 
23 from falcon import responders, HTTP_METHODS
24 import falcon.status_codes as status
25 
26 
27 IGNORE_BODY_STATUS_CODES = set([
28     status.HTTP_100,
29     status.HTTP_101,
30     status.HTTP_204,
31     status.HTTP_416,
32     status.HTTP_304
33 ])
34 
35 
36 def prepare_global_hooks(hooks):
37     if hooks is not None:
38         if not isinstance(hooks, list):
39             hooks = [hooks]
40 
41         for action in hooks:
42             if not callable(action):
43                 raise TypeError('One or more hooks are not callable')
44 
45     return hooks
46 
47 
48 def should_ignore_body(status, method):
49     """Return True if the status or method indicates no body, per RFC 2616
50 
51     Args:
52         status: An HTTP status line, e.g., "204 No Content"
53 
54     Returns:
55         True if method is HEAD, or the status is 1xx, 204, or 304; returns
56         False otherwise.
57 
58     """
59 
60     return (method == 'HEAD' or status in IGNORE_BODY_STATUS_CODES)
61 
62 
63 def set_content_length(resp):
64     """Set Content-Length when given a fully-buffered body or stream length
65 
66     Pre:
67         Either resp.body or resp.stream is set
68     Post:
69         resp contains a "Content-Length" header unless a stream is given, but
70         resp.stream_len is not set (in which case, the length cannot be
71             derived reliably).
72     Args:
73         resp: The response object on which to set the content length.
74 
75     """
76 
77     content_length = 0
78 
79     if resp.body_encoded is not None:
80         # Since body is assumed to be a byte string (str in Python 2, bytes in
81         # Python 3), figure out the length using standard functions.
82         content_length = len(resp.body_encoded)
83     elif resp.data is not None:
84         content_length = len(resp.data)
85     elif resp.stream is not None:
86         if resp.stream_len is not None:
87             # Total stream length is known in advance (e.g., streaming a file)
88             content_length = resp.stream_len
89         else:
90             # Stream given, but length is unknown (dynamically-generated body)
91             # ...do not set the header.
92             return -1
93 
94     resp.set_header('Content-Length', str(content_length))
95     return content_length
96 
97 
98 def get_body(resp):
99     """Converts resp content into an iterable as required by PEP 333
100 
101     Args:
102         resp: Instance of falcon.Response
103 
104     Returns:
105         * If resp.body is not None, returns [resp.body], encoded as UTF-8 if
106           it is a Unicode string. Bytestrings are returned as-is.
107         * If resp.data is not None, returns [resp.data]
108         * If resp.stream is not None, returns resp.stream
109         * Otherwise, returns []
110 
111     """
112 
113     body = resp.body_encoded
114 
115     if body is not None:
116         return [body]
117 
118     elif resp.data is not None:
119         return [resp.data]
120 
121     elif resp.stream is not None:
122         return resp.stream
123 
124     return []
125 
126 
127 def compile_uri_template(template):
128     """Compile the given URI template string into a pattern matcher.
129 
130     Currently only recognizes Level 1 URI templates, and only for the path
131     portion of the URI.
132 
133     See also: http://tools.ietf.org/html/rfc6570
134 
135     Args:
136         template: A Level 1 URI template. Method responders must accept, as
137         arguments, all fields specified in the template (default '/').
138 
139     Returns:
140         (template_field_names, template_regex)
141 
142     """
143 
144     if not isinstance(template, str):
145         raise TypeError('uri_template is not a string')
146 
147     if not template.startswith('/'):
148         raise ValueError("uri_template must start with '/'")
149 
150     if '//' in template:
151         raise ValueError("uri_template may not contain '//'")
152 
153     if template != '/' and template.endswith('/'):
154         template = template[:-1]
155 
156     expression_pattern = r'{([a-zA-Z][a-zA-Z_]*)}'
157 
158     # Get a list of field names
159     fields = set(re.findall(expression_pattern, template))
160 
161     # Convert Level 1 var patterns to equivalent named regex groups
162     escaped = re.sub(r'[\.\(\)\[\]\?\*\+\^\|]', r'\\\g<0>', template)
163     pattern = re.sub(expression_pattern, r'(?P<\1>[^/]+)', escaped)
164     pattern = r'\A' + pattern + r'\Z'
165 
166     return fields, re.compile(pattern, re.IGNORECASE)
167 
168 
169 def create_http_method_map(resource, uri_fields, before, after):
170     """Maps HTTP methods (such as GET and POST) to methods of resource object
171 
172     Args:
173         resource: An object with "responder" methods, starting with on_*, that
174             correspond to each method the resource supports. For example, if a
175             resource supports GET and POST, it should define
176             on_get(self, req, resp) and on_post(self,req,resp).
177         uri_fields: A set of field names from the route's URI template that
178             a responder must support in order to avoid "method not allowed".
179         before: An action hook or list of hooks to be called before each
180             on_* responder defined by the resource.
181         after: An action hook or list of hooks to be called after each on_*
182             responder defined by the resource.
183 
184     Returns:
185         A tuple containing a dict mapping HTTP methods to responders, and
186         the method-not-allowed responder.
187 
188     """
189 
190     method_map = {}
191 
192     for method in HTTP_METHODS:
193         try:
194             responder = getattr(resource, 'on_' + method.lower())
195         except AttributeError:
196             # resource does not implement this method
197             pass
198         else:
199             # Usually expect a method, but any callable will do
200             if callable(responder):
201                 responder = _wrap_with_hooks(before, after, responder)
202                 method_map[method] = responder
203 
204     # Attach a resource for unsupported HTTP methods
205     allowed_methods = sorted(list(method_map.keys()))
206 
207     if 'OPTIONS' not in method_map:
208         # OPTIONS itself is intentionally excluded from the Allow header
209         # This default responder does not run the hooks
210         method_map['OPTIONS'] = responders.create_default_options(
211             allowed_methods)
212         allowed_methods.append('OPTIONS')
213 
214     na_responder = responders.create_method_not_allowed(allowed_methods)
215 
216     for method in HTTP_METHODS:
217         if method not in allowed_methods:
218             method_map[method] = na_responder
219 
220     return method_map
221 
222 
223 #-----------------------------------------------------------------------------
224 # Helpers
225 #-----------------------------------------------------------------------------
226 
227 
228 def _wrap_with_hooks(before, after, responder):
229     if after is not None:
230         for action in after:
231             responder = _wrap_with_after(action, responder)
232 
233     if before is not None:
234         # Wrap in reversed order to achieve natural (first...last)
235         # execution order.
236         for action in reversed(before):
237             responder = _wrap_with_before(action, responder)
238 
239     return responder
240 
241 
242 def _propagate_argspec(wrapper, responder):
243     if hasattr(responder, 'wrapped_argspec'):
244         wrapper.wrapped_argspec = responder.wrapped_argspec
245     else:
246         wrapper.wrapped_argspec = inspect.getargspec(responder)
247 
248 
249 def _wrap_with_before(action, responder):
250     """Execute the given action function before a bound responder.
251 
252     Args:
253         action: A function with a similar signature to a resource responder
254             method, taking (req, resp, params).
255         responder: The bound responder to wrap.
256 
257     """
258 
259     @wraps(responder)
260     def do_before(req, resp, **kwargs):
261         action(req, resp, kwargs)
262         responder(req, resp, **kwargs)
263 
264     _propagate_argspec(do_before, responder)
265 
266     return do_before
267 
268 
269 def _wrap_with_after(action, responder):
270     """Execute the given action function after a bound responder.
271 
272     Args:
273         action: A function with a signature similar to a resource responder
274             method, taking (req, resp).
275         responder: The bound responder to wrap.
276 
277     """
278 
279     @wraps(responder)
280     def do_after(req, resp, **kwargs):
281         responder(req, resp, **kwargs)
282         action(req, resp)
283 
284     _propagate_argspec(do_after, responder)
285 
286     return do_after
287
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/falcon/api_helpers.py b/falcon/api_helpers.py
--- a/falcon/api_helpers.py
+++ b/falcon/api_helpers.py
@@ -204,18 +204,20 @@
     # Attach a resource for unsupported HTTP methods
     allowed_methods = sorted(list(method_map.keys()))
 
+    # NOTE(sebasmagri): We want the OPTIONS and 405 (Not Allowed) methods
+    # responders to be wrapped on global hooks
     if 'OPTIONS' not in method_map:
         # OPTIONS itself is intentionally excluded from the Allow header
-        # This default responder does not run the hooks
-        method_map['OPTIONS'] = responders.create_default_options(
+        responder = responders.create_default_options(
             allowed_methods)
+        method_map['OPTIONS'] = _wrap_with_hooks(before, after, responder)
         allowed_methods.append('OPTIONS')
 
     na_responder = responders.create_method_not_allowed(allowed_methods)
 
     for method in HTTP_METHODS:
         if method not in allowed_methods:
-            method_map[method] = na_responder
+            method_map[method] = _wrap_with_hooks(before, after, na_responder)
 
     return method_map

{"golden_diff": "diff --git a/falcon/api_helpers.py b/falcon/api_helpers.py\n--- a/falcon/api_helpers.py\n+++ b/falcon/api_helpers.py\n@@ -204,18 +204,20 @@\n     # Attach a resource for unsupported HTTP methods\n     allowed_methods = sorted(list(method_map.keys()))\n \n+    # NOTE(sebasmagri): We want the OPTIONS and 405 (Not Allowed) methods\n+    # responders to be wrapped on global hooks\n     if 'OPTIONS' not in method_map:\n         # OPTIONS itself is intentionally excluded from the Allow header\n-        # This default responder does not run the hooks\n-        method_map['OPTIONS'] = responders.create_default_options(\n+        responder = responders.create_default_options(\n             allowed_methods)\n+        method_map['OPTIONS'] = _wrap_with_hooks(before, after, responder)\n         allowed_methods.append('OPTIONS')\n \n     na_responder = responders.create_method_not_allowed(allowed_methods)\n \n     for method in HTTP_METHODS:\n         if method not in allowed_methods:\n-            method_map[method] = na_responder\n+            method_map[method] = _wrap_with_hooks(before, after, na_responder)\n \n     return method_map\n", "issue": "Default responders aren't wrapped with global hooks\nThis includes the OPTIONS responder and 405 responder (method not allowed because no responder is defined for the method, for the requested resource).\n\n", "before_files": [{"content": "\"\"\"Includes private helpers for the API class.\n\nCopyright 2013 by Rackspace Hosting, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\"\"\"\n\nimport inspect\nimport re\nfrom functools import wraps\n\nfrom falcon import responders, HTTP_METHODS\nimport falcon.status_codes as status\n\n\nIGNORE_BODY_STATUS_CODES = set([\n    status.HTTP_100,\n    status.HTTP_101,\n    status.HTTP_204,\n    status.HTTP_416,\n    status.HTTP_304\n])\n\n\ndef prepare_global_hooks(hooks):\n    if hooks is not None:\n        if not isinstance(hooks, list):\n            hooks = [hooks]\n\n        for action in hooks:\n            if not callable(action):\n                raise TypeError('One or more hooks are not callable')\n\n    return hooks\n\n\ndef should_ignore_body(status, method):\n    \"\"\"Return True if the status or method indicates no body, per RFC 2616\n\n    Args:\n        status: An HTTP status line, e.g., \"204 No Content\"\n\n    Returns:\n        True if method is HEAD, or the status is 1xx, 204, or 304; returns\n        False otherwise.\n\n    \"\"\"\n\n    return (method == 'HEAD' or status in IGNORE_BODY_STATUS_CODES)\n\n\ndef set_content_length(resp):\n    \"\"\"Set Content-Length when given a fully-buffered body or stream length\n\n    Pre:\n        Either resp.body or resp.stream is set\n    Post:\n        resp contains a \"Content-Length\" header unless a stream is given, but\n        resp.stream_len is not set (in which case, the length cannot be\n            derived reliably).\n    Args:\n        resp: The response object on which to set the content length.\n\n    \"\"\"\n\n    content_length = 0\n\n    if resp.body_encoded is not None:\n        # Since body is assumed to be a byte string (str in Python 2, bytes in\n        # Python 3), figure out the length using standard functions.\n        content_length = len(resp.body_encoded)\n    elif resp.data is not None:\n        content_length = len(resp.data)\n    elif resp.stream is not None:\n        if resp.stream_len is not None:\n            # Total stream length is known in advance (e.g., streaming a file)\n            content_length = resp.stream_len\n        else:\n            # Stream given, but length is unknown (dynamically-generated body)\n            # ...do not set the header.\n            return -1\n\n    resp.set_header('Content-Length', str(content_length))\n    return content_length\n\n\ndef get_body(resp):\n    \"\"\"Converts resp content into an iterable as required by PEP 333\n\n    Args:\n        resp: Instance of falcon.Response\n\n    Returns:\n        * If resp.body is not None, returns [resp.body], encoded as UTF-8 if\n          it is a Unicode string. Bytestrings are returned as-is.\n        * If resp.data is not None, returns [resp.data]\n        * If resp.stream is not None, returns resp.stream\n        * Otherwise, returns []\n\n    \"\"\"\n\n    body = resp.body_encoded\n\n    if body is not None:\n        return [body]\n\n    elif resp.data is not None:\n        return [resp.data]\n\n    elif resp.stream is not None:\n        return resp.stream\n\n    return []\n\n\ndef compile_uri_template(template):\n    \"\"\"Compile the given URI template string into a pattern matcher.\n\n    Currently only recognizes Level 1 URI templates, and only for the path\n    portion of the URI.\n\n    See also: http://tools.ietf.org/html/rfc6570\n\n    Args:\n        template: A Level 1 URI template. Method responders must accept, as\n        arguments, all fields specified in the template (default '/').\n\n    Returns:\n        (template_field_names, template_regex)\n\n    \"\"\"\n\n    if not isinstance(template, str):\n        raise TypeError('uri_template is not a string')\n\n    if not template.startswith('/'):\n        raise ValueError(\"uri_template must start with '/'\")\n\n    if '//' in template:\n        raise ValueError(\"uri_template may not contain '//'\")\n\n    if template != '/' and template.endswith('/'):\n        template = template[:-1]\n\n    expression_pattern = r'{([a-zA-Z][a-zA-Z_]*)}'\n\n    # Get a list of field names\n    fields = set(re.findall(expression_pattern, template))\n\n    # Convert Level 1 var patterns to equivalent named regex groups\n    escaped = re.sub(r'[\\.\\(\\)\\[\\]\\?\\*\\+\\^\\|]', r'\\\\\\g<0>', template)\n    pattern = re.sub(expression_pattern, r'(?P<\\1>[^/]+)', escaped)\n    pattern = r'\\A' + pattern + r'\\Z'\n\n    return fields, re.compile(pattern, re.IGNORECASE)\n\n\ndef create_http_method_map(resource, uri_fields, before, after):\n    \"\"\"Maps HTTP methods (such as GET and POST) to methods of resource object\n\n    Args:\n        resource: An object with \"responder\" methods, starting with on_*, that\n            correspond to each method the resource supports. For example, if a\n            resource supports GET and POST, it should define\n            on_get(self, req, resp) and on_post(self,req,resp).\n        uri_fields: A set of field names from the route's URI template that\n            a responder must support in order to avoid \"method not allowed\".\n        before: An action hook or list of hooks to be called before each\n            on_* responder defined by the resource.\n        after: An action hook or list of hooks to be called after each on_*\n            responder defined by the resource.\n\n    Returns:\n        A tuple containing a dict mapping HTTP methods to responders, and\n        the method-not-allowed responder.\n\n    \"\"\"\n\n    method_map = {}\n\n    for method in HTTP_METHODS:\n        try:\n            responder = getattr(resource, 'on_' + method.lower())\n        except AttributeError:\n            # resource does not implement this method\n            pass\n        else:\n            # Usually expect a method, but any callable will do\n            if callable(responder):\n                responder = _wrap_with_hooks(before, after, responder)\n                method_map[method] = responder\n\n    # Attach a resource for unsupported HTTP methods\n    allowed_methods = sorted(list(method_map.keys()))\n\n    if 'OPTIONS' not in method_map:\n        # OPTIONS itself is intentionally excluded from the Allow header\n        # This default responder does not run the hooks\n        method_map['OPTIONS'] = responders.create_default_options(\n            allowed_methods)\n        allowed_methods.append('OPTIONS')\n\n    na_responder = responders.create_method_not_allowed(allowed_methods)\n\n    for method in HTTP_METHODS:\n        if method not in allowed_methods:\n            method_map[method] = na_responder\n\n    return method_map\n\n\n#-----------------------------------------------------------------------------\n# Helpers\n#-----------------------------------------------------------------------------\n\n\ndef _wrap_with_hooks(before, after, responder):\n    if after is not None:\n        for action in after:\n            responder = _wrap_with_after(action, responder)\n\n    if before is not None:\n        # Wrap in reversed order to achieve natural (first...last)\n        # execution order.\n        for action in reversed(before):\n            responder = _wrap_with_before(action, responder)\n\n    return responder\n\n\ndef _propagate_argspec(wrapper, responder):\n    if hasattr(responder, 'wrapped_argspec'):\n        wrapper.wrapped_argspec = responder.wrapped_argspec\n    else:\n        wrapper.wrapped_argspec = inspect.getargspec(responder)\n\n\ndef _wrap_with_before(action, responder):\n    \"\"\"Execute the given action function before a bound responder.\n\n    Args:\n        action: A function with a similar signature to a resource responder\n            method, taking (req, resp, params).\n        responder: The bound responder to wrap.\n\n    \"\"\"\n\n    @wraps(responder)\n    def do_before(req, resp, **kwargs):\n        action(req, resp, kwargs)\n        responder(req, resp, **kwargs)\n\n    _propagate_argspec(do_before, responder)\n\n    return do_before\n\n\ndef _wrap_with_after(action, responder):\n    \"\"\"Execute the given action function after a bound responder.\n\n    Args:\n        action: A function with a signature similar to a resource responder\n            method, taking (req, resp).\n        responder: The bound responder to wrap.\n\n    \"\"\"\n\n    @wraps(responder)\n    def do_after(req, resp, **kwargs):\n        responder(req, resp, **kwargs)\n        action(req, resp)\n\n    _propagate_argspec(do_after, responder)\n\n    return do_after\n", "path": "falcon/api_helpers.py"}], "after_files": [{"content": "\"\"\"Includes private helpers for the API class.\n\nCopyright 2013 by Rackspace Hosting, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\"\"\"\n\nimport inspect\nimport re\nfrom functools import wraps\n\nfrom falcon import responders, HTTP_METHODS\nimport falcon.status_codes as status\n\n\nIGNORE_BODY_STATUS_CODES = set([\n    status.HTTP_100,\n    status.HTTP_101,\n    status.HTTP_204,\n    status.HTTP_416,\n    status.HTTP_304\n])\n\n\ndef prepare_global_hooks(hooks):\n    if hooks is not None:\n        if not isinstance(hooks, list):\n            hooks = [hooks]\n\n        for action in hooks:\n            if not callable(action):\n                raise TypeError('One or more hooks are not callable')\n\n    return hooks\n\n\ndef should_ignore_body(status, method):\n    \"\"\"Return True if the status or method indicates no body, per RFC 2616\n\n    Args:\n        status: An HTTP status line, e.g., \"204 No Content\"\n\n    Returns:\n        True if method is HEAD, or the status is 1xx, 204, or 304; returns\n        False otherwise.\n\n    \"\"\"\n\n    return (method == 'HEAD' or status in IGNORE_BODY_STATUS_CODES)\n\n\ndef set_content_length(resp):\n    \"\"\"Set Content-Length when given a fully-buffered body or stream length\n\n    Pre:\n        Either resp.body or resp.stream is set\n    Post:\n        resp contains a \"Content-Length\" header unless a stream is given, but\n        resp.stream_len is not set (in which case, the length cannot be\n            derived reliably).\n    Args:\n        resp: The response object on which to set the content length.\n\n    \"\"\"\n\n    content_length = 0\n\n    if resp.body_encoded is not None:\n        # Since body is assumed to be a byte string (str in Python 2, bytes in\n        # Python 3), figure out the length using standard functions.\n        content_length = len(resp.body_encoded)\n    elif resp.data is not None:\n        content_length = len(resp.data)\n    elif resp.stream is not None:\n        if resp.stream_len is not None:\n            # Total stream length is known in advance (e.g., streaming a file)\n            content_length = resp.stream_len\n        else:\n            # Stream given, but length is unknown (dynamically-generated body)\n            # ...do not set the header.\n            return -1\n\n    resp.set_header('Content-Length', str(content_length))\n    return content_length\n\n\ndef get_body(resp):\n    \"\"\"Converts resp content into an iterable as required by PEP 333\n\n    Args:\n        resp: Instance of falcon.Response\n\n    Returns:\n        * If resp.body is not None, returns [resp.body], encoded as UTF-8 if\n          it is a Unicode string. Bytestrings are returned as-is.\n        * If resp.data is not None, returns [resp.data]\n        * If resp.stream is not None, returns resp.stream\n        * Otherwise, returns []\n\n    \"\"\"\n\n    body = resp.body_encoded\n\n    if body is not None:\n        return [body]\n\n    elif resp.data is not None:\n        return [resp.data]\n\n    elif resp.stream is not None:\n        return resp.stream\n\n    return []\n\n\ndef compile_uri_template(template):\n    \"\"\"Compile the given URI template string into a pattern matcher.\n\n    Currently only recognizes Level 1 URI templates, and only for the path\n    portion of the URI.\n\n    See also: http://tools.ietf.org/html/rfc6570\n\n    Args:\n        template: A Level 1 URI template. Method responders must accept, as\n        arguments, all fields specified in the template (default '/').\n\n    Returns:\n        (template_field_names, template_regex)\n\n    \"\"\"\n\n    if not isinstance(template, str):\n        raise TypeError('uri_template is not a string')\n\n    if not template.startswith('/'):\n        raise ValueError(\"uri_template must start with '/'\")\n\n    if '//' in template:\n        raise ValueError(\"uri_template may not contain '//'\")\n\n    if template != '/' and template.endswith('/'):\n        template = template[:-1]\n\n    expression_pattern = r'{([a-zA-Z][a-zA-Z_]*)}'\n\n    # Get a list of field names\n    fields = set(re.findall(expression_pattern, template))\n\n    # Convert Level 1 var patterns to equivalent named regex groups\n    escaped = re.sub(r'[\\.\\(\\)\\[\\]\\?\\*\\+\\^\\|]', r'\\\\\\g<0>', template)\n    pattern = re.sub(expression_pattern, r'(?P<\\1>[^/]+)', escaped)\n    pattern = r'\\A' + pattern + r'\\Z'\n\n    return fields, re.compile(pattern, re.IGNORECASE)\n\n\ndef create_http_method_map(resource, uri_fields, before, after):\n    \"\"\"Maps HTTP methods (such as GET and POST) to methods of resource object\n\n    Args:\n        resource: An object with \"responder\" methods, starting with on_*, that\n            correspond to each method the resource supports. For example, if a\n            resource supports GET and POST, it should define\n            on_get(self, req, resp) and on_post(self,req,resp).\n        uri_fields: A set of field names from the route's URI template that\n            a responder must support in order to avoid \"method not allowed\".\n        before: An action hook or list of hooks to be called before each\n            on_* responder defined by the resource.\n        after: An action hook or list of hooks to be called after each on_*\n            responder defined by the resource.\n\n    Returns:\n        A tuple containing a dict mapping HTTP methods to responders, and\n        the method-not-allowed responder.\n\n    \"\"\"\n\n    method_map = {}\n\n    for method in HTTP_METHODS:\n        try:\n            responder = getattr(resource, 'on_' + method.lower())\n        except AttributeError:\n            # resource does not implement this method\n            pass\n        else:\n            # Usually expect a method, but any callable will do\n            if callable(responder):\n                responder = _wrap_with_hooks(before, after, responder)\n                method_map[method] = responder\n\n    # Attach a resource for unsupported HTTP methods\n    allowed_methods = sorted(list(method_map.keys()))\n\n    # NOTE(sebasmagri): We want the OPTIONS and 405 (Not Allowed) methods\n    # responders to be wrapped on global hooks\n    if 'OPTIONS' not in method_map:\n        # OPTIONS itself is intentionally excluded from the Allow header\n        responder = responders.create_default_options(\n            allowed_methods)\n        method_map['OPTIONS'] = _wrap_with_hooks(before, after, responder)\n        allowed_methods.append('OPTIONS')\n\n    na_responder = responders.create_method_not_allowed(allowed_methods)\n\n    for method in HTTP_METHODS:\n        if method not in allowed_methods:\n            method_map[method] = _wrap_with_hooks(before, after, na_responder)\n\n    return method_map\n\n\n#-----------------------------------------------------------------------------\n# Helpers\n#-----------------------------------------------------------------------------\n\n\ndef _wrap_with_hooks(before, after, responder):\n    if after is not None:\n        for action in after:\n            responder = _wrap_with_after(action, responder)\n\n    if before is not None:\n        # Wrap in reversed order to achieve natural (first...last)\n        # execution order.\n        for action in reversed(before):\n            responder = _wrap_with_before(action, responder)\n\n    return responder\n\n\ndef _propagate_argspec(wrapper, responder):\n    if hasattr(responder, 'wrapped_argspec'):\n        wrapper.wrapped_argspec = responder.wrapped_argspec\n    else:\n        wrapper.wrapped_argspec = inspect.getargspec(responder)\n\n\ndef _wrap_with_before(action, responder):\n    \"\"\"Execute the given action function before a bound responder.\n\n    Args:\n        action: A function with a similar signature to a resource responder\n            method, taking (req, resp, params).\n        responder: The bound responder to wrap.\n\n    \"\"\"\n\n    @wraps(responder)\n    def do_before(req, resp, **kwargs):\n        action(req, resp, kwargs)\n        responder(req, resp, **kwargs)\n\n    _propagate_argspec(do_before, responder)\n\n    return do_before\n\n\ndef _wrap_with_after(action, responder):\n    \"\"\"Execute the given action function after a bound responder.\n\n    Args:\n        action: A function with a signature similar to a resource responder\n            method, taking (req, resp).\n        responder: The bound responder to wrap.\n\n    \"\"\"\n\n    @wraps(responder)\n    def do_after(req, resp, **kwargs):\n        responder(req, resp, **kwargs)\n        action(req, resp)\n\n    _propagate_argspec(do_after, responder)\n\n    return do_after\n", "path": "falcon/api_helpers.py"}]}