fresco 3.3.4__py3-none-any.whl → 3.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fresco/__init__.py CHANGED
@@ -12,10 +12,63 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
- __version__ = "3.3.4"
15
+ from fresco.request import Request
16
+ from fresco.request import currentrequest
17
+ from fresco.requestcontext import context
18
+ from fresco.response import Response
19
+ from fresco.core import FrescoApp
20
+ from fresco.core import urlfor
21
+ from fresco.defaults import DEFAULT_CHARSET
22
+ from fresco.options import Options
23
+ from fresco.routing import Route
24
+ from fresco.routing import RRoute
25
+ from fresco.routing import RouteCollection
26
+ from fresco.routing import DelegateRoute
27
+ from fresco.routing import routefor
28
+ from fresco.routing import ALL_METHODS
29
+ from fresco.routing import GET
30
+ from fresco.routing import HEAD
31
+ from fresco.routing import POST
32
+ from fresco.routing import PUT
33
+ from fresco.routing import DELETE
34
+ from fresco.routing import OPTIONS
35
+ from fresco.routing import TRACE
36
+ from fresco.routing import CONNECT
37
+ from fresco.routing import VERSION_CONTROL
38
+ from fresco.routing import REPORT
39
+ from fresco.routing import CHECKOUT
40
+ from fresco.routing import CHECKIN
41
+ from fresco.routing import UNCHECKOUT
42
+ from fresco.routing import MKWORKSPACE
43
+ from fresco.routing import UPDATE
44
+ from fresco.routing import LABEL
45
+ from fresco.routing import MERGE
46
+ from fresco.routing import BASELINE_CONTROL
47
+ from fresco.routing import MKACTIVITY
48
+ from fresco.routing import ORDERPATCH
49
+ from fresco.routing import ACL
50
+ from fresco.routing import SEARCH
51
+ from fresco.routing import PATCH
52
+ from fresco.routeargs import routearg
53
+ from fresco.routeargs import FormArg
54
+ from fresco.routeargs import PostArg
55
+ from fresco.routeargs import QueryArg
56
+ from fresco.routeargs import GetArg
57
+ from fresco.routeargs import CookieArg
58
+ from fresco.routeargs import SessionArg
59
+ from fresco.routeargs import RequestObject
60
+ from fresco.routeargs import FormData
61
+ from fresco.routeargs import PostData
62
+ from fresco.routeargs import QueryData
63
+ from fresco.routeargs import GetData
64
+ from fresco.middleware import XForwarded
65
+ from fresco.subrequests import subrequest
66
+ from fresco.subrequests import subrequest_bytes
67
+ from fresco.subrequests import subrequest_raw
68
+ from fresco.util.common import object_or_404
16
69
 
17
- DEFAULT_CHARSET = "UTF-8"
18
70
 
71
+ __version__ = "3.9.0"
19
72
  __all__ = [
20
73
  "Request",
21
74
  "currentrequest",
@@ -72,57 +125,3 @@ __all__ = [
72
125
  "subrequest_raw",
73
126
  "object_or_404",
74
127
  ]
75
-
76
- from fresco.request import Request
77
- from fresco.request import currentrequest
78
- from fresco.requestcontext import context
79
- from fresco.response import Response
80
- from fresco.core import FrescoApp
81
- from fresco.core import urlfor
82
- from fresco.options import Options
83
- from fresco.routing import Route
84
- from fresco.routing import RRoute
85
- from fresco.routing import RouteCollection
86
- from fresco.routing import DelegateRoute
87
- from fresco.routing import routefor
88
- from fresco.routing import ALL_METHODS
89
- from fresco.routing import GET
90
- from fresco.routing import HEAD
91
- from fresco.routing import POST
92
- from fresco.routing import PUT
93
- from fresco.routing import DELETE
94
- from fresco.routing import OPTIONS
95
- from fresco.routing import TRACE
96
- from fresco.routing import CONNECT
97
- from fresco.routing import VERSION_CONTROL
98
- from fresco.routing import REPORT
99
- from fresco.routing import CHECKOUT
100
- from fresco.routing import CHECKIN
101
- from fresco.routing import UNCHECKOUT
102
- from fresco.routing import MKWORKSPACE
103
- from fresco.routing import UPDATE
104
- from fresco.routing import LABEL
105
- from fresco.routing import MERGE
106
- from fresco.routing import BASELINE_CONTROL
107
- from fresco.routing import MKACTIVITY
108
- from fresco.routing import ORDERPATCH
109
- from fresco.routing import ACL
110
- from fresco.routing import SEARCH
111
- from fresco.routing import PATCH
112
- from fresco.routeargs import routearg
113
- from fresco.routeargs import FormArg
114
- from fresco.routeargs import PostArg
115
- from fresco.routeargs import QueryArg
116
- from fresco.routeargs import GetArg
117
- from fresco.routeargs import CookieArg
118
- from fresco.routeargs import SessionArg
119
- from fresco.routeargs import RequestObject
120
- from fresco.routeargs import FormData
121
- from fresco.routeargs import PostData
122
- from fresco.routeargs import QueryData
123
- from fresco.routeargs import GetData
124
- from fresco.middleware import XForwarded
125
- from fresco.subrequests import subrequest
126
- from fresco.subrequests import subrequest_bytes
127
- from fresco.subrequests import subrequest_raw
128
- from fresco.util.common import object_or_404
fresco/core.py CHANGED
@@ -33,7 +33,10 @@ from fresco.util.http import encode_multipart
33
33
  from fresco.util.urls import normpath, make_query
34
34
  from fresco.util.common import fq_path
35
35
  from fresco.util.wsgi import make_environ
36
- from fresco.typing import WSGICallable
36
+ from fresco.types import WSGIApplication
37
+ from fresco.types import HeaderList
38
+ from fresco.types import OptionalExcInfo
39
+ from fresco.types import WriteCallable
37
40
 
38
41
  from fresco.exceptions import ResponseException
39
42
  from fresco.requestcontext import context
@@ -46,10 +49,12 @@ from fresco.routing import (
46
49
  )
47
50
  from fresco.options import Options
48
51
 
49
- __all__ = ("FrescoApp", "urlfor")
52
+ __all__ = ("FrescoApp", "urlfor", "context")
50
53
 
51
54
  logger = logging.getLogger(__name__)
52
55
 
56
+ ExcInfo = tuple[t.Type[BaseException], BaseException, types.TracebackType]
57
+
53
58
 
54
59
  class FrescoApp(RouteCollection):
55
60
  """\
@@ -115,7 +120,9 @@ class FrescoApp(RouteCollection):
115
120
  #: If a function returns a value (other than ``None``),
116
121
  #: this value will be
117
122
  #: returned as the response instead of calling the scheduled view.
118
- self.process_http_error_response_handlers: List[Tuple[int, Callable]] = []
123
+ self.process_http_error_response_handlers: list[
124
+ tuple[t.Optional[int], Callable[[Request, Response], t.Optional[Response]]]
125
+ ] = []
119
126
 
120
127
  #: Functions to be called if an exception is raised during a view
121
128
  #: Each function will be passed ``request, exc_info``.
@@ -123,7 +130,9 @@ class FrescoApp(RouteCollection):
123
130
  #: this value will be
124
131
  #: returned as the response and the error will not be propagated.
125
132
  #: If all exception handlers return None then the error will be raised
126
- self.process_exception_handlers: List[Tuple[Exception, Callable]] = []
133
+ self.process_exception_handlers: list[
134
+ tuple[Type[Exception], Callable[[Request, ExcInfo], Union[Response, None]]]
135
+ ] = []
127
136
 
128
137
  #: Functions to be called at the end of request processing,
129
138
  #: after all content has been output.
@@ -156,7 +165,7 @@ class FrescoApp(RouteCollection):
156
165
  method: str,
157
166
  currentcontext=context.currentcontext,
158
167
  normpath=normpath,
159
- ):
168
+ ) -> Response:
160
169
  ctx = currentcontext()
161
170
  ctx["app"] = self
162
171
  environ = request.environ
@@ -178,19 +187,19 @@ class FrescoApp(RouteCollection):
178
187
 
179
188
  try:
180
189
  for traversal in self.get_route_traversals(path, method, request):
190
+ route = traversal.route
181
191
  try:
182
- route = traversal.route
183
192
  environ["wsgiorg.routing_args"] = (
184
193
  traversal.args,
185
194
  traversal.kwargs,
186
195
  )
187
- view = route.getview(method)
196
+ view = traversal.view
188
197
  ctx["view_self"] = getattr(view, "__self__", None)
189
198
  ctx["route_traversal"] = traversal
190
199
  if self.logger:
191
200
  self.logger.info(
192
201
  "matched route: %s %r => %r",
193
- request.method,
202
+ method,
194
203
  path,
195
204
  fq_path(view),
196
205
  )
@@ -271,7 +280,9 @@ class FrescoApp(RouteCollection):
271
280
  except ResponseException as e:
272
281
  response = e.response
273
282
  else:
274
- response = self.get_response(request, path, request.method)
283
+ response = self.get_response(
284
+ request, path, request.environ["REQUEST_METHOD"]
285
+ )
275
286
 
276
287
  for f in self.process_response_handlers:
277
288
  try:
@@ -324,12 +335,7 @@ class FrescoApp(RouteCollection):
324
335
  exc_info=exc_info,
325
336
  )
326
337
 
327
- def handle_exception(
328
- self, request, allow_reraise=True
329
- ) -> Union[
330
- Response,
331
- Tuple[Type[BaseException], BaseException, types.TracebackType],
332
- ]:
338
+ def handle_exception(self, request, allow_reraise=True) -> Response:
333
339
  exc_info = sys.exc_info()
334
340
  if exc_info[0] is None:
335
341
  raise AssertionError(
@@ -346,10 +352,7 @@ class FrescoApp(RouteCollection):
346
352
  # server handle it
347
353
  if allow_reraise and not have_error_handlers:
348
354
  raise exc_info[1].with_traceback(exc_info[2]) # type: ignore
349
- response: Union[
350
- Response,
351
- Tuple[Type[BaseException], BaseException, types.TracebackType],
352
- ] = Response.internal_server_error()
355
+ response: Response = Response.internal_server_error()
353
356
 
354
357
  if not self.process_exception_handlers:
355
358
  self.log_exception(request, exc_info)
@@ -406,7 +409,7 @@ class FrescoApp(RouteCollection):
406
409
  self.reset_wsgi_app()
407
410
  self._middleware.insert(position, (middleware, args, kwargs))
408
411
 
409
- def make_wsgi_app(self, wsgi_app=None, use_middleware=True) -> WSGICallable:
412
+ def make_wsgi_app(self, wsgi_app=None, use_middleware=True) -> WSGIApplication:
410
413
  """
411
414
  Return a WSGI (PEP-3333) compliant application that drives this
412
415
  FrescoApp object.
@@ -419,7 +422,7 @@ class FrescoApp(RouteCollection):
419
422
  """
420
423
  if wsgi_app is None:
421
424
 
422
- def wsgi_app(
425
+ def _wsgi_app(
423
426
  environ,
424
427
  start_response,
425
428
  view=self.view,
@@ -428,15 +431,18 @@ class FrescoApp(RouteCollection):
428
431
  request = request_class(environ)
429
432
  return view(request)(environ, start_response)
430
433
 
434
+ else:
435
+ _wsgi_app = wsgi_app
436
+
431
437
  if use_middleware:
432
438
  for m, m_args, m_kwargs in self._middleware:
433
- wsgi_app = m(wsgi_app, *m_args, **m_kwargs)
439
+ _wsgi_app = m(_wsgi_app, *m_args, **m_kwargs)
434
440
 
435
441
  def fresco_wsgi_app(
436
442
  environ,
437
443
  start_response,
438
444
  frescoapp=self,
439
- wsgi_app=wsgi_app,
445
+ wsgi_app=_wsgi_app,
440
446
  request_class=self.request_class,
441
447
  process_teardown_handlers=self.process_teardown_handlers,
442
448
  call_process_teardown_handlers=self.call_process_teardown_handlers,
@@ -602,7 +608,11 @@ class FrescoApp(RouteCollection):
602
608
  self.process_response_handlers.append(func)
603
609
  return func
604
610
 
605
- def process_exception(self, func, exc_type=Exception):
611
+ def process_exception(
612
+ self,
613
+ func: Callable[[Request, ExcInfo], Union[Response, None]],
614
+ exc_type: Type[Exception] = Exception,
615
+ ):
606
616
  """
607
617
  Register a ``process_exception`` hook function
608
618
  """
@@ -668,7 +678,9 @@ class FrescoApp(RouteCollection):
668
678
  start_response("200 OK", [])
669
679
  yield b""
670
680
 
671
- def fake_start_response(status, headers, exc_info=None):
681
+ def fake_start_response(
682
+ status: str, headers: HeaderList, exc_info: OptionalExcInfo = None
683
+ ) -> WriteCallable:
672
684
  return lambda s: None
673
685
 
674
686
  environ = make_environ(url, environ, wsgi_input, **kwargs)
@@ -693,12 +705,12 @@ class FrescoApp(RouteCollection):
693
705
  if multipart:
694
706
  wsgi_input, headers = encode_multipart(data, files)
695
707
  kwargs.update(headers)
696
- elif hasattr(data, "read"):
708
+ elif isinstance(data, t.BinaryIO):
697
709
  wsgi_input = data.read()
698
710
  elif isinstance(data, bytes):
699
711
  wsgi_input = data
700
712
  elif data is None:
701
- wsgi_input = ""
713
+ wsgi_input = b""
702
714
  else:
703
715
  wsgi_input = make_query(data).encode("ascii")
704
716
 
fresco/decorators.py CHANGED
@@ -14,20 +14,23 @@
14
14
  #
15
15
  import sys
16
16
  from functools import wraps
17
+ import typing as t
17
18
 
18
19
  from fresco import Response
19
20
 
20
21
  _marker = object()
21
22
 
22
23
 
23
- def onerror(exceptions, handler):
24
- """\
24
+ def onerror(
25
+ exceptions: t.Union[t.Type[Exception], tuple[t.Type[Exception], ...]], handler
26
+ ) -> t.Callable:
27
+ """
25
28
  Return a decorator that can replace or update the return value of the
26
29
  function if an exception is raised
27
30
  """
28
31
 
29
32
  try:
30
- if isinstance(exceptions, Exception):
33
+ if isinstance(exceptions, type):
31
34
  exceptions = (exceptions,)
32
35
  except TypeError:
33
36
  pass
fresco/defaults.py ADDED
@@ -0,0 +1 @@
1
+ DEFAULT_CHARSET = "UTF-8"
fresco/middleware.py CHANGED
@@ -12,11 +12,17 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+ import typing as t
16
+
17
+ from fresco.types import WSGIApplication
18
+ from fresco.types import WSGIEnviron
19
+ from fresco.types import StartResponse
20
+
15
21
  __all__ = ["XForwarded"]
16
22
 
17
23
 
18
24
  class XForwarded(object):
19
- """\
25
+ """
20
26
  Modify the WSGI environment so that the X_FORWARDED_* headers are observed
21
27
  and generated URIs are correct in a proxied environment.
22
28
 
@@ -33,6 +39,13 @@ class XForwarded(object):
33
39
  the wsgi.url_scheme is modified to ``https`` and ``HTTPS`` is set to
34
40
  ``on``.
35
41
 
42
+ :param trusted:
43
+ List of IP addresses trusted to set the HTTP_X_FORWARDED_* headers
44
+
45
+ :param force_https:
46
+ If True, the following environ keys will be set unconditionally:
47
+ ``"HTTPS": "on"`` and ``"wsgi.url_scheme": "https"`` will be set
48
+
36
49
  Example::
37
50
 
38
51
  >>> from fresco import FrescoApp, context, GET, Response
@@ -68,24 +81,36 @@ class XForwarded(object):
68
81
  u'URL is https://real-name/; REMOTE_ADDR is 1.2.3.4'
69
82
  """
70
83
 
71
- def __init__(self, app, trusted=None):
84
+ def __init__(
85
+ self,
86
+ app: WSGIApplication,
87
+ trusted: t.Optional[t.Iterable[str]] = None,
88
+ force_https: t.Optional[bool] = None,
89
+ ) -> None:
72
90
  self.app = app
91
+ self.force_https = force_https
73
92
  if trusted:
74
93
  self.trusted = set(trusted)
75
94
  else:
76
95
  self.trusted = set()
77
96
 
78
- def __call__(self, environ, start_response):
79
- """\
97
+ def __call__(
98
+ self, environ: WSGIEnviron, start_response: StartResponse
99
+ ) -> t.Iterable[bytes]:
100
+ """
80
101
  Call the WSGI app, passing it a modified environ
81
102
  """
82
103
  env = environ.get
83
- is_ssl = (
84
- env("HTTP_X_FORWARDED_PROTO") == "https"
85
- or env("HTTP_X_FORWARDED_SSL") == "on"
86
- )
87
104
 
105
+ if self.force_https is None:
106
+ is_ssl = (
107
+ env("HTTP_X_FORWARDED_PROTO") == "https"
108
+ or env("HTTP_X_FORWARDED_SSL") == "on"
109
+ )
110
+ else:
111
+ is_ssl = self.force_https
88
112
  host = env("HTTP_X_FORWARDED_HOST")
113
+
89
114
  if host is not None:
90
115
  if ":" in host:
91
116
  port = host.split(":")[1]
@@ -99,21 +124,17 @@ class XForwarded(object):
99
124
  environ["wsgi.url_scheme"] = "https"
100
125
  environ["HTTPS"] = "on"
101
126
 
102
- try:
103
- forwards = environ["HTTP_X_FORWARDED_FOR"].split(", ") + [
104
- env("REMOTE_ADDR", "")
105
- ]
106
- except KeyError:
107
- # No X-Forwarded-For header?
108
- return self.app(environ, start_response)
109
-
110
- if self.trusted:
111
- for ip in forwards[::-1]:
112
- # Find the first non-trusted ip; this is our remote address
113
- if ip not in self.trusted:
114
- environ["REMOTE_ADDR"] = ip
115
- break
116
- else:
117
- environ["REMOTE_ADDR"] = forwards[0]
127
+ forwarded_for = env("HTTP_X_FORWARDED_FOR")
128
+ if forwarded_for:
129
+ addrs = forwarded_for.split(", ")
130
+
131
+ if self.trusted:
132
+ for ip in addrs[::-1]:
133
+ # Find the first non-trusted ip; this is our remote address
134
+ if ip not in self.trusted:
135
+ environ["REMOTE_ADDR"] = ip
136
+ break
137
+ else:
138
+ environ["REMOTE_ADDR"] = addrs[0]
118
139
 
119
140
  return self.app(environ, start_response)
fresco/multidict.py CHANGED
@@ -15,17 +15,19 @@
15
15
  """
16
16
  An order preserving multidict implementation
17
17
  """
18
- from collections.abc import MutableMapping
19
18
  from collections.abc import Mapping
19
+ from collections.abc import KeysView
20
+ from collections.abc import ItemsView
21
+ from collections.abc import ValuesView
22
+ from collections.abc import MutableMapping
20
23
  from itertools import repeat
21
24
  import typing as t
22
25
  from typing import Any
23
26
  from typing import Dict
24
- from typing import Set
25
27
 
26
28
 
27
29
  if t.TYPE_CHECKING:
28
- from typeshed import SupportsKeysAndGetItem
30
+ from typeshed import SupportsKeysAndGetItem # type: ignore
29
31
  else:
30
32
  SupportsKeysAndGetItem = Mapping
31
33
 
@@ -33,6 +35,8 @@ SupportsKeysAndGetItemOrIterable = t.Union[
33
35
  SupportsKeysAndGetItem, t.Iterable[t.Tuple[Any, Any]]
34
36
  ]
35
37
 
38
+ _NO_DEFAULT = object()
39
+
36
40
 
37
41
  class MultiDict(MutableMapping):
38
42
  """
@@ -58,11 +62,11 @@ class MultiDict(MutableMapping):
58
62
 
59
63
  _dict: Dict[Any, Any]
60
64
  _order: t.List[Any]
65
+ __slots__ = ("_dict", "_order")
66
+
61
67
  setdefault = MutableMapping.setdefault
62
68
 
63
- def __init__(
64
- self, mapping_or_iterable: SupportsKeysAndGetItemOrIterable = tuple(), **kwargs
65
- ):
69
+ def __init__(self, other: SupportsKeysAndGetItemOrIterable = tuple(), **kwargs):
66
70
  """
67
71
  MultiDicts can be constructed in the following ways:
68
72
 
@@ -94,7 +98,7 @@ class MultiDict(MutableMapping):
94
98
  """
95
99
  self._order = []
96
100
  self._dict = {}
97
- self._update(mapping_or_iterable, True, kwargs)
101
+ self._update(other, True, kwargs)
98
102
 
99
103
  def __getitem__(self, key):
100
104
  """
@@ -134,7 +138,7 @@ class MultiDict(MutableMapping):
134
138
  """
135
139
  Return an iterator over all keys
136
140
  """
137
- return (k for k in self._dict)
141
+ return iter(self._dict)
138
142
 
139
143
  def get(self, key, default=None):
140
144
  """
@@ -155,7 +159,7 @@ class MultiDict(MutableMapping):
155
159
  """
156
160
  return self._dict.get(key, [])
157
161
 
158
- def copy(self):
162
+ def copy(self) -> "MultiDict":
159
163
  """\
160
164
  Return a shallow copy of the dictionary:
161
165
 
@@ -169,7 +173,7 @@ class MultiDict(MutableMapping):
169
173
  return self.__class__(self)
170
174
 
171
175
  @classmethod
172
- def fromkeys(cls, seq, value=None):
176
+ def fromkeys(cls, seq, value=None) -> "MultiDict":
173
177
  """\
174
178
  Create a new MultiDict with keys from seq and values set to value.
175
179
 
@@ -189,8 +193,8 @@ class MultiDict(MutableMapping):
189
193
  """
190
194
  return cls(zip(seq, repeat(value)))
191
195
 
192
- def items(self):
193
- """\
196
+ def items(self) -> ItemsView:
197
+ """
194
198
  Return a list of ``(key, value)`` tuples. Only the first ``(key,
195
199
  value)`` is returned where keys have multiple values:
196
200
 
@@ -198,14 +202,9 @@ class MultiDict(MutableMapping):
198
202
  >>> list(d.items())
199
203
  [('a', 1), ('b', 3)]
200
204
  """
201
- seen: Set[Any] = set()
202
- for k, v in self._order:
203
- if k in seen:
204
- continue
205
- yield k, v
206
- seen.add(k)
205
+ return {k: v[0] for k, v in self._dict.items()}.items()
207
206
 
208
- def listitems(self):
207
+ def listitems(self) -> ItemsView:
209
208
  """\
210
209
  Like ``items``, but returns lists of values:
211
210
 
@@ -213,10 +212,9 @@ class MultiDict(MutableMapping):
213
212
  >>> list(d.listitems())
214
213
  [('a', [1, 2]), ('b', [3])]
215
214
  """
216
- for k in self.keys():
217
- yield k, self._dict[k]
215
+ return self._dict.items()
218
216
 
219
- def allitems(self):
217
+ def allitems(self) -> t.Iterable[tuple[t.Any, t.Any]]:
220
218
  """\
221
219
  Return ``(key, value)`` pairs for each item in the MultiDict.
222
220
  Items with multiple keys will have multiple key-value pairs returned:
@@ -228,7 +226,7 @@ class MultiDict(MutableMapping):
228
226
  """
229
227
  return iter(self._order)
230
228
 
231
- def keys(self):
229
+ def keys(self) -> KeysView:
232
230
  """\
233
231
  Return dictionary keys. Each key is returned only once, even if
234
232
  multiple values are present.
@@ -238,10 +236,10 @@ class MultiDict(MutableMapping):
238
236
  >>> list(d.keys())
239
237
  ['a', 'b']
240
238
  """
241
- return (k for k, v in self.items())
239
+ return self._dict.keys()
242
240
 
243
- def values(self):
244
- """\
241
+ def values(self) -> ValuesView:
242
+ """
245
243
  Return values from the dictionary. Where keys have multiple values,
246
244
  only the first is returned:
247
245
 
@@ -250,9 +248,9 @@ class MultiDict(MutableMapping):
250
248
  >>> list(d.values())
251
249
  [1, 3]
252
250
  """
253
- return (v for k, v in self.items())
251
+ return {k: v[0] for k, v in self._dict.items()}.values()
254
252
 
255
- def listvalues(self):
253
+ def listvalues(self) -> ValuesView:
256
254
  """\
257
255
  Return an iterator over lists of values. Each item will be the list of
258
256
  values associated with a single key.
@@ -264,9 +262,9 @@ class MultiDict(MutableMapping):
264
262
  >>> list(d.listvalues())
265
263
  [[1, 2], [3]]
266
264
  """
267
- return (self._dict[k] for k in self.keys())
265
+ return self._dict.values()
268
266
 
269
- def pop(self, key, *args):
267
+ def pop(self, key, default=_NO_DEFAULT) -> t.Any:
270
268
  """
271
269
  Dictionary ``pop`` method. Return and remove the value associated with
272
270
  ``key``. If more than one value is associated with ``key``, only the
@@ -288,16 +286,12 @@ class MultiDict(MutableMapping):
288
286
  ...
289
287
  KeyError: 'a'
290
288
  """
291
- if len(args) > 1:
292
- raise TypeError(
293
- "pop expected at most 2 arguments, got %d" % (1 + len(args))
294
- )
295
289
  try:
296
290
  value = self._dict[key].pop(0)
297
291
  except (KeyError, IndexError):
298
- if args:
299
- return args[0]
300
- raise KeyError(key)
292
+ if default is _NO_DEFAULT:
293
+ raise KeyError(key)
294
+ return default
301
295
  self._order.remove((key, value))
302
296
  return value
303
297
 
@@ -326,17 +320,9 @@ class MultiDict(MutableMapping):
326
320
  raise KeyError("popitem(): dictionary is empty")
327
321
  return key, self.pop(key)
328
322
 
329
- @t.overload
330
323
  def update(
331
- self, mapping_or_iterable: SupportsKeysAndGetItem, /, **kwargs: Any
324
+ self, other: SupportsKeysAndGetItemOrIterable = tuple(), /, **kwargs: Any
332
325
  ) -> None:
333
- ...
334
-
335
- @t.overload
336
- def update(self, /, **kwargs: Any) -> None:
337
- ...
338
-
339
- def update(self, mapping_or_iterable=tuple(), **kwargs):
340
326
  r"""
341
327
  Update the MultiDict from another MultiDict, regular dictionary or a
342
328
  iterable of ``(key, value)`` pairs. New keys overwrite old keys - use
@@ -371,17 +357,15 @@ class MultiDict(MutableMapping):
371
357
  >>> d.update(mood='okay')
372
358
 
373
359
  """
374
- self._update(mapping_or_iterable, True, kwargs)
360
+ self._update(other, True, kwargs)
375
361
 
376
- def extend(
377
- self, mapping_or_iterable: SupportsKeysAndGetItemOrIterable = tuple(), **kwargs
378
- ):
362
+ def extend(self, other: SupportsKeysAndGetItemOrIterable = tuple(), **kwargs):
379
363
  """
380
364
  Extend the MultiDict with another MultiDict, regular dictionary or a
381
365
  iterable of ``(key, value)`` pairs. This is similar to :meth:`update`
382
366
  except that new keys are added to old keys.
383
367
  """
384
- return self._update(mapping_or_iterable, False, kwargs)
368
+ return self._update(other, False, kwargs)
385
369
 
386
370
  def _update(
387
371
  self,