Open
Description
Sometimes you have to move a bunch of files together to another folder. If you use move_to_using_path concurrently using ThreadPoolExecutor, ClientRequestException is raised.
How to reproduce
import concurrent.futures
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.files.move_operations import MoveOperations
ctx: ClientContext = ClientContext(base_url='https://example.sharepoint.com/sites/tenant').with_user_credentials(
username='test_user', password='test_password')
def main(source_filename):
"""Moves a file from source folder to destination folder.
Args:
source_filename: Name of the file.
"""
ctx.web.get_file_by_server_relative_path(path=f'Shared Documents/{source_filename}').move_to_using_path(
destination='Shared Documents/destination_folder', flag=MoveOperations.overwrite).execute_query()
>>> with concurrent.futures.ThreadPoolExecutor() as executor:
... executor.map(main, ['one.csv', 'two.csv', 'three.csv'])
office365.runtime.client_request_exception.ClientRequestException: ('-2147024809, System.ArgumentException', 'Server relative urls must start with SPWeb.ServerRelativeUrl', "400 Client Error: Bad Request for url: https://example.sharepoint.com/sites/tenant/_api/Web/getFileByServerRelativePath(DecodedUrl='%2Fsites%2Ftenant%2FShared%20Documents%2Fone.csv')/MoveToUsingPath(DecodedUrl='%2Fone.csv',moveOperations=1)")
It will work fine if there is only one thread.
>>> with concurrent.futures.ThreadPoolExecutor() as executor:
... executor.map(main, ['one.csv'])
Workaround
Use copy.deepcopy
to prevent the statefulness of the object from being overwritten by other threads.
def main(source_filename):
"""Moves a file from source folder to destination folder.
Args:
source_filename: Name of the file.
"""
import copy
copy.deepcopy(ctx).web.get_file_by_server_relative_path(path=f'Shared Documents/{source_filename}').move_to_using_path(
destination='Shared Documents/destination_folder', flag=MoveOperations.overwrite).execute_query()