22import logging
33import os
44from typing import IO , Any , Dict , List , Optional
5+ from urllib .parse import urlparse
56
67import mwclient .errors
8+ from oold .backend .auth import get_credential as _get_registered_credential
79
10+ from osw .auth import CredentialManager
811from osw .controller .file .base import FileController
912from osw .controller .file .remote import RemoteFileController
1013from osw .core import OSW , model
@@ -79,13 +82,42 @@ def store_params_for_upload(
7982 return {** se_params , "overwrite" : "replace remote" }
8083
8184
85+ _sessions : Dict [str , OSW ] = {}
86+ """Sessions by host, so several files of one wiki share a connection."""
87+
88+
89+ def session_for (url : str ) -> OSW :
90+ """An OSW session for the host of ``url``, from the registered credential.
91+
92+ The file entity may be stored somewhere other than its bytes, so the
93+ lookup key is the URL the content lives at, not the entity's own IRI.
94+ """
95+ host = urlparse (url ).netloc or url
96+ session = _sessions .get (host )
97+ if session is not None :
98+ return session
99+ try :
100+ credential = _get_registered_credential (url )
101+ except ValueError as error :
102+ raise ValueError (
103+ f"No credential registered for { host } . Pass osw= explicitly or "
104+ "register one with oold.backend.auth.set_credential."
105+ ) from error
106+ cred_mngr = CredentialManager ()
107+ cred_mngr .add_credential (credential )
108+ session = OSW (site = WtSite (WtSite .WtSiteConfig (iri = host , cred_mngr = cred_mngr )))
109+ _sessions [host ] = session
110+ return session
111+
112+
82113class WikiFileController (model .WikiFile , RemoteFileController ):
83114 """File controller for wiki files"""
84115
85116 label : Optional [List [model .Label ]] = [model .Label (text = "Unnamed file" )]
86117 """the label of the file, defaults to 'Unnamed file'"""
87- osw : OSW
88- """the OSW instance to connect with"""
118+ osw : Optional [OSW ] = None
119+ """the OSW instance to connect with. Left unset, it is created on first
120+ use from the credential registered for download_url / access_url."""
89121 namespace : Optional [str ] = "File"
90122 """the namespace of the file, defaults to 'File'"""
91123 title : Optional [str ] = None
@@ -98,7 +130,10 @@ class Config:
98130
99131 @classmethod
100132 def from_other (
101- cls , other : FileController , osw : OSW , ** kwargs : Dict [str , Any ]
133+ cls ,
134+ other : FileController ,
135+ osw : Optional [OSW ] = None ,
136+ ** kwargs : Dict [str , Any ],
102137 ) -> "WikiFileController" :
103138 return super ().from_other (other , osw = osw , ** kwargs )
104139
@@ -177,6 +212,7 @@ def get_to(self, other: "FileController"):
177212 other .put (response .raw )
178213
179214 def put (self , file : IO , ** kwargs : Dict [str , Any ]):
215+ self ._ensure_session ()
180216 # extract file meta information
181217 if hasattr (file , "name" ) and file .name is not None :
182218 name = os .path .basename (file .name )
@@ -235,6 +271,7 @@ def put_from(self, other: FileController, **kwargs: Dict[str, Any]):
235271 super ().put_from (other , ** kwargs )
236272
237273 def delete (self ):
274+ self ._ensure_session ()
238275 file_page_name = f"{ self .namespace } :{ self .title } "
239276 file_page = self .osw .site .get_page (
240277 WtSite .GetPageParam (titles = [file_page_name ])
@@ -244,12 +281,20 @@ def delete(self):
244281
245282 @property
246283 def url (self ):
284+ self ._ensure_session ()
247285 return (
248286 f"{ self .osw .mw_site .scheme } ://{ self .osw .mw_site .host } "
249287 f"/wiki/File:{ self .title } "
250288 )
251289
290+ def _ensure_session (self ) -> OSW :
291+ """The session, connecting on first use when none was passed in."""
292+ if self .osw is None :
293+ self .osw = session_for (self ._url ())
294+ return self .osw
295+
252296 def _init (self , name = None , suffix = None ):
297+ self ._ensure_session ()
253298 # set the name attribute to the actual file name, e.g., "image.png"
254299 if self .name is None :
255300 self .name = name
0 commit comments