spaces 0.49b9__tar.gz → 0.50.dev0__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 (78) hide show
  1. spaces-0.50.dev0/PKG-INFO +24 -0
  2. spaces-0.50.dev0/pyproject.toml +127 -0
  3. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/__init__.py +12 -38
  4. spaces-0.50.dev0/spaces/_vendor/asgiref/typing.py +279 -0
  5. spaces-0.50.dev0/spaces/_vendor/sse_starlette/py.typed +0 -0
  6. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/config.py +0 -2
  7. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/reloading/__init__.py +6 -2
  8. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/reloading/server.py +20 -9
  9. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/reloading/types.py +6 -0
  10. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/reloading/types.ts +6 -1
  11. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/utils.py +5 -12
  12. spaces-0.50.dev0/spaces/zero/__init__.py +28 -0
  13. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/client.py +90 -96
  14. spaces-0.50.dev0/spaces/zero/context.py +114 -0
  15. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/decorator.py +2 -3
  16. spaces-0.50.dev0/spaces/zero/gradio.py +131 -0
  17. spaces-0.50.dev0/spaces/zero/lifecycle.py +30 -0
  18. spaces-0.50.dev0/spaces/zero/middleware.py +107 -0
  19. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/aoti.py +7 -141
  20. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/patching.py +5 -4
  21. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/static.py +1 -1
  22. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/types.py +18 -10
  23. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/utils.py +39 -1
  24. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/wrappers.py +81 -94
  25. spaces-0.49b9/PKG-INFO +0 -32
  26. spaces-0.49b9/pyproject.toml +0 -101
  27. spaces-0.49b9/spaces/gradio.py +0 -53
  28. spaces-0.49b9/spaces/zero/__init__.py +0 -28
  29. spaces-0.49b9/spaces/zero/elf.py +0 -37
  30. spaces-0.49b9/spaces/zero/gradio.py +0 -189
  31. spaces-0.49b9/spaces/zero/torch/kernels.py +0 -65
  32. {spaces-0.49b9 → spaces-0.50.dev0}/README.md +0 -0
  33. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/__init__.py +0 -0
  34. /spaces-0.49b9/spaces/_vendor/ovld/py.typed → /spaces-0.50.dev0/spaces/_vendor/asgiref/__init__.py +0 -0
  35. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/codefind/LICENSE +0 -0
  36. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/codefind/__init__.py +0 -0
  37. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/codefind/registry.py +0 -0
  38. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/codefind/version.py +0 -0
  39. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/LICENSE +0 -0
  40. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/__init__.py +0 -0
  41. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/codetools.py +0 -0
  42. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/parse.py +0 -0
  43. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/register.py +0 -0
  44. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/utils.py +0 -0
  45. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/jurigged/version.py +0 -0
  46. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/LICENSE +0 -0
  47. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/__init__.py +0 -0
  48. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/abc.py +0 -0
  49. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/codegen.py +0 -0
  50. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/core.py +0 -0
  51. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/dependent.py +0 -0
  52. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/medley.py +0 -0
  53. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/mro.py +0 -0
  54. {spaces-0.49b9/spaces/_vendor/sse_starlette → spaces-0.50.dev0/spaces/_vendor/ovld}/py.typed +0 -0
  55. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/recode.py +0 -0
  56. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/signatures.py +0 -0
  57. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/typemap.py +0 -0
  58. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/types.py +0 -0
  59. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/utils.py +0 -0
  60. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/ovld/version.py +0 -0
  61. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/sse_starlette/LICENSE +0 -0
  62. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/sse_starlette/__init__.py +0 -0
  63. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/sse_starlette/event.py +0 -0
  64. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/sse_starlette/sse.py +0 -0
  65. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/sseclient/LICENSE +0 -0
  66. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/_vendor/sseclient/__init__.py +0 -0
  67. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/reloading/client.py +0 -0
  68. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/api.py +0 -0
  69. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/mmap.py +0 -0
  70. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/__init__.py +0 -0
  71. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/bitsandbytes.py +0 -0
  72. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/bitsandbytes_legacy.py +0 -0
  73. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/cudart.py +0 -0
  74. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/packing.py +0 -0
  75. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/patching_legacy.py +0 -0
  76. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/types.py +0 -0
  77. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/torch/utils.py +0 -0
  78. {spaces-0.49b9 → spaces-0.50.dev0}/spaces/zero/tqdm.py +0 -0
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: spaces
3
+ Version: 0.50.dev0
4
+ Summary: Utilities for Hugging Face Spaces
5
+ Author: Charles Bensimon
6
+ Author-email: Charles Bensimon <charles@huggingface.co>
7
+ License-Expression: Apache-2.0
8
+ Requires-Dist: packaging
9
+ Requires-Dist: typing-extensions>=4,<5
10
+ Requires-Dist: pydantic>=1,<3
11
+ Requires-Dist: httpx>=0.20
12
+ Requires-Dist: fastapi ; extra == 'serving'
13
+ Requires-Dist: uvicorn ; extra == 'serving'
14
+ Requires-Python: >=3.10, <3.15
15
+ Project-URL: Homepage, https://huggingface.co
16
+ Project-URL: Repository, https://github.com/huggingface/huggingface_hub
17
+ Provides-Extra: serving
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Hugging Face Spaces
21
+
22
+ ## Installation
23
+
24
+ `pip install spaces`
@@ -0,0 +1,127 @@
1
+ [project]
2
+ name = "spaces"
3
+ version = "0.50.dev0"
4
+ description = "Utilities for Hugging Face Spaces"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10,<3.15"
7
+ license = "Apache-2.0"
8
+ authors = [
9
+ { name = "Charles Bensimon", email = "charles@huggingface.co" },
10
+ ]
11
+ dependencies = [
12
+ "packaging",
13
+ "typing-extensions>=4,<5",
14
+ "pydantic>=1,<3",
15
+ "httpx>=0.20",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ serving = [
20
+ "fastapi",
21
+ "uvicorn"
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://huggingface.co"
26
+ Repository = "https://github.com/huggingface/huggingface_hub"
27
+
28
+ [dependency-groups]
29
+ dev = [
30
+ "pytest>=7.3.1,<8",
31
+ "pytest-dotenv>=0.5.2,<0.6",
32
+ "pytest-mock>=3.10.0,<4",
33
+ "pytest-timeout>=2.1.0,<3",
34
+ "poethepoet>=0.20.0,<0.21",
35
+ "coverage>=7.2.6,<8",
36
+ "pyright>=1.1.403,<2",
37
+ "pytest-order>=1.1.0,<2",
38
+ "pynvml>=11.5.0,<12",
39
+ "pytest-asyncio>=0.21.1,<0.22",
40
+ "pytest-httpx>=0.27.0,<0.28",
41
+ "pydantic-to-typescript>=2.0.0,<3",
42
+ "requests>=2.19,<3",
43
+ "setuptools",
44
+ ]
45
+ test = [
46
+ "gradio",
47
+ "diffusers",
48
+ "transformers",
49
+ "accelerate",
50
+ "bitsandbytes",
51
+ "torch==2.9.1",
52
+ "torchao>=0.10.0,<0.11",
53
+ "scipy",
54
+ "peft",
55
+ "kernels>=0.10.2,<0.11",
56
+ ]
57
+
58
+ [tool.uv]
59
+ constraint-dependencies = [
60
+ "numpy<2",
61
+ "peft<0.19",
62
+ "transformers<4.51",
63
+ ]
64
+
65
+ [tool.pytest.ini_options]
66
+ addopts = "-v"
67
+ testpaths = ["tests"]
68
+ env_files = ["tests/.env"]
69
+
70
+ [tool.coverage.run]
71
+ source = ["spaces"]
72
+ omit = [
73
+ "spaces/zero/api.py",
74
+ "spaces/zero/torch/patching_legacy.py",
75
+ "spaces/zero/torch/bitsandbytes_legacy.py",
76
+ "spaces/_vendor/*",
77
+ ]
78
+ concurrency = ["thread", "multiprocessing"]
79
+
80
+ [tool.coverage.report]
81
+ fail_under = 100
82
+ exclude_also = [
83
+ "^\\s*if TYPE_CHECKING:\\s*$",
84
+ "^\\s*assert_never\\(.*\\)\\s*$",
85
+ "^\\s*debug\\(.*\\)\\s*$",
86
+ "^\\s*\\.\\.\\.\\s*$",
87
+ ]
88
+
89
+ [tool.poe.tasks.test]
90
+ shell = """set -e
91
+ pyright
92
+ coverage erase
93
+ coverage run -m pytest tests/cpu
94
+ coverage run -m pytest tests/gpu
95
+ find e2e -name '*.py' | xargs -I {} coverage run {}
96
+ pytest tests/cpu/serving/test_middleware.py::test_fds_server_hang --fd-xfail
97
+ pytest tests/cpu/serving/test_middleware.py::test_fds_requests_hang --fd-xfail
98
+ coverage combine -q
99
+ coverage xml || true
100
+ coverage html || true
101
+ coverage report -m
102
+ """
103
+
104
+ [tool.poe.tasks.publish]
105
+ shell = """set -e
106
+ git diff --quiet
107
+ rm -rf dist
108
+ uv build
109
+ uv publish
110
+ """
111
+
112
+ [tool.pyright]
113
+ include = ["spaces", "tests"]
114
+ exclude = ["spaces/_vendor"]
115
+ pythonVersion = "3.10"
116
+ reportMatchNotExhaustive = "error"
117
+ reportPossiblyUnboundVariable = "error"
118
+ reportUnnecessaryComparison = true
119
+ reportPrivateImportUsage = "information"
120
+
121
+ [build-system]
122
+ requires = ["uv_build>=0.11.6,<0.12"]
123
+ build-backend = "uv_build"
124
+
125
+ [tool.uv.build-backend]
126
+ module-name = "spaces"
127
+ module-root = ""
@@ -1,6 +1,8 @@
1
1
  """
2
2
  """
3
+
3
4
  import sys
5
+ import warnings
4
6
  from types import FunctionType
5
7
  from typing import TYPE_CHECKING
6
8
  from typing import Callable
@@ -15,9 +17,16 @@ if (gr := sys.modules.get('gradio')) is not None: # pragma: no cover
15
17
 
16
18
 
17
19
  from .zero.decorator import GPU
18
- from .gradio import gradio_auto_wrap
19
- from .gradio import disable_gradio_auto_wrap
20
- from .gradio import enable_gradio_auto_wrap
20
+
21
+
22
+ def gradio_auto_wrap(*args, **kwargs): # pragma: no cover
23
+ warnings.warn("gradio_auto_wrap has no effect and is deprecated")
24
+
25
+ def disable_gradio_auto_wrap(*args, **kwargs): # pragma: no cover
26
+ warnings.warn("gradio_auto_wrap has no effect and is deprecated")
27
+
28
+ def enable_gradio_auto_wrap(*args, **kwargs): # pragma: no cover
29
+ warnings.warn("gradio_auto_wrap has no effect and is deprecated")
21
30
 
22
31
 
23
32
  class LazyImported:
@@ -50,30 +59,10 @@ def _aoti_compile():
50
59
  from .zero.torch.aoti import aoti_compile
51
60
  return aoti_compile
52
61
 
53
- def _aoti_compile_and_save():
54
- from .zero.torch.aoti import aoti_compile_and_save
55
- return aoti_compile_and_save
56
-
57
62
  def _aoti_apply():
58
63
  from .zero.torch.aoti import aoti_apply
59
64
  return aoti_apply
60
65
 
61
- def _aoti_patch():
62
- from .zero.torch.aoti import aoti_patch
63
- return aoti_patch
64
-
65
- def _aoti_load():
66
- from .zero.torch.aoti import aoti_load
67
- return aoti_load
68
-
69
- def _aoti_load_call_source():
70
- from .zero.torch.aoti import aoti_load_call_source
71
- return aoti_load_call_source
72
-
73
- def _aoti_load_from_package_dir():
74
- from .zero.torch.aoti import aoti_load_from_package_dir
75
- return aoti_load_from_package_dir
76
-
77
66
  def _aoti_blocks_load():
78
67
  from .zero.torch.aoti import aoti_blocks_load
79
68
  return aoti_blocks_load
@@ -81,24 +70,14 @@ def _aoti_blocks_load():
81
70
 
82
71
  aoti_capture = LazyImported(_aoti_capture)
83
72
  aoti_compile = LazyImported(_aoti_compile)
84
- aoti_compile_and_save = LazyImported(_aoti_compile_and_save)
85
73
  aoti_apply = LazyImported(_aoti_apply)
86
- aoti_patch = LazyImported(_aoti_patch)
87
- aoti_load = LazyImported(_aoti_load)
88
- aoti_load_call_source = LazyImported(_aoti_load_call_source)
89
- aoti_load_from_package_dir = LazyImported(_aoti_load_from_package_dir)
90
74
  aoti_blocks_load = LazyImported(_aoti_blocks_load)
91
75
 
92
76
 
93
77
  if TYPE_CHECKING:
94
78
  from .zero.torch.aoti import aoti_capture
95
79
  from .zero.torch.aoti import aoti_compile
96
- from .zero.torch.aoti import aoti_compile_and_save
97
80
  from .zero.torch.aoti import aoti_apply
98
- from .zero.torch.aoti import aoti_patch
99
- from .zero.torch.aoti import aoti_load
100
- from .zero.torch.aoti import aoti_load_call_source
101
- from .zero.torch.aoti import aoti_load_from_package_dir
102
81
  from .zero.torch.aoti import aoti_blocks_load
103
82
 
104
83
 
@@ -109,11 +88,6 @@ __all__ = [
109
88
  'enable_gradio_auto_wrap',
110
89
  'aoti_capture',
111
90
  'aoti_compile',
112
- 'aoti_compile_and_save',
113
91
  'aoti_apply',
114
- 'aoti_patch',
115
- 'aoti_load',
116
- 'aoti_load_call_source',
117
- 'aoti_load_from_package_dir',
118
92
  'aoti_blocks_load',
119
93
  ]
@@ -0,0 +1,279 @@
1
+ import sys
2
+ from typing import (
3
+ Any,
4
+ Awaitable,
5
+ Callable,
6
+ Dict,
7
+ Iterable,
8
+ Literal,
9
+ Optional,
10
+ Protocol,
11
+ Tuple,
12
+ Type,
13
+ TypedDict,
14
+ Union,
15
+ )
16
+
17
+ if sys.version_info >= (3, 11):
18
+ from typing import NotRequired
19
+ else:
20
+ from typing_extensions import NotRequired
21
+
22
+ __all__ = (
23
+ "ASGIVersions",
24
+ "HTTPScope",
25
+ "WebSocketScope",
26
+ "LifespanScope",
27
+ "WWWScope",
28
+ "Scope",
29
+ "HTTPRequestEvent",
30
+ "HTTPResponseStartEvent",
31
+ "HTTPResponseBodyEvent",
32
+ "HTTPResponseTrailersEvent",
33
+ "HTTPResponsePathsendEvent",
34
+ "HTTPServerPushEvent",
35
+ "HTTPDisconnectEvent",
36
+ "WebSocketConnectEvent",
37
+ "WebSocketAcceptEvent",
38
+ "WebSocketReceiveEvent",
39
+ "WebSocketSendEvent",
40
+ "WebSocketResponseStartEvent",
41
+ "WebSocketResponseBodyEvent",
42
+ "WebSocketDisconnectEvent",
43
+ "WebSocketCloseEvent",
44
+ "LifespanStartupEvent",
45
+ "LifespanShutdownEvent",
46
+ "LifespanStartupCompleteEvent",
47
+ "LifespanStartupFailedEvent",
48
+ "LifespanShutdownCompleteEvent",
49
+ "LifespanShutdownFailedEvent",
50
+ "ASGIReceiveEvent",
51
+ "ASGISendEvent",
52
+ "ASGIReceiveCallable",
53
+ "ASGISendCallable",
54
+ "ASGI2Protocol",
55
+ "ASGI2Application",
56
+ "ASGI3Application",
57
+ "ASGIApplication",
58
+ )
59
+
60
+
61
+ class ASGIVersions(TypedDict):
62
+ spec_version: str
63
+ version: Union[Literal["2.0"], Literal["3.0"]]
64
+
65
+
66
+ class HTTPScope(TypedDict):
67
+ type: Literal["http"]
68
+ asgi: ASGIVersions
69
+ http_version: str
70
+ method: str
71
+ scheme: str
72
+ path: str
73
+ raw_path: bytes
74
+ query_string: bytes
75
+ root_path: str
76
+ headers: Iterable[Tuple[bytes, bytes]]
77
+ client: Optional[Tuple[str, int]]
78
+ server: Optional[Tuple[str, Optional[int]]]
79
+ state: NotRequired[Dict[str, Any]]
80
+ extensions: Optional[Dict[str, Dict[object, object]]]
81
+
82
+
83
+ class WebSocketScope(TypedDict):
84
+ type: Literal["websocket"]
85
+ asgi: ASGIVersions
86
+ http_version: str
87
+ scheme: str
88
+ path: str
89
+ raw_path: bytes
90
+ query_string: bytes
91
+ root_path: str
92
+ headers: Iterable[Tuple[bytes, bytes]]
93
+ client: Optional[Tuple[str, int]]
94
+ server: Optional[Tuple[str, Optional[int]]]
95
+ subprotocols: Iterable[str]
96
+ state: NotRequired[Dict[str, Any]]
97
+ extensions: Optional[Dict[str, Dict[object, object]]]
98
+
99
+
100
+ class LifespanScope(TypedDict):
101
+ type: Literal["lifespan"]
102
+ asgi: ASGIVersions
103
+ state: NotRequired[Dict[str, Any]]
104
+
105
+
106
+ WWWScope = Union[HTTPScope, WebSocketScope]
107
+ Scope = Union[HTTPScope, WebSocketScope, LifespanScope]
108
+
109
+
110
+ class HTTPRequestEvent(TypedDict):
111
+ type: Literal["http.request"]
112
+ body: bytes
113
+ more_body: bool
114
+
115
+
116
+ class HTTPResponseDebugEvent(TypedDict):
117
+ type: Literal["http.response.debug"]
118
+ info: Dict[str, object]
119
+
120
+
121
+ class HTTPResponseStartEvent(TypedDict):
122
+ type: Literal["http.response.start"]
123
+ status: int
124
+ headers: Iterable[Tuple[bytes, bytes]]
125
+ trailers: bool
126
+
127
+
128
+ class HTTPResponseBodyEvent(TypedDict):
129
+ type: Literal["http.response.body"]
130
+ body: bytes
131
+ more_body: bool
132
+
133
+
134
+ class HTTPResponseTrailersEvent(TypedDict):
135
+ type: Literal["http.response.trailers"]
136
+ headers: Iterable[Tuple[bytes, bytes]]
137
+ more_trailers: bool
138
+
139
+
140
+ class HTTPResponsePathsendEvent(TypedDict):
141
+ type: Literal["http.response.pathsend"]
142
+ path: str
143
+
144
+
145
+ class HTTPServerPushEvent(TypedDict):
146
+ type: Literal["http.response.push"]
147
+ path: str
148
+ headers: Iterable[Tuple[bytes, bytes]]
149
+
150
+
151
+ class HTTPDisconnectEvent(TypedDict):
152
+ type: Literal["http.disconnect"]
153
+
154
+
155
+ class WebSocketConnectEvent(TypedDict):
156
+ type: Literal["websocket.connect"]
157
+
158
+
159
+ class WebSocketAcceptEvent(TypedDict):
160
+ type: Literal["websocket.accept"]
161
+ subprotocol: Optional[str]
162
+ headers: Iterable[Tuple[bytes, bytes]]
163
+
164
+
165
+ class WebSocketReceiveEvent(TypedDict):
166
+ type: Literal["websocket.receive"]
167
+ bytes: Optional[bytes]
168
+ text: Optional[str]
169
+
170
+
171
+ class WebSocketSendEvent(TypedDict):
172
+ type: Literal["websocket.send"]
173
+ bytes: Optional[bytes]
174
+ text: Optional[str]
175
+
176
+
177
+ class WebSocketResponseStartEvent(TypedDict):
178
+ type: Literal["websocket.http.response.start"]
179
+ status: int
180
+ headers: Iterable[Tuple[bytes, bytes]]
181
+
182
+
183
+ class WebSocketResponseBodyEvent(TypedDict):
184
+ type: Literal["websocket.http.response.body"]
185
+ body: bytes
186
+ more_body: bool
187
+
188
+
189
+ class WebSocketDisconnectEvent(TypedDict):
190
+ type: Literal["websocket.disconnect"]
191
+ code: int
192
+ reason: Optional[str]
193
+
194
+
195
+ class WebSocketCloseEvent(TypedDict):
196
+ type: Literal["websocket.close"]
197
+ code: int
198
+ reason: Optional[str]
199
+
200
+
201
+ class LifespanStartupEvent(TypedDict):
202
+ type: Literal["lifespan.startup"]
203
+
204
+
205
+ class LifespanShutdownEvent(TypedDict):
206
+ type: Literal["lifespan.shutdown"]
207
+
208
+
209
+ class LifespanStartupCompleteEvent(TypedDict):
210
+ type: Literal["lifespan.startup.complete"]
211
+
212
+
213
+ class LifespanStartupFailedEvent(TypedDict):
214
+ type: Literal["lifespan.startup.failed"]
215
+ message: str
216
+
217
+
218
+ class LifespanShutdownCompleteEvent(TypedDict):
219
+ type: Literal["lifespan.shutdown.complete"]
220
+
221
+
222
+ class LifespanShutdownFailedEvent(TypedDict):
223
+ type: Literal["lifespan.shutdown.failed"]
224
+ message: str
225
+
226
+
227
+ ASGIReceiveEvent = Union[
228
+ HTTPRequestEvent,
229
+ HTTPDisconnectEvent,
230
+ WebSocketConnectEvent,
231
+ WebSocketReceiveEvent,
232
+ WebSocketDisconnectEvent,
233
+ LifespanStartupEvent,
234
+ LifespanShutdownEvent,
235
+ ]
236
+
237
+
238
+ ASGISendEvent = Union[
239
+ HTTPResponseStartEvent,
240
+ HTTPResponseBodyEvent,
241
+ HTTPResponseTrailersEvent,
242
+ HTTPServerPushEvent,
243
+ HTTPDisconnectEvent,
244
+ WebSocketAcceptEvent,
245
+ WebSocketSendEvent,
246
+ WebSocketResponseStartEvent,
247
+ WebSocketResponseBodyEvent,
248
+ WebSocketCloseEvent,
249
+ LifespanStartupCompleteEvent,
250
+ LifespanStartupFailedEvent,
251
+ LifespanShutdownCompleteEvent,
252
+ LifespanShutdownFailedEvent,
253
+ ]
254
+
255
+
256
+ ASGIReceiveCallable = Callable[[], Awaitable[ASGIReceiveEvent]]
257
+ ASGISendCallable = Callable[[ASGISendEvent], Awaitable[None]]
258
+
259
+
260
+ class ASGI2Protocol(Protocol):
261
+ def __init__(self, scope: Scope) -> None:
262
+ ...
263
+
264
+ async def __call__(
265
+ self, receive: ASGIReceiveCallable, send: ASGISendCallable
266
+ ) -> None:
267
+ ...
268
+
269
+
270
+ ASGI2Application = Type[ASGI2Protocol]
271
+ ASGI3Application = Callable[
272
+ [
273
+ Scope,
274
+ ASGIReceiveCallable,
275
+ ASGISendCallable,
276
+ ],
277
+ Awaitable[None],
278
+ ]
279
+ ASGIApplication = Union[ASGI2Application, ASGI3Application]
File without changes
@@ -18,8 +18,6 @@ class Settings:
18
18
  os.getenv('SPACES_ZERO_GPU'))
19
19
  self.zero_device_api_url = (
20
20
  os.getenv('SPACES_ZERO_DEVICE_API_URL'))
21
- self.gradio_auto_wrap = boolean(
22
- os.getenv('SPACES_GRADIO_AUTO_WRAP'))
23
21
  self.zero_patch_torch_device = boolean(
24
22
  os.getenv('ZERO_GPU_PATCH_TORCH_DEVICE'))
25
23
  self.zero_gpu_v2 = boolean(
@@ -1,8 +1,10 @@
1
1
  """
2
2
  """
3
+
3
4
  import threading
4
5
  from typing import Any
5
6
  from typing import Callable
7
+ from uuid import uuid4
6
8
 
7
9
 
8
10
  reload_server_ports: set[int] = set()
@@ -14,6 +16,7 @@ def start_reload_server(
14
16
  stop_event: threading.Event,
15
17
  port: int = 7887,
16
18
  ):
19
+ from ..zero.context import GlobalContext
17
20
  from .server import ReloadServer
18
21
 
19
22
  reload_server = ReloadServer(
@@ -22,8 +25,9 @@ def start_reload_server(
22
25
  stop_event=stop_event,
23
26
  )
24
27
 
25
- reload_server_ports.add(port)
28
+ server_port_uuid = uuid4()
29
+ GlobalContext.server_ports[server_port_uuid] = port
26
30
  try:
27
31
  reload_server.run(port=port)
28
32
  finally:
29
- reload_server_ports.remove(port)
33
+ del GlobalContext.server_ports[server_port_uuid]
@@ -12,6 +12,11 @@ from typing_extensions import assert_type
12
12
  from typing_extensions import Never
13
13
  from uuid import uuid4
14
14
 
15
+ try:
16
+ import fastapi, uvicorn
17
+ except ModuleNotFoundError: # pragma: no cover
18
+ raise RuntimeError("spaces.reloading.server requires FastAPI and uvicorn")
19
+
15
20
  from fastapi import APIRouter
16
21
  from fastapi import FastAPI
17
22
  from fastapi import HTTPException
@@ -55,6 +60,7 @@ from .types import ApiFetchContentsResponseSuccess
55
60
  from .types import ReloadRegion
56
61
  from .types import ReloadOperationError
57
62
  from .types import ReloadOperationException
63
+ from .types import ReloadOperationFile
58
64
  from .types import ReloadOperationObject
59
65
  from .types import ReloadOperationRun
60
66
  from .types import ReloadOperationUI
@@ -62,7 +68,6 @@ from .types import ReloadOperationUI
62
68
 
63
69
  class ReloadRun(NamedTuple):
64
70
  activity: EventSource
65
- thread: threading.Thread
66
71
 
67
72
 
68
73
  class ReloadServer:
@@ -115,16 +120,22 @@ class ReloadServer:
115
120
  return ApiCreateReloadResponseError(status='alreadyReloading')
116
121
 
117
122
  filepath = (Path.cwd() / req.filepath).resolve()
118
- if not filepath.is_relative_to(Path.cwd()) or (code_file := self.registry.get(str(filepath))) is None:
123
+ if not filepath.is_relative_to(Path.cwd()):
119
124
  return ApiCreateReloadResponseError(status='fileNotFound')
120
125
 
121
- Path(req.filepath).write_text(req.contents)
126
+ created = not filepath.exists()
127
+ filepath.write_text(req.contents)
122
128
  activity = EventSource(save_history=True) # TODO: Generic EventSource[ApiGetReloadEventSourceMessage]
123
- code_file.activity = activity
129
+ reload_id = str(uuid4()) if req.reloadId is None else req.reloadId
130
+ self.reload_runs[reload_id] = ReloadRun(activity)
124
131
 
132
+ if (code_file := self.registry.get(str(filepath))) is None:
133
+ activity.emit(ApiGetReloadEventSourceData(data=ReloadOperationFile(kind='file', created=created)))
134
+ activity.emit(None)
135
+ return ApiCreateReloadResponseSuccess(status='created', reloadId=reload_id)
136
+
137
+ code_file.activity = activity
125
138
  thread = create_thread(self.run_reload, code_file)
126
- reload_id = str(uuid4()) if req.reloadId is None else req.reloadId
127
- self.reload_runs[reload_id] = ReloadRun(activity, thread)
128
139
  self.active_reload_id = reload_id
129
140
  thread.start()
130
141
 
@@ -149,7 +160,7 @@ class ReloadServer:
149
160
 
150
161
  reload_id = req.reloadId
151
162
  if (run := self.reload_runs.get(reload_id, None)) is None:
152
- raise HTTPException(404, f"{reload_id=} not found")
163
+ raise HTTPException(204, f"{reload_id=} not found")
153
164
 
154
165
  queue = Queue()
155
166
  run.activity.register(queue.put, apply_history=True)
@@ -166,7 +177,7 @@ class ReloadServer:
166
177
  for data in activity_stream(queue):
167
178
  yield data.model_dump_json()
168
179
 
169
- return EventSourceResponse(event_source_stream())
180
+ return EventSourceResponse(event_source_stream(), ping=5)
170
181
 
171
182
 
172
183
  def run_reload(self, code_file: CodeFile):
@@ -211,7 +222,7 @@ def serialize_code_operation(cf_operation: CodeFileOperation):
211
222
  if isinstance(cf_operation, AddOperation):
212
223
  kind = 'add'
213
224
  elif isinstance(cf_operation, UpdateOperation):
214
- if not isinstance(defn, FunctionDefinition):
225
+ if not isinstance(defn, (ClassDefinition, FunctionDefinition)):
215
226
  return None
216
227
  kind = 'update'
217
228
  else:
@@ -43,6 +43,11 @@ class ReloadOperationUI(BaseModel):
43
43
  updated: bool
44
44
 
45
45
 
46
+ class ReloadOperationFile(BaseModel):
47
+ kind: Literal['file']
48
+ created: bool
49
+
50
+
46
51
  class ApiCreateReloadRequest(BaseModel):
47
52
  filepath: str
48
53
  contents: str
@@ -72,6 +77,7 @@ class ApiGetReloadEventSourceData(BaseModel):
72
77
  | ReloadOperationObject \
73
78
  | ReloadOperationRun \
74
79
  | ReloadOperationUI \
80
+ | ReloadOperationFile \
75
81
 
76
82
 
77
83
  class ApiGetStatusRequest(BaseModel):
@@ -39,7 +39,8 @@ export interface ApiGetReloadEventSourceData {
39
39
  | ReloadOperationException
40
40
  | ReloadOperationObject
41
41
  | ReloadOperationRun
42
- | ReloadOperationUI;
42
+ | ReloadOperationUI
43
+ | ReloadOperationFile;
43
44
  }
44
45
  export interface ReloadOperationError {
45
46
  kind: "error";
@@ -73,6 +74,10 @@ export interface ReloadOperationUI {
73
74
  kind: "ui";
74
75
  updated: boolean;
75
76
  }
77
+ export interface ReloadOperationFile {
78
+ kind: "file";
79
+ created: boolean;
80
+ }
76
81
  export interface ApiGetReloadRequest {
77
82
  reloadId: string;
78
83
  }