1{
2 lib,
3 stdenv,
4 buildPythonPackage,
5 fetchFromGitHub,
6 writeShellScriptBin,
7 gradio,
8
9 # build-system
10 hatchling,
11 hatch-requirements-txt,
12 hatch-fancy-pypi-readme,
13
14 # web assets
15 zip,
16 nodejs,
17 pnpm_9,
18
19 # dependencies
20 setuptools,
21 aiofiles,
22 anyio,
23 diffusers,
24 fastapi,
25 ffmpy,
26 gradio-client,
27 groovy,
28 httpx,
29 huggingface-hub,
30 importlib-resources,
31 jinja2,
32 markupsafe,
33 matplotlib,
34 numpy,
35 orjson,
36 packaging,
37 pandas,
38 pillow,
39 pydantic,
40 python-multipart,
41 pydub,
42 pyyaml,
43 safehttpx,
44 semantic-version,
45 typing-extensions,
46 uvicorn,
47 typer,
48 tomlkit,
49
50 # oauth
51 authlib,
52 itsdangerous,
53
54 # tests
55 pytestCheckHook,
56 hypothesis,
57 altair,
58 boto3,
59 docker,
60 gradio-pdf,
61 ffmpeg,
62 ipython,
63 mcp,
64 pytest-asyncio,
65 respx,
66 scikit-image,
67 torch,
68 tqdm,
69 transformers,
70 vega-datasets,
71 writableTmpDirAsHomeHook,
72}:
73
74buildPythonPackage rec {
75 pname = "gradio";
76 version = "5.29.0";
77 pyproject = true;
78
79 src = fetchFromGitHub {
80 owner = "gradio-app";
81 repo = "gradio";
82 tag = "gradio@${version}";
83 hash = "sha256-zNqWJHnjWezueev5J2Ew8FsbHXUBDkfjCOmWhJJng8k=";
84 };
85
86 pnpmDeps = pnpm_9.fetchDeps {
87 inherit pname version src;
88 hash = "sha256-h3ulPik0Uf8X687Se3J7h3+8jYzwXtbO6obsO27zyfA=";
89 };
90
91 pythonRelaxDeps = [
92 "aiofiles"
93 "markupsafe"
94 ];
95
96 pythonRemoveDeps = [
97 # this isn't a real runtime dependency
98 "ruff"
99 ];
100
101 nativeBuildInputs = [
102 zip
103 nodejs
104 pnpm_9.configHook
105 ];
106
107 build-system = [
108 hatchling
109 hatch-requirements-txt
110 hatch-fancy-pypi-readme
111 ];
112
113 dependencies = [
114 setuptools # needed for 'pkg_resources'
115 aiofiles
116 anyio
117 diffusers
118 fastapi
119 ffmpy
120 gradio-client
121 groovy
122 httpx
123 huggingface-hub
124 importlib-resources
125 jinja2
126 markupsafe
127 matplotlib
128 numpy
129 orjson
130 packaging
131 pandas
132 pillow
133 pydantic
134 python-multipart
135 pydub
136 pyyaml
137 safehttpx
138 semantic-version
139 typing-extensions
140 uvicorn
141 typer
142 tomlkit
143 ];
144
145 optional-dependencies.oauth = [
146 authlib
147 itsdangerous
148 ];
149
150 nativeCheckInputs =
151 [
152 altair
153 boto3
154 docker
155 ffmpeg
156 gradio-pdf
157 hypothesis
158 ipython
159 mcp
160 pytest-asyncio
161 pytestCheckHook
162 respx
163 # shap is needed as well, but breaks too often
164 scikit-image
165 torch
166 tqdm
167 transformers
168 vega-datasets
169
170 # mock calls to `shutil.which(...)`
171 (writeShellScriptBin "npm" "false")
172 writableTmpDirAsHomeHook
173 ]
174 ++ optional-dependencies.oauth
175 ++ pydantic.optional-dependencies.email;
176
177 preBuild = ''
178 pnpm build
179 pnpm package
180 '';
181
182 postBuild = ''
183 # SyntaxError: 'await' outside function
184 zip -d dist/gradio-*.whl gradio/_frontend_code/lite/examples/transformers_basic/run.py
185 '';
186
187 # Add a pytest hook skipping tests that access network, marking them as "Expected fail" (xfail).
188 # We additionally xfail FileNotFoundError, since the gradio devs often fail to upload test assets to pypi.
189 preCheck =
190 ''
191 cat ${./conftest-skip-network-errors.py} >> test/conftest.py
192 ''
193 # OSError: [Errno 24] Too many open files
194 + lib.optionalString stdenv.hostPlatform.isDarwin ''
195 ulimit -n 4096
196 '';
197
198 disabledTests =
199 [
200 # Actually broken
201 "test_mount_gradio_app"
202 "test_processing_utils_backwards_compatibility" # type error
203
204 # requires network, it caught our xfail exception
205 "test_error_analytics_successful"
206
207 # Flaky, tries to pin dependency behaviour. Sensitive to dep versions
208 # These error only affect downstream use of the check dependencies.
209 "test_no_color"
210 "test_in_interface_as_output"
211 "test_should_warn_url_not_having_version"
212
213 # Flaky, unknown reason
214 "test_in_interface"
215
216 # shap is too often broken in nixpkgs
217 "test_shapley_text"
218
219 # fails without network
220 "test_download_if_url_correct_parse"
221 "test_encode_url_to_base64_doesnt_encode_errors"
222
223 # flaky: OSError: Cannot find empty port in range: 7860-7959
224 "test_docs_url"
225 "test_orjson_serialization"
226 "test_dataset_is_updated"
227 "test_multimodal_api"
228 "test_examples_keep_all_suffixes"
229 "test_progress_bar"
230 "test_progress_bar_track_tqdm"
231 "test_info_and_warning_alerts"
232 "test_info_isolation[True]"
233 "test_info_isolation[False]"
234 "test_examples_no_cache_optional_inputs"
235 "test_start_server[127.0.0.1]"
236 "test_start_server[[::1]]"
237 "test_single_request"
238 "test_all_status_messages"
239 "test_default_concurrency_limits[not_set-statuses0]"
240 "test_default_concurrency_limits[None-statuses1]"
241 "test_default_concurrency_limits[1-statuses2]"
242 "test_default_concurrency_limits[2-statuses3]"
243 "test_concurrency_limits"
244
245 # tests if pip and other tools are installed
246 "test_get_executable_path"
247 ]
248 ++ lib.optionals stdenv.hostPlatform.isDarwin [
249 # TypeError: argument should be a str or an os.PathLike object where __fspath__ returns a str, not 'NoneType'
250 "test_component_example_values"
251 "test_component_functions"
252 "test_public_request_pass"
253
254 # Failed: DID NOT RAISE <class 'ValueError'>
255 # test.conftest.NixNetworkAccessDeniedError
256 "test_private_request_fail"
257 "test_theme_builder_launches"
258
259 # flaky on darwin (depend on port availability)
260 "test_all_status_messages"
261 "test_async_generators"
262 "test_async_generators_interface"
263 "test_async_iterator_update_with_new_component"
264 "test_concurrency_limits"
265 "test_default_concurrency_limits"
266 "test_default_flagging_callback"
267 "test_end_to_end"
268 "test_end_to_end_cache_examples"
269 "test_event_data"
270 "test_every_does_not_block_queue"
271 "test_example_caching_relaunch"
272 "test_example_caching_relaunch"
273 "test_exit_called_at_launch"
274 "test_file_component_uploads"
275 "test_files_saved_as_file_paths"
276 "test_flagging_does_not_create_unnecessary_directories"
277 "test_flagging_no_permission_error_with_flagging_disabled"
278 "test_info_and_warning_alerts"
279 "test_info_isolation"
280 "test_launch_analytics_does_not_error_with_invalid_blocks"
281 "test_no_empty_audio_files"
282 "test_no_empty_image_files"
283 "test_no_empty_video_files"
284 "test_non_streaming_api"
285 "test_non_streaming_api_async"
286 "test_pil_images_hashed"
287 "test_progress_bar"
288 "test_progress_bar_track_tqdm"
289 "test_queue_when_using_auth"
290 "test_restart_after_close"
291 "test_set_share_in_colab"
292 "test_show_error"
293 "test_simple_csv_flagging_callback"
294 "test_single_request"
295 "test_socket_reuse"
296 "test_start_server"
297 "test_state_holder_is_used_in_postprocess"
298 "test_state_stored_up_to_capacity"
299 "test_static_files_single_app"
300 "test_streaming_api"
301 "test_streaming_api_async"
302 "test_streaming_api_with_additional_inputs"
303 "test_sync_generators"
304 "test_time_to_live_and_delete_callback_for_state"
305 "test_updates_stored_up_to_capacity"
306 "test_varying_output_forms_with_generators"
307 ];
308
309 disabledTestPaths = [
310 # 100% touches network
311 "test/test_networking.py"
312 "client/python/test/test_client.py"
313 # makes pytest freeze 50% of the time
314 "test/test_interfaces.py"
315
316 # Local network tests dependant on port availability (port 7860-7959)
317 "test/test_routes.py"
318
319 # No module named build.__main__; 'build' is a package and cannot be directly executed
320 "test/test_docker/test_reverse_proxy/test_reverse_proxy.py"
321 "test/test_docker/test_reverse_proxy_fastapi_mount/test_reverse_proxy_fastapi_mount.py"
322 "test/test_docker/test_reverse_proxy_root_path/test_reverse_proxy_root_path.py"
323 ];
324
325 pytestFlagsArray = [
326 "-x" # abort on first failure
327 "-m 'not flaky'"
328 #"-W" "ignore" # uncomment for debugging help
329 ];
330
331 # check the binary works outside the build env
332 postCheck = ''
333 env --ignore-environment $out/bin/gradio environment >/dev/null
334 '';
335
336 pythonImportsCheck = [ "gradio" ];
337
338 # Cyclic dependencies are fun!
339 # This is gradio without gradio-client and gradio-pdf
340 passthru.sans-reverse-dependencies =
341 (gradio.override (old: {
342 gradio-client = null;
343 gradio-pdf = null;
344 })).overridePythonAttrs
345 (old: {
346 pname = old.pname + "-sans-reverse-dependencies";
347 pythonRemoveDeps = (old.pythonRemoveDeps or [ ]) ++ [ "gradio-client" ];
348 doInstallCheck = false;
349 doCheck = false;
350 preCheck = "";
351 postInstall = ''
352 shopt -s globstar
353 for f in $out/**/*.py; do
354 cp $f "$f"i
355 done
356 shopt -u globstar
357 '';
358 pythonImportsCheck = null;
359 dontCheckRuntimeDeps = true;
360 });
361
362 meta = {
363 homepage = "https://www.gradio.app/";
364 changelog = "https://github.com/gradio-app/gradio/releases/tag/gradio@${version}";
365 description = "Python library for easily interacting with trained machine learning models";
366 license = lib.licenses.asl20;
367 maintainers = with lib.maintainers; [ pbsds ];
368 };
369}