@@ -46,6 +46,24 @@ def reraise_upload_error(
4646 ) from error
4747
4848
49+ def assert_upload_success (result : Any , title : str , host : str ) -> None :
50+ """Raises unless the upload API reports that it stored the file
51+
52+ mwclient raises only when the response carries an 'error' key. A file that
53+ MediaWiki declined for any other reason comes back as a normal return value
54+ with a result other than 'Success', so without this check the upload would
55+ fail silently.
56+ """
57+ status = result .get ("result" ) if isinstance (result , dict ) else None
58+ if status == "Success" :
59+ return
60+ warnings = result .get ("warnings" ) if isinstance (result , dict ) else None
61+ detail = f" Warnings: { warnings } ." if warnings else ""
62+ raise ValueError (
63+ f"Upload of '{ title } ' to { host } did not succeed (result: { status } ).{ detail } "
64+ )
65+
66+
4967class WikiFileController (model .WikiFile , RemoteFileController ):
5068 """File controller for wiki files"""
5169
@@ -168,15 +186,12 @@ def put(self, file: IO, **kwargs: Dict[str, Any]):
168186 }
169187 for key in ["entities" , "namespace" ]:
170188 se_params .pop (key , None ) # avoid duplicated kwargs
171- self .osw .store_entity (
172- OSW .StoreEntityParam (
173- entities = [self .cast (model .WikiFile , ** wf_params )],
174- namespace = self .namespace ,
175- ** se_params ,
176- )
177- )
189+ # Upload before storing the entity: MediaWiki offers no transaction
190+ # across the two writes, so one of them can be left standing. A file
191+ # page without metadata is visibly incomplete, while metadata without a
192+ # file looks like a valid entity until someone tries to download it.
178193 try :
179- self .osw .mw_site .upload (
194+ result = self .osw .mw_site .upload (
180195 file = file ,
181196 filename = self .title ,
182197 # comment="",
@@ -185,6 +200,14 @@ def put(self, file: IO, **kwargs: Dict[str, Any]):
185200 )
186201 except mwclient .errors .APIError as e :
187202 reraise_upload_error (e , self .osw .mw_site , self .title , self .suffix )
203+ assert_upload_success (result , self .title , self .osw .mw_site .host )
204+ self .osw .store_entity (
205+ OSW .StoreEntityParam (
206+ entities = [self .cast (model .WikiFile , ** wf_params )],
207+ namespace = self .namespace ,
208+ ** se_params ,
209+ )
210+ )
188211
189212 def put_from (self , other : FileController , ** kwargs : Dict [str , Any ]):
190213 # if isinstance(file, LocalFileController) and self.suffix is None:
0 commit comments