fastapi-extra 0.2.0__tar.gz → 0.2.1__tar.gz

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.
Files changed (29) hide show
  1. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/PKG-INFO +1 -1
  2. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/__init__.py +1 -1
  3. fastapi_extra-0.2.1/fastapi_extra/cursor.pyi +8 -0
  4. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/native/cursor.pyx +2 -2
  5. fastapi_extra-0.2.1/fastapi_extra/native/routing.pyx +192 -0
  6. fastapi_extra-0.2.1/fastapi_extra/routing.pyi +16 -0
  7. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra.egg-info/PKG-INFO +1 -1
  8. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra.egg-info/SOURCES.txt +2 -0
  9. fastapi_extra-0.2.0/fastapi_extra/native/routing.pyx +0 -135
  10. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/LICENSE +0 -0
  11. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/README.rst +0 -0
  12. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/cache/__init__.py +0 -0
  13. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/cache/redis.py +0 -0
  14. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/database/__init__.py +0 -0
  15. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/database/model.py +0 -0
  16. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/database/service.py +0 -0
  17. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/database/session.py +0 -0
  18. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/dependency.py +0 -0
  19. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/form.py +0 -0
  20. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/py.typed +0 -0
  21. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/response.py +0 -0
  22. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/settings.py +0 -0
  23. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/types.py +0 -0
  24. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra/utils.py +0 -0
  25. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra.egg-info/dependency_links.txt +0 -0
  26. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra.egg-info/requires.txt +0 -0
  27. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/fastapi_extra.egg-info/top_level.txt +0 -0
  28. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/pyproject.toml +0 -0
  29. {fastapi_extra-0.2.0 → fastapi_extra-0.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-extra
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.0"
1
+ __version__ = "0.2.1"
2
2
 
3
3
 
4
4
  from fastapi import FastAPI
@@ -0,0 +1,8 @@
1
+ __author__ = "ziyan.yin"
2
+ __describe__ = ""
3
+
4
+
5
+ class Cursor:
6
+
7
+ def next_val(self) -> int:
8
+ ...
@@ -37,8 +37,8 @@ cdef class Cursor:
37
37
  self.cursor = count
38
38
  return (point << (_sequence_length + 4)) + (self.seed << _sequence_length) + count
39
39
 
40
- def next_val(self) -> str:
40
+ def next_val(self) -> int:
41
41
  index = self.fetch()
42
42
  while index == 0:
43
43
  index = self.fetch()
44
- return str(index)
44
+ return index
@@ -0,0 +1,192 @@
1
+ __author__ = "ziyan.yin"
2
+ __describe__ = ""
3
+
4
+ cimport cython
5
+
6
+ from starlette import _utils as starlette_utils
7
+ from starlette.datastructures import URL
8
+ from starlette.responses import RedirectResponse
9
+
10
+
11
+ cdef int find_params(unicode path):
12
+ for i, ch in enumerate(path):
13
+ if ch == "{":
14
+ return i
15
+ return -1
16
+
17
+
18
+ cdef int get_longest_common_prefix(unicode path, unicode node_path):
19
+ cdef int i
20
+ cdef int max_len = min(len(path), len(node_path))
21
+ for i in range(max_len):
22
+ if path[i] != node_path[i]:
23
+ return i
24
+ return max_len
25
+
26
+
27
+ @cython.no_gc
28
+ cdef class RouteNode:
29
+
30
+ cdef readonly:
31
+ unicode prefix
32
+ list params_routes
33
+ list static_routes
34
+ dict children
35
+
36
+ cdef public object parent
37
+
38
+ def __cinit__(self, prefix: str):
39
+ self.prefix = prefix
40
+ self.params_routes = []
41
+ self.static_routes = []
42
+ self.children = {}
43
+ self.parent = None
44
+
45
+ def add_route(self, fullpath: str, handler: object):
46
+ wild_child = False
47
+ if (index := find_params(fullpath)) >= 0:
48
+ wild_child = True
49
+ path = fullpath[:index]
50
+ else:
51
+ path = fullpath
52
+ insert_route(self, path, wild_child, handler)
53
+
54
+
55
+ cdef void insert_route(RouteNode node, unicode path, bint wild_child, object handler):
56
+ if node.prefix == path:
57
+ add_node(node, wild_child, handler)
58
+ return
59
+
60
+ cdef Py_UCS4 key = path.removeprefix(node.prefix)[0]
61
+ if key not in node.children:
62
+ add_child_node(node, key, path, wild_child, handler)
63
+ return
64
+
65
+ child_node = node.children[key]
66
+ i = get_longest_common_prefix(child_node.prefix, path)
67
+ longest_prefix = child_node.prefix[0: i]
68
+ if i == len(child_node.prefix):
69
+ insert_route(node.children[key], path, wild_child, handler)
70
+ return
71
+ next_node = RouteNode.__new__(RouteNode, longest_prefix)
72
+ next_node.parent = node
73
+ node.children[key] = next_node
74
+ next_node.children[child_node.prefix[i]] = child_node
75
+ child_node.parent = next_node
76
+ insert_route(next_node, path, wild_child, handler)
77
+
78
+
79
+ cdef inline void add_child_node(RouteNode node, Py_UCS4 key, unicode path, bint wild_child, object handler):
80
+ child = RouteNode.__new__(RouteNode, path)
81
+ child.parent = node
82
+ add_node(child, wild_child, handler)
83
+ node.children[key] = child
84
+
85
+
86
+ cdef inline void add_node(RouteNode node, bint wild_child, object handler):
87
+ if wild_child:
88
+ node.params_routes.append(handler)
89
+ else:
90
+ node.static_routes.append(handler)
91
+
92
+
93
+ root_node = RouteNode.__new__(RouteNode, "")
94
+
95
+
96
+ cdef RouteNode search_node(unicode url):
97
+ cdef RouteNode current_node = root_node
98
+ cdef int n = len(url)
99
+ cdef int i = get_longest_common_prefix(url, current_node.prefix)
100
+
101
+ while i < n:
102
+ key = url[i]
103
+ if key not in current_node.children:
104
+ break
105
+ current_node = current_node.children[key]
106
+ i = get_longest_common_prefix(url, current_node.prefix)
107
+
108
+ return current_node
109
+
110
+
111
+ async def handle(scope, receive, send):
112
+ router = scope["app"].router
113
+ assert scope["type"] in ("http", "websocket", "lifespan")
114
+
115
+ if "router" not in scope:
116
+ scope["router"] = router
117
+
118
+ if scope["type"] == "lifespan":
119
+ await router.lifespan(scope, receive, send)
120
+ return
121
+
122
+ partial = None
123
+
124
+ scope["path"] = route_path = starlette_utils.get_route_path(scope)
125
+ scope["root_path"] = ""
126
+ leaf_node = search_node(route_path)
127
+
128
+ if leaf_node.prefix == route_path:
129
+ for route in leaf_node.static_routes:
130
+ match, child_scope = route.matches(scope)
131
+ if match.value == 2:
132
+ scope.update(child_scope)
133
+ await route.handle(scope, receive, send)
134
+ return
135
+ elif match.value == 1 and partial is None:
136
+ partial = route
137
+ partial_scope = child_scope
138
+ else:
139
+ current_node = leaf_node
140
+ routes = current_node.params_routes
141
+ while current_node.parent:
142
+ for route in routes:
143
+ match, child_scope = route.matches(scope)
144
+ if match.value == 2:
145
+ scope.update(child_scope)
146
+ await route.handle(scope, receive, send)
147
+ return
148
+ elif match.value == 1 and partial is None:
149
+ partial = route
150
+ partial_scope = child_scope
151
+ current_node = current_node.parent
152
+
153
+ if partial is not None:
154
+ scope.update(partial_scope)
155
+ await partial.handle(scope, receive, send)
156
+ return
157
+
158
+ if scope["type"] == "http" and router.redirect_slashes and route_path != "/":
159
+ redirect_scope = dict(scope)
160
+ if route_path.endswith("/"):
161
+ redirect_scope["path"] = redirect_scope["path"].rstrip("/")
162
+ else:
163
+ redirect_scope["path"] = redirect_scope["path"] + "/"
164
+
165
+ if leaf_node.prefix == redirect_scope["path"]:
166
+ for route in leaf_node.static_routes:
167
+ match, child_scope = route.matches(redirect_scope)
168
+ if match.value != 0:
169
+ redirect_url = URL(scope=redirect_scope)
170
+ response = RedirectResponse(url=str(redirect_url))
171
+ await response(scope, receive, send)
172
+ return
173
+ else:
174
+ current_node = leaf_node
175
+ routes = current_node.params_routes
176
+ while current_node.parent:
177
+ for route in routes:
178
+ if match.value != 0:
179
+ redirect_url = URL(scope=redirect_scope)
180
+ response = RedirectResponse(url=str(redirect_url))
181
+ await response(scope, receive, send)
182
+ return
183
+ current_node = current_node.parent
184
+
185
+ await router.default(scope, receive, send)
186
+
187
+
188
+ def install(app):
189
+ for route in app.routes:
190
+ root_node.add_route(route.path, route)
191
+
192
+ app.router.middleware_stack = handle
@@ -0,0 +1,16 @@
1
+ __author__ = "ziyan.yin"
2
+ __describe__ = ""
3
+
4
+
5
+ from typing import Any
6
+ from fastapi import FastAPI
7
+
8
+
9
+ class RouteNode:
10
+
11
+ def add_route(self, fullpath: str, handler: Any):
12
+ ...
13
+
14
+
15
+ def install(app: FastAPI) -> None:
16
+ ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-extra
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -2,10 +2,12 @@ LICENSE
2
2
  README.rst
3
3
  pyproject.toml
4
4
  fastapi_extra/__init__.py
5
+ fastapi_extra/cursor.pyi
5
6
  fastapi_extra/dependency.py
6
7
  fastapi_extra/form.py
7
8
  fastapi_extra/py.typed
8
9
  fastapi_extra/response.py
10
+ fastapi_extra/routing.pyi
9
11
  fastapi_extra/settings.py
10
12
  fastapi_extra/types.py
11
13
  fastapi_extra/utils.py
@@ -1,135 +0,0 @@
1
- __author__ = "ziyan.yin"
2
- __describe__ = ""
3
-
4
- cimport cython
5
-
6
- from typing import MutableMapping
7
-
8
- from starlette import _utils as starlette_utils
9
- from starlette.datastructures import URL
10
- from starlette.responses import RedirectResponse
11
-
12
-
13
- @cython.no_gc
14
- cdef class RouteNode:
15
- cdef readonly:
16
- list routes
17
- dict leaves
18
- unicode prefix
19
-
20
- def __cinit__(self, prefix):
21
- self.prefix = prefix
22
- self.routes = []
23
- self.leaves = {}
24
-
25
- def add_route(self, route):
26
- self.routes.append(route)
27
-
28
- def add_leaf(self, node):
29
- if node.prefix in self.leaves:
30
- raise KeyError(node.prefix)
31
- else:
32
- self.leaves[node.prefix] = node
33
-
34
-
35
- cdef list change_path_to_ranks(unicode path):
36
- ranks = path.lstrip('/').split('/')
37
- return ranks
38
-
39
-
40
- cdef void add_route(unicode path, RouteNode root, object route):
41
- current_node = root
42
- ranks = change_path_to_ranks(path)
43
- for r in ranks:
44
- if r.find('{') >= 0 and r.find('}') > 0:
45
- break
46
- if not r:
47
- continue
48
- if r in current_node.leaves:
49
- current_node = current_node.leaves[r]
50
- else:
51
- next_node = RouteNode.__new__(RouteNode, r)
52
- current_node.add_leaf(next_node)
53
- current_node = next_node
54
- current_node.add_route(route)
55
-
56
-
57
- cdef list find_routes(unicode path, RouteNode root):
58
- current_node = root
59
- ranks = change_path_to_ranks(path)
60
-
61
- routes = []
62
- if current_node.routes:
63
- routes += current_node.routes
64
- for r in ranks:
65
- if not r:
66
- continue
67
- if r in current_node.leaves:
68
- current_node = current_node.leaves[r]
69
- if current_node.routes:
70
- routes += current_node.routes
71
- continue
72
- break
73
- return routes
74
-
75
-
76
- root_node = RouteNode.__new__(RouteNode, "")
77
-
78
-
79
- async def handle(router, scope, receive, send):
80
- assert scope["type"] in ("http", "websocket", "lifespan")
81
-
82
- if "router" not in scope:
83
- scope["router"] = router
84
-
85
- if scope["type"] == "lifespan":
86
- await router.lifespan(scope, receive, send)
87
- return
88
-
89
- partial = None
90
-
91
- scope["path"] = route_path = starlette_utils.get_route_path(scope)
92
- scope["root_path"] = ""
93
- matched_routes = find_routes(route_path, root_node)
94
- n = len(matched_routes)
95
-
96
- for i in range(n):
97
- route = matched_routes[n - i - 1]
98
- match, child_scope = route.matches(scope)
99
- if match.value == 2:
100
- scope.update(child_scope)
101
- await route.handle(scope, receive, send)
102
- return
103
- elif match.value == 1 and partial is None:
104
- partial = route
105
- partial_scope = child_scope
106
-
107
- if partial is not None:
108
- scope.update(partial_scope)
109
- await partial.handle(scope, receive, send)
110
- return
111
-
112
-
113
- if scope["type"] == "http" and router.redirect_slashes and route_path != "/":
114
- redirect_scope = dict(scope)
115
- if route_path.endswith("/"):
116
- redirect_scope["path"] = redirect_scope["path"].rstrip("/")
117
- else:
118
- redirect_scope["path"] = redirect_scope["path"] + "/"
119
-
120
- for i in range(n):
121
- route = matched_routes[n - i - 1]
122
- match, child_scope = route.matches(redirect_scope)
123
- if match.value != 0:
124
- redirect_url = URL(scope=redirect_scope)
125
- response = RedirectResponse(url=str(redirect_url))
126
- await response(scope, receive, send)
127
- return
128
-
129
- await router.default(scope, receive, send)
130
-
131
-
132
- def install(app):
133
- for route in app.routes:
134
- add_route(route.path, root_node, route)
135
- app.router.app = handle
File without changes
File without changes
File without changes