1+ from dataclasses import dataclass
12from typing import Any , Iterable , List , Optional , Tuple , AsyncIterator , Union
23from filesender .download import files_from_page , DownloadFile
34import filesender .response_types as response
@@ -99,13 +100,41 @@ def iter_files(paths: Iterable[Path], root: Optional[Path] = None) -> Iterable[T
99100 # If this is a nested file, use the relative path from the root directory as the name
100101 yield str (path .relative_to (root )), path
101102
103+ @dataclass
104+ class EndpointHandler :
105+ base : str
106+
107+ def api (self ) -> str :
108+ return f"{ self .base } /rest.php"
109+
110+ def download (self ) -> str :
111+ return f"{ self .base } /download.php"
112+
113+ def create_transfer (self ) -> str :
114+ return f"{ self .api ()} /transfer"
115+
116+ def single_transfer (self , transfer_id : int ) -> str :
117+ return f"{ self .api ()} /transfer/{ transfer_id } "
118+
119+ def chunk (self , file_id : int , offset : int ) -> str :
120+ return f"{ self .api ()} /file/{ file_id } /chunk/{ offset } "
121+
122+ def file (self , file_id : int ) -> str :
123+ return f"{ self .api ()} /file/{ file_id } "
124+
125+ def guest (self ) -> str :
126+ return f"{ self .api ()} /guest"
127+
128+ def server_info (self ) -> str :
129+ return f"{ self .api ()} /info"
130+
102131class FileSenderClient :
103132 """
104133 A client that can be used to programmatically interact with FileSender.
105134 """
106135
107136 #: The base url of the file sender's API. For example https://filesender.aarnet.edu.au/rest.php
108- base_url : str
137+ urls : EndpointHandler
109138 #: Size of upload chunks
110139 chunk_size : Optional [int ]
111140 #: Authentication provider that will be used for all privileged requests
@@ -141,14 +170,16 @@ def __init__(
141170 speed up transfers, or reduce this number to reduce memory usage and network errors.
142171 This can be set to `None` to enable unlimited concurrency, but use at your own risk.
143172 """
144- self .base_url = base_url
173+ # self.base_url = base_url
174+ self .urls = EndpointHandler (base_url )
145175 self .auth = auth
146176 # FileSender seems to sometimes use redirects
147177 self .http_client = AsyncClient (timeout = None , follow_redirects = True )
148178 self .chunk_size = chunk_size
149179 self .concurrent_chunks = concurrent_chunks
150180 self .concurrent_files = concurrent_files
151181
182+
152183 async def prepare (self ) -> None :
153184 """
154185 Checks that the chunk size is appropriate and/or sets the chunk size based on the server info.
@@ -177,7 +208,7 @@ def on_retry(state: RetryCallState) -> None:
177208 if e is not None :
178209 message = exception_to_message (e )
179210
180- logger .warn (f"Attempt { state .attempt_number } . { message } " )
211+ logger .warning (f"Attempt { state .attempt_number } . { message } " )
181212
182213 @retry (
183214 retry = retry_if_exception (should_retry ),
@@ -209,7 +240,7 @@ async def create_transfer(
209240 return await self ._sign_send (
210241 self .http_client .build_request (
211242 "POST" ,
212- f" { self .base_url } /transfer" ,
243+ self .urls . create_transfer () ,
213244 json = body ,
214245 )
215246 )
@@ -228,12 +259,12 @@ async def update_transfer(
228259 body: See [`TransferUpdate`][filesender.request_types.TransferUpdate]
229260
230261 Returns:
231- : See [`Transfer`][filesender.response_types.Transfer]
262+ See [`Transfer`][filesender.response_types.Transfer]
232263 """
233264 return await self ._sign_send (
234265 self .http_client .build_request (
235266 "PUT" ,
236- f" { self .base_url } /transfer/ { transfer_id } " ,
267+ self .urls . single_transfer ( transfer_id ) ,
237268 json = body ,
238269 )
239270 )
@@ -254,7 +285,7 @@ async def update_file(
254285 await self ._sign_send (
255286 self .http_client .build_request (
256287 "PUT" ,
257- f" { self .base_url } / file/ { file_info ['id' ]} " ,
288+ self .urls . file ( file_info ['id' ]) ,
258289 params = {"key" : file_info ["uid" ]},
259290 json = body ,
260291 )
@@ -300,7 +331,7 @@ async def _upload_chunk(
300331 return await self ._sign_send (
301332 self .http_client .build_request (
302333 "PUT" ,
303- f" { self .base_url } /file/ { file_info ['id' ] } /chunk/ { offset } " ,
334+ self .urls . chunk ( file_info ["id" ], offset ) ,
304335 params = {"key" : file_info ["uid" ]},
305336 content = chunk ,
306337 headers = {
@@ -323,15 +354,15 @@ async def create_guest(self, body: request.Guest) -> response.Guest:
323354 : See [`Guest`][filesender.response_types.Guest]
324355 """
325356 return await self ._sign_send (
326- self .http_client .build_request ("POST" , f" { self .base_url } / guest" , json = body )
357+ self .http_client .build_request ("POST" , self .urls . guest () , json = body )
327358 )
328359
329360 async def _files_from_token (self , token : str ) -> Iterable [DownloadFile ]:
330361 """
331362 Internal function that returns a list of file IDs for a given guest token
332363 """
333364 download_page = await self .http_client .get (
334- "https://filesender.aarnet.edu.au" , params = {"s" : "download" , "token" : token }
365+ self . urls . base , params = {"s" : "download" , "token" : token }
335366 )
336367 return files_from_page (download_page .content )
337368
@@ -377,11 +408,8 @@ async def download_file(
377408 file_size: The file size in bytes, optionally.
378409 file_name: The file name of the file being downloaded. This will impact the name by which it's saved.
379410 """
380- download_endpoint = urlunparse (
381- urlparse (self .base_url )._replace (path = "/download.php" )
382- )
383411 async with self .http_client .stream (
384- "GET" , download_endpoint , params = {"files_ids" : file_id , "token" : token }
412+ "GET" , self . urls . download () , params = {"files_ids" : file_id , "token" : token }
385413 ) as res :
386414 # Determine filename from response, if not provided
387415 if file_name is None :
@@ -411,7 +439,7 @@ async def get_server_info(self) -> response.ServerInfo:
411439 Returns:
412440 : See [`ServerInfo`][filesender.response_types.ServerInfo].
413441 """
414- return (await self .http_client .get (f" { self .base_url } /info" )).json ()
442+ return (await self .http_client .get (self .urls . server_info () )).json ()
415443
416444 async def upload_workflow (
417445 self , files : List [Path ], transfer_args : request .PartialTransfer = {}
0 commit comments