fresco 3.5.0__py3-none-any.whl → 3.6.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.

Potentially problematic release.


This version of fresco might be problematic. Click here for more details.

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.5.0"
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.6.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
@@ -50,6 +53,8 @@ __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,8 +187,8 @@ 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,
@@ -324,12 +333,7 @@ class FrescoApp(RouteCollection):
324
333
  exc_info=exc_info,
325
334
  )
326
335
 
327
- def handle_exception(
328
- self, request, allow_reraise=True
329
- ) -> Union[
330
- Response,
331
- Tuple[Type[BaseException], BaseException, types.TracebackType],
332
- ]:
336
+ def handle_exception(self, request, allow_reraise=True) -> Response:
333
337
  exc_info = sys.exc_info()
334
338
  if exc_info[0] is None:
335
339
  raise AssertionError(
@@ -346,10 +350,7 @@ class FrescoApp(RouteCollection):
346
350
  # server handle it
347
351
  if allow_reraise and not have_error_handlers:
348
352
  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()
353
+ response: Response = Response.internal_server_error()
353
354
 
354
355
  if not self.process_exception_handlers:
355
356
  self.log_exception(request, exc_info)
@@ -406,7 +407,7 @@ class FrescoApp(RouteCollection):
406
407
  self.reset_wsgi_app()
407
408
  self._middleware.insert(position, (middleware, args, kwargs))
408
409
 
409
- def make_wsgi_app(self, wsgi_app=None, use_middleware=True) -> WSGICallable:
410
+ def make_wsgi_app(self, wsgi_app=None, use_middleware=True) -> WSGIApplication:
410
411
  """
411
412
  Return a WSGI (PEP-3333) compliant application that drives this
412
413
  FrescoApp object.
@@ -419,7 +420,7 @@ class FrescoApp(RouteCollection):
419
420
  """
420
421
  if wsgi_app is None:
421
422
 
422
- def wsgi_app(
423
+ def _wsgi_app(
423
424
  environ,
424
425
  start_response,
425
426
  view=self.view,
@@ -428,15 +429,18 @@ class FrescoApp(RouteCollection):
428
429
  request = request_class(environ)
429
430
  return view(request)(environ, start_response)
430
431
 
432
+ else:
433
+ _wsgi_app = wsgi_app
434
+
431
435
  if use_middleware:
432
436
  for m, m_args, m_kwargs in self._middleware:
433
- wsgi_app = m(wsgi_app, *m_args, **m_kwargs)
437
+ _wsgi_app = m(_wsgi_app, *m_args, **m_kwargs)
434
438
 
435
439
  def fresco_wsgi_app(
436
440
  environ,
437
441
  start_response,
438
442
  frescoapp=self,
439
- wsgi_app=wsgi_app,
443
+ wsgi_app=_wsgi_app,
440
444
  request_class=self.request_class,
441
445
  process_teardown_handlers=self.process_teardown_handlers,
442
446
  call_process_teardown_handlers=self.call_process_teardown_handlers,
@@ -602,7 +606,11 @@ class FrescoApp(RouteCollection):
602
606
  self.process_response_handlers.append(func)
603
607
  return func
604
608
 
605
- def process_exception(self, func, exc_type=Exception):
609
+ def process_exception(
610
+ self,
611
+ func: Callable[[Request, ExcInfo], Union[Response, None]],
612
+ exc_type: Type[Exception] = Exception,
613
+ ):
606
614
  """
607
615
  Register a ``process_exception`` hook function
608
616
  """
@@ -668,7 +676,9 @@ class FrescoApp(RouteCollection):
668
676
  start_response("200 OK", [])
669
677
  yield b""
670
678
 
671
- def fake_start_response(status, headers, exc_info=None):
679
+ def fake_start_response(
680
+ status: str, headers: HeaderList, exc_info: OptionalExcInfo = None
681
+ ) -> WriteCallable:
672
682
  return lambda s: None
673
683
 
674
684
  environ = make_environ(url, environ, wsgi_input, **kwargs)
@@ -693,12 +703,12 @@ class FrescoApp(RouteCollection):
693
703
  if multipart:
694
704
  wsgi_input, headers = encode_multipart(data, files)
695
705
  kwargs.update(headers)
696
- elif hasattr(data, "read"):
706
+ elif isinstance(data, t.BinaryIO):
697
707
  wsgi_input = data.read()
698
708
  elif isinstance(data, bytes):
699
709
  wsgi_input = data
700
710
  elif data is None:
701
- wsgi_input = ""
711
+ wsgi_input = b""
702
712
  else:
703
713
  wsgi_input = make_query(data).encode("ascii")
704
714
 
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
@@ -14,9 +14,9 @@
14
14
  #
15
15
  import typing as t
16
16
 
17
- from fresco.typing import WSGICallable
18
- from fresco.typing import WSGIEnviron
19
- from fresco.typing import StartResponse
17
+ from fresco.types import WSGIApplication
18
+ from fresco.types import WSGIEnviron
19
+ from fresco.types import StartResponse
20
20
 
21
21
  __all__ = ["XForwarded"]
22
22
 
@@ -83,7 +83,7 @@ class XForwarded(object):
83
83
 
84
84
  def __init__(
85
85
  self,
86
- app: WSGICallable,
86
+ app: WSGIApplication,
87
87
  trusted: t.Optional[t.Iterable[str]] = None,
88
88
  force_https: t.Optional[bool] = None,
89
89
  ) -> None:
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,