Skip to content

Commit ee26737

Browse files
authored
add simple retry to requests (#40)
* add simple retry to requests Signed-off-by: vsoch <[email protected]>
1 parent 770bc06 commit ee26737

File tree

7 files changed

+142
-64
lines changed

7 files changed

+142
-64
lines changed

app/library/auth.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def check_auth(credentials: HTTPBasicCredentials = Depends(security)):
4545
current_username_bytes, correct_username_bytes
4646
)
4747
current_password_bytes = credentials.password.encode("utf8")
48+
4849
correct_password_bytes = bytes(settings.flux_token.encode("utf8"))
4950
is_correct_password = secrets.compare_digest(
5051
current_password_bytes, correct_password_bytes

app/routers/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ async def list_nodes():
113113
rpc = flux.resource.list.resource_list(app.handle)
114114
listing = rpc.get()
115115
nodes = jsonable_encoder(
116-
{"nodes": list({str(node) for node in listing.free.nodelist})}
116+
{"nodes": list({str(node) for node in listing.up.nodelist})}
117117
)
118118
return JSONResponse(content=nodes, status_code=200)
119119

clients/python/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and **Merged pull requests**. Critical items to know are:
1414
The versions coincide with releases on pip. Only major versions will be released as tags on Github.
1515

1616
## [0.0.x](https://github.com/flux-framework/flux-restful-api/tree/main) (0.0.x)
17+
- add simple retry to requests (0.0.16)
1718
- support for adding option flags to submit (0.0.15)
1819
- support for `is_launcher` parameter to indicate a launcher should be used instead (0.0.14)
1920
- support for streaming job output (0.0.13)

clients/python/flux_restful_client/main/client.py

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import os
2+
import time
23
from copy import deepcopy
34

45
import flux_restful_client.main.schemas as schemas
56
import flux_restful_client.utils as utils
67
import jsonschema
78
import requests
89
from flux_restful_client.logger import logger
10+
from requests.auth import HTTPBasicAuth
911

1012
from .settings import Settings
1113

@@ -23,6 +25,8 @@ def __init__(
2325
quiet=False,
2426
settings_file=None,
2527
prefix="v1",
28+
attempts=5,
29+
timeout=2,
2630
**kwargs,
2731
):
2832

@@ -36,13 +40,19 @@ def __init__(
3640
self.headers = {}
3741
self.quiet = quiet
3842
self.prefix = prefix
39-
if self.user and self.token:
40-
self.set_basic_auth(self.user, self.token)
43+
self.attempts = attempts
44+
self.timeout = timeout
4145
self.session = requests.Session()
4246

4347
def set_header(self, name, value):
4448
self.headers.update({name: value})
4549

50+
def set_bearer_auth(self, token):
51+
"""
52+
Add a token directly to a request
53+
"""
54+
self.set_header("Authorization", "Bearer %s" % token)
55+
4656
def set_basic_auth(self, username, password):
4757
"""
4858
A wrapper to adding basic authentication to the Request
@@ -62,21 +72,57 @@ def reset(self):
6272
self.headers = {}
6373

6474
def do_request(
65-
self, endpoint, method="GET", data=None, headers=None, params=None, stream=False
75+
self,
76+
endpoint,
77+
method="GET",
78+
data=None,
79+
headers=None,
80+
params=None,
81+
stream=False,
82+
timeout=None,
83+
attempts=None,
6684
):
6785
"""
6886
Do a request. This is a wrapper around requests.
6987
"""
88+
attempts = self.attempts if attempts is None else attempts
89+
timeout = self.timeout if timeout is None else timeout
90+
7091
# Always reset headers for new request.
7192
self.reset()
7293

94+
basic = None
95+
if self.user and self.token:
96+
basic = HTTPBasicAuth(self.user, self.token)
97+
7398
headers = headers or self.headers
7499
url = "%s/%s/%s" % (self.host, self.prefix, endpoint)
75100

76101
# Make the request and return to calling function, unless requires auth
77-
response = self.session.request(
78-
method, url, params=params, json=data, headers=headers, stream=stream
79-
)
102+
try:
103+
response = self.session.request(
104+
method,
105+
url,
106+
params=params,
107+
json=data,
108+
headers=headers,
109+
stream=stream,
110+
auth=basic,
111+
)
112+
except Exception as e:
113+
if attempts > 0:
114+
time.sleep(timeout)
115+
return self.do_request(
116+
endpoint,
117+
method,
118+
data,
119+
headers,
120+
params,
121+
stream,
122+
timeout=timeout * 2,
123+
attempts=attempts - 1,
124+
)
125+
raise e
80126

81127
# A 401 response is a request for authentication
82128
if response.status_code != 401:
@@ -99,8 +145,8 @@ def authenticate_request(self, originalResponse):
99145
return False
100146

101147
# If we have a username and password, set basic auth automatically
102-
if self.token and self.username:
103-
self.set_basic_auth(self.username, self.token)
148+
if self.token and self.user:
149+
self.set_basic_auth(self.user, self.token)
104150

105151
headers = deepcopy(self.headers)
106152
if "Authorization" not in headers:
@@ -112,6 +158,7 @@ def authenticate_request(self, originalResponse):
112158
return False
113159

114160
# Prepare request to retry
161+
115162
h = utils.parse_auth_header(authHeaderRaw)
116163
headers.update(
117164
{
@@ -239,7 +286,6 @@ def submit(self, command, **kwargs):
239286
data[optional] = kwargs[optional]
240287

241288
# Validate the data first.
242-
print(data)
243289
jsonschema.validate(data, schema=schemas.job_submit_schema)
244290
result = self.do_request("jobs/submit", "POST", data=data)
245291
if result.status_code == 404:

clients/python/flux_restful_client/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.0.15"
1+
__version__ = "0.0.16"
22
AUTHOR = "Vanessa Sochat"
33
44
NAME = "flux-restful-client"

docs/conf.py

Lines changed: 82 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.abspath("."))))
1717
from distutils.version import LooseVersion # noqa
1818

19-
import sphinx_material # noqa
19+
import sphinx_immaterial # noqa
2020
from recommonmark.transform import AutoStructify # noqa
2121

2222
FORCE_CLASSIC = os.environ.get("SPHINX_MATERIAL_FORCE_CLASSIC", False)
@@ -30,8 +30,6 @@
3030
copyright = "2022, Flux RESTful API Developers"
3131
author = "@vsoch"
3232

33-
# The full version, including alpha/beta/rc tags
34-
release = LooseVersion(sphinx_material.__version__).vstring
3533

3634
# -- General configuration ---------------------------------------------------
3735

@@ -48,14 +46,18 @@
4846
"sphinx.ext.todo",
4947
"sphinx.ext.mathjax",
5048
"sphinx.ext.viewcode",
49+
"sphinx_immaterial.theme_result",
50+
"sphinx_immaterial.kbd_keys",
51+
"sphinx_immaterial.apidoc.format_signatures",
52+
"sphinx_immaterial.apidoc.json.domain",
53+
"sphinx_immaterial.apidoc.python.apigen",
54+
"sphinx_immaterial.graphviz",
5155
"nbsphinx",
5256
"sphinx_markdown_tables",
53-
"sphinx_gallery.gen_gallery",
5457
"sphinx_copybutton",
5558
"sphinx_search.extension",
5659
]
5760

58-
5961
autosummary_generate = True
6062
autoclass_content = "class"
6163

@@ -102,71 +104,99 @@
102104
# Allows us to add to the default template
103105
templates_path = ["_templates"]
104106

105-
extensions.append("sphinx_material")
106-
html_theme_path = sphinx_material.html_theme_path()
107-
html_context = sphinx_material.get_html_context()
108-
html_theme = "sphinx_material"
107+
extensions.append("sphinx_immaterial")
108+
html_theme = "sphinx_immaterial"
109109
html_css_files = ["custom.css"]
110110

111111
# Custom sphinx material variables
112112
theme_logo_icon = "images/oras.png"
113113

114114

115+
# material theme options (see theme.conf for more information)
115116
html_theme_options = {
116-
"base_url": "http://flux-framework.github.io/flux-restful-api/",
117+
"icon": {
118+
"repo": "fontawesome/brands/github",
119+
"edit": "material/file-edit-outline",
120+
},
117121
"repo_url": "https://github.com/flux-framework/flux-restful-api/",
118-
"repo_name": "Flux RESTful API",
119-
"html_minify": False,
120-
"html_prettify": True,
121-
"css_minify": False,
122-
# https://fonts.google.com/icons?icon.query=cycle
123-
"logo_icon": "cycle",
122+
"repo_name": "Flux RESTFul API",
124123
"repo_type": "github",
125-
"globaltoc_depth": 2,
126-
# red, pink, purple, deep-purple, indigo, blue, light-blue, cyan, teal, green, light-green, lime, yellow, amber, orange, deep-orange, brown, grey, blue-grey, and white.
127-
"color_primary": "blue",
128-
# red, pink, purple, deep-purple, indigo, blue, light-blue, cyan, teal, green, light-green, lime, yellow, amber, orange, and deep-orange.
129-
"color_accent": "blue",
130-
"touch_icon": "images/logo.png",
131-
"theme_color": "#036291",
132-
"master_doc": False,
133-
"nav_links": [
124+
"edit_uri": "blob/main/docs",
125+
"globaltoc_collapse": True,
126+
"features": [
127+
"navigation.expand",
128+
"navigation.tabs",
129+
"toc.integrate",
130+
"navigation.sections",
131+
"navigation.instant",
132+
"header.autohide",
133+
"navigation.top",
134+
"navigation.tracking",
135+
"search.highlight",
136+
"search.share",
137+
"toc.follow",
138+
"toc.sticky",
139+
"content.tabs.link",
140+
"announce.dismiss",
141+
],
142+
"palette": [
143+
{
144+
"media": "(prefers-color-scheme: light)",
145+
"scheme": "default",
146+
"primary": "blue",
147+
"accent": "light-blue",
148+
"toggle": {
149+
"icon": "material/lightbulb-outline",
150+
"name": "Switch to dark mode",
151+
},
152+
},
134153
{
135-
"href": "https://flux-framework.org/",
136-
"internal": False,
137-
"title": "Flux Framework",
154+
"media": "(prefers-color-scheme: dark)",
155+
"scheme": "slate",
156+
"primary": "blue",
157+
"accent": "light-blue",
158+
"toggle": {
159+
"icon": "material/lightbulb",
160+
"name": "Switch to light mode",
161+
},
138162
},
163+
],
164+
# BEGIN: version_dropdown
165+
"version_dropdown": False,
166+
"version_info": [
139167
{
140-
"href": "https://github.com/flux-framework",
141-
"internal": False,
142-
"title": "Flux Framework on GitHub",
168+
"version": "https://sphinx-immaterial.rtfd.io",
169+
"title": "ReadTheDocs",
170+
"aliases": [],
143171
},
144172
{
145-
"href": "https://github.com/flux-framework/flux-restful-api",
146-
"internal": False,
147-
"title": "Flux RESTful API on GitHub",
173+
"version": "https://jbms.github.io/sphinx-immaterial",
174+
"title": "Github Pages",
175+
"aliases": [],
148176
},
149177
],
150-
"heroes": {
151-
"index": "Flux RESTful API",
152-
"customization": "Flux RESTful API",
153-
},
154-
# Include the version dropdown top right? (e.g., if we use readthedocs)
155-
"version_dropdown": False,
156-
# Format of this is dict with [label,path]
157-
# Since we are rendering on gh-pages without readthedocs, we don't
158-
# have versions
159-
# "version_json": "_static/versions.json",
160-
# "version_info": {
161-
# "Release": "https://online-ml.github.io/viz/",
162-
# "Development": "https://online-ml.github.io/viz/devel/",
163-
# "Release (rel)": "/viz/",
164-
# "Development (rel)": "/viz/devel/",
165-
# },
166-
# Do NOT strip these classes from tables!
167-
"table_classes": ["plain"],
178+
# END: version_dropdown
179+
"toc_title_is_page_title": True,
180+
# BEGIN: social icons
181+
"social": [
182+
{
183+
"icon": "fontawesome/brands/github",
184+
"link": "https://github.com/flux-framework/flux-restful-api",
185+
"name": "Flux RESTFul API on GitHub",
186+
},
187+
{
188+
"icon": "material/chart-donut-variant",
189+
"link": "https://flux-framework.org/",
190+
"name": "Flux Framework",
191+
},
192+
],
193+
# END: social icons
168194
}
169195

196+
todo_include_todos = True
197+
sphinx_immaterial_icon_path = html_static_path
198+
sphinx_immaterial_bundle_source_maps = True
199+
170200
if FORCE_CLASSIC:
171201
print("!!!!!!!!! Forcing classic !!!!!!!!!!!")
172202
html_theme = "classic"

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
sphinx_material
1+
sphinx_immaterial
22
numpy>=1.16
33
pandas
44
matplotlib

0 commit comments

Comments
 (0)