33See /docs (Swagger UI) for full request/response schemas.
44"""
55
6+ import asyncio
7+ import json
68import logging
79
810from fastapi import APIRouter , Query , Request
@@ -122,25 +124,25 @@ async def contributions_get(
122124
123125 if doi :
124126 try :
125- contributions , project_name = _resolve_project ( doi )
127+ contributions , project_name = await asyncio . to_thread ( _resolve_project , doi )
126128 except FileNotFoundError as e :
127129 return JSONResponse (status_code = 404 , content = {"error" : str (e )})
128130 except Exception as e :
129131 _logger .exception ("GET /contributions/get doi=%s" , doi )
130132 return JSONResponse (status_code = 500 , content = {"error" : str (e )})
131133
132- if not verify_project_password ( project_name , password or "" ):
134+ if not await asyncio . to_thread ( verify_project_password , project_name , password or "" ):
133135 return JSONResponse (status_code = 401 , content = {"error" : "Unauthorized" })
134136
135- contributions .locked = is_project_locked ( project_name )
137+ contributions .locked = await asyncio . to_thread ( is_project_locked , project_name )
136138 fmt = format .lower ()
137139 if fmt == "yaml" :
138140 return Response (content = to_yaml (contributions ), media_type = "text/plain; charset=utf-8" )
139141 return Response (content = to_json (contributions ), media_type = "application/json" )
140142
141143 if history == "true" :
142144 try :
143- commits = list_project_commits ( project )
145+ commits = await asyncio . to_thread ( list_project_commits , project )
144146 except FileNotFoundError as e :
145147 return JSONResponse (status_code = 404 , content = {"error" : str (e )})
146148 except Exception as e :
@@ -151,14 +153,14 @@ async def contributions_get(
151153 fmt = format .lower ()
152154
153155 try :
154- contributions = get_contributions ( project , commit_hash = commit )
156+ contributions = await asyncio . to_thread ( get_contributions , project , commit_hash = commit )
155157 except FileNotFoundError as e :
156158 return JSONResponse (status_code = 404 , content = {"error" : str (e )})
157159 except Exception as e :
158160 _logger .exception ("GET /contributions/get project=%s commit=%s" , project , commit )
159161 return JSONResponse (status_code = 500 , content = {"error" : str (e )})
160162
161- contributions .locked = is_project_locked ( project )
163+ contributions .locked = await asyncio . to_thread ( is_project_locked , project )
162164 if fmt == "yaml" :
163165 return Response (content = to_yaml (contributions ), media_type = "text/plain; charset=utf-8" )
164166 return Response (content = to_json (contributions ), media_type = "application/json" )
@@ -206,47 +208,51 @@ async def contributions_post(
206208 new_author_name = None
207209
208210 if password :
209- token_record = lookup_token ( project , password )
211+ token_record = await asyncio . to_thread ( lookup_token , project , password )
210212 if token_record is not None :
211213 token_id = password
212214 token_type = token_record ["token_type" ]
213215 token_author = token_record .get ("author_name" )
214- ok , err = _validate_token_scope (project , token_type , token_author , new_contributions )
216+ ok , err = await asyncio .to_thread (
217+ _validate_token_scope , project , token_type , token_author , new_contributions
218+ )
215219 if not ok :
216220 return JSONResponse (status_code = 403 , content = {"error" : err })
217221 if token_type in ("add_author" , "multi_author" ):
218222 try :
219- existing_pre = get_contributions ( project )
223+ existing_pre = await asyncio . to_thread ( get_contributions , project )
220224 existing_names_pre = {c .author .name for c in existing_pre .contributors }
221225 except FileNotFoundError :
222226 existing_names_pre = set ()
223227 added = {c .author .name for c in new_contributions .contributors } - existing_names_pre
224228 if len (added ) == 1 :
225229 new_author_name = next (iter (added ))
226230 else :
227- if not verify_project_password ( project , password ):
231+ if not await asyncio . to_thread ( verify_project_password , project , password ):
228232 return JSONResponse (status_code = 401 , content = {"error" : "Unauthorized" })
229233 else :
230- if not verify_project_password ( project , "" ):
234+ if not await asyncio . to_thread ( verify_project_password , project , "" ):
231235 return JSONResponse (status_code = 401 , content = {"error" : "Unauthorized" })
232236
233- if password and token_id is None and not is_project_locked ( project ):
234- set_project_password ( project , password )
237+ if password and token_id is None and not await asyncio . to_thread ( is_project_locked , project ):
238+ await asyncio . to_thread ( set_project_password , project , password )
235239
236240 try :
237- commit_hash = store_contributions ( project , new_contributions , message = message )
241+ commit_hash = await asyncio . to_thread ( store_contributions , project , new_contributions , message = message )
238242 except Exception as e :
239243 _logger .exception ("POST /contributions/post project=%s" , project )
240244 return JSONResponse (status_code = 500 , content = {"error" : str (e )})
241245
242246 if token_id and token_type == "add_author" :
243- consume_token ( project , token_id )
247+ await asyncio . to_thread ( consume_token , project , token_id )
244248
245249 response_body = {"commit" : commit_hash , "project" : project }
246250
247251 if new_author_name :
248252 try :
249- existing_edit = find_active_token (project , "edit_author" , author_name = new_author_name )
253+ existing_edit = await asyncio .to_thread (
254+ find_active_token , project , "edit_author" , author_name = new_author_name
255+ )
250256 except Exception :
251257 _logger .exception (
252258 "POST /contributions/post find_active_token project=%s author=%s" ,
@@ -258,7 +264,8 @@ async def contributions_post(
258264 if existing_edit is not None :
259265 edit_token = existing_edit ["token_id" ]
260266 else :
261- edit_token = create_token (
267+ edit_token = await asyncio .to_thread (
268+ create_token ,
262269 project ,
263270 "edit_author" ,
264271 author_name = new_author_name ,
@@ -315,19 +322,21 @@ async def contributions_token(
315322 return JSONResponse (status_code = 400 , content = {"error" : "days must be an integer" })
316323
317324 try :
318- _ , project_name = _resolve_project ( doi )
325+ _ , project_name = await asyncio . to_thread ( _resolve_project , doi )
319326 except FileNotFoundError as e :
320327 return JSONResponse (status_code = 404 , content = {"error" : str (e )})
321328 except Exception as e :
322329 _logger .exception ("GET /contributions/token doi=%s" , doi )
323330 return JSONResponse (status_code = 500 , content = {"error" : str (e )})
324331
325- if not verify_project_password ( project_name , password or "" ):
332+ if not await asyncio . to_thread ( verify_project_password , project_name , password or "" ):
326333 return JSONResponse (status_code = 401 , content = {"error" : "Unauthorized" })
327334
328335 if token_type == "edit_author" :
329336 try :
330- existing = find_active_token (project_name , "edit_author" , author_name = author )
337+ existing = await asyncio .to_thread (
338+ find_active_token , project_name , "edit_author" , author_name = author
339+ )
331340 except Exception :
332341 _logger .exception (
333342 "GET /contributions/token find_active_token project=%s author=%s" ,
@@ -351,7 +360,9 @@ async def contributions_token(
351360 )
352361
353362 try :
354- token_id = create_token (project_name , token_type , author_name = author , expires_days = days )
363+ token_id = await asyncio .to_thread (
364+ create_token , project_name , token_type , author_name = author , expires_days = days
365+ )
355366 except Exception as e :
356367 _logger .exception ("GET /contributions/token create_token project=%s type=%s" , project_name , token_type )
357368 return JSONResponse (status_code = 500 , content = {"error" : str (e )})
@@ -370,7 +381,7 @@ async def contributions_author_image(
370381):
371382 if not author :
372383 return JSONResponse (status_code = 400 , content = {"error" : "author query parameter is required" })
373- key = get_author_image_key ( author )
384+ key = await asyncio . to_thread ( get_author_image_key , author )
374385 if key is None :
375386 return JSONResponse (status_code = 404 , content = {"error" : f"No image found for author '{ author } '" })
376387 return JSONResponse (content = {"author" : author , "image_key" : key })
0 commit comments