gradio/test/test_blocks.py
pngwn 287fe67828
Version 4 development branch (#5498)
* squash commit

* BugFix: Make FileExplorer Component Templateable (#5933)

* Make FileExplorer templateable

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix notebooks (#5935)

* V4: Update Component pyi file (#5937)

* Add code

* add changeset

* delete

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Use beta release versions for '@gradio' packages (#5938)

* Fix versions

* add changeset

* fix

* add changeset

* fix

* add changeset

* Fix

* fix preview

* add changeset

* final fix

* add changeset

* fix

* add changeset

* fix

* add changeset

* revert change

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* maybe fix (#5946)

* Merge main again (#5948)

* maybe fix

* fix

* Merge main again (#5949)

* maybe fix

* fix

* weird weird weird

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Merge main again (#5950)

* maybe fix

* fix

* weird weird weird

* fix

* log

* log

* fix changesets

* rererefactor frontend files (#5960)

* do all the things

* add changeset

* remove codemod

* fix

* change casing

* condense upload button

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Strip vite import warning (#5962)

* Add code

* Remove comment

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5769)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* release wasm (#5963)

* release wasm

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Wasm release (#5964)

* release wasm

* release wasm

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5965)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Add json schema unit tests (#5970)

* Add tests

* add changeset

* Fix tests

* api-info

* Add test

* Add test

* Add email tests

* 3.8 fix 🙄

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Simple textbox (#5990)

* Simple textbox

* add changeset

* Trim down events

* Trim

* Clean up change event

* Add comment

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* File upload optimization (#5961)

* Use custom multipart parser

* add changeset

* remove print

* Add comment

* Lint

* fix code

* remove print

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Removes deprecated arguments and parameters from v4 (#5968)

* Removes deprecated arguments and methods for v4

* remove update

* style

* add changeset

* added params

* typign

* fixed all of the typing

* tests

* fix layouts

* layout

* fix config

* fix

* fix form

* fix

* fix blocks tests

* fix some more tests

* lint

* fix test routes

* doc

* notebooks

* remove doc

* format

* format

* remove group

* label docstring

* remov test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Simple dropdown (#5996)

* Add code

* lint

* comment

* add changeset

* revert

* Fix update + docstring cleanup

* Add code

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* pass props to example components and to example outputs (#6014)

* pass props to example components and to example outputs

* add changeset

* make util less egenric/ more useful

* fix demo

* fix demo

* fix

* fix test

* Fix test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>

* Format js in v4 branch (#6016)

* Format v4 branch js

* add changeset

* lint

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Fix component update bug  (#6027)

* Fix tests + bugs

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* lockfile update

* Fixing test

* fix test

* Add a cli command to list available templates (#6018)

* Add code

* dataset

* Add test

* add changeset

* add changeset

* add changeset

* add changeset

* Exclude FormComponent

* Paginate the output

* No colors but emojis:

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* fix tests (#6046)

* fix storybook (#6065)

* fix storybook

* add changeset

* fix

* add changeset

* fix

* fix

* fix components

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* remove dupe component (#6067)

* remove dupe component

* add changeset

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Simplify File Component (#6044)

* Add code

* Add code

* Add code

* Reuse code UploadButton

* Use two files for UploadButton

* Address feedback

* Lint

* add changeset

* Update js/uploadbutton/shared/UploadButton.svelte

Co-authored-by: pngwn <hello@pngwn.io>

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: pngwn <hello@pngwn.io>

* Clean up backend of `File` and `UploadButton` and change the return type of `preprocess()` from TemporaryFIle to string filepath (#6060)

* changes

* add changeset

* upload button

* file

* add changeset

* valid types

* fix tests

* address review

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix Dropdown Example component and example processing logic (#6075)

* fix examples

* helpers

* dropdown

* fix cc build (#6079)

* fix cc build

* add changeset

* throw error if build fails

* fix entry points

* add changeset

* fix lockfile

* Add test

* update workflow

* try resolve

* lint 🙄

* Use xfail

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>

* Fixes markdown rendering in examples (#6071)

* Fixes markdown rendering in examples

* add changeset

* update delimiters

* format

* format

* docstrings

* test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Proposal: sample demo for custom components should be a `gr.Interface` (#6077)

* update sample demo to interface

* add changeset

* modify

* Update gradio/cli/commands/components/_create_utils.py

Co-authored-by: Freddy Boulton <alfonsoboulton@gmail.com>

* added layout and static

* fix

* refine

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Freddy Boulton <alfonsoboulton@gmail.com>

* WIP: Fix docs (#6082)

* Website fix

* add changeset

* Fix code

* pin version

* Add code

* skip code

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Pending events behavior (#5826)

* enter changesets pre-release mode

* release first version (#5500)

* release packages

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* fix changeset (#5502)

* add changeset

* release first version

* Custom components (#5507)

* Add flag

* Copy front-end

* Add backend code

* Remove breakpoint

* Fix init

* Add code

* File reorg

* Fix types

* Upload files

* Fix code

* Custom components

* Add code

* Client changes

* More changes

* Add code

* Add code

* Fix reversion

* build hook integration

* Add code

* rm file

* Add code

* rename cli

* Add code

* Nicer cli

* Add display class

* Add backend devmide

* Add code?

* Add code

* Add paths and fix reload mdoe

* Fix .then

* changes

* fix

* fix

* changes

* changes

* changes

* Fix versions

* Fix wheel build from source

* Add code

* Get no template case working

* Cc fe (#5509)

* tweaks

* fix 18n

* fix 18n

* tweaks

* Change name

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>

* Do not use live display for dev mode

* Clean up front-end server print statements a little bit

* Fix success event

* Cc fe 2 (#5526)

* tweaks

* fix examples

* fix backend port

* fix things

* fix

* Fix json component flagging

* fix

* fuix

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>

* Fix template (#5533)

* Fix type hint (#5536)

* fix custom components when installed from wheel (#5552)

* Fix all demos custom components (#5555)

* Fix all_demos

* Fix issues

* trigger ci

* add changeset

* Fix merge

* lockfile

* Add code

* add code

* Fix command

* Add code

* Fix highlighted text

* fix interactive highlighted text

* tweak ci

* Disable example caching for load right now

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* image fixes (#5589)

* Add flag

* Copy front-end

* Add backend code

* Remove breakpoint

* Fix init

* Add code

* File reorg

* Fix types

* Upload files

* Fix code

* Custom components

* Add code

* Client changes

* More changes

* Add code

* Add code

* Fix reversion

* build hook integration

* Add code

* rm file

* Add code

* rename cli

* Add code

* Nicer cli

* Add display class

* Add backend devmide

* Add code?

* Add code

* Add paths and fix reload mdoe

* Fix .then

* changes

* fix

* fix

* changes

* changes

* changes

* Fix versions

* Fix wheel build from source

* Add code

* Get no template case working

* Cc fe (#5509)

* tweaks

* fix 18n

* fix 18n

* tweaks

* Change name

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>

* Do not use live display for dev mode

* Clean up front-end server print statements a little bit

* Fix success event

* Cc fe 2 (#5526)

* tweaks

* fix examples

* fix backend port

* fix things

* fix

* Fix json component flagging

* fix

* fuix

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>

* Fix template (#5533)

* Fix type hint (#5536)

* fix custom components when installed from wheel (#5552)

* Fix all demos custom components (#5555)

* Fix all_demos

* Fix issues

* trigger ci

* add changeset

* Fix merge

* lockfile

* Add code

* add code

* Fix command

* Add code

* Fix highlighted text

* fix interactive highlighted text

* tweak ci

* fix image uploads

* add changeset

* remove changeset

* add changeset

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Support Literal Typehints (#5591)

* Fix updateable

* Deploy for failing actions

* fix enum

* Add code

* remove gradio lite from publish ci

* fix

* fix

* chore: update versions (beta) (#5501)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix js deps in cli and add gradio-preview artifacts to build (#5610)

* Fix js dependencies in cli

* Add preview to build command

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5612)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* backend linting (#5613)

* backend linting

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Publish js theme (#5615)

* Publish js theme

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5614)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Add code (#5617)

* Add docstring to trigger release (#5618)

* Add docstring

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5619)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix build and broken imports (#5620)

* fix broken import

* fix build scripts

* add changeset

* Update js/statustracker/static/index.svelte

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5621)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix esbuild (#5624)

* fix build

* fix build

* add changeset

* fix build

* fix

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5626)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix esbuild (#5629)

* fix build

* fix build

* add changeset

* fix build

* fix

* asd

* more fix

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* asd (#5630)

* chore: update versions (beta) (#5631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Some minor v4 fixes (#5637)

* Add code

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Publish all components to npm (#5648)

* Publish image component

* publish components

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5638)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix front-end imports + other misc fixes (#5649)

* Fix console error

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Add overwrite flag to create command (#5651)

* Add overwrite flag to create command

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5650)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Make layout components templateable (#5665)

* Split out layouts

* Refactor cli

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Patch fixes (#5678)

* Handle lowercase template name frontend

* Lint

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix build and file route (#5680)

* Add code

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5662)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* V4 fix typing (#5686)

* Add examples for series and parallel

* v4 fix typing

* add changeset

* Fix

* Fix

* Fix 3.8

* Fix typing 3.8

* Lint

* Add code

* Add key

* Fix typing

* Add code

* Fix deps

* Fix fastapi

* Fix version
'
:

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Use overrides (#5695)

* Use overrides

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* ensure client is copied along with components (#5709)

* ensure client is copied along with components

* add changeset

* add changeset

* chore: update versions (beta) (#5688)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix build config (#5710)

* Fix python unit tests for v4 (#5715)

* Add code

* Fix tests

* Add ci

* Add code

* fix test_blocks

* More fixes

* Last changes

* add changeset

* Add code

* Continue for the sake of some green ci

* Proper api info

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Better test dir check (#5719)

* FIx test-dir check

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Use path to npm executable in subprocess (#5736)

* Use path to npm in subprocess

* Fix dev mode too

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Add Error + test (#5738)

* Add Error + test

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix component regex (#5739)

* Add code

* Fix component regex

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix layout templates (#5740)

* Fix templates

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix template remaining components (#5743)

* add code

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* --overwrite deletes previous content (#5744)

* Add code

* Add code

* add changeset

* Add test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Support call method (#5751)

* Support call method

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix windows ci build (#5752)

* Try bash

* Use cross-env

* Trigger CI

* Fix comma

* add changeset

* Modify lock file

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix windows paths (#5745)

* Use posix path

* add changeset

* add changeset

* Try normalize

* to_posix v2

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* chore: update versions (beta) (#5717)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Simplify how files are handled in components in 4.0 (#5768)

* Client and upload route hash files

* Refactor logic

* add changeset

* Add code

* View API fix

* Fix client tests

* Dumb windows fix

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix deployed demos on v4 branch (#5770)

* Template fixes

* add changeset

* add changeset

* Fix streaming audio

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Name Endpoints if api_name is None (#5782)

* Implementation and test

* add changeset

* fix lint

* Fix nits

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* pending events

* add changeset

* Set api=False for cancel events (#5851)

* Set api=False for cancel events

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Set cache dir for some component tests (#5852)

* Add code

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Use async version of shutil in upload route (#5853)

* Use async shutil copy

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* trigger_mode

* changes

* undo

* undo

* add changeset

* undo

* add changeset

* image undo

* add changeset

* fix merge errors

* remove prints

* change

* fix

* fix error

* change

* quick fix

* trigger_mode param

* type

* value error

---------

Co-authored-by: pngwn <hello@pngwn.io>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Fixes: slider bar are too thin on FireFox (#5984)

* firefox slider fix

* add changeset

* fix

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Fix remaining xfail tests in backend (#6073)

* Add code'

* Add code

* Add code

* Fix dataset

* add changeset

* Add some comments

* Add recover_kwargs

* Remove from docstring

* Audio-to-audio

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix dev mode model3D (#5955)

* Fix dev mode

* Lint

* add changeset

* Fix dev mode

* Lint

* remove console.log

* add changeset

* interactive fix

* Fix tests

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Dawood <dawoodkhan82@gmail.com>

* V4: Single-file implementation of form components (#6026)

* Checkbox and number

* Number, Checkboxgroup, Radio, and Slider

* Format

* remove range

* Refactor checkbox

* Forgot to add

* Refactor

* Lint

* add changeset

* use rich emoji markup

* Fix ts

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Update logos for v4 (#6089)

* update v4 logo

* add changeset

* size

* fix on website

* fix on demos

* add changeset

* favicon for demos

* margin on footer logo

* margin bottom to footer logo

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Ali Abdalla <ali.si3luwa@gmail.com>

* Add a stand-alone install command and tidy-up the fallback template (#6092)

* Add code

* add changeset

* Add test

* Make install default

* Better error message

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4: Fix constructor_args (#6093)

* Fix

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix: Move to cache in init postprocess + Fallback Fixes (#6107)

* Add code and test

* restore

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* fix build (#6112)

* fix build

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Update cold-lemons-roll.md

* Revert "Update cold-lemons-roll.md" (#6113)

This reverts commit 20dbf90811.

* Try to trigger a major beta release (#6114)

* pyproject fix

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Delete bad changelog (#6115)

* Delete bad changelog

* Revert "Delete bad changelog"

This reverts commit a8ca614eaf.

* Delete bad

* formatting

* Revert "formatting"

This reverts commit 94f43859e7.

* Improve Audio Component (#5966)

* replace <audio> with wavesurfer: add recording, playing and trimming, playback

* add changeset

* merge cleanup

* improving recording styling

* add recording timer

* add trim region duration

* allow trimming recordings

* clean up playing logic

* add pause_recording event

* remove crop min/max

* add waveform options param

* remove trimmingmode and use mode

* streaming + cleanup

* add changeset

* clean up types

* mobile adjustments

* add min/max length  + trim accessibility

* update pnpm lock

* amend source to a list and allow source switching

* fix no microphone found logic

* change undo logic to reset trims

* tweaks

* tweak reset logic

* ensure recording is sent to backend

* fix audio duration reactivity

* list tweak

* clean up

* change source -> sources + restore wasm changes

* formatting

* fix tests

* fix test

* add default sources value in fe + fix audio demos

* fix audio file name test

* add better sources typing

* ui test tweaks

* add default value in templates.py

* formatting

* remove unused prop

* add audio story

* add changeset

* revert sources changes

* remove story id

* fix be test

* fix be test

* fix notebooks

* formatting

* fix test

* fix test again

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
Co-authored-by: pngwn <hello@pngwn.io>

* Fix changesets 2 (#6116)

* fix changesets

* release major

* Fix changesets 2 (#6117)

* fix changesets

* release major

* asd

* fix version

* chore: update versions (beta) (#5973)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* v4

* Open source FRP server and allow `gradio` to connect to custom share servers (#6091)

* add param

* add support for custom address

* add changeset

* share guide

* guide

* add params

* add changeset

* fix launch

* add test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Gradio custom component publish (#6098)

* Add code

* add changeset

* F-string

* Add pwd

* Add source to space

* Add template

* Twine import

* add changeset

* ignore

* restore

* add changeset

* Address feedback

* Update gradio/cli/commands/components/publish.py

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Fix static issues with Lite on v4 (#6124)

* fix missing props

* fix path to types

* add changeset

* fix path to i18n

* fix audio tests

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Ali Abid <aabid94@gmail.com>

* Refactor `Blocks.load()` so that it is in the same style as the other listeners (#6126)

* restore decorator

* remove

* refactor load

* remove restriction

* add changeset

* add pyi

* add test

* add test

* fix tests

* metaclass

* future

* reduce'

* docstring

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Small change to make `api_open=False` by default (#6138)

* set api_open to False by default

* add changeset

* tests

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Image v4 (#6094)

* simplify image interface

* changes

* asd

* asd

* more

* add code (#6095)

* more

* fix tests

* add changeset

* fix client build

* fix linting

* fix test

* lint

* Fix tests + lint

* asd

* finish

* webcam selection

* fix backend

* address comments

* fix static checks

* fix everything

* add changeset

* Apply suggestions from code review

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* fix examples

* fix tests

* fix tests

---------

Co-authored-by: Freddy Boulton <alfonsoboulton@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Fix fallback demo app template code (#6129)

* Add test

* add changeset

* Add JSON note

* Fix test

* Update gradio/cli/commands/components/_create_utils.py

* Update test/test_gradio_component_cli.py

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* fix video path

* Revert "fix video path"

This reverts commit 5916b3dc8a.

* Fix selectable prop in the backend (#6135)

* Add to init

* Fix events

* add changeset

* Add code

* add changeset

* Fix typo

* Add code

* Fix highlighted text typo

* remove from docs

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix video (#6140)

* fix video

* add changeset

* fixes

* fixes

* lint

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* fix some tests on mac

* fix circular dependency with client + upload (#6143)

* fix circular dependency

* add changeset

* fix test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix image double change bug (#6146)

* fix test

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* make lite private (#6147)

* make lite private

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* fix dropdown arrow size (#6148)

* fix dropdown arrow size

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* swap `mode` on the frontned to `interactive` to match the backend (#6149)

* JS READMEs and Storybook on Docs (#6142)

* changes

* changes

* working

* remove storybook path

* add changeset

* pin code

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Don't serve files in working directory by default (#6128)

* don't serve files in working directory by default

* add changeset

* handle examples

* remove dotfiles

* guide

* guide

* fix tests

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Fix issues with website deploy (#6151)

* fixes

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Swap websockets for SSE (#6069)

* changes

* changes

* changes

* changes

* changes

* merge

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* add changeset

* changes

* changes

* changes

* add changeset

* changes

* changes

* changes

* add changeset

* changes

* changes

* changes

* add changeset

* changes

* changes

* changes

* changes

* changes

* add changeset

* Fix client tests sse branch (#6150)

* Switch spaces

* Fix tests

* Add code

* changes

* changes

---------

Co-authored-by: Ali Abid <aabid94@gmail.com>

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Freddy Boulton <alfonsoboulton@gmail.com>

* Remove duplicate `elem_ids` from components (#6152)

* remove elem_ids from components

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Moves `gradio_cached_folder` inside the gradio temp direcotry (#6155)

* cache -> temp

* restore

* add changeset

* add exception for cached examples

* security policy

* join

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Remove `show_edit_button` param in Audio (#6153)

* remove `show_edit_button` param

* add changeset

* hide overflow

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Remove interpretation for good (#6154)

* interpretation

* notebooks

* add changeset

* removed from readme

* lint

* fix demo

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Make output components not editable if they are being updated (#6157)

* make outputs not editable

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* JS Component Documentation (#6136)

* Simple template

* More changes

* Properly handle change event video

* add changeset

* Add code'

* Fix play/pause bugs

* add changeset

* atoms doc

* Chatbot

* add changeset

* Some more components

* Add code

* add changeset

* Lint

* add changeset

* Simple template

* More changes

* Properly handle change event video

* add changeset

* Add code'

* Fix play/pause bugs

* add changeset

* atoms doc

* Chatbot

* add changeset

* Some more components

* Add code

* add changeset

* Lint

* Merge in latest changes

* Fix typo

* Fix build

* Lockfile

* Fix

* Fix interactive

* correct the title

* add versions to readme

* only document public

* add changeset

* Remove simplevideo and audio

* add changeset

* changes to readmes

* add changeset

* remove tootils and theme

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Ali Abdalla <ali.si3luwa@gmail.com>

* Improve Video Component (#6118)

* change source to sources + add length logic

* add changeset

* add min/max logic

* tweak tests

* change sources

* formatting

* fix test_components.py test

* add trimming

* add changeset

* add keyboard events to trimming timeline

* UX improvements

* Add fix

* add loading status

* flip if webcam is only source

* generate notebooks

* remove scroll

* add file extension

* trim (#6156)

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* add loading indicator to video

* ensure correct file ext is used

* tweak

* change where ffmpeg is loaded

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* chore: update versions (beta) (#6122)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* V4 fixes (#6161)

* changes

* changes

* Pin 8.9 (#6162)

* Pending outputs fix (#6160)

* fix

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* Clean root url (#6159)

* clean

* cleanup

* formatting

* add changeset

* fe

* add changeset

* selectable

* block

* fix

* fixes

* fix update

* gradio/events

* configs

* remove

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

* V4 fixes (#6167)

* changes

* changes

* Fixes remaining backend tests (#6165)

* fix backend tests

* add changeset

* changes

* fix strings

* lint

* lint

* fixes

* delete test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>

---------

Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Dawood Khan <dawoodkhan82@gmail.com>
Co-authored-by: Ali Abdalla <ali.si3luwa@gmail.com>
Co-authored-by: Hannah <hannahblair@users.noreply.github.com>
Co-authored-by: Ali Abid <aabid94@gmail.com>
2023-10-31 04:46:02 +00:00

1536 lines
52 KiB
Python

import asyncio
import copy
import io
import os
import pathlib
import random
import sys
import time
import unittest.mock as mock
import uuid
from concurrent.futures import wait
from contextlib import contextmanager
from functools import partial
from string import capwords
from unittest.mock import patch
import gradio_client as grc
import numpy as np
import pytest
import uvicorn
from fastapi.testclient import TestClient
from gradio_client import media_data
from PIL import Image
import gradio as gr
from gradio.events import SelectData
from gradio.exceptions import DuplicateBlockError
from gradio.networking import Server, get_first_available_port
from gradio.utils import assert_configs_are_equivalent_besides_ids
pytest_plugins = ("pytest_asyncio",)
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
@contextmanager
def captured_output():
new_out, new_err = io.StringIO(), io.StringIO()
old_out, old_err = sys.stdout, sys.stderr
try:
sys.stdout, sys.stderr = new_out, new_err
yield sys.stdout, sys.stderr
finally:
sys.stdout, sys.stderr = old_out, old_err
class TestBlocksMethods:
maxDiff = None
def test_set_share_is_false_by_default(self):
with gr.Blocks() as demo:
assert not demo.share
@patch("gradio.networking.setup_tunnel")
@patch("gradio.utils.colab_check")
def test_set_share_in_colab(self, mock_colab_check, mock_setup_tunnel):
mock_colab_check.return_value = True
mock_setup_tunnel.return_value = "http://localhost:7860/"
with gr.Blocks() as demo:
# self.share is False when instantiating the class
assert not demo.share
# share default is True, if share is None in colab and queueing
demo.launch(prevent_thread_lock=True)
assert demo.share
demo.close()
# share is also true, if share is None in colab with queueing
demo.queue()
demo.launch(prevent_thread_lock=True)
assert demo.share
demo.close()
def test_load_from_config(self):
fake_url = "https://fake.hf.space"
def update(name):
return f"Welcome to Gradio, {name}!"
with gr.Blocks() as demo1:
inp = gr.Textbox(placeholder="What is your name?")
out = gr.Textbox()
inp.submit(fn=update, inputs=inp, outputs=out, api_name="greet")
gr.Image(height=54, width=240)
config1 = demo1.get_config_file()
demo2 = gr.Blocks.from_config(config1, [update], "https://fake.hf.space")
for component in config1["components"]:
component["props"]["proxy_url"] = f"{fake_url}/"
config2 = demo2.get_config_file()
assert assert_configs_are_equivalent_besides_ids(config1, config2)
def test_partial_fn_in_config(self):
def greet(name, formatter):
return formatter(f"Hello {name}!")
greet_upper_case = partial(greet, formatter=capwords)
with gr.Blocks() as demo:
t = gr.Textbox()
o = gr.Textbox()
t.change(greet_upper_case, t, o)
assert len(demo.fns) == 1
assert "fn" in str(demo.fns[0])
@pytest.mark.asyncio
async def test_dict_inputs_in_config(self):
with gr.Blocks() as demo:
first = gr.Textbox()
last = gr.Textbox()
btn = gr.Button()
greeting = gr.Textbox()
def greet(data):
return f"Hello {data[first]} {data[last]}"
btn.click(greet, {first, last}, greeting)
result = await demo.process_api(
inputs=["huggy", "face"], fn_index=0, state=None
)
assert result["data"] == ["Hello huggy face"]
@pytest.mark.asyncio
async def test_async_function(self):
async def wait(x):
await asyncio.sleep(0.01)
return x
with gr.Blocks() as demo:
text = gr.Textbox()
button = gr.Button()
button.click(wait, [text], [text])
start = time.time()
result = await demo.process_api(inputs=[1], fn_index=0, state=None)
end = time.time()
difference = end - start
assert difference >= 0.01
assert result
@mock.patch("gradio.analytics._do_analytics_request")
def test_initiated_analytics(self, mock_anlaytics, monkeypatch):
monkeypatch.setenv("GRADIO_ANALYTICS_ENABLED", "True")
with gr.Blocks():
pass
mock_anlaytics.assert_called_once()
@mock.patch("gradio.analytics._do_analytics_request")
def test_launch_analytics_does_not_error_with_invalid_blocks(
self, mock_anlaytics, monkeypatch
):
monkeypatch.setenv("GRADIO_ANALYTICS_ENABLED", "True")
with gr.Blocks():
t1 = gr.Textbox()
with gr.Blocks() as demo:
t2 = gr.Textbox()
t2.change(lambda x: x, t2, t1)
demo.launch(prevent_thread_lock=True)
mock_anlaytics.assert_called()
def test_show_error(self):
with gr.Blocks() as demo:
pass
assert demo.show_error
demo.launch(prevent_thread_lock=True)
assert not demo.show_error
demo.close()
demo.launch(show_error=True, prevent_thread_lock=True)
assert demo.show_error
demo.close()
def test_custom_css(self):
css = """
.gr-button {
color: white;
border-color: black;
background: black;
}
"""
css = css * 5 # simulate a long css string
block = gr.Blocks(css=css)
assert block.css == css
@pytest.mark.asyncio
async def test_restart_after_close(self, connect):
io = gr.Interface(lambda s: s, gr.Textbox(), gr.Textbox()).queue()
with connect(io) as client:
assert client.predict("freddy", api_name="/predict") == "freddy"
# connect launches the interface which is what we need to test
with connect(io) as client:
assert client.predict("Victor", api_name="/predict") == "Victor"
@pytest.mark.asyncio
async def test_async_generators(self, connect):
async def async_iteration(count: int):
for i in range(count):
yield i
await asyncio.sleep(0.2)
def iteration(count: int):
for i in range(count):
yield i
time.sleep(0.2)
with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
num1 = gr.Number(value=4, precision=0)
o1 = gr.Number()
async_iterate = gr.Button(value="Async Iteration")
async_iterate.click(async_iteration, num1, o1)
with gr.Column():
num2 = gr.Number(value=4, precision=0)
o2 = gr.Number()
iterate = gr.Button(value="Iterate")
iterate.click(iteration, num2, o2)
demo.queue(concurrency_count=2)
with connect(demo) as client:
job_1 = client.submit(3, fn_index=0)
job_2 = client.submit(4, fn_index=1)
wait([job_1, job_2])
assert job_1.outputs()[-1] == 2
assert job_2.outputs()[-1] == 3
def test_async_generators_interface(self, connect):
async def async_iteration(count: int):
for i in range(count):
yield i
await asyncio.sleep(0.2)
demo = gr.Interface(
async_iteration, gr.Number(precision=0), gr.Number()
).queue()
outputs = []
with connect(demo) as client:
for output in client.submit(3, api_name="/predict"):
outputs.append(output)
assert outputs == [0, 1, 2]
def test_sync_generators(self, connect):
def generator(string):
yield from string
demo = gr.Interface(generator, "text", "text").queue()
outputs = []
with connect(demo) as client:
for output in client.submit("abc", api_name="/predict"):
outputs.append(output)
assert outputs == ["a", "b", "c"]
demo.queue().launch(prevent_thread_lock=True)
def test_socket_reuse(self):
try:
io = gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox())
io.launch(server_port=9441, prevent_thread_lock=True)
io.close()
io.launch(server_port=9441, prevent_thread_lock=True)
finally:
io.close()
def test_function_types_documented_in_config(self):
def continuous_fn():
return 42
def generator_function():
yield from range(10)
with gr.Blocks() as demo:
gr.Number(value=lambda: 2, every=2)
meaning_of_life = gr.Number()
counter = gr.Number()
generator_btn = gr.Button(value="Generate")
greeting = gr.Textbox()
greet_btn = gr.Button(value="Greet")
greet_btn.click(lambda: "Hello!", inputs=None, outputs=[greeting])
generator_btn.click(generator_function, inputs=None, outputs=[counter])
demo.load(continuous_fn, inputs=None, outputs=[meaning_of_life], every=1)
for i, dependency in enumerate(demo.config["dependencies"]):
if i == 3:
assert dependency["types"] == {"continuous": True, "generator": True}
if i == 0:
assert dependency["types"] == {"continuous": False, "generator": False}
if i == 1:
assert dependency["types"] == {"continuous": False, "generator": True}
if i == 2:
assert dependency["types"] == {"continuous": True, "generator": True}
@pytest.mark.asyncio
async def test_run_without_launching(self):
"""Test that we can start the app and use queue without calling .launch().
This is essentially what the 'gradio' reload mode does
"""
port = get_first_available_port(7860, 7870)
io = gr.Interface(lambda s: s, gr.Textbox(), gr.Textbox()).queue()
config = uvicorn.Config(app=io.app, port=port, log_level="warning")
server = Server(config=config)
server.run_in_thread()
try:
client = grc.Client(f"http://localhost:{port}")
result = client.predict("Victor", api_name="/predict")
assert result == "Victor"
finally:
server.close()
@patch(
"gradio.themes.ThemeClass.from_hub",
side_effect=ValueError("Something went wrong!"),
)
def test_use_default_theme_as_fallback(self, mock_from_hub):
with pytest.warns(
UserWarning, match="Cannot load freddyaboulton/this-theme-does-not-exist"
):
with gr.Blocks(theme="freddyaboulton/this-theme-does-not-exist") as demo:
assert demo.theme.to_dict() == gr.themes.Default().to_dict()
def test_exit_called_at_launch(self):
with gr.Blocks() as demo:
gr.Textbox(uuid.uuid4)
demo.launch(prevent_thread_lock=True)
assert len(demo.get_config_file()["dependencies"]) == 1
def test_concurrency_count_zero_gpu(self, monkeypatch):
monkeypatch.setenv("SPACES_ZERO_GPU", "true")
demo = gr.Blocks()
with pytest.warns():
demo.queue(concurrency_count=42)
with pytest.warns():
demo.queue(42)
assert demo._queue.max_thread_count == demo.max_threads
class TestTempFile:
def test_pil_images_hashed(self, connect, gradio_temp_dir):
images = [
Image.new("RGB", (512, 512), color) for color in ("red", "green", "blue")
]
def create_images(n_images):
return random.sample(images, n_images)
gallery = gr.Gallery()
demo = gr.Interface(
create_images,
inputs="slider",
outputs=gallery,
)
with connect(demo) as client:
client.predict(3, api_name="/predict")
_ = client.predict(3, api_name="/predict")
# only three files created and in temp directory
assert len([f for f in gradio_temp_dir.glob("**/*") if f.is_file()]) == 3
def test_no_empty_image_files(self, gradio_temp_dir, connect):
file_dir = pathlib.Path(pathlib.Path(__file__).parent, "test_files")
image = str(file_dir / "bus.png")
demo = gr.Interface(
lambda x: x,
inputs=gr.Image(type="filepath"),
outputs=gr.Image(),
)
with connect(demo) as client:
_ = client.predict(image, api_name="/predict")
_ = client.predict(image, api_name="/predict")
_ = client.predict(image, api_name="/predict")
# Upload creates a file. image preprocessing creates another one.
assert len([f for f in gradio_temp_dir.glob("**/*") if f.is_file()]) == 2
@pytest.mark.parametrize("component", [gr.UploadButton, gr.File])
def test_file_component_uploads(self, component, connect, gradio_temp_dir):
code_file = str(pathlib.Path(__file__))
demo = gr.Interface(lambda x: x.name, component(), gr.File())
with connect(demo) as client:
_ = client.predict(code_file, api_name="/predict")
_ = client.predict(code_file, api_name="/predict")
# the upload route hashees the files so we get 1 from there
# We create two tempfiles (empty) because API says we return
# preprocess/postprocess will create the same file as the upload route
# so 1 + 2 = 3
assert len([f for f in gradio_temp_dir.glob("**/*") if f.is_file()]) == 3
def test_no_empty_video_files(self, gradio_temp_dir, connect):
file_dir = pathlib.Path(pathlib.Path(__file__).parent, "test_files")
video = str(file_dir / "video_sample.mp4")
demo = gr.Interface(lambda x: x, gr.Video(), gr.Video())
with connect(demo) as client:
_ = client.predict({"video": video}, api_name="/predict")
_ = client.predict({"video": video}, api_name="/predict")
# Upload route and postprocessing return the same file
assert len([f for f in gradio_temp_dir.glob("**/*") if f.is_file()]) == 1
def test_no_empty_audio_files(self, gradio_temp_dir, connect):
file_dir = pathlib.Path(pathlib.Path(__file__).parent, "test_files")
audio = str(file_dir / "audio_sample.wav")
def reverse_audio(audio):
sr, data = audio
return (sr, np.flipud(data))
demo = gr.Interface(fn=reverse_audio, inputs=gr.Audio(), outputs=gr.Audio())
with connect(demo) as client:
_ = client.predict(audio, api_name="/predict")
_ = client.predict(audio, api_name="/predict")
# One for upload and one for reversal
assert len([f for f in gradio_temp_dir.glob("**/*") if f.is_file()]) == 2
class TestComponentsInBlocks:
def test_slider_random_value_config(self):
with gr.Blocks() as demo:
gr.Slider(
value=11.2,
minimum=-10.2,
maximum=15,
label="Non-random Slider (Static)",
)
gr.Slider(
randomize=True,
minimum=100,
maximum=200,
label="Random Slider (Input 1)",
)
gr.Slider(
randomize=True,
minimum=10,
maximum=23.2,
label="Random Slider (Input 2)",
)
for component in demo.blocks.values():
if isinstance(component, gr.components.Component):
if "Non-random" in component.label:
assert not component.load_event_to_attach
else:
assert component.load_event_to_attach
dependencies_on_load = [
dep["targets"][0][1] == "load" for dep in demo.config["dependencies"]
]
assert all(dependencies_on_load)
assert len(dependencies_on_load) == 2
# Queue should be explicitly false for these events
assert all(dep["queue"] is False for dep in demo.config["dependencies"])
def test_io_components_attach_load_events_when_value_is_fn(self, io_components):
io_components = [comp for comp in io_components if comp not in [gr.State]]
interface = gr.Interface(
lambda *args: None,
inputs=[comp(value=lambda: None, every=1) for comp in io_components],
outputs=None,
)
dependencies_on_load = [
dep
for dep in interface.config["dependencies"]
if dep["targets"][0][1] == "load"
]
assert len(dependencies_on_load) == len(io_components)
assert all(dep["every"] == 1 for dep in dependencies_on_load)
def test_get_load_events(self, io_components):
components = []
with gr.Blocks() as demo:
for component in io_components:
components.append(component(value=lambda: None, every=1))
assert all(comp.load_event in demo.dependencies for comp in components)
class TestBlocksPostprocessing:
def test_blocks_do_not_filter_none_values_from_updates(self, io_components):
io_components = [
c()
for c in io_components
if c
not in [
gr.State,
gr.Button,
gr.ScatterPlot,
gr.LinePlot,
gr.BarPlot,
gr.components.Fallback,
gr.FileExplorer,
]
]
with gr.Blocks() as demo:
for component in io_components:
component.render()
btn = gr.Button(value="Reset")
btn.click(
lambda: [gr.update(value=None) for _ in io_components],
inputs=[],
outputs=io_components,
)
output = demo.postprocess_data(
0, [gr.update(value=None) for _ in io_components], state=None
)
assert all(
o["value"] == c.postprocess(None) for o, c in zip(output, io_components)
)
def test_blocks_does_not_replace_keyword_literal(self):
with gr.Blocks() as demo:
text = gr.Textbox()
btn = gr.Button(value="Reset")
btn.click(
lambda: gr.update(value="NO_VALUE"),
inputs=[],
outputs=text,
)
output = demo.postprocess_data(0, gr.update(value="NO_VALUE"), state=None)
assert output[0]["value"] == "NO_VALUE"
def test_blocks_does_not_del_dict_keys_inplace(self):
with gr.Blocks() as demo:
im_list = [gr.Image() for i in range(2)]
def change_visibility(value):
return [gr.update(visible=value)] * 2
checkbox = gr.Checkbox(value=True, label="Show image")
checkbox.change(change_visibility, inputs=checkbox, outputs=im_list)
output = demo.postprocess_data(0, [gr.update(visible=False)] * 2, state=None)
assert output == [
{"visible": False, "__type__": "update"},
{"visible": False, "__type__": "update"},
]
def test_blocks_returns_correct_output_dict_single_key(self):
with gr.Blocks() as demo:
num = gr.Number()
num2 = gr.Number()
update = gr.Button(value="update")
def update_values(val):
return {num2: gr.Number(value=42)}
update.click(update_values, inputs=[num], outputs=[num2])
output = demo.postprocess_data(0, {num2: gr.Number(value=42)}, state=None)
assert output[0]["value"] == 42
output = demo.postprocess_data(0, {num2: 23}, state=None)
assert output[0] == 23
@pytest.mark.asyncio
async def test_blocks_update_dict_without_postprocessing(self):
def infer(x):
return media_data.BASE64_IMAGE, gr.update(visible=True)
with gr.Blocks() as demo:
prompt = gr.Textbox()
image = gr.Image()
run_button = gr.Button()
share_button = gr.Button("share", visible=False)
run_button.click(infer, prompt, [image, share_button], postprocess=False)
output = await demo.process_api(0, ["test"], state=None)
assert output["data"][0] == media_data.BASE64_IMAGE
assert output["data"][1] == {"__type__": "update", "visible": True}
@pytest.mark.asyncio
async def test_blocks_update_dict_does_not_postprocess_value_if_postprocessing_false(
self,
):
def infer(x):
return gr.Image(value=media_data.BASE64_IMAGE)
with gr.Blocks() as demo:
prompt = gr.Textbox()
image = gr.Image()
run_button = gr.Button()
run_button.click(infer, [prompt], [image], postprocess=False)
output = await demo.process_api(0, ["test"], state=None)
assert output["data"][0] == {
"__type__": "update",
"value": media_data.BASE64_IMAGE,
}
@pytest.mark.asyncio
async def test_blocks_update_interactive(
self,
):
def specific_update():
return [
gr.Image(interactive=True),
gr.Textbox(interactive=True),
]
def generic_update():
return [gr.update(interactive=True), gr.update(interactive=True)]
with gr.Blocks() as demo:
run = gr.Button(value="Make interactive")
image = gr.Image()
textbox = gr.Text()
run.click(specific_update, None, [image, textbox])
run.click(generic_update, None, [image, textbox])
for fn_index in range(2):
output = await demo.process_api(fn_index, [], state=None)
assert output["data"][0] == {
"__type__": "update",
"mode": "dynamic",
}
assert output["data"][1] == {"__type__": "update", "mode": "dynamic"}
def test_error_raised_if_num_outputs_mismatch(self):
with gr.Blocks() as demo:
textbox1 = gr.Textbox()
textbox2 = gr.Textbox()
button = gr.Button()
button.click(lambda x: x, textbox1, [textbox1, textbox2])
with pytest.raises(
ValueError,
match=r'An event handler didn\'t receive enough output values \(needed: 2, received: 1\)\.\nWanted outputs:\n \[textbox, textbox\]\nReceived outputs:\n \["test"\]',
):
demo.postprocess_data(fn_index=0, predictions=["test"], state=None)
def test_error_raised_if_num_outputs_mismatch_with_function_name(self):
def infer(x):
return x
with gr.Blocks() as demo:
textbox1 = gr.Textbox()
textbox2 = gr.Textbox()
button = gr.Button()
button.click(infer, textbox1, [textbox1, textbox2])
with pytest.raises(
ValueError,
match=r'An event handler \(infer\) didn\'t receive enough output values \(needed: 2, received: 1\)\.\nWanted outputs:\n \[textbox, textbox\]\nReceived outputs:\n \["test"\]',
):
demo.postprocess_data(fn_index=0, predictions=["test"], state=None)
def test_error_raised_if_num_outputs_mismatch_single_output(self):
with gr.Blocks() as demo:
num1 = gr.Number()
num2 = gr.Number()
btn = gr.Button(value="1")
btn.click(lambda a: a, num1, [num1, num2])
with pytest.raises(
ValueError,
match=r"An event handler didn\'t receive enough output values \(needed: 2, received: 1\)\.\nWanted outputs:\n \[number, number\]\nReceived outputs:\n \[1\]",
):
demo.postprocess_data(fn_index=0, predictions=1, state=None)
def test_error_raised_if_num_outputs_mismatch_tuple_output(self):
def infer(a, b):
return a, b
with gr.Blocks() as demo:
num1 = gr.Number()
num2 = gr.Number()
num3 = gr.Number()
btn = gr.Button(value="1")
btn.click(infer, num1, [num1, num2, num3])
with pytest.raises(
ValueError,
match=r"An event handler \(infer\) didn\'t receive enough output values \(needed: 3, received: 2\)\.\nWanted outputs:\n \[number, number, number\]\nReceived outputs:\n \[1, 2\]",
):
demo.postprocess_data(fn_index=0, predictions=(1, 2), state=None)
class TestStateHolder:
@pytest.mark.asyncio
async def test_state_stored_up_to_capacity(self):
with gr.Blocks() as demo:
num = gr.Number()
state = gr.State(value=0)
def run(x, s):
return s, s + 1
num.submit(
run,
inputs=[num, state],
outputs=[num, state],
)
app, _, _ = demo.launch(prevent_thread_lock=True, state_session_capacity=2)
client = TestClient(app)
session_1 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "1", "fn_index": 0},
)
assert session_1.json()["data"][0] == 0
session_2 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "2", "fn_index": 0},
)
assert session_2.json()["data"][0] == 0
session_1 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "1", "fn_index": 0},
)
assert session_1.json()["data"][0] == 1
session_2 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "2", "fn_index": 0},
)
assert session_2.json()["data"][0] == 1
session_3 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "3", "fn_index": 0},
)
assert session_3.json()["data"][0] == 0
session_2 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "2", "fn_index": 0},
)
assert session_2.json()["data"][0] == 2
session_1 = client.post(
"/api/predict/",
json={"data": [1, None], "session_hash": "1", "fn_index": 0},
)
assert (
session_1.json()["data"][0] == 0
) # state was lost for session 1 when session 3 was added, since state_session_capacity=2
@pytest.mark.asyncio
async def test_updates_stored_up_to_capacity(self):
with gr.Blocks() as demo:
min = gr.Number()
num = gr.Number()
def run(min, num):
return min, gr.Number(value=num, minimum=min)
num.submit(
run,
inputs=[min, num],
outputs=[min, num],
)
app, _, _ = demo.launch(prevent_thread_lock=True, state_session_capacity=2)
client = TestClient(app)
session_1 = client.post(
"/api/predict/", json={"data": [5, 5], "session_hash": "1", "fn_index": 0}
)
assert session_1.json()["data"][0] == 5
session_1 = client.post(
"/api/predict/", json={"data": [2, 2], "session_hash": "1", "fn_index": 0}
)
assert "error" in session_1.json() # error because min is 5 and num is 2
session_2 = client.post(
"/api/predict/", json={"data": [5, 5], "session_hash": "2", "fn_index": 0}
)
assert session_2.json()["data"][0] == 5
session_3 = client.post(
"/api/predict/", json={"data": [5, 5], "session_hash": "3", "fn_index": 0}
)
assert session_3.json()["data"][0] == 5
session_1 = client.post(
"/api/predict/", json={"data": [2, 2], "session_hash": "1", "fn_index": 0}
)
assert (
"error" not in session_1.json()
) # no error because sesssion 1 block config was lost when session 3 was added
class TestCallFunction:
@pytest.mark.asyncio
async def test_call_regular_function(self):
with gr.Blocks() as demo:
text = gr.Textbox()
btn = gr.Button()
btn.click(
lambda x: f"Hello, {x}",
inputs=text,
outputs=text,
)
output = await demo.call_function(0, ["World"])
assert output["prediction"] == "Hello, World"
output = demo("World")
assert output == "Hello, World"
output = await demo.call_function(0, ["Abubakar"])
assert output["prediction"] == "Hello, Abubakar"
@pytest.mark.asyncio
async def test_call_multiple_functions(self):
with gr.Blocks() as demo:
text = gr.Textbox()
text2 = gr.Textbox()
btn = gr.Button()
btn.click(
lambda x: f"Hello, {x}",
inputs=text,
outputs=text,
)
text.change(
lambda x: f"Hi, {x}",
inputs=text,
outputs=text2,
)
output = await demo.call_function(0, ["World"])
assert output["prediction"] == "Hello, World"
output = demo("World")
assert output == "Hello, World"
output = await demo.call_function(1, ["World"])
assert output["prediction"] == "Hi, World"
output = demo("World", fn_index=1) # fn_index must be a keyword argument
assert output == "Hi, World"
@pytest.mark.asyncio
async def test_call_decorated_functions(self):
with gr.Blocks() as demo:
name = gr.Textbox(value="Abubakar")
output = gr.Textbox(label="Output Box")
@name.submit(inputs=name, outputs=output)
@demo.load(inputs=name, outputs=output)
def test(x):
return "Hello " + x
output = await demo.call_function(0, ["Adam"])
assert output["prediction"] == "Hello Adam"
output = await demo.call_function(1, ["Adam"])
assert output["prediction"] == "Hello Adam"
@pytest.mark.asyncio
async def test_call_generator(self):
def generator(x):
yield from range(x)
with gr.Blocks() as demo:
inp = gr.Number()
out = gr.Number()
btn = gr.Button()
btn.click(
generator,
inputs=inp,
outputs=out,
)
demo.queue()
assert demo.config["enable_queue"]
output = await demo.call_function(0, [3])
assert output["prediction"] == 0
output = await demo.call_function(0, [3], iterator=output["iterator"])
assert output["prediction"] == 1
output = await demo.call_function(0, [3], iterator=output["iterator"])
assert output["prediction"] == 2
output = await demo.call_function(0, [3], iterator=output["iterator"])
assert output["prediction"] == gr.components._Keywords.FINISHED_ITERATING
assert output["iterator"] is None
output = await demo.call_function(0, [3], iterator=output["iterator"])
assert output["prediction"] == 0
@pytest.mark.asyncio
async def test_call_both_generator_and_function(self):
def generator(x):
for i in range(x):
yield i, x
with gr.Blocks() as demo:
inp = gr.Number()
out1 = gr.Number()
out2 = gr.Number()
btn = gr.Button()
inp.change(lambda x: x + x, inp, out1)
btn.click(
generator,
inputs=inp,
outputs=[out1, out2],
)
demo.queue()
output = await demo.call_function(0, [2])
assert output["prediction"] == 4
output = await demo.call_function(0, [-1])
assert output["prediction"] == -2
output = await demo.call_function(1, [3])
assert output["prediction"] == (0, 3)
output = await demo.call_function(1, [3], iterator=output["iterator"])
assert output["prediction"] == (1, 3)
output = await demo.call_function(1, [3], iterator=output["iterator"])
assert output["prediction"] == (2, 3)
output = await demo.call_function(1, [3], iterator=output["iterator"])
assert output["prediction"] == (gr.components._Keywords.FINISHED_ITERATING,) * 2
assert output["iterator"] is None
output = await demo.call_function(1, [3], iterator=output["iterator"])
assert output["prediction"] == (0, 3)
class TestBatchProcessing:
def test_raise_exception_if_batching_an_event_thats_not_queued(self):
def trim(words, lens):
trimmed_words = [word[: int(length)] for word, length in zip(words, lens)]
return [trimmed_words]
msg = "In order to use batching, the queue must be enabled."
with pytest.raises(ValueError, match=msg):
with gr.Blocks() as demo:
with gr.Row():
word = gr.Textbox(label="word")
leng = gr.Number(label="leng")
output = gr.Textbox(label="Output")
with gr.Row():
run = gr.Button()
run.click(
trim,
[word, leng],
output,
batch=True,
max_batch_size=16,
queue=False,
)
demo.queue()
demo.launch(prevent_thread_lock=True)
@pytest.mark.asyncio
async def test_call_regular_function(self):
def batch_fn(x):
results = []
for word in x:
results.append(f"Hello {word}")
return (results,)
with gr.Blocks() as demo:
text = gr.Textbox()
btn = gr.Button()
btn.click(batch_fn, inputs=text, outputs=text, batch=True)
output = await demo.call_function(0, [["Adam", "Yahya"]])
assert output["prediction"][0] == ["Hello Adam", "Hello Yahya"]
output = demo("Abubakar")
assert output == "Hello Abubakar"
@pytest.mark.asyncio
async def test_functions_multiple_parameters(self):
def regular_fn(word1, word2):
return len(word1) > len(word2)
def batch_fn(words, lengths):
comparisons = []
trim_words = []
for word, length in zip(words, lengths):
trim_words.append(word[:length])
comparisons.append(len(word) > length)
return trim_words, comparisons
with gr.Blocks() as demo:
text1 = gr.Textbox()
text2 = gr.Textbox()
leng = gr.Number(precision=0)
bigger = gr.Checkbox()
btn1 = gr.Button("Check")
btn2 = gr.Button("Trim")
btn1.click(regular_fn, inputs=[text1, text2], outputs=bigger)
btn2.click(
batch_fn,
inputs=[text1, leng],
outputs=[text1, bigger],
batch=True,
)
output = await demo.call_function(0, ["Adam", "Yahya"])
assert output["prediction"] is False
output = demo("Abubakar", "Abid")
assert output
output = await demo.call_function(1, [["Adam", "Mary"], [3, 5]])
assert output["prediction"] == (
["Ada", "Mary"],
[True, False],
)
output = demo("Abubakar", 3, fn_index=1)
assert output == ["Abu", True]
@pytest.mark.asyncio
async def test_invalid_batch_generator(self):
with pytest.raises(ValueError):
def batch_fn(x):
results = []
for word in x:
results.append(f"Hello {word}")
yield (results,)
with gr.Blocks() as demo:
text = gr.Textbox()
btn = gr.Button()
btn.click(batch_fn, inputs=text, outputs=text, batch=True)
await demo.process_api(0, [["Adam", "Yahya"]], state=None)
@pytest.mark.asyncio
async def test_exceeds_max_batch_size(self):
with pytest.raises(ValueError):
def batch_fn(x):
results = []
for word in x:
results.append(f"Hello {word}")
return (results,)
with gr.Blocks() as demo:
text = gr.Textbox()
btn = gr.Button()
btn.click(
batch_fn, inputs=text, outputs=text, batch=True, max_batch_size=2
)
await demo.process_api(0, [["A", "B", "C"]], state=None)
@pytest.mark.asyncio
async def test_unequal_batch_sizes(self):
with pytest.raises(ValueError):
def batch_fn(x, y):
results = []
for word1, word2 in zip(x, y):
results.append(f"Hello {word1}{word2}")
return (results,)
with gr.Blocks() as demo:
t1 = gr.Textbox()
t2 = gr.Textbox()
btn = gr.Button()
btn.click(batch_fn, inputs=[t1, t2], outputs=t1, batch=True)
await demo.process_api(0, [["A", "B", "C"], ["D", "E"]], state=None)
class TestUpdate:
@pytest.mark.asyncio
async def test_accordion_update(self):
with gr.Blocks() as demo:
with gr.Accordion(label="Open for greeting", open=False) as accordion:
gr.Textbox("Hello!")
open_btn = gr.Button("Open Accordion")
close_btn = gr.Button("Close Accordion")
open_btn.click(
lambda: gr.Accordion(open=True, label="Open Accordion"),
inputs=None,
outputs=[accordion],
)
close_btn.click(
lambda: gr.Accordion(open=False, label="Closed Accordion"),
inputs=None,
outputs=[accordion],
)
result = await demo.process_api(
fn_index=0, inputs=[None], request=None, state=None
)
assert result["data"][0] == {
"open": True,
"label": "Open Accordion",
"__type__": "update",
}
result = await demo.process_api(
fn_index=1, inputs=[None], request=None, state=None
)
assert result["data"][0] == {
"open": False,
"label": "Closed Accordion",
"__type__": "update",
}
class TestRender:
def test_duplicate_error(self):
with pytest.raises(DuplicateBlockError):
t = gr.Textbox()
with gr.Blocks():
t.render()
gr.Number()
t.render()
with pytest.raises(DuplicateBlockError):
with gr.Blocks():
t = gr.Textbox()
t.render()
with pytest.raises(DuplicateBlockError):
io = gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox())
with gr.Blocks():
io.render()
io.render()
with pytest.raises(DuplicateBlockError):
t = gr.Textbox()
io = gr.Interface(lambda x: x, t, gr.Textbox())
with gr.Blocks():
io.render()
t.render()
def test_no_error(self):
t = gr.Textbox()
t2 = gr.Textbox()
with gr.Blocks():
t.render()
t3 = t2.render()
assert t2 == t3
t = gr.Textbox()
io = gr.Interface(lambda x: x, t, gr.Textbox())
with gr.Blocks():
io.render()
gr.Textbox()
io = gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox())
io2 = gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox())
with gr.Blocks():
io.render()
io3 = io2.render()
assert io2 == io3
def test_is_rendered(self):
t = gr.Textbox()
with gr.Blocks():
pass
assert not t.is_rendered
t = gr.Textbox()
with gr.Blocks():
t.render()
assert t.is_rendered
t = gr.Textbox()
with gr.Blocks():
t.render()
t.unrender()
assert not t.is_rendered
with gr.Blocks():
t = gr.Textbox()
assert t.is_rendered
with gr.Blocks():
t = gr.Textbox()
with gr.Blocks():
pass
assert t.is_rendered
t = gr.Textbox()
gr.Interface(lambda x: x, "textbox", t)
assert t.is_rendered
def test_no_error_if_state_rendered_multiple_times(self):
state = gr.State("")
gr.TabbedInterface(
[
gr.Interface(
lambda _, x: (x, "I don't know"),
inputs=[state, gr.Textbox()],
outputs=[state, gr.Textbox()],
),
gr.Interface(
lambda s: (s, f"User question: {s}"),
inputs=[state],
outputs=[state, gr.Textbox(interactive=False)],
),
],
["Ask question", "Show question"],
)
class TestCancel:
@pytest.mark.asyncio
async def test_cancel_function(self, capsys):
async def long_job():
await asyncio.sleep(10)
print("HELLO FROM LONG JOB")
with gr.Blocks() as demo:
button = gr.Button(value="Start")
click = button.click(long_job, None, None)
cancel = gr.Button(value="Cancel")
cancel.click(None, None, None, cancels=[click])
cancel_fun = demo.fns[-1].fn
task = asyncio.create_task(long_job())
task.set_name("foo_0")
# If cancel_fun didn't cancel long_job the message would be printed to the console
# The test would also take 10 seconds
await asyncio.gather(task, cancel_fun("foo"), return_exceptions=True)
captured = capsys.readouterr()
assert "HELLO FROM LONG JOB" not in captured.out
@pytest.mark.asyncio
async def test_cancel_function_with_multiple_blocks(self, capsys):
async def long_job():
await asyncio.sleep(10)
print("HELLO FROM LONG JOB")
with gr.Blocks() as demo1:
textbox = gr.Textbox()
button1 = gr.Button(value="Start")
button1.click(lambda x: x, textbox, textbox)
with gr.Blocks() as demo2:
button2 = gr.Button(value="Start")
click = button2.click(long_job, None, None)
cancel = gr.Button(value="Cancel")
cancel.click(None, None, None, cancels=[click])
with gr.Blocks() as demo:
with gr.Tab("Demo 1"):
demo1.render()
with gr.Tab("Demo 2"):
demo2.render()
cancel_fun = demo.fns[-1].fn
task = asyncio.create_task(long_job())
task.set_name("foo_1")
await asyncio.gather(task, cancel_fun("foo"), return_exceptions=True)
captured = capsys.readouterr()
assert "HELLO FROM LONG JOB" not in captured.out
def test_raise_exception_if_cancelling_an_event_thats_not_queued(self):
def iteration(a):
yield a
msg = "Queue needs to be enabled!"
with pytest.raises(ValueError, match=msg):
with gr.Blocks() as demo:
button = gr.Button(value="Predict")
click = button.click(None, None, None)
cancel = gr.Button(value="Cancel")
cancel.click(None, None, None, cancels=[click], queue=False)
demo.launch(prevent_thread_lock=True)
with pytest.raises(ValueError, match=msg):
with gr.Blocks() as demo:
button = gr.Button(value="Predict")
click = button.click(None, None, None, queue=False)
cancel = gr.Button(value="Cancel")
cancel.click(None, None, None, cancels=[click])
demo.queue().launch(prevent_thread_lock=True)
class TestEvery:
def test_raise_exception_if_parameters_invalid(self):
with pytest.raises(
ValueError, match="Cannot run event in a batch and every 0.5 seconds"
):
with gr.Blocks():
num = gr.Number()
num.change(
lambda s: s + 1, inputs=[num], outputs=[num], every=0.5, batch=True
)
with pytest.raises(
ValueError, match="Parameter every must be positive or None"
):
with gr.Blocks():
num = gr.Number()
num.change(lambda s: s + 1, inputs=[num], outputs=[num], every=-0.1)
class TestGetAPIInfo:
def test_many_endpoints(self):
with gr.Blocks() as demo:
t1 = gr.Textbox()
t2 = gr.Textbox()
t3 = gr.Textbox()
t4 = gr.Textbox()
t5 = gr.Textbox()
t1.change(lambda x: x, t1, t2, api_name="change1")
t2.change(lambda x: x, t2, t3, api_name="change2")
t3.change(lambda x: x, t3, t4, api_name=False)
t4.change(lambda x: x, t4, t5, api_name=False)
api_info = demo.get_api_info()
assert len(api_info["named_endpoints"]) == 2
assert len(api_info["unnamed_endpoints"]) == 0
def test_no_endpoints(self):
with gr.Blocks() as demo:
t1 = gr.Textbox()
t2 = gr.Textbox()
t1.change(lambda x: x, t1, t2, api_name=False)
api_info = demo.get_api_info()
assert len(api_info["named_endpoints"]) == 0
assert len(api_info["unnamed_endpoints"]) == 0
class TestAddRequests:
def test_no_type_hints(self):
def moo(a, b):
return a + b
inputs = [1, 2]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs
boo = partial(moo, a=1)
inputs = [2]
inputs_ = gr.helpers.special_args(boo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs
def test_no_type_hints_with_request(self):
def moo(a: str, b: int):
return a + str(b)
inputs = ["abc", 2]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs
boo = partial(moo, a="def")
inputs = [2]
inputs_ = gr.helpers.special_args(boo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs
def test_type_hints_with_request(self):
def moo(a: str, b: gr.Request):
return a
inputs = ["abc"]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs + [request]
def moo(a: gr.Request, b, c: int):
return c
inputs = ["abc", 5]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == [request] + inputs
def test_type_hints_with_multiple_requests(self):
def moo(a: str, b: gr.Request, c: gr.Request):
return a
inputs = ["abc"]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs + [request, request]
def moo(a: gr.Request, b, c: int, d: gr.Request):
return c
inputs = ["abc", 5]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == [request] + inputs + [request]
def test_default_args(self):
def moo(a, b, c=42):
return a + b + c
inputs = [1, 2]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs + [42]
inputs = [1, 2, 24]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs
def test_default_args_with_progress(self):
pr = gr.Progress()
def moo(a, b, c=42, pr=pr):
return a + b + c
inputs = [1, 2]
request = gr.Request()
inputs_, progress_index, _ = gr.helpers.special_args(
moo, copy.deepcopy(inputs), request
)
assert inputs_ == inputs + [42, pr]
assert progress_index == 3
inputs = [1, 2, 24]
request = gr.Request()
inputs_, progress_index, _ = gr.helpers.special_args(
moo, copy.deepcopy(inputs), request
)
assert inputs_ == inputs + [pr]
assert progress_index == 3
def moo(a, b, pr=pr, c=42):
return a + b + c
inputs = [1, 2]
request = gr.Request()
inputs_, progress_index, _ = gr.helpers.special_args(
moo, copy.deepcopy(inputs), request
)
assert inputs_ == inputs + [pr, 42]
assert progress_index == 2
def test_default_args_with_request(self):
pr = gr.Progress()
def moo(a, b, req: gr.Request, c=42):
return a + b + c
inputs = [1, 2]
request = gr.Request()
inputs_ = gr.helpers.special_args(moo, copy.deepcopy(inputs), request)[0]
assert inputs_ == inputs + [request, 42]
def moo(a, b, req: gr.Request, c=42, pr=pr):
return a + b + c
inputs = [1, 2]
request = gr.Request()
inputs_, progress_index, _ = gr.helpers.special_args(
moo, copy.deepcopy(inputs), request
)
assert inputs_ == inputs + [request, 42, pr]
assert progress_index == 4
def test_default_args_with_event_data(self):
pr = gr.Progress()
target = gr.Textbox()
def moo(a, b, ed: SelectData, c=42):
return a + b + c
event_data = SelectData(target=target, data={"index": 24, "value": "foo"})
inputs = [1, 2]
request = gr.Request()
inputs_ = gr.helpers.special_args(
moo, copy.deepcopy(inputs), request, event_data
)[0]
assert len(inputs_) == 4
new_event_data = inputs_[2]
assert inputs_ == inputs + [new_event_data, 42]
assert isinstance(new_event_data, SelectData)
assert new_event_data.target == target
assert new_event_data.index == 24
assert new_event_data.value == "foo"
def moo(a, b, ed: SelectData, c=42, pr=pr):
return a + b + c
inputs = [1, 2]
request = gr.Request()
inputs_, progress_index, _ = gr.helpers.special_args(
moo, copy.deepcopy(inputs), request, event_data
)
assert len(inputs_) == 5
new_event_data = inputs_[2]
assert inputs_ == inputs + [new_event_data, 42, pr]
assert progress_index == 4
assert isinstance(new_event_data, SelectData)
assert new_event_data.target == target
assert new_event_data.index == 24
assert new_event_data.value == "foo"
@pytest.mark.asyncio
async def test_queue_when_using_auth():
sleep_time = 1
async def say_hello(name):
await asyncio.sleep(sleep_time)
return f"Hello {name}!"
with gr.Blocks() as demo:
_input = gr.Textbox()
_output = gr.Textbox()
button = gr.Button()
button.click(say_hello, _input, _output)
demo.queue()
app, _, _ = demo.launch(auth=("abc", "123"), prevent_thread_lock=True)
with pytest.raises(ValueError):
grc.Client(f"http://localhost:{demo.server_port}")
client = grc.Client(f"http://localhost:{demo.server_port}", auth=("abc", "123"))
jobs = []
for i in range(3):
jobs.append(client.submit(f"World {i}", fn_index=0))
for i, job in enumerate(jobs):
assert job.result() == f"Hello World {i}!"
def test_temp_file_sets_get_extended():
test_file_dir = pathlib.Path(pathlib.Path(__file__).parent, "test_files")
with gr.Blocks() as demo1:
gr.Video(str(test_file_dir / "video_sample.mp4"))
with gr.Blocks() as demo2:
gr.Audio(str(test_file_dir / "audio_sample.wav"))
with gr.Blocks() as demo3:
demo1.render()
demo2.render()
assert demo3.temp_file_sets == demo1.temp_file_sets + demo2.temp_file_sets
def test_recover_kwargs():
audio = gr.Audio(format="wav", autoplay=True)
props = audio.recover_kwargs(
{"format": "wav", "value": "foo.wav", "autoplay": False, "foo": "bar"}
)
assert props == {"format": "wav", "value": "foo.wav", "autoplay": False}
props = audio.recover_kwargs(
{"format": "wav", "value": "foo.wav", "autoplay": False, "foo": "bar"},
["value"],
)
assert props == {"format": "wav", "autoplay": False}