spaces 0.50.dev2__tar.gz → 0.50.dev3__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.
- {spaces-0.50.dev2 → spaces-0.50.dev3}/PKG-INFO +1 -1
- {spaces-0.50.dev2 → spaces-0.50.dev3}/pyproject.toml +1 -1
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/client.py +10 -5
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/context.py +12 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/middleware.py +11 -2
- {spaces-0.50.dev2 → spaces-0.50.dev3}/README.md +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/asgiref/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/asgiref/typing.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/codefind/LICENSE +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/codefind/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/codefind/registry.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/codefind/version.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/LICENSE +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/codetools.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/parse.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/register.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/utils.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/jurigged/version.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/LICENSE +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/abc.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/codegen.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/core.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/dependent.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/medley.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/mro.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/py.typed +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/recode.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/signatures.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/typemap.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/types.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/utils.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/ovld/version.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sse_starlette/LICENSE +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sse_starlette/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sse_starlette/event.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sse_starlette/py.typed +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sse_starlette/sse.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sseclient/LICENSE +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/_vendor/sseclient/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/config.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/reloading/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/reloading/client.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/reloading/server.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/reloading/types.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/reloading/types.ts +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/utils.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/api.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/decorator.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/gradio.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/lifecycle.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/mmap.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/__init__.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/aoti.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/bitsandbytes.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/bitsandbytes_legacy.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/cudart.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/packing.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/patching.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/patching_legacy.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/static.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/types.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/torch/utils.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/tqdm.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/types.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/utils.py +0 -0
- {spaces-0.50.dev2 → spaces-0.50.dev3}/spaces/zero/wrappers.py +0 -0
|
@@ -60,6 +60,7 @@ def html_string(html_contents: str, text_contents: str): # pragma: no cover
|
|
|
60
60
|
return HTMLString(html_contents)
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
# TODO: Remove (make it like ZeroGPU quota exceeded errors)
|
|
63
64
|
def _toast_action(
|
|
64
65
|
auth: AuthLevel | None,
|
|
65
66
|
pro_message: str,
|
|
@@ -126,7 +127,7 @@ def schedule(
|
|
|
126
127
|
f"is larger than the maximum allowed"
|
|
127
128
|
)
|
|
128
129
|
elif token is None:
|
|
129
|
-
title = "ZeroGPU quota exceeded"
|
|
130
|
+
title = "ZeroGPU quota exceeded" # TODO: different title?
|
|
130
131
|
message = (
|
|
131
132
|
f"Space app has reached its GPU limit. "
|
|
132
133
|
f"{EXAMPLES_RETRY_MESSAGE}"
|
|
@@ -189,11 +190,13 @@ def schedule(
|
|
|
189
190
|
raise RuntimeError("Error while re-trying after queue disconnect")
|
|
190
191
|
return schedule(task_id, duration, _first_attempt=False)
|
|
191
192
|
if event.event == 'ping':
|
|
192
|
-
|
|
193
|
+
print('ping', getattr(connection_state, 'id', None))
|
|
194
|
+
if ctx.disconnected:
|
|
193
195
|
res.close()
|
|
194
|
-
raise RuntimeError("Connection closed by visitor while queueing")
|
|
196
|
+
raise RuntimeError(f"Connection closed by visitor while queueing ({getattr(connection_state, 'id', None)})")
|
|
195
197
|
continue
|
|
196
198
|
if event.event == 'failed':
|
|
199
|
+
print('failed', getattr(connection_state, 'id', None))
|
|
197
200
|
if token is None:
|
|
198
201
|
message = f"{NO_GPU_MESSAGE_INQUEUE}. {EXAMPLES_RETRY_MESSAGE}"
|
|
199
202
|
else:
|
|
@@ -205,16 +208,18 @@ def schedule(
|
|
|
205
208
|
ending="priority in ZeroGPU queues",
|
|
206
209
|
)
|
|
207
210
|
message = f"{NO_GPU_MESSAGE_INQUEUE}. {details}"
|
|
211
|
+
# TODO: API vs GUI message commme pour quota exceeded non ?
|
|
208
212
|
raise ctx.exception(
|
|
209
213
|
title="ZeroGPU queue timeout",
|
|
210
214
|
message=message,
|
|
211
215
|
status_code=503,
|
|
212
216
|
)
|
|
213
217
|
if event.event == 'succeeded':
|
|
218
|
+
print('succeeded', getattr(connection_state, 'id', None))
|
|
214
219
|
assert event.data is not None
|
|
215
|
-
if
|
|
220
|
+
if ctx.disconnected:
|
|
216
221
|
release(event.data.allowToken)
|
|
217
|
-
raise RuntimeError("Connection closed by visitor on queue success")
|
|
222
|
+
raise RuntimeError(f"Connection closed by visitor on queue success ({getattr(connection_state, 'id', None)})")
|
|
218
223
|
ctx.success(
|
|
219
224
|
title="ZeroGPU queue",
|
|
220
225
|
message="Successfully acquired a GPU",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
"""
|
|
3
3
|
|
|
4
|
+
import secrets
|
|
4
5
|
from contextvars import ContextVar
|
|
5
6
|
from typing import Callable
|
|
6
7
|
from typing import Literal
|
|
@@ -54,6 +55,7 @@ class WorkerContextualizerDefault:
|
|
|
54
55
|
|
|
55
56
|
class ConnectionState:
|
|
56
57
|
def __init__(self) -> None:
|
|
58
|
+
self.id = secrets.token_hex(2)
|
|
57
59
|
self.alive = True
|
|
58
60
|
|
|
59
61
|
|
|
@@ -63,6 +65,7 @@ class ZeroGPUContext:
|
|
|
63
65
|
exception_mapper: Callable[[ZeroGPUErrorResponse, Exception | None], Exception] | None = None,
|
|
64
66
|
log_emitter: Callable[[ZeroGPULog], None] | None = None,
|
|
65
67
|
worker_contextualizer: Callable[[], Picklable[Callable[[], None]]] | None = None,
|
|
68
|
+
disconnect_detector: Callable[[], bool] | None = None,
|
|
66
69
|
connection_state: ConnectionState | None = None,
|
|
67
70
|
server_port: int | None = None,
|
|
68
71
|
headers: dict[str, str] | None = None,
|
|
@@ -70,6 +73,7 @@ class ZeroGPUContext:
|
|
|
70
73
|
self.exception_mapper = exception_mapper or exception_mapper_default
|
|
71
74
|
self.log_emitter = log_emitter or log_emitter_default
|
|
72
75
|
self.worker_contextualizer = worker_contextualizer or WorkerContextualizerDefault
|
|
76
|
+
self.disconnect_detector = disconnect_detector
|
|
73
77
|
self.connection_state = connection_state
|
|
74
78
|
self.server_port = server_port
|
|
75
79
|
self.headers = headers or {}
|
|
@@ -110,5 +114,13 @@ class ZeroGPUContext:
|
|
|
110
114
|
res = {self.server_port} if self.server_port is not None else set()
|
|
111
115
|
return res | set(GlobalContext.server_ports.values())
|
|
112
116
|
|
|
117
|
+
@property
|
|
118
|
+
def disconnected(self):
|
|
119
|
+
if self.disconnect_detector is not None:
|
|
120
|
+
return self.disconnect_detector()
|
|
121
|
+
if self.connection_state is not None:
|
|
122
|
+
return not self.connection_state.alive
|
|
123
|
+
return False
|
|
124
|
+
|
|
113
125
|
|
|
114
126
|
ctx_var: ContextVar[ZeroGPUContext] = ContextVar('zerogpu_context', default=ZeroGPUContext())
|
|
@@ -40,11 +40,14 @@ class ZeroGPUMiddleware:
|
|
|
40
40
|
exception_mapper: Callable[[ZeroGPUErrorResponse, Exception | None], Exception] | None = None,
|
|
41
41
|
log_emitter: Callable[[ZeroGPULog], None] | None = None,
|
|
42
42
|
worker_contextualizer: Callable[[], Picklable[Callable[[], None]]] | None = None,
|
|
43
|
+
disconnect_detector: Callable[[], bool] | None = None,
|
|
43
44
|
):
|
|
45
|
+
print("ZeroGPUMiddleware.__init__")
|
|
44
46
|
self.app = app
|
|
45
47
|
self.exception_mapper = exception_mapper
|
|
46
48
|
self.log_emitter = log_emitter
|
|
47
49
|
self.worker_contextualizer = worker_contextualizer
|
|
50
|
+
self.disconnect_detector = disconnect_detector
|
|
48
51
|
|
|
49
52
|
async def __call__(self, scope: Scope, receive: Receive, send: Send):
|
|
50
53
|
|
|
@@ -69,27 +72,33 @@ class ZeroGPUMiddleware:
|
|
|
69
72
|
connection_state = ConnectionState()
|
|
70
73
|
|
|
71
74
|
async def wrapped_receive() -> ASGIReceiveEvent:
|
|
75
|
+
print('wrapped_receive', connection_state.id)
|
|
72
76
|
message = await asgi_receive()
|
|
77
|
+
print('wrapped_receive', connection_state.id, message['type'])
|
|
73
78
|
if (False
|
|
74
79
|
or message['type'] == 'http.disconnect'
|
|
75
80
|
or message['type'] == 'websocket.disconnect'
|
|
76
81
|
):
|
|
82
|
+
print("\n==== disconnect =====\n", connection_state.id)
|
|
77
83
|
connection_state.alive = False
|
|
78
84
|
return message
|
|
79
85
|
|
|
80
|
-
ctx_var.set(ZeroGPUContext(
|
|
86
|
+
token = ctx_var.set(ZeroGPUContext(
|
|
81
87
|
exception_mapper=self.exception_mapper,
|
|
82
88
|
log_emitter=self.log_emitter,
|
|
83
89
|
worker_contextualizer=self.worker_contextualizer,
|
|
90
|
+
disconnect_detector=self.disconnect_detector,
|
|
84
91
|
connection_state=connection_state,
|
|
85
92
|
server_port=_server_port(asgi_scope),
|
|
86
93
|
headers=_decode_headers(asgi_scope.get('headers', [])),
|
|
87
94
|
))
|
|
88
95
|
|
|
89
96
|
try:
|
|
97
|
+
print("\n==== await =====\n", connection_state.id)
|
|
90
98
|
await asgi_app(asgi_scope, wrapped_receive, asgi_send)
|
|
91
99
|
finally:
|
|
92
|
-
connection_state.
|
|
100
|
+
print("\n==== finally =====\n", connection_state.id)
|
|
101
|
+
ctx_var.reset(token)
|
|
93
102
|
|
|
94
103
|
|
|
95
104
|
def _server_port(scope: ASGIScope):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|