fresco 3.4.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.

@@ -24,6 +24,7 @@ import sys
24
24
  import pytest
25
25
 
26
26
  from fresco.options import Options
27
+ from fresco.options import override_options
27
28
  from fresco.options import parse_key_value_pairs
28
29
  from fresco.options import dict_from_options
29
30
 
@@ -104,6 +105,24 @@ class TestOptions(object):
104
105
  assert isinstance(Options().copy(), Options)
105
106
 
106
107
 
108
+ class TestOverrideOptions:
109
+ def test_override_options_with_object(self):
110
+ options = Options(foo=1)
111
+ with override_options(options, {"foo": 2, "bar": "a"}):
112
+ assert options["foo"] == 2
113
+ assert options["bar"] == "a"
114
+ assert options["foo"] == 1
115
+ assert "bar" not in options
116
+
117
+ def test_override_options_with_kwargs(self):
118
+ options = Options(foo=1)
119
+ with override_options(options, foo=2, bar="a"):
120
+ assert options["foo"] == 2
121
+ assert options["bar"] == "a"
122
+ assert options["foo"] == 1
123
+ assert "bar" not in options
124
+
125
+
107
126
  class TestLoadKeyValuePairs:
108
127
  def test_it_loads_strings(self):
109
128
  assert parse_key_value_pairs({}, ["a=b"]) == {"a": "b"}
@@ -143,7 +162,6 @@ class TestLoadKeyValuePairs:
143
162
 
144
163
 
145
164
  class TestLoadOptions:
146
-
147
165
  def check_loadoptions(self, tmpdir, files, sources="*", tags=[], expected={}):
148
166
  """
149
167
  Write the files indicated in ``sources`` to the given temporary directory,
@@ -192,6 +210,11 @@ class TestLoadOptions:
192
210
  def test_it_loads_py_files(self, tmpdir):
193
211
  self.check_loadoptions(tmpdir, {"a.py": "x = 2 * 2"}, expected={"x": 4})
194
212
 
213
+ def test_py_files_have_options_in_namespace(self, tmpdir):
214
+ self.check_loadoptions(
215
+ tmpdir, {"a.py": "options['foo'] = 'bar'"}, expected={"foo": "bar"}
216
+ )
217
+
195
218
  def test_it_selects_by_tag(self, tmpdir):
196
219
  with self.check_loadoptions(
197
220
  tmpdir,
@@ -217,14 +240,38 @@ class TestLoadOptions:
217
240
  with self.check_loadoptions(
218
241
  tmpdir,
219
242
  {
220
- "a": "a = 0",
221
- "a.dev.txt": "a = ${a}-1",
222
- "a.local.txt": "a = ${a}-2",
223
- "b.dev.txt": "a = ${a}-3",
243
+ "a": "a = 'a'",
244
+ "a.0-dev.txt": "a = ${a} a.dev",
245
+ "a.local.txt": "a = ${a} a.local",
246
+ "b.dev.txt": "a = ${a} b.dev",
247
+ },
248
+ ) as loadopts:
249
+ assert loadopts("*", ["dev", "local"]) == {"a": "a a.dev b.dev a.local"}
250
+ assert loadopts("*", ["local", "dev"]) == {"a": "a a.local a.dev b.dev"}
251
+
252
+ def test_it_loads_in_priority_order(self, tmpdir):
253
+ with self.check_loadoptions(
254
+ tmpdir,
255
+ {
256
+ "a": "a = 'a'",
257
+ "a.100-dev.txt": "a = ${a} a.100-dev",
258
+ "a.local.txt": "a = ${a} a.local",
259
+ "b.dev.txt": "a = ${a} b.dev",
224
260
  },
225
261
  ) as loadopts:
226
- assert loadopts("*", ["dev", "local"]) == {"a": "0-1-3-2"}
227
- assert loadopts("*", ["local", "dev"]) == {"a": "0-2-1-3"}
262
+ assert loadopts("*", ["dev", "local"]) == {"a": "a b.dev a.local a.100-dev"}
263
+ assert loadopts("*", ["local", "dev"]) == {"a": "a a.local b.dev a.100-dev"}
264
+
265
+ def test_it_loads_in_priority_order_without_tags(self, tmpdir):
266
+ with self.check_loadoptions(
267
+ tmpdir,
268
+ {
269
+ "a": "a = a",
270
+ "b.100": "a = ${a} b",
271
+ "a.200.txt": "a = ${a} 100",
272
+ },
273
+ ) as loadopts:
274
+ assert loadopts("*") == {"a": "a b 100"}
228
275
 
229
276
  def test_it_loads_from_os_environ(self, tmpdir):
230
277
  with setenv(a="2"):
@@ -252,7 +299,7 @@ class TestLoadOptions:
252
299
  tmpdir,
253
300
  {"a.txt": "a=1", "b.txt": "b=1"},
254
301
  sources=["a.*", "b.*"],
255
- expected={"a": 1, "b": 1}
302
+ expected={"a": 1, "b": 1},
256
303
  )
257
304
 
258
305
  def test_it_substitutes_from_environment_variables(self, tmpdir):
@@ -261,7 +308,7 @@ class TestLoadOptions:
261
308
  tmpdir,
262
309
  {"a.txt": "a=1", "a.bar.txt": "a=2"},
263
310
  tags=["{FOO}"],
264
- expected={"a": 2}
311
+ expected={"a": 2},
265
312
  )
266
313
 
267
314
  with setenv(FOO="baz"):
@@ -269,7 +316,7 @@ class TestLoadOptions:
269
316
  tmpdir,
270
317
  {"a.txt": "a=1", "a.bar.txt": "a=2"},
271
318
  tags=["{FOO}"],
272
- expected={"a": 1}
319
+ expected={"a": 1},
273
320
  )
274
321
 
275
322
  def test_it_allows_missing_environment_variables(self, tmpdir):
@@ -278,19 +325,16 @@ class TestLoadOptions:
278
325
  tmpdir,
279
326
  {"a.txt": "a=1", "a.bar.txt": "a=2"},
280
327
  tags=["{FOO}"],
281
- expected={"a": 1}
328
+ expected={"a": 1},
282
329
  )
283
330
 
284
331
 
285
332
  class TestDictFromOptions:
286
-
287
333
  def test_it_splits_on_prefix(self):
288
-
289
334
  options = Options(FOO_BAR=1, FOO_BAZ=2, FOO_BAR_BAZ=3, BAR=4)
290
335
  assert dict_from_options("FOO_", options) == {"BAR": 1, "BAZ": 2, "BAR_BAZ": 3}
291
336
 
292
337
  def test_it_splits_recursively(self):
293
-
294
338
  options = Options(
295
339
  A_A=1,
296
340
  A_B_C_D=2,
@@ -303,7 +347,7 @@ class TestDictFromOptions:
303
347
  "A": 1,
304
348
  "B": {"C": {"D": 2}, "E": 3},
305
349
  "F": {"G": {"H": 4}},
306
- "I": 5
350
+ "I": 5,
307
351
  }
308
352
 
309
353
 
@@ -99,7 +99,7 @@ class TestRequestProperties(object):
99
99
  CONTENT_LENGTH="2",
100
100
  CONTENT_TYPE="text/plain; charset=UTF-8",
101
101
  ) as c:
102
- c.request.body_bytes == b"\xc3a"
102
+ assert c.request.body_bytes == b"\xc3a"
103
103
 
104
104
  def test_get_json_decodes_json(self):
105
105
  with context(
@@ -107,7 +107,7 @@ class TestRequestProperties(object):
107
107
  CONTENT_LENGTH="14",
108
108
  CONTENT_TYPE="application/json",
109
109
  ) as c:
110
- c.request.get_json() == {"foo": "bar"}
110
+ assert c.request.get_json() == {"foo": "bar"}
111
111
 
112
112
  def test_get_json_ignores_mime_type(self):
113
113
  with context(
@@ -115,7 +115,7 @@ class TestRequestProperties(object):
115
115
  CONTENT_LENGTH="14",
116
116
  CONTENT_TYPE="application/broken",
117
117
  ) as c:
118
- c.request.get_json() == {"foo": "bar"}
118
+ assert c.request.get_json() == {"foo": "bar"}
119
119
 
120
120
  def test_get_json_passes_args_to_decoder(self):
121
121
  with context(
@@ -123,19 +123,30 @@ class TestRequestProperties(object):
123
123
  CONTENT_LENGTH="10",
124
124
  CONTENT_TYPE="application/broken",
125
125
  ) as c:
126
- c.request.get_json(parse_int=lambda s: s + "!") == {"foo": "1!"}
126
+ assert c.request.get_json(parse_int=lambda s: s + "!") == {"foo": "1!"}
127
127
 
128
- def test_get_does_type_conversion(self):
128
+ def test_get_required_raises_badrequest(self):
129
129
  with context(
130
130
  QUERY_STRING="x=10",
131
131
  CONTENT_LENGTH="10",
132
132
  CONTENT_TYPE="application/broken",
133
133
  ) as c:
134
- assert c.request.get("x") == "10"
135
- assert c.request.get("x", type=int) == 10
134
+ req = c.request
135
+ assert req.get("x", required=True) == "10"
136
136
  with pytest.raises(exceptions.BadRequest):
137
- c.request.get("y", type=int)
138
- assert c.request.get("y", None, type=int) is None
137
+ req.get("y", required=True)
138
+
139
+ def test_get_does_type_conversion(self):
140
+ with context(
141
+ QUERY_STRING="x=10",
142
+ CONTENT_LENGTH="10",
143
+ CONTENT_TYPE="application/broken",
144
+ ) as c:
145
+ req = c.request
146
+ assert req.get("x") == "10"
147
+ assert req.get("x", type=int) == 10
148
+ assert req.get("y", type=int) is None
149
+ assert req.get("y", "20", type=int) == "20"
139
150
 
140
151
  def test_is_secure_returns_correct_value(self):
141
152
  with context("https://example.org/") as c:
@@ -150,7 +161,7 @@ class TestRequestProperties(object):
150
161
  def test_getint_raises_badrequest(self):
151
162
  with pytest.raises(exceptions.BadRequest):
152
163
  with context("http://example.org/") as c:
153
- c.request.getint("a")
164
+ c.request.getint("a", required=True)
154
165
 
155
166
  with pytest.raises(exceptions.BadRequest):
156
167
  with context("http://example.org/?a=four") as c:
@@ -24,6 +24,7 @@ import tms
24
24
 
25
25
  from fresco import FrescoApp
26
26
  from fresco.core import urlfor
27
+ from fresco.request import Request
27
28
  from fresco.exceptions import NotFound
28
29
  from fresco.response import Response
29
30
  from fresco.routing import ALL_METHODS
@@ -31,6 +32,7 @@ from fresco.routing import ALL
31
32
  from fresco.routing import GET
32
33
  from fresco.routing import OPTIONS
33
34
  from fresco.routing import POST
35
+ from fresco.routing import _has_request_parameter
34
36
  from fresco.routing import (
35
37
  Route,
36
38
  DelegateRoute,
@@ -45,6 +47,10 @@ from fresco.routing import (
45
47
  from . import fixtures
46
48
 
47
49
 
50
+ def dummyview() -> Response:
51
+ return Response()
52
+
53
+
48
54
  def assert_method_bound_to(method, ob):
49
55
  try:
50
56
  assert method.__self__ is ob
@@ -347,36 +353,36 @@ class TestPredicates(object):
347
353
 
348
354
  class TestRouteNames(object):
349
355
  def test_name_present_in_route_keys(self):
350
- r = Route("/", GET, None, name="foo")
356
+ r = Route("/", GET, lambda: None, name="foo")
351
357
  assert "foo" in list(r.route_keys())
352
358
 
353
359
  def test_name_with_other_kwargs(self):
354
- r = Route("/", GET, None, name="foo", x="bar")
360
+ r = Route("/", GET, lambda: None, name="foo", x="bar")
355
361
  assert "foo" in list(r.route_keys())
356
362
 
357
363
  def test_name_cannot_contain_colon(self):
358
364
  with pytest.raises(ValueError):
359
- Route("/", GET, None, name="foo:bar")
365
+ Route("/", GET, lambda: None, name="foo:bar")
360
366
 
361
367
 
362
368
  class TestRouteCollection(object):
363
369
  def test_it_adds_routes_from_constructor(self):
364
- r1 = Route("/1", GET, None, name="1")
365
- r2 = Route("/2", POST, None, name="2")
370
+ r1 = Route("/1", GET, dummyview, name="1")
371
+ r2 = Route("/2", POST, dummyview, name="2")
366
372
  rc = RouteCollection([r1, r2])
367
373
  assert [r.name for r in rc] == ["1", "2"]
368
374
 
369
375
  def test_it_adds_routecollections_from_constructor(self):
370
- r1 = Route("/", GET, None, name="1")
371
- r2 = Route("/", POST, None, name="2")
372
- r3 = Route("/", POST, None, name="3")
376
+ r1 = Route("/", GET, dummyview, name="1")
377
+ r2 = Route("/", POST, dummyview, name="2")
378
+ r3 = Route("/", POST, dummyview, name="3")
373
379
  rc = RouteCollection([r1, RouteCollection([r2, r3])])
374
380
  assert [r.name for r in rc] == ["1", "2", "3"]
375
381
 
376
382
  def test_it_adds_dunderroutes_from_constructor(self):
377
- r1 = Route("/", GET, None, name="1")
378
- r2 = Route("/", POST, None, name="2")
379
- r3 = Route("/", POST, None, name="3")
383
+ r1 = Route("/", GET, dummyview, name="1")
384
+ r2 = Route("/", POST, dummyview, name="2")
385
+ r3 = Route("/", POST, dummyview, name="3")
380
386
 
381
387
  class A:
382
388
  __routes__ = [r2, r3]
@@ -385,8 +391,8 @@ class TestRouteCollection(object):
385
391
  assert [r.name for r in rc] == ["1", "2", "3"]
386
392
 
387
393
  def test_get_routes_matches_on_method(self):
388
- r_get = Route("/", GET, None)
389
- r_post = Route("/", POST, None)
394
+ r_get = Route("/", GET, dummyview)
395
+ r_post = Route("/", POST, dummyview)
390
396
 
391
397
  rc = RouteCollection([r_post, r_get])
392
398
 
@@ -394,8 +400,8 @@ class TestRouteCollection(object):
394
400
  assert [r.route for r in rc.get_route_traversals("/", POST)] == [r_post]
395
401
 
396
402
  def test_get_routes_matches_on_path(self):
397
- r1 = Route("/1", GET, None)
398
- r2 = Route("/2", GET, None)
403
+ r1 = Route("/1", GET, dummyview)
404
+ r2 = Route("/2", GET, dummyview)
399
405
 
400
406
  rc = RouteCollection([r1, r2])
401
407
 
@@ -403,8 +409,8 @@ class TestRouteCollection(object):
403
409
  assert [r.route for r in rc.get_route_traversals("/2", GET)] == [r2]
404
410
 
405
411
  def test_get_routes_can_match_all_methods(self):
406
- r1 = Route("/1", GET, None)
407
- r2 = Route("/1", POST, None)
412
+ r1 = Route("/1", GET, dummyview)
413
+ r2 = Route("/1", POST, dummyview)
408
414
 
409
415
  rc = RouteCollection([r1, r2])
410
416
  assert [r.route for r in rc.get_route_traversals("/1", None)] == [
@@ -416,8 +422,8 @@ class TestRouteCollection(object):
416
422
  a = RouteCollection()
417
423
  b = RouteCollection()
418
424
 
419
- a_route = Route("/harvey", GET, lambda: None)
420
- b_route = Route("/harvey", GET, lambda: None)
425
+ a_route = Route("/harvey", GET, dummyview)
426
+ b_route = Route("/harvey", GET, dummyview)
421
427
 
422
428
  a.add_route(a_route)
423
429
  b.add_route(b_route)
@@ -428,7 +434,7 @@ class TestRouteCollection(object):
428
434
  a.add_route(a_delegate_route)
429
435
  b.add_route(b_delegate_route)
430
436
 
431
- r = next(a.get_route_traversals("/rabbit/hole/rabbit/harvey", None))
437
+ r = next(a.get_route_traversals("/rabbit/hole/rabbit/harvey", "GET"))
432
438
 
433
439
  assert r.collections_traversed == [
434
440
  (a, "", a_delegate_route, (), {}, (), {}),
@@ -525,7 +531,9 @@ class TestRouteCollection(object):
525
531
  ]
526
532
 
527
533
  def test_add_prefix_returns_prefixed_collection(self):
528
- rc = RouteCollection([Route("/fish", GET, None), Route("/beans", GET, None)])
534
+ rc = RouteCollection(
535
+ [Route("/fish", GET, dummyview), Route("/beans", GET, dummyview)]
536
+ )
529
537
  prefixed = rc.add_prefix("/jelly")
530
538
  assert [str(r.pattern) for r in prefixed] == [
531
539
  "/jelly/fish",
@@ -628,15 +636,14 @@ class TestRouteCollection(object):
628
636
  assert list(response.content_iterator) == [b"ok"]
629
637
 
630
638
  def test_it_isolates_request_for_wsgi_app(self):
631
-
632
639
  params: t.Dict[str, t.Any] = {}
633
640
  outerapp = FrescoApp()
634
641
  innerapp = FrescoApp()
635
642
  innerapp.route("/y", GET=Response)
636
643
  outerapp.route_wsgi("/x", innerapp)
637
644
 
638
- innerapp.process_request(partial(params.__setitem__, 'inner_request'))
639
- outerapp.process_request(partial(params.__setitem__, 'outer_request'))
645
+ innerapp.process_request(partial(params.__setitem__, "inner_request"))
646
+ outerapp.process_request(partial(params.__setitem__, "outer_request"))
640
647
 
641
648
  with outerapp.requestcontext("/x/y"):
642
649
  outerapp.view()
@@ -911,25 +918,25 @@ class TestRRoute:
911
918
 
912
919
  class TestViewArgs(object):
913
920
  def test_it_uses_args(self):
914
- routes = RouteCollection([Route("/", GET, None, args=(1, 2))])
921
+ routes = RouteCollection([Route("/", GET, dummyview, args=(1, 2))])
915
922
  assert list(routes.get_route_traversals("/", GET)) == [
916
923
  tms.InstanceOf(RouteTraversal, args=(1, 2))
917
924
  ]
918
925
 
919
926
  def test_it_uses_view_args(self):
920
- routes = RouteCollection([Route("/", GET, None, view_args=(1, 2))])
927
+ routes = RouteCollection([Route("/", GET, dummyview, view_args=(1, 2))])
921
928
  assert list(routes.get_route_traversals("/", GET)) == [
922
929
  tms.InstanceOf(RouteTraversal, args=(1, 2))
923
930
  ]
924
931
 
925
932
  def test_it_appends_args_extracted_from_path(self):
926
- routes = RouteCollection([Route("/<:int>", GET, None, view_args=(1, 2))])
933
+ routes = RouteCollection([Route("/<:int>", GET, dummyview, view_args=(1, 2))])
927
934
  assert list(routes.get_route_traversals("/3", GET)) == [
928
935
  tms.InstanceOf(RouteTraversal, args=(1, 2, 3))
929
936
  ]
930
937
 
931
938
  def test_it_keeps_traversal_args_separate(self):
932
- routes = RouteCollection([Route("/<:int>", GET, None, view_args=(1,))])
939
+ routes = RouteCollection([Route("/<:int>", GET, dummyview, view_args=(1,))])
933
940
  assert list(routes.get_route_traversals("/2", GET)) == [
934
941
  tms.InstanceOf(
935
942
  RouteTraversal,
@@ -945,25 +952,27 @@ class TestViewArgs(object):
945
952
 
946
953
  class TestViewKwargs(object):
947
954
  def test_it_reads_from_route_kwargs(self):
948
- routes = RouteCollection([Route("/", GET, None, x=1)])
955
+ routes = RouteCollection([Route("/", GET, dummyview, x=1)])
949
956
  assert list(routes.get_route_traversals("/", GET)) == [
950
957
  tms.InstanceOf(RouteTraversal, kwargs={"x": 1})
951
958
  ]
952
959
 
953
960
  def test_it_reads_from_kwargs(self):
954
- routes = RouteCollection([Route("/", GET, None, kwargs={"x": 1})])
961
+ routes = RouteCollection([Route("/", GET, dummyview, kwargs={"x": 1})])
955
962
  assert list(routes.get_route_traversals("/", GET)) == [
956
963
  tms.InstanceOf(RouteTraversal, kwargs={"x": 1})
957
964
  ]
958
965
 
959
966
  def test_it_reads_from_view_kwargs(self):
960
- routes = RouteCollection([Route("/", GET, None, view_kwargs={"x": 1})])
967
+ routes = RouteCollection([Route("/", GET, dummyview, view_kwargs={"x": 1})])
961
968
  assert list(routes.get_route_traversals("/", GET)) == [
962
969
  tms.InstanceOf(RouteTraversal, kwargs={"x": 1})
963
970
  ]
964
971
 
965
972
  def test_it_keeps_traversal_kwargs_separate(self):
966
- routes = RouteCollection([Route("/<x:int>", GET, None, view_kwargs={"y": 1})])
973
+ routes = RouteCollection(
974
+ [Route("/<x:int>", GET, dummyview, view_kwargs={"y": 1})]
975
+ )
967
976
  assert list(routes.get_route_traversals("/2", GET)) == [
968
977
  tms.InstanceOf(
969
978
  RouteTraversal,
@@ -1130,7 +1139,6 @@ class TestRouteCache(object):
1130
1139
 
1131
1140
 
1132
1141
  class TestRouteAll:
1133
-
1134
1142
  def test_route_all_matches_on_separator(self):
1135
1143
  def view():
1136
1144
  return Response()
@@ -1141,3 +1149,48 @@ class TestRouteAll:
1141
1149
  assert len(list(app.get_route_traversals("/x", GET))) == 1
1142
1150
  assert len(list(app.get_route_traversals("/x/y", GET))) == 1
1143
1151
  assert len(list(app.get_route_traversals("/xy", GET))) == 0
1152
+
1153
+
1154
+ class TestRequestParameter:
1155
+ def test_has_request_parameter(self):
1156
+ def a(request: Request):
1157
+ pass
1158
+
1159
+ def b(request: t.Optional[Request]):
1160
+ pass
1161
+
1162
+ def c(request: t.Union[Request, None]):
1163
+ pass
1164
+
1165
+ def d(request: t.Union[None, Request]):
1166
+ pass
1167
+
1168
+ def e(request: int):
1169
+ pass
1170
+
1171
+ def f(request):
1172
+ pass
1173
+
1174
+ assert _has_request_parameter(a) is True
1175
+ assert _has_request_parameter(b) is True
1176
+ assert _has_request_parameter(c) is True
1177
+ assert _has_request_parameter(d) is True
1178
+ assert _has_request_parameter(e) is False
1179
+ assert _has_request_parameter(f) is False
1180
+
1181
+ def test_request_is_provided_automatically(self):
1182
+ def a(request: Request):
1183
+ return Response("a")
1184
+
1185
+ def b():
1186
+ return Response("b")
1187
+
1188
+ app = FrescoApp()
1189
+ app.route("/a", GET=a)
1190
+ app.route("/b", GET=b)
1191
+
1192
+ with app.requestcontext("/a"):
1193
+ assert app.view().content == "a"
1194
+
1195
+ with app.requestcontext("/b"):
1196
+ assert app.view().content == "b"
@@ -42,7 +42,7 @@ class TestParseQueryString(object):
42
42
  return list(parse_querystring(value))
43
43
 
44
44
  def test_empty(self):
45
- self.p("") == []
45
+ assert self.p("") == []
46
46
 
47
47
  def test_simple_key_value(self):
48
48
  assert self.p("a=b") == [("a", "b")]
@@ -276,12 +276,10 @@ class TestParseFormEncodedData(object):
276
276
  class TestEncodeMultipart(object):
277
277
  def test_it_encodes_a_data_dict(self):
278
278
  data, headers = encode_multipart([("foo", "bar baf")])
279
- data = data.getvalue()
280
279
  assert b'Content-Disposition: form-data; name="foo"\r\n\r\nbar baf' in data
281
280
 
282
281
  def test_it_encodes_a_file_tuple(self):
283
282
  data, headers = encode_multipart(files=[("foo", "foo.txt", "ascii", "bar")])
284
- data = data.getvalue()
285
283
  expected = (
286
284
  b"Content-Disposition: form-data; "
287
285
  b'name="foo"; filename="foo.txt"\r\n'
fresco/types.py CHANGED
@@ -1,6 +1,32 @@
1
+ from collections.abc import Mapping
2
+ from collections.abc import Iterable
3
+ from collections.abc import Callable
4
+ from types import TracebackType
5
+ from typing import Any
6
+ from typing import Optional
1
7
  import typing as t
2
8
 
9
+ import fresco.response # noqa
10
+
11
+
3
12
  QuerySpec = t.Union[
4
- t.Mapping[str, t.Any],
5
- t.Iterable[t.Tuple[str, t.Any]]
13
+ Mapping[str, Any],
14
+ Iterable[tuple[str, Any]]
15
+ ]
16
+ ViewCallable = Callable[..., "fresco.response.Response"]
17
+ ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
18
+ OptionalExcInfo = Optional[ExcInfo]
19
+
20
+
21
+ HeaderList = list[tuple[str, str]]
22
+ HeadersList = HeaderList
23
+ WSGIEnviron = dict[str, Any]
24
+ WriteCallable = Callable[[bytes], object]
25
+ StartResponse = Callable[[str, HeaderList, OptionalExcInfo], WriteCallable]
26
+ WSGIApplication = Callable[
27
+ [
28
+ WSGIEnviron,
29
+ StartResponse,
30
+ ],
31
+ Iterable[bytes]
6
32
  ]
fresco/util/cache.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from typing import Any
2
+ from typing import Callable
2
3
  from typing import Dict
3
4
  from typing import List
4
5
  import sys
@@ -46,7 +47,7 @@ def cache_generator(original_function, maxsize):
46
47
  link[NEXT] = root
47
48
 
48
49
 
49
- def make_cache(original_function, maxsize=100):
50
+ def make_cache(original_function, maxsize=100) -> Callable[[Any], Any]:
50
51
  "Create a cache around a function that takes a single argument"
51
52
  c = cache_generator(original_function, maxsize)
52
53
  next(c)