Wauplin HF staff commited on
Commit
91bbd67
1 Parent(s): 7c223c6

some improvements

Browse files
Files changed (6) hide show
  1. .gitignore +140 -0
  2. app.py +25 -14
  3. auth.py +13 -8
  4. pyproject.toml +11 -0
  5. requirements.txt +1 -5
  6. setup.cfg +14 -0
.gitignore ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib64/
18
+ parts/
19
+ sdist/
20
+ var/
21
+ wheels/
22
+ pip-wheel-metadata/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+
53
+ # Translations
54
+ *.mo
55
+ *.pot
56
+
57
+ # Django stuff:
58
+ *.log
59
+ local_settings.py
60
+ db.sqlite3
61
+ db.sqlite3-journal
62
+
63
+ # Flask stuff:
64
+ instance/
65
+ .webassets-cache
66
+
67
+ # Scrapy stuff:
68
+ .scrapy
69
+
70
+ # Sphinx documentation
71
+ docs/_build/
72
+
73
+ # PyBuilder
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+
79
+ # IPython
80
+ profile_default/
81
+ ipython_config.py
82
+
83
+ # pyenv
84
+ .python-version
85
+
86
+ # pipenv
87
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
88
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
89
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
90
+ # install all needed dependencies.
91
+ #Pipfile.lock
92
+
93
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
94
+ __pypackages__/
95
+
96
+ # Celery stuff
97
+ celerybeat-schedule
98
+ celerybeat.pid
99
+
100
+ # SageMath parsed files
101
+ *.sage.py
102
+
103
+ # Environments
104
+ .env
105
+ .venv
106
+ .venv*
107
+ env/
108
+ venv/
109
+ ENV/
110
+ env.bak/
111
+ venv.bak/
112
+ .venv*
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+ .vscode/
132
+ .idea/
133
+
134
+ .DS_Store
135
+
136
+ # Ruff
137
+ .ruff_cache
138
+
139
+ # Spell checker config
140
+ cspell.json
app.py CHANGED
@@ -1,8 +1,14 @@
1
  import gradio as gr
2
  import gradio.blocks
 
 
3
 
4
  from auth import attach_oauth
5
 
 
 
 
 
6
 
7
  TEMPLATE = """
8
  ### Name: {name}
@@ -17,35 +23,40 @@ You can manage your connected applications in your [settings](https://huggingfac
17
 
18
 
19
  def show_profile(request: gr.Request) -> str:
20
- if "user" not in request.request.session:
 
21
  return "Please login first"
22
  return TEMPLATE.format(**request.request.session["user"])
23
 
24
 
25
- with gr.Blocks() as demo:
26
  # Taken from https://cmgdo.com/external-link-in-gradio-button/
 
 
 
 
27
  login_button = gr.Button("Login")
28
- login_button.click(
29
- None, None, None, _js="function() {window.open('https://wauplin-gradio-oauth-test.hf.space/login/huggingface');}"
30
- )
31
 
32
- # Taken from https://cmgdo.com/external-link-in-gradio-button/
33
- logout_button = gr.Button("Logout", variant="secondary")
34
- logout_button.click(None, None, None, _js="function() {window.open('https://wauplin-gradio-oauth-test.hf.space/logout');}")
35
 
36
  profile_btn = gr.Button("Show profile")
37
  output = gr.Markdown()
38
  profile_btn.click(fn=show_profile, outputs=output)
39
 
40
- old_start_server = gradio.blocks.networking.start_server
 
41
 
42
 
43
- def patched_start_server(*args, **kwargs):
44
- server_name, server_port, local_url, app, server = old_start_server(*args, **kwargs)
45
  attach_oauth(app)
46
- return server_name, server_port, local_url, app, server
 
47
 
 
48
 
49
- gradio.blocks.networking.start_server = patched_start_server
50
 
51
- demo.launch()
 
1
  import gradio as gr
2
  import gradio.blocks
3
+ import gradio.networking
4
+ import gradio.utils
5
 
6
  from auth import attach_oauth
7
 
8
+ if gradio.utils.get_space() is not None:
9
+ URL, PORT = "https://wauplin-gradio-oauth-test.hf.space", 7860
10
+ else:
11
+ URL, PORT = "http://localhost:5173", 5173
12
 
13
  TEMPLATE = """
14
  ### Name: {name}
 
23
 
24
 
25
  def show_profile(request: gr.Request) -> str:
26
+ fastapi_request = request.request
27
+ if fastapi_request is None or "user" not in request.request.session:
28
  return "Please login first"
29
  return TEMPLATE.format(**request.request.session["user"])
30
 
31
 
32
+ def js_open(url: str) -> str:
33
  # Taken from https://cmgdo.com/external-link-in-gradio-button/
34
+ return f"function() {{window.open('{url}', '_blank');}}"
35
+
36
+
37
+ with gr.Blocks() as demo:
38
  login_button = gr.Button("Login")
39
+ login_button.click(None, None, None, _js=js_open(f"{URL}/login/huggingface"))
 
 
40
 
41
+ logout_button = gr.Button("Logout")
42
+ logout_button.click(None, None, None, _js=js_open(f"{URL}/logout"))
 
43
 
44
  profile_btn = gr.Button("Show profile")
45
  output = gr.Markdown()
46
  profile_btn.click(fn=show_profile, outputs=output)
47
 
48
+
49
+ old_create_app = gradio.networking.App.create_app
50
 
51
 
52
+ def patched_create_app(*args, **kwargs):
53
+ app = old_create_app(*args, **kwargs)
54
  attach_oauth(app)
55
+ return app
56
+
57
 
58
+ gradio.networking.App.create_app = patched_create_app
59
 
60
+ print(URL)
61
 
62
+ demo.launch(server_port=PORT)
auth.py CHANGED
@@ -1,16 +1,15 @@
1
  import os
2
- import httpx
3
 
4
  from authlib.integrations.starlette_client import OAuth
5
  from fastapi import FastAPI
6
  from fastapi.requests import Request
7
- from fastapi.responses import RedirectResponse
8
  from starlette.middleware.sessions import SessionMiddleware
9
 
10
  OAUTH_CLIENT_ID = os.environ.get("OAUTH_CLIENT_ID")
11
  OAUTH_CLIENT_SECRET = os.environ.get("OAUTH_CLIENT_SECRET")
12
  OAUTH_SCOPES = os.environ.get("OAUTH_SCOPES")
13
- OPENID_PROVIDER_URL = os.environ.get("OPENID_PROVIDER_URL")
14
 
15
  for value in (OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_SCOPES, OPENID_PROVIDER_URL):
16
  if value is None:
@@ -28,21 +27,27 @@ oauth.register(
28
  server_metadata_url=METADATA_URL,
29
  )
30
 
 
 
 
 
31
 
32
  async def oauth_login(request: Request):
33
- redirect_uri = request.url_for("oauth_redirect_callback").replace('http://', 'https://')
 
 
34
  return await oauth.huggingface.authorize_redirect(request, redirect_uri)
35
 
36
 
37
- async def oauth_logout(request: Request):
38
  request.session.pop("user", None)
39
- return "logged out"
40
 
41
 
42
- async def oauth_redirect_callback(request: Request):
43
  token = await oauth.huggingface.authorize_access_token(request)
44
  request.session["user"] = token["userinfo"] # TODO: we should store entire token
45
- return RedirectResponse("/")
46
 
47
 
48
  def attach_oauth(app: FastAPI) -> None:
 
1
  import os
 
2
 
3
  from authlib.integrations.starlette_client import OAuth
4
  from fastapi import FastAPI
5
  from fastapi.requests import Request
6
+ from fastapi.responses import HTMLResponse
7
  from starlette.middleware.sessions import SessionMiddleware
8
 
9
  OAUTH_CLIENT_ID = os.environ.get("OAUTH_CLIENT_ID")
10
  OAUTH_CLIENT_SECRET = os.environ.get("OAUTH_CLIENT_SECRET")
11
  OAUTH_SCOPES = os.environ.get("OAUTH_SCOPES")
12
+ OPENID_PROVIDER_URL = os.environ.get("OPENID_PROVIDER_URL")
13
 
14
  for value in (OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_SCOPES, OPENID_PROVIDER_URL):
15
  if value is None:
 
27
  server_metadata_url=METADATA_URL,
28
  )
29
 
30
+ # Hack to close the login/logout page once the user is logged in/out.
31
+ # TODO: can it be less hacky?
32
+ CLOSE_WINDOW_HTML = HTMLResponse("<script>window.close();</script>")
33
+
34
 
35
  async def oauth_login(request: Request):
36
+ redirect_uri = str(request.url_for("oauth_redirect_callback"))
37
+ if ".hf.space" in redirect_uri: # In Space, FastAPI redirect as http but we want https
38
+ redirect_uri = redirect_uri.replace("http://", "https://")
39
  return await oauth.huggingface.authorize_redirect(request, redirect_uri)
40
 
41
 
42
+ async def oauth_logout(request: Request) -> HTMLResponse:
43
  request.session.pop("user", None)
44
+ return CLOSE_WINDOW_HTML
45
 
46
 
47
+ async def oauth_redirect_callback(request: Request) -> HTMLResponse:
48
  token = await oauth.huggingface.authorize_access_token(request)
49
  request.session["user"] = token["userinfo"] # TODO: we should store entire token
50
+ return CLOSE_WINDOW_HTML
51
 
52
 
53
  def attach_oauth(app: FastAPI) -> None:
pyproject.toml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [tool.black]
2
+ line-length = 119
3
+ target_version = ['py38', 'py39', 'py310']
4
+ preview = true
5
+
6
+ [tool.ruff]
7
+ select = ["E", "F", "I", "W"]
8
+ line-length = 119
9
+
10
+ [tool.ruff.isort]
11
+ lines-after-imports = 2
requirements.txt CHANGED
@@ -1,6 +1,2 @@
1
  authlib==1.2.1
2
- itsdangerous==2.1.2
3
- # hardcoded version to be able to set a middleware after an application has started
4
- # SessionMiddleware is needed to set the user in the session.
5
- # Pinned dependency won't be necessary anymore once we integrate more tightly inside gradio library
6
- fastapi==0.88.0
 
1
  authlib==1.2.1
2
+ itsdangerous==2.1.2
 
 
 
 
setup.cfg ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [isort]
2
+ default_section = FIRSTPARTY
3
+ ensure_newline_before_comments = True
4
+ force_grid_wrap = 0
5
+ include_trailing_comma = True
6
+ line_length = 119
7
+ lines_after_imports = 2
8
+ multi_line_output = 3
9
+ use_parentheses = True
10
+
11
+ [flake8]
12
+ exclude = .git,__pycache__,old,build,dist,.venv*
13
+ ignore = B028, E203, E501, E741, W503
14
+ max-line-length = 119