fastapi-extra 0.2.0__tar.gz → 0.2.2__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.2}/PKG-INFO +1 -1
  2. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/__init__.py +1 -1
  3. fastapi_extra-0.2.2/fastapi_extra/cursor.pyi +8 -0
  4. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/database/service.py +2 -2
  5. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/native/cursor.pyx +2 -2
  6. fastapi_extra-0.2.2/fastapi_extra/native/routing.pyx +191 -0
  7. fastapi_extra-0.2.2/fastapi_extra/routing.pyi +16 -0
  8. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra.egg-info/PKG-INFO +1 -1
  9. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra.egg-info/SOURCES.txt +2 -0
  10. fastapi_extra-0.2.0/fastapi_extra/native/routing.pyx +0 -135
  11. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/LICENSE +0 -0
  12. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/README.rst +0 -0
  13. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/cache/__init__.py +0 -0
  14. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/cache/redis.py +0 -0
  15. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/database/__init__.py +0 -0
  16. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/database/model.py +0 -0
  17. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/database/session.py +0 -0
  18. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/dependency.py +0 -0
  19. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/form.py +0 -0
  20. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/py.typed +0 -0
  21. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/response.py +0 -0
  22. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/settings.py +0 -0
  23. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/types.py +0 -0
  24. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra/utils.py +0 -0
  25. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra.egg-info/dependency_links.txt +0 -0
  26. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra.egg-info/requires.txt +0 -0
  27. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/fastapi_extra.egg-info/top_level.txt +0 -0
  28. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/pyproject.toml +0 -0
  29. {fastapi_extra-0.2.0 → fastapi_extra-0.2.2}/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.2
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.2"
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
+ ...
@@ -5,7 +5,7 @@ from contextvars import ContextVar
5
5
  from typing import Any, Generic, Self, TypeVar
6
6
 
7
7
  from fastapi_extra.database.model import SQLModel
8
- from fastapi_extra.database.session import DefaultSession
8
+ from fastapi_extra.database.session import AsyncSession, DefaultSession
9
9
  from fastapi_extra.dependency import AbstractService
10
10
 
11
11
  Model = TypeVar("Model", bound=SQLModel)
@@ -32,7 +32,7 @@ class ModelService(AbstractService, Generic[Model], abstract=True):
32
32
  self.__session_container__.set(session)
33
33
 
34
34
  @property
35
- def session(self):
35
+ def session(self) -> AsyncSession:
36
36
  _session = self.__session_container__.get()
37
37
  assert _session is not None, "Session is not initialized"
38
38
  return _session
@@ -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,191 @@
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
+ leaf_node = search_node(route_path)
126
+
127
+ if leaf_node.prefix == route_path:
128
+ for route in leaf_node.static_routes:
129
+ match, child_scope = route.matches(scope)
130
+ if match.value == 2:
131
+ scope.update(child_scope)
132
+ await route.handle(scope, receive, send)
133
+ return
134
+ elif match.value == 1 and partial is None:
135
+ partial = route
136
+ partial_scope = child_scope
137
+ else:
138
+ current_node = leaf_node
139
+ routes = current_node.params_routes
140
+ while current_node.parent:
141
+ for route in routes:
142
+ match, child_scope = route.matches(scope)
143
+ if match.value == 2:
144
+ scope.update(child_scope)
145
+ await route.handle(scope, receive, send)
146
+ return
147
+ elif match.value == 1 and partial is None:
148
+ partial = route
149
+ partial_scope = child_scope
150
+ current_node = current_node.parent
151
+
152
+ if partial is not None:
153
+ scope.update(partial_scope)
154
+ await partial.handle(scope, receive, send)
155
+ return
156
+
157
+ if scope["type"] == "http" and router.redirect_slashes and route_path != "/":
158
+ redirect_scope = dict(scope)
159
+ if route_path.endswith("/"):
160
+ redirect_scope["path"] = redirect_scope["path"].rstrip("/")
161
+ else:
162
+ redirect_scope["path"] = redirect_scope["path"] + "/"
163
+
164
+ if leaf_node.prefix == redirect_scope["path"]:
165
+ for route in leaf_node.static_routes:
166
+ match, child_scope = route.matches(redirect_scope)
167
+ if match.value != 0:
168
+ redirect_url = URL(scope=redirect_scope)
169
+ response = RedirectResponse(url=str(redirect_url))
170
+ await response(scope, receive, send)
171
+ return
172
+ else:
173
+ current_node = leaf_node
174
+ routes = current_node.params_routes
175
+ while current_node.parent:
176
+ for route in routes:
177
+ if match.value != 0:
178
+ redirect_url = URL(scope=redirect_scope)
179
+ response = RedirectResponse(url=str(redirect_url))
180
+ await response(scope, receive, send)
181
+ return
182
+ current_node = current_node.parent
183
+
184
+ await router.default(scope, receive, send)
185
+
186
+
187
+ def install(app):
188
+ for route in app.routes:
189
+ root_node.add_route(route.path, route)
190
+
191
+ 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.2
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