@@ -131,26 +131,33 @@ async def update_profile(
131131 session : Session = Depends (get_session ),
132132):
133133 avatar_changed = bool (avatar_file and avatar_file .filename )
134- avatar_data : Optional [bytes ] = None
135- avatar_content_type : Optional [str ] = None
136134
137- # Async chunked read must stay on the event loop; sync image/DB work is offloaded.
135+ # Async upload read stays on the event loop. CPU-bound image work is
136+ # offloaded; Session/ORM mutations stay here (do not pass Session into the
137+ # thread pool — see issue #237).
138138 if avatar_changed :
139139 assert avatar_file is not None
140140 reject_oversized_content_length (
141141 request .headers .get ("content-length" ), MAX_AVATAR_UPLOAD_BYTES
142142 )
143143 avatar_data = await read_upload_with_size_limit (avatar_file , MAX_FILE_SIZE )
144- avatar_content_type = avatar_file .content_type
144+ processed_image , content_type = await run_in_threadpool (
145+ validate_and_process_image , avatar_data , avatar_file .content_type
146+ )
147+ if user .avatar :
148+ user .avatar .avatar_data = processed_image
149+ user .avatar .avatar_content_type = content_type
150+ else :
151+ assert user .id is not None
152+ user .avatar = UserAvatar (
153+ user_id = user .id ,
154+ avatar_data = processed_image ,
155+ avatar_content_type = content_type ,
156+ )
145157
146- await run_in_threadpool (
147- _apply_profile_update ,
148- session ,
149- user ,
150- name ,
151- avatar_data ,
152- avatar_content_type ,
153- )
158+ user .name = name
159+ session .commit ()
160+ session .refresh (user )
154161
155162 if is_htmx_request (request ):
156163 response = templates .TemplateResponse (
@@ -176,34 +183,6 @@ async def update_profile(
176183 return RedirectResponse (url = router .url_path_for ("read_profile" ), status_code = 303 )
177184
178185
179- def _apply_profile_update (
180- session : Session ,
181- user : User ,
182- name : Optional [str ],
183- avatar_data : Optional [bytes ],
184- avatar_content_type : Optional [str ],
185- ) -> None :
186- """Sync image processing and DB persistence for update_profile."""
187- if avatar_data is not None :
188- processed_image , content_type = validate_and_process_image (
189- avatar_data , avatar_content_type
190- )
191- if user .avatar :
192- user .avatar .avatar_data = processed_image
193- user .avatar .avatar_content_type = content_type
194- else :
195- assert user .id is not None
196- user .avatar = UserAvatar (
197- user_id = user .id ,
198- avatar_data = processed_image ,
199- avatar_content_type = content_type ,
200- )
201-
202- user .name = name
203- session .commit ()
204- session .refresh (user )
205-
206-
207186@router .post ("/communication-preferences" , response_class = RedirectResponse )
208187def update_communication_preferences (
209188 request : Request ,
0 commit comments