fresco 3.7.0__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
@@ -68,7 +68,7 @@ from fresco.subrequests import subrequest_raw
68
68
  from fresco.util.common import object_or_404
69
69
 
70
70
 
71
- __version__ = "3.7.0"
71
+ __version__ = "3.9.0"
72
72
  __all__ = [
73
73
  "Request",
74
74
  "currentrequest",
fresco/core.py CHANGED
@@ -280,7 +280,9 @@ class FrescoApp(RouteCollection):
280
280
  except ResponseException as e:
281
281
  response = e.response
282
282
  else:
283
- response = self.get_response(request, path, request.method)
283
+ response = self.get_response(
284
+ request, path, request.environ["REQUEST_METHOD"]
285
+ )
284
286
 
285
287
  for f in self.process_response_handlers:
286
288
  try:
fresco/options.py CHANGED
@@ -285,11 +285,15 @@ class Options(dict):
285
285
  )
286
286
  )
287
287
 
288
- if strict and existing_keys and set(self.keys()) != existing_keys:
289
- raise AssertionError(
288
+ if existing_keys and set(self.keys()) != existing_keys:
289
+ error_msg = (
290
290
  f"settings file {path} created undefined options: "
291
291
  f"{set(self.keys()) - existing_keys}"
292
292
  )
293
+ if strict:
294
+ raise AssertionError(error_msg)
295
+ else:
296
+ logger.warning(error_msg)
293
297
 
294
298
  if use_environ:
295
299
  self |= {
fresco/request.py CHANGED
@@ -195,7 +195,7 @@ class Request(object):
195
195
  "Payload contains data that could not be decoded using " + encoding
196
196
  )
197
197
 
198
- def get_json(self, *args, **kwargs):
198
+ def get_json(self, *args, **kwargs) -> t.Any:
199
199
  """
200
200
  Return a decoded JSON request body.
201
201
 
@@ -213,7 +213,7 @@ class Request(object):
213
213
  raise exceptions.RequestParseError("Payload is not valid JSON")
214
214
 
215
215
  @property
216
- def files(self):
216
+ def files(self) -> MultiDict:
217
217
  """
218
218
  Return a MultiDict of all ``FileUpload`` objects available.
219
219
  """
@@ -263,7 +263,7 @@ class Request(object):
263
263
  ...
264
264
 
265
265
  @t.overload
266
- def get(self, key: str, *, type: t.Callable[[str], T2]) -> t.Union[T2, None]:
266
+ def get(self, key: str, *, type: t.Callable[[str], T2] = str) -> t.Union[T2, None]:
267
267
  ...
268
268
 
269
269
  @t.overload
@@ -391,7 +391,7 @@ class Request(object):
391
391
  ...
392
392
 
393
393
  @t.overload
394
- def getint(self, key: str, default: T1) -> t.Union[int, T1, None]:
394
+ def getint(self, key: str, default: T1) -> t.Union[int, T1]:
395
395
  ...
396
396
 
397
397
  @t.overload
fresco/response.py CHANGED
@@ -277,7 +277,7 @@ def encoder(stream, charset):
277
277
 
278
278
 
279
279
  def make_header_name(name):
280
- """\
280
+ """
281
281
  Return a formatted header name from a python idenfier.
282
282
 
283
283
  Example usage::
@@ -677,8 +677,10 @@ class Response(object):
677
677
  newheaders.append((k, v))
678
678
  return self.replace(headers=newheaders + [("Vary", ", ".join(_vary_on))])
679
679
 
680
- def replace(self, content=None, status=None, headers=None, **kwheaders):
681
- """\
680
+ def replace(
681
+ self, content=None, status=None, headers=None, **kwheaders
682
+ ) -> "Response":
683
+ """
682
684
  Return a new response object with any of content, status or headers
683
685
  changed.
684
686
 
@@ -758,7 +760,7 @@ class Response(object):
758
760
  return default_charset
759
761
 
760
762
  @classmethod
761
- def not_found(cls, request=None):
763
+ def not_found(cls, request=None) -> "Response":
762
764
  """
763
765
  Return an HTTP not found response (404).
764
766
 
@@ -781,7 +783,7 @@ class Response(object):
781
783
  )
782
784
 
783
785
  @classmethod
784
- def error(cls, message="500 Internal Server Error"):
786
+ def error(cls, message="500 Internal Server Error") -> "Response":
785
787
  """
786
788
  Return an HTTP server error response (500).
787
789
 
@@ -803,7 +805,7 @@ class Response(object):
803
805
  )
804
806
 
805
807
  @classmethod
806
- def unauthorized(cls, authenticate):
808
+ def unauthorized(cls, authenticate) -> "Response":
807
809
  """
808
810
  Return an HTTP unauthorized response (401)
809
811
  """
@@ -818,7 +820,7 @@ class Response(object):
818
820
  )
819
821
 
820
822
  @classmethod
821
- def unauthorized_basic(cls, realm):
823
+ def unauthorized_basic(cls, realm) -> "Response":
822
824
  """
823
825
  Return an HTTP unauthorized response (401) with a WWW-Authenticate
824
826
  header set for HTTP Basic authentication..
@@ -826,7 +828,7 @@ class Response(object):
826
828
  return cls.unauthorized(authenticate=f'Basic realm="{realm}"')
827
829
 
828
830
  @classmethod
829
- def forbidden(cls, message="Sorry, access is denied"):
831
+ def forbidden(cls, message="Sorry, access is denied") -> "Response":
830
832
  """
831
833
  Return an HTTP forbidden response (403).
832
834
 
@@ -842,7 +844,9 @@ class Response(object):
842
844
  )
843
845
 
844
846
  @classmethod
845
- def bad_request(cls, request=None):
847
+ def bad_request(
848
+ cls, message="The server could not understand your request", request=None
849
+ ) -> "Response":
846
850
  """
847
851
  Return an HTTP bad request response.
848
852
 
@@ -856,16 +860,17 @@ class Response(object):
856
860
  return cls(
857
861
  status=STATUS_BAD_REQUEST,
858
862
  content=[
859
- "<html>"
860
- "<body>"
861
- "<h1>The server could not understand your request</h1>"
862
- "</body>"
863
- "</html>"
863
+ f"<html>"
864
+ f"<body>"
865
+ f"<h1>Bad request</h1>"
866
+ f"<p>{message}</p>"
867
+ f"</body>"
868
+ f"</html>"
864
869
  ],
865
870
  )
866
871
 
867
872
  @classmethod
868
- def length_required(cls, request=None):
873
+ def length_required(cls, request=None) -> "Response":
869
874
  """
870
875
  Return an HTTP Length Required response (411).
871
876
 
@@ -888,7 +893,7 @@ class Response(object):
888
893
  )
889
894
 
890
895
  @classmethod
891
- def payload_too_large(cls, request=None):
896
+ def payload_too_large(cls, request=None) -> "Response":
892
897
  """
893
898
  Return an HTTP Payload Too Large response (413)::
894
899
 
@@ -903,7 +908,7 @@ class Response(object):
903
908
  request_entity_too_large = payload_too_large
904
909
 
905
910
  @classmethod
906
- def method_not_allowed(cls, valid_methods):
911
+ def method_not_allowed(cls, valid_methods) -> "Response":
907
912
  """
908
913
  Return an HTTP method not allowed response (405)::
909
914
 
@@ -925,7 +930,7 @@ class Response(object):
925
930
  )
926
931
 
927
932
  @classmethod
928
- def internal_server_error(cls):
933
+ def internal_server_error(cls) -> "Response":
929
934
  """
930
935
  Return an HTTP internal server error response (500).
931
936
 
@@ -946,7 +951,7 @@ class Response(object):
946
951
  @classmethod
947
952
  def unrestricted_redirect(
948
953
  cls, location, request=None, status=STATUS_FOUND, **kwargs
949
- ):
954
+ ) -> "Response":
950
955
  """
951
956
  Return an HTTP redirect reponse (30x).
952
957
 
@@ -1004,7 +1009,7 @@ class Response(object):
1004
1009
  request = fresco.context.request
1005
1010
  location = request.resolve_url(location)
1006
1011
 
1007
- return Response(
1012
+ return cls(
1008
1013
  "<html><head></head><body>\n"
1009
1014
  "<h1>Page has moved</h1>\n"
1010
1015
  "<p><a href='%s'>%s</a></p>\n"
@@ -1014,8 +1019,8 @@ class Response(object):
1014
1019
  )
1015
1020
 
1016
1021
  @classmethod
1017
- def unrestricted_redirect_permanent(cls, *args, **kwargs):
1018
- """\
1022
+ def unrestricted_redirect_permanent(cls, *args, **kwargs) -> "Response":
1023
+ """
1019
1024
  Return an HTTP permanent redirect reponse.
1020
1025
 
1021
1026
  :param location: the URI of the new location. If relative this will be
@@ -1027,8 +1032,8 @@ class Response(object):
1027
1032
  return cls.unrestricted_redirect(*args, **kwargs)
1028
1033
 
1029
1034
  @classmethod
1030
- def unrestricted_redirect_temporary(cls, *args, **kwargs):
1031
- """\
1035
+ def unrestricted_redirect_temporary(cls, *args, **kwargs) -> "Response":
1036
+ """
1032
1037
  Return an HTTP permanent redirect reponse.
1033
1038
 
1034
1039
  :param location: the URI of the new location. If relative this will be
@@ -1048,7 +1053,7 @@ class Response(object):
1048
1053
  _is_safe_url=is_safe_url,
1049
1054
  allowed_hosts=frozenset(),
1050
1055
  **kwargs,
1051
- ):
1056
+ ) -> "Response":
1052
1057
  """
1053
1058
  Return an HTTP redirect reponse (30x). Will only redirect to the
1054
1059
  current host or hosts in the ``allowed_hosts`` list. A ``ValueError``
@@ -1075,19 +1080,22 @@ class Response(object):
1075
1080
  """
1076
1081
  # Create the fallback redirect response always so that
1077
1082
  # changes to route names don't result in latent bugs
1083
+ fallback_response = None
1078
1084
  if fallback:
1079
- fallback = Response.unrestricted_redirect(fallback, status=status, **kwargs)
1080
- if callable(url) or "//" not in url:
1085
+ fallback_response = cls.unrestricted_redirect(
1086
+ fallback, status=status, **kwargs
1087
+ )
1088
+ if url and (
1089
+ callable(url) or "//" not in url or _is_safe_url(url, allowed_hosts)
1090
+ ):
1081
1091
  return cls.unrestricted_redirect(url, status=status, **kwargs)
1082
1092
 
1083
- if url and _is_safe_url(url, allowed_hosts):
1084
- return cls.unrestricted_redirect(url, status=status, **kwargs)
1085
- if fallback:
1086
- return fallback
1093
+ if fallback_response:
1094
+ return fallback_response
1087
1095
  raise ValueError("Unsafe URL")
1088
1096
 
1089
1097
  @classmethod
1090
- def redirect_permanent(cls, *args, **kwargs):
1098
+ def redirect_permanent(cls, *args, **kwargs) -> "Response":
1091
1099
  """
1092
1100
  Return an HTTP permanent redirect reponse.
1093
1101
 
@@ -1100,7 +1108,7 @@ class Response(object):
1100
1108
  return cls.redirect(*args, **kwargs)
1101
1109
 
1102
1110
  @classmethod
1103
- def redirect_temporary(cls, *args, **kwargs):
1111
+ def redirect_temporary(cls, *args, **kwargs) -> "Response":
1104
1112
  """
1105
1113
  Return an HTTP permanent redirect reponse.
1106
1114
 
@@ -1113,7 +1121,7 @@ class Response(object):
1113
1121
  return cls.redirect(*args, **kwargs)
1114
1122
 
1115
1123
  @classmethod
1116
- def meta_refresh(cls, location, delay=1, request=None):
1124
+ def meta_refresh(cls, location, delay=1, **kwargs) -> "Response":
1117
1125
  """
1118
1126
  Return an HTML page containing a <meta http-equiv="refresh"> tag,
1119
1127
  causing the browser to redirect to the given location after ``delay``
@@ -1124,21 +1132,19 @@ class Response(object):
1124
1132
  request.
1125
1133
 
1126
1134
  """
1127
- if "://" not in location:
1128
- if request is None:
1129
- request = fresco.context.request
1130
- location = request.resolve_url(location)
1131
- return cls(
1132
- [
1133
- (
1134
- "<!DOCTYPE html>"
1135
- "<html>"
1136
- '<head><meta http-equiv="refresh" content="0; url={0}"></head>'
1137
- '<body><p><a href="{0}">Click here to continue</a></p></body>'
1138
- "</html>"
1139
- ).format(location)
1135
+ response = cls.redirect(location, **kwargs)
1136
+ location = response.get_header("location")
1137
+ return response.replace(
1138
+ status="200 OK",
1139
+ content=[
1140
+ f"<!DOCTYPE html>"
1141
+ f"<html>"
1142
+ f'<head><meta http-equiv="refresh" content="0; url={location}"></head>'
1143
+ f'<body><p><a href="{location}">Click here to continue</a></p></body>'
1144
+ f"</html>"
1140
1145
  ],
1141
1146
  content_type="text/html",
1147
+ location=None,
1142
1148
  )
1143
1149
 
1144
1150
  @classmethod
@@ -1152,7 +1158,7 @@ class Response(object):
1152
1158
  headers=None,
1153
1159
  dumps=stdlib_json.dumps,
1154
1160
  **kwargs,
1155
- ):
1161
+ ) -> "Response":
1156
1162
  """
1157
1163
  Return an ``application/json`` response with the given data
1158
1164
  JSON serialized
fresco/routing.py CHANGED
@@ -21,6 +21,7 @@ from collections import defaultdict
21
21
  from collections import namedtuple
22
22
  from collections.abc import Collection
23
23
  from collections.abc import MutableSequence
24
+ from collections.abc import Sequence
24
25
  from importlib import import_module
25
26
  from functools import partial
26
27
  from typing import Any
@@ -732,11 +733,11 @@ class Route(object):
732
733
  view: Optional[Union[ViewCallable, "RouteCollection", str]] = None,
733
734
  kwargs: Optional[dict[str, Any]] = None,
734
735
  args=None,
735
- name=None,
736
+ name: Optional[str] = None,
736
737
  predicate=None,
737
738
  decorators=None,
738
739
  filters=None,
739
- fallthrough_on=None,
740
+ fallthrough_on: Optional[Sequence[int]] = None,
740
741
  provide_request: Optional[bool] = None,
741
742
  **_kwargs: ViewCallable,
742
743
  ):
@@ -771,7 +772,7 @@ class Route(object):
771
772
  :param **_kwargs: Keyword arguments matching HTTP method names
772
773
  (GET, POST etc) can used to specify views
773
774
  associated with those methods.
774
- Other keyword aruments are passed through to the
775
+ Other keyword arguments are passed through to the
775
776
  view callable.
776
777
 
777
778
  Naming routes
@@ -839,6 +840,12 @@ class Route(object):
839
840
  for method in ALL_METHODS:
840
841
  if method in _kwargs:
841
842
  method_view_map[method] = _kwargs.pop(method)
843
+ if _kwargs:
844
+ for k in list(_kwargs):
845
+ trymethods = k.split("_")
846
+ if all(m in ALL_METHODS for m in trymethods):
847
+ method_view_map.update((m, _kwargs[k]) for m in trymethods)
848
+ _kwargs.pop(k)
842
849
 
843
850
  if not isinstance(pattern, Pattern):
844
851
  pattern = self.pattern_class(pattern)
@@ -885,7 +892,7 @@ class Route(object):
885
892
  args or _kwargs.pop("view_args", tuple()) # type: ignore
886
893
  )
887
894
  self.view_kwargs: dict[str, Any] = dict(
888
- kwargs or _kwargs.pop("view_kwargs", {}), **_kwargs # type: ignore
895
+ kwargs or _kwargs.pop("view_kwargs", {}), **_kwargs # type: ignore
889
896
  )
890
897
 
891
898
  for arg in self.view_args:
@@ -87,6 +87,12 @@ class TestCannedResponses(object):
87
87
  r = Response.redirect("http://bad.example.com/", "gloop", barfle=23)
88
88
  assert r.get_header("Location") == "http://localhost/arfle/23"
89
89
 
90
+ def test_redirect_uses_fallback_for_empty_url(self):
91
+ app = FrescoApp()
92
+ with app.requestcontext():
93
+ r = Response.redirect("", "http://localhost/foo")
94
+ assert r.get_header("Location") == "http://localhost/foo"
95
+
90
96
  def test_not_found(self):
91
97
  with FrescoApp().requestcontext():
92
98
  r = Response.not_found()
@@ -130,6 +136,16 @@ class TestCannedResponses(object):
130
136
  assert r.status == "200 OK"
131
137
  assert r.get_header("Content-Type") == "text/html"
132
138
 
139
+ def test_meta_refresh_to_view(self):
140
+
141
+ app = FrescoApp()
142
+ view = Mock(return_value=Response())
143
+ app.route("/arfle/<barfle:int>", GET, view)
144
+ with app.requestcontext():
145
+ r = Response.meta_refresh(view, barfle=42)
146
+ content = b"".join(r.content_iterator)
147
+ assert b'url=http://localhost/arfle/42"' in content
148
+
133
149
  def test_json(self):
134
150
  with FrescoApp().requestcontext():
135
151
  r = Response.json(
@@ -137,6 +137,16 @@ class TestRouteConstructor(object):
137
137
  assert len(list(app.get_route_traversals("/y", POST))) == 1
138
138
  assert len(list(app.get_route_traversals("/y", OPTIONS))) == 1
139
139
 
140
+ def test_multiple_methods_can_be_expressed_using_kwargs(self):
141
+ def view():
142
+ return Response()
143
+
144
+ app = FrescoApp()
145
+ app.route("/x", GET_POST=view)
146
+ assert len(list(app.get_route_traversals("/x", GET))) == 1
147
+ assert len(list(app.get_route_traversals("/x", POST))) == 1
148
+ assert len(list(app.get_route_traversals("/x", OPTIONS))) == 0
149
+
140
150
 
141
151
  class TestRouteBeforeHooks(object):
142
152
  def test_hook_is_called(self):
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fresco
3
- Version: 3.7.0
3
+ Version: 3.9.0
4
4
  Summary: A Web/WSGI micro-framework
5
5
  Author-email: Oliver Cope <oliver@redgecko.org>
6
- License: Apache
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://ollycope.com/software/fresco/latest/
8
8
  Keywords: wsgi,web,www,framework
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Environment :: Web Environment
11
11
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: Apache Software License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
@@ -1,18 +1,18 @@
1
- fresco/__init__.py,sha256=Jo5gW2Xce2hmtXhfaXXJR4aBW1KFYb0Murx2JfIo_E8,3537
1
+ fresco/__init__.py,sha256=gy3VchKsKsoWqZFqIbMhgzIjNCeFlcNnTl225TC_8qw,3537
2
2
  fresco/cookie.py,sha256=Qnx8yOjU4LUJ1fqi7YvqbhAA01rCsclJGl_fxI68slw,7055
3
- fresco/core.py,sha256=bqGDgm4OH46j6HqnC5nJyn9n1dQw-I1ntUY8dIpeO_M,26988
3
+ fresco/core.py,sha256=5sV6dTOhtEgEpMH_4sjoI0ewZYLvAPnRjyTfNkm490Q,27037
4
4
  fresco/decorators.py,sha256=JL4MlsJz4RWRAuOCCB3fx3rtd-E1CvwO2MD7bf94yHM,3324
5
5
  fresco/defaults.py,sha256=YStD4MPcCtq-fREFoupSaAS0SY8lH1oEnKDONlR86zs,26
6
6
  fresco/exceptions.py,sha256=KE-LoYUGnho6KltzkU6cnm9vUiUhAiDIjPqn5ba-YCA,4410
7
7
  fresco/middleware.py,sha256=TH1I5NthLDwnOdluOSFpP_9SQQYhYqh-8lGuAXT74dc,4811
8
8
  fresco/multidict.py,sha256=lKRpSP1XNghLp2-i5cXPfPQxuoJclHpNkWk91F4GI1o,13152
9
- fresco/options.py,sha256=X66xZV45vWIyQR2GwFTLgV2QGoXOfLhHURWQ_edoxmY,17024
9
+ fresco/options.py,sha256=xMh23WS6HB7P9ka_S0X3DoGSSvb8ESl1m8Pqb52Mork,17152
10
10
  fresco/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- fresco/request.py,sha256=XJ9SJAe9zILfWt-YB4zxjIrjECrLYzyxjZ9xk8h4tAw,30024
11
+ fresco/request.py,sha256=B3OeD4H_2HrgRbg5HhkjiHZ6JWiXTe98wYdtpLbNBCw,30046
12
12
  fresco/requestcontext.py,sha256=_AJ7DMT5vlIO4Uc-WlRsYJdRge4WRO7ygibKImOS0iM,3601
13
- fresco/response.py,sha256=ilJ_Wbp7LVP1E6bXp0iAy1o6D0hboXtpLvm2ZCmUGmk,37277
13
+ fresco/response.py,sha256=9RB9f6tgS_voM-MK36vGzWpkpp6JISnbF--F2FsAT7g,37605
14
14
  fresco/routeargs.py,sha256=E-LQutqg6AojtagZojF7NeMjFBk9joRNMGo842IMTBk,10598
15
- fresco/routing.py,sha256=r_vPD1xc7brOrbw1iO0X2IP7WX5wlIWlcXwcNndU1ZY,61259
15
+ fresco/routing.py,sha256=d8Z2QyW-X5W4_7wrG39G7PgzwSgLe-8RvxeBJfoh0L0,61615
16
16
  fresco/static.py,sha256=rNQz_8-PVhSGup_w-HycqatLEbFes7VSlnNQ0rz24pU,2529
17
17
  fresco/subrequests.py,sha256=fOG9Bd3w9Ova0S3qv5ssxQ_otrSxaFcKG7TOy6CxNCg,11068
18
18
  fresco/types.py,sha256=PbXppEcckX4ohU8npteU1mCkHOuXQxrnqxk1RNEFXkE,797
@@ -27,9 +27,9 @@ fresco/tests/test_multidict.py,sha256=uIa1cu5DMAukX2nLOjTiB12oh0icBeSPfsUnqsu85m
27
27
  fresco/tests/test_options.py,sha256=394MLLTyf4vmfkUyqQ8EGIiTTJsQh7P9M6eta2wgUbw,12764
28
28
  fresco/tests/test_request.py,sha256=wGDwxCZMbzyGgQJwM2Nlsf0ogcGenXN3srO8dQEhE24,16777
29
29
  fresco/tests/test_requestcontext.py,sha256=t8hm-lzIk85ryb3sdlpVoPQyLDWpCjB86dg8nVG1yRw,3115
30
- fresco/tests/test_response.py,sha256=MrhHIDg81pJlTeEcn2rGtU-i59s1KzEccF81u4Up6xs,8934
30
+ fresco/tests/test_response.py,sha256=PYMEr7aJfHMU3eIkBnGN5I6TpYf0xua2-95aNhU1Y7w,9544
31
31
  fresco/tests/test_routeargs.py,sha256=VMWUbrXegTLN9Tx2AcrpbjAAEaxAANzkcy02SmpFOmY,8162
32
- fresco/tests/test_routing.py,sha256=NBBJ6UcsdSoUAFNgUtCC0lEEeKB6JxS8XyrUeef25M8,40018
32
+ fresco/tests/test_routing.py,sha256=smRhdbru9mRXd0x_bmawCSZbZV7bR1z-9BRiFB9hOJc,40408
33
33
  fresco/tests/test_static.py,sha256=y73dqzE2flpACQ_dvNhDzk_WlvNQfkhYF_8YY4MMGDo,4686
34
34
  fresco/tests/test_subrequests.py,sha256=7rluJnw-elXRXfrzvAQvGBHRBU93zwnL827mTxBGd3Y,7909
35
35
  fresco/tests/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -51,8 +51,8 @@ fresco/util/security.py,sha256=nXEdoCak_2c4OA1L1wGwhZygS22s2fzwR0Kp-DdwKZg,1058
51
51
  fresco/util/textproc.py,sha256=e5jLTofKCqdm6_Fy8XOyR43AJr5APtL59Kd8cNA9PrQ,2309
52
52
  fresco/util/urls.py,sha256=V6-s2WgeARkEcuzpwqtiM8ebOXe5-mNUKOJ8N9LNBHA,10051
53
53
  fresco/util/wsgi.py,sha256=RYw4KeOUjzzPOL_HhEtgLyCngofjMdAT8BTLOSKU6KA,13021
54
- fresco-3.7.0.dist-info/licenses/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
55
- fresco-3.7.0.dist-info/METADATA,sha256=bqLRh85Q1K4S3Orlpyl-1vUXemvnWGrx1Y-qKtymOI0,1571
56
- fresco-3.7.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
57
- fresco-3.7.0.dist-info/top_level.txt,sha256=p_1aMce5Shjq9fIfdbB-aN8wCDhjF_iYnn98bUebbII,7
58
- fresco-3.7.0.dist-info/RECORD,,
54
+ fresco-3.9.0.dist-info/licenses/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
55
+ fresco-3.9.0.dist-info/METADATA,sha256=wQn0fuVCpyBdudhkG3lu_8azHj5P__kApDRqeYHDs6M,1523
56
+ fresco-3.9.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
57
+ fresco-3.9.0.dist-info/top_level.txt,sha256=p_1aMce5Shjq9fIfdbB-aN8wCDhjF_iYnn98bUebbII,7
58
+ fresco-3.9.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5