Bit-Trading-Company commited on
Commit
c425891
·
verified ·
1 Parent(s): b656fb9

CI deploy b5b8ab30

Browse files
Files changed (2) hide show
  1. app.py +9 -10
  2. tests/test_ui.py +41 -0
app.py CHANGED
@@ -764,16 +764,15 @@ def build_app() -> gr.Blocks:
764
  # is exactly what broke the UI while leaving the API working.
765
  demo.load(fn=None, inputs=None, outputs=None, js=BRIDGE_LOAD_JS)
766
 
767
- # Sign-in state is only meaningful on a Space. Off-Space, Gradio mocks
768
- # OAuth by calling whoami and raises without a token, so this handler
769
- # is only registered where OAuth actually exists.
770
- if ON_SPACE:
771
- def whoami(st, profile: gr.OAuthProfile | None = None):
772
- st = copy.deepcopy(st)
773
- st["user"] = getattr(profile, "username", None) if profile else None
774
- return st, render_top(st, None)
775
-
776
- demo.load(whoami, [state], [state, top_html])
777
 
778
  return demo
779
 
 
764
  # is exactly what broke the UI while leaving the API working.
765
  demo.load(fn=None, inputs=None, outputs=None, js=BRIDGE_LOAD_JS)
766
 
767
+ # No OAuth-annotated `demo.load` here, deliberately. A handler that
768
+ # takes `gr.OAuthProfile` asks Gradio for an authenticated session; on
769
+ # a *load* event that fires every render, an unauthenticated visitor is
770
+ # sent to sign in, comes back, fires load again, and is sent to sign in
771
+ # again -- the browser reports it as "redirected you too many times".
772
+ #
773
+ # `gr.LoginButton` already shows signed-in state on its own, and the
774
+ # profile is read where it is actually needed: the user-initiated
775
+ # extension handlers in src/extension.py.
 
776
 
777
  return demo
778
 
tests/test_ui.py CHANGED
@@ -736,3 +736,44 @@ def test_bridge_js_is_loaded_without_outputs():
736
 
737
  src = inspect.getsource(bitapp.build_app)
738
  assert "demo.load(fn=None, inputs=None, outputs=None, js=BRIDGE_LOAD_JS)" in src
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
736
 
737
  src = inspect.getsource(bitapp.build_app)
738
  assert "demo.load(fn=None, inputs=None, outputs=None, js=BRIDGE_LOAD_JS)" in src
739
+
740
+
741
+ def test_no_oauth_annotated_load_handler():
742
+ """A `demo.load` handler that takes `gr.OAuthProfile` asks for an
743
+ authenticated session on every page render. An unauthenticated visitor is
744
+ sent to sign in, returns, fires load again, and is sent back -- an infinite
745
+ redirect. The profile belongs on user-initiated handlers only."""
746
+ import inspect
747
+ import re
748
+
749
+ src = inspect.getsource(bitapp.build_app)
750
+ # Strip comments so the explanation of this rule does not trip it.
751
+ code = "\n".join(ln for ln in src.splitlines()
752
+ if not ln.lstrip().startswith("#"))
753
+ assert "OAuthProfile" not in code, \
754
+ "an OAuth-annotated handler is registered in build_app"
755
+
756
+ for call in re.findall(r"demo\.load\((.*?)\)\n", code, re.S):
757
+ assert "profile" not in call, f"load handler takes a profile: {call[:80]}"
758
+
759
+
760
+ def test_profile_is_read_only_on_user_initiated_handlers():
761
+ """Where the profile *is* needed, it must hang off a click, not a load."""
762
+ import inspect
763
+
764
+ from src import extension
765
+
766
+ for fn in (extension.extend_ui, extension.add_model_ui):
767
+ params = inspect.signature(fn).parameters
768
+ assert "profile" in params, f"{fn.__name__} should receive the profile"
769
+
770
+
771
+ def test_login_button_is_the_only_sign_in_mechanism():
772
+ import inspect
773
+
774
+ from src.ui import shell
775
+
776
+ src = inspect.getsource(bitapp.build_app)
777
+ assert "gr.LoginButton" in src
778
+ # The header must not also hand-roll a link to the OAuth route.
779
+ assert "/login/huggingface" not in shell.top_bar(on_space=True)