1# ------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for 4# license information. 5# -------------------------------------------------------------------------- 6# pylint: disable=too-many-lines, too-many-public-methods 7import functools 8import time 9from io import BytesIO 10from typing import ( # pylint: disable=unused-import 11 Optional, Union, IO, List, Dict, Any, Iterable, Tuple, 12 TYPE_CHECKING 13) 14 15 16try: 17 from urllib.parse import urlparse, quote, unquote 18except ImportError: 19 from urlparse import urlparse # type: ignore 20 from urllib2 import quote, unquote # type: ignore 21 22import six 23from azure.core.exceptions import HttpResponseError 24from azure.core.paging import ItemPaged # pylint: disable=ungrouped-imports 25from azure.core.tracing.decorator import distributed_trace 26 27from ._generated import AzureFileStorage 28from ._generated.models import FileHTTPHeaders 29from ._shared.uploads import IterStreamer, FileChunkUploader, upload_data_chunks 30from ._shared.base_client import StorageAccountHostsMixin, parse_connection_str, parse_query 31from ._shared.request_handlers import add_metadata_headers, get_length 32from ._shared.response_handlers import return_response_headers, process_storage_error 33from ._shared.parser import _str 34from ._parser import _get_file_permission, _datetime_to_str 35from ._lease import ShareLeaseClient 36from ._serialize import get_source_conditions, get_access_conditions, get_smb_properties, get_api_version 37from ._deserialize import deserialize_file_properties, deserialize_file_stream, get_file_ranges_result 38from ._models import HandlesPaged, NTFSAttributes # pylint: disable=unused-import 39from ._download import StorageStreamDownloader 40 41if TYPE_CHECKING: 42 from datetime import datetime 43 from ._models import ShareProperties, ContentSettings, FileProperties, Handle 44 from ._generated.models import HandleItem 45 46 47def _upload_file_helper( 48 client, 49 stream, 50 size, 51 metadata, 52 content_settings, 53 validate_content, 54 timeout, 55 max_concurrency, 56 file_settings, 57 file_attributes="none", 58 file_creation_time="now", 59 file_last_write_time="now", 60 file_permission=None, 61 file_permission_key=None, 62 **kwargs): 63 try: 64 if size is None or size < 0: 65 raise ValueError("A content size must be specified for a File.") 66 response = client.create_file( 67 size, 68 content_settings=content_settings, 69 metadata=metadata, 70 timeout=timeout, 71 file_attributes=file_attributes, 72 file_creation_time=file_creation_time, 73 file_last_write_time=file_last_write_time, 74 file_permission=file_permission, 75 permission_key=file_permission_key, 76 **kwargs 77 ) 78 if size == 0: 79 return response 80 81 responses = upload_data_chunks( 82 service=client, 83 uploader_class=FileChunkUploader, 84 total_size=size, 85 chunk_size=file_settings.max_range_size, 86 stream=stream, 87 max_concurrency=max_concurrency, 88 validate_content=validate_content, 89 timeout=timeout, 90 **kwargs 91 ) 92 return sorted(responses, key=lambda r: r.get('last_modified'))[-1] 93 except HttpResponseError as error: 94 process_storage_error(error) 95 96 97class ShareFileClient(StorageAccountHostsMixin): 98 """A client to interact with a specific file, although that file may not yet exist. 99 100 :param str account_url: 101 The URI to the storage account. In order to create a client given the full URI to the 102 file, use the :func:`from_file_url` classmethod. 103 :param share_name: 104 The name of the share for the file. 105 :type share_name: str 106 :param str file_path: 107 The file path to the file with which to interact. If specified, this value will override 108 a file value specified in the file URL. 109 :param str snapshot: 110 An optional file snapshot on which to operate. This can be the snapshot ID string 111 or the response returned from :func:`ShareClient.create_snapshot`. 112 :param credential: 113 The credential with which to authenticate. This is optional if the 114 account URL already has a SAS token. The value can be a SAS token string, 115 an instance of a AzureSasCredential from azure.core.credentials or an account 116 shared access key. 117 :keyword str api_version: 118 The Storage API version to use for requests. Default value is '2019-07-07'. 119 Setting to an older version may result in reduced feature compatibility. 120 121 .. versionadded:: 12.1.0 122 123 :keyword str secondary_hostname: 124 The hostname of the secondary endpoint. 125 :keyword int max_range_size: The maximum range size used for a file upload. Defaults to 4*1024*1024. 126 """ 127 def __init__( # type: ignore 128 self, account_url, # type: str 129 share_name, # type: str 130 file_path, # type: str 131 snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] 132 credential=None, # type: Optional[Any] 133 **kwargs # type: Any 134 ): 135 # type: (...) -> None 136 try: 137 if not account_url.lower().startswith('http'): 138 account_url = "https://" + account_url 139 except AttributeError: 140 raise ValueError("Account URL must be a string.") 141 parsed_url = urlparse(account_url.rstrip('/')) 142 if not (share_name and file_path): 143 raise ValueError("Please specify a share name and file name.") 144 if not parsed_url.netloc: 145 raise ValueError("Invalid URL: {}".format(account_url)) 146 if hasattr(credential, 'get_token'): 147 raise ValueError("Token credentials not supported by the File service.") 148 149 path_snapshot = None 150 path_snapshot, sas_token = parse_query(parsed_url.query) 151 if not sas_token and not credential: 152 raise ValueError( 153 'You need to provide either an account shared key or SAS token when creating a storage service.') 154 try: 155 self.snapshot = snapshot.snapshot # type: ignore 156 except AttributeError: 157 try: 158 self.snapshot = snapshot['snapshot'] # type: ignore 159 except TypeError: 160 self.snapshot = snapshot or path_snapshot 161 162 self.share_name = share_name 163 self.file_path = file_path.split('/') 164 self.file_name = self.file_path[-1] 165 self.directory_path = "/".join(self.file_path[:-1]) 166 167 self._query_str, credential = self._format_query_string( 168 sas_token, credential, share_snapshot=self.snapshot) 169 super(ShareFileClient, self).__init__(parsed_url, service='file-share', credential=credential, **kwargs) 170 self._client = AzureFileStorage(url=self.url, pipeline=self._pipeline) 171 default_api_version = self._client._config.version # pylint: disable=protected-access 172 self._client._config.version = get_api_version(kwargs, default_api_version) # pylint: disable=protected-access 173 174 @classmethod 175 def from_file_url( 176 cls, file_url, # type: str 177 snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] 178 credential=None, # type: Optional[Any] 179 **kwargs # type: Any 180 ): 181 # type: (...) -> ShareFileClient 182 """A client to interact with a specific file, although that file may not yet exist. 183 184 :param str file_url: The full URI to the file. 185 :param str snapshot: 186 An optional file snapshot on which to operate. This can be the snapshot ID string 187 or the response returned from :func:`ShareClient.create_snapshot`. 188 :param credential: 189 The credential with which to authenticate. This is optional if the 190 account URL already has a SAS token. The value can be a SAS token string, 191 an instance of a AzureSasCredential from azure.core.credentials or an account 192 shared access key. 193 :returns: A File client. 194 :rtype: ~azure.storage.fileshare.ShareFileClient 195 """ 196 try: 197 if not file_url.lower().startswith('http'): 198 file_url = "https://" + file_url 199 except AttributeError: 200 raise ValueError("File URL must be a string.") 201 parsed_url = urlparse(file_url.rstrip('/')) 202 203 if not (parsed_url.netloc and parsed_url.path): 204 raise ValueError("Invalid URL: {}".format(file_url)) 205 account_url = parsed_url.netloc.rstrip('/') + "?" + parsed_url.query 206 207 path_share, _, path_file = parsed_url.path.lstrip('/').partition('/') 208 path_snapshot, _ = parse_query(parsed_url.query) 209 snapshot = snapshot or path_snapshot 210 share_name = unquote(path_share) 211 file_path = '/'.join([unquote(p) for p in path_file.split('/')]) 212 return cls(account_url, share_name, file_path, snapshot, credential, **kwargs) 213 214 def _format_url(self, hostname): 215 """Format the endpoint URL according to the current location 216 mode hostname. 217 """ 218 share_name = self.share_name 219 if isinstance(share_name, six.text_type): 220 share_name = share_name.encode('UTF-8') 221 return "{}://{}/{}/{}{}".format( 222 self.scheme, 223 hostname, 224 quote(share_name), 225 "/".join([quote(p, safe='~') for p in self.file_path]), 226 self._query_str) 227 228 @classmethod 229 def from_connection_string( 230 cls, conn_str, # type: str 231 share_name, # type: str 232 file_path, # type: str 233 snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] 234 credential=None, # type: Optional[Any] 235 **kwargs # type: Any 236 ): 237 # type: (...) -> ShareFileClient 238 """Create ShareFileClient from a Connection String. 239 240 :param str conn_str: 241 A connection string to an Azure Storage account. 242 :param share_name: The name of the share. 243 :type share_name: str 244 :param str file_path: 245 The file path. 246 :param str snapshot: 247 An optional file snapshot on which to operate. This can be the snapshot ID string 248 or the response returned from :func:`ShareClient.create_snapshot`. 249 :param credential: 250 The credential with which to authenticate. This is optional if the 251 account URL already has a SAS token. The value can be a SAS token string, 252 an instance of a AzureSasCredential from azure.core.credentials or an account 253 shared access key. 254 :returns: A File client. 255 :rtype: ~azure.storage.fileshare.ShareFileClient 256 257 .. admonition:: Example: 258 259 .. literalinclude:: ../samples/file_samples_hello_world.py 260 :start-after: [START create_file_client] 261 :end-before: [END create_file_client] 262 :language: python 263 :dedent: 12 264 :caption: Creates the file client with connection string. 265 """ 266 account_url, secondary, credential = parse_connection_str(conn_str, credential, 'file') 267 if 'secondary_hostname' not in kwargs: 268 kwargs['secondary_hostname'] = secondary 269 return cls( 270 account_url, share_name=share_name, file_path=file_path, snapshot=snapshot, credential=credential, **kwargs) 271 272 @distributed_trace 273 def acquire_lease(self, lease_id=None, **kwargs): 274 # type: (Optional[str], **Any) -> ShareLeaseClient 275 """Requests a new lease. 276 277 If the file does not have an active lease, the File 278 Service creates a lease on the blob and returns a new lease. 279 280 :param str lease_id: 281 Proposed lease ID, in a GUID string format. The File Service 282 returns 400 (Invalid request) if the proposed lease ID is not 283 in the correct format. 284 :keyword int timeout: 285 The timeout parameter is expressed in seconds. 286 :returns: A ShareLeaseClient object. 287 :rtype: ~azure.storage.fileshare.ShareLeaseClient 288 289 .. admonition:: Example: 290 291 .. literalinclude:: ../samples/file_samples_client.py 292 :start-after: [START acquire_and_release_lease_on_file] 293 :end-before: [END acquire_and_release_lease_on_file] 294 :language: python 295 :dedent: 12 296 :caption: Acquiring a lease on a file. 297 """ 298 kwargs['lease_duration'] = -1 299 lease = ShareLeaseClient(self, lease_id=lease_id) # type: ignore 300 lease.acquire(**kwargs) 301 return lease 302 303 @distributed_trace 304 def create_file( # type: ignore 305 self, size, # type: int 306 file_attributes="none", # type: Union[str, NTFSAttributes] 307 file_creation_time="now", # type: Union[str, datetime] 308 file_last_write_time="now", # type: Union[str, datetime] 309 file_permission=None, # type: Optional[str] 310 permission_key=None, # type: Optional[str] 311 **kwargs # type: Any 312 ): 313 # type: (...) -> Dict[str, Any] 314 """Creates a new file. 315 316 Note that it only initializes the file with no content. 317 318 :param int size: Specifies the maximum size for the file, 319 up to 1 TB. 320 :param file_attributes: 321 The file system attributes for files and directories. 322 If not set, the default value would be "None" and the attributes will be set to "Archive". 323 Here is an example for when the var type is str: 'Temporary|Archive'. 324 file_attributes value is not case sensitive. 325 :type file_attributes: str or :class:`~azure.storage.fileshare.NTFSAttributes` 326 :param file_creation_time: Creation time for the file 327 Default value: Now. 328 :type file_creation_time: str or ~datetime.datetime 329 :param file_last_write_time: Last write time for the file 330 Default value: Now. 331 :type file_last_write_time: str or ~datetime.datetime 332 :param file_permission: If specified the permission (security 333 descriptor) shall be set for the directory/file. This header can be 334 used if Permission size is <= 8KB, else x-ms-file-permission-key 335 header shall be used. Default value: Inherit. If SDDL is specified as 336 input, it must have owner, group and dacl. Note: Only one of the 337 x-ms-file-permission or x-ms-file-permission-key should be specified. 338 :type file_permission: str 339 :param permission_key: Key of the permission to be set for the 340 directory/file. Note: Only one of the x-ms-file-permission or 341 x-ms-file-permission-key should be specified. 342 :type permission_key: str 343 :keyword ~azure.storage.fileshare.ContentSettings content_settings: 344 ContentSettings object used to set file properties. Used to set content type, encoding, 345 language, disposition, md5, and cache control. 346 :keyword dict(str,str) metadata: 347 Name-value pairs associated with the file as metadata. 348 :keyword lease: 349 Required if the file has an active lease. Value can be a ShareLeaseClient object 350 or the lease ID as a string. 351 352 .. versionadded:: 12.1.0 353 354 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 355 :keyword int timeout: 356 The timeout parameter is expressed in seconds. 357 :returns: File-updated property dict (Etag and last modified). 358 :rtype: dict(str, Any) 359 360 .. admonition:: Example: 361 362 .. literalinclude:: ../samples/file_samples_client.py 363 :start-after: [START create_file] 364 :end-before: [END create_file] 365 :language: python 366 :dedent: 12 367 :caption: Create a file. 368 """ 369 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 370 content_settings = kwargs.pop('content_settings', None) 371 metadata = kwargs.pop('metadata', None) 372 timeout = kwargs.pop('timeout', None) 373 if self.require_encryption and not self.key_encryption_key: 374 raise ValueError("Encryption required but no key was provided.") 375 376 headers = kwargs.pop('headers', {}) 377 headers.update(add_metadata_headers(metadata)) 378 file_http_headers = None 379 if content_settings: 380 file_http_headers = FileHTTPHeaders( 381 file_cache_control=content_settings.cache_control, 382 file_content_type=content_settings.content_type, 383 file_content_md5=bytearray(content_settings.content_md5) if content_settings.content_md5 else None, 384 file_content_encoding=content_settings.content_encoding, 385 file_content_language=content_settings.content_language, 386 file_content_disposition=content_settings.content_disposition 387 ) 388 file_permission = _get_file_permission(file_permission, permission_key, 'Inherit') 389 try: 390 return self._client.file.create( # type: ignore 391 file_content_length=size, 392 metadata=metadata, 393 file_attributes=_str(file_attributes), 394 file_creation_time=_datetime_to_str(file_creation_time), 395 file_last_write_time=_datetime_to_str(file_last_write_time), 396 file_permission=file_permission, 397 file_permission_key=permission_key, 398 file_http_headers=file_http_headers, 399 lease_access_conditions=access_conditions, 400 headers=headers, 401 timeout=timeout, 402 cls=return_response_headers, 403 **kwargs) 404 except HttpResponseError as error: 405 process_storage_error(error) 406 407 @distributed_trace 408 def upload_file( 409 self, data, # type: Any 410 length=None, # type: Optional[int] 411 file_attributes="none", # type: Union[str, NTFSAttributes] 412 file_creation_time="now", # type: Union[str, datetime] 413 file_last_write_time="now", # type: Union[str, datetime] 414 file_permission=None, # type: Optional[str] 415 permission_key=None, # type: Optional[str] 416 **kwargs # type: Any 417 ): 418 # type: (...) -> Dict[str, Any] 419 """Uploads a new file. 420 421 :param Any data: 422 Content of the file. 423 :param int length: 424 Length of the file in bytes. Specify its maximum size, up to 1 TiB. 425 :param file_attributes: 426 The file system attributes for files and directories. 427 If not set, the default value would be "None" and the attributes will be set to "Archive". 428 Here is an example for when the var type is str: 'Temporary|Archive'. 429 file_attributes value is not case sensitive. 430 :type file_attributes: str or ~azure.storage.fileshare.NTFSAttributes 431 :param file_creation_time: Creation time for the file 432 Default value: Now. 433 :type file_creation_time: str or ~datetime.datetime 434 :param file_last_write_time: Last write time for the file 435 Default value: Now. 436 :type file_last_write_time: str or ~datetime.datetime 437 :param file_permission: If specified the permission (security 438 descriptor) shall be set for the directory/file. This header can be 439 used if Permission size is <= 8KB, else x-ms-file-permission-key 440 header shall be used. Default value: Inherit. If SDDL is specified as 441 input, it must have owner, group and dacl. Note: Only one of the 442 x-ms-file-permission or x-ms-file-permission-key should be specified. 443 :type file_permission: str 444 :param permission_key: Key of the permission to be set for the 445 directory/file. Note: Only one of the x-ms-file-permission or 446 x-ms-file-permission-key should be specified. 447 :type permission_key: str 448 :keyword dict(str,str) metadata: 449 Name-value pairs associated with the file as metadata. 450 :keyword ~azure.storage.fileshare.ContentSettings content_settings: 451 ContentSettings object used to set file properties. Used to set content type, encoding, 452 language, disposition, md5, and cache control. 453 :keyword bool validate_content: 454 If true, calculates an MD5 hash for each range of the file. The storage 455 service checks the hash of the content that has arrived with the hash 456 that was sent. This is primarily valuable for detecting bitflips on 457 the wire if using http instead of https as https (the default) will 458 already validate. Note that this MD5 hash is not stored with the 459 file. 460 :keyword int max_concurrency: 461 Maximum number of parallel connections to use. 462 :keyword lease: 463 Required if the file has an active lease. Value can be a ShareLeaseClient object 464 or the lease ID as a string. 465 466 .. versionadded:: 12.1.0 467 468 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 469 :keyword int timeout: 470 The timeout parameter is expressed in seconds. 471 :keyword str encoding: 472 Defaults to UTF-8. 473 :returns: File-updated property dict (Etag and last modified). 474 :rtype: dict(str, Any) 475 476 .. admonition:: Example: 477 478 .. literalinclude:: ../samples/file_samples_client.py 479 :start-after: [START upload_file] 480 :end-before: [END upload_file] 481 :language: python 482 :dedent: 12 483 :caption: Upload a file. 484 """ 485 metadata = kwargs.pop('metadata', None) 486 content_settings = kwargs.pop('content_settings', None) 487 max_concurrency = kwargs.pop('max_concurrency', 1) 488 validate_content = kwargs.pop('validate_content', False) 489 timeout = kwargs.pop('timeout', None) 490 encoding = kwargs.pop('encoding', 'UTF-8') 491 if self.require_encryption or (self.key_encryption_key is not None): 492 raise ValueError("Encryption not supported.") 493 494 if isinstance(data, six.text_type): 495 data = data.encode(encoding) 496 if length is None: 497 length = get_length(data) 498 if isinstance(data, bytes): 499 data = data[:length] 500 501 if isinstance(data, bytes): 502 stream = BytesIO(data) 503 elif hasattr(data, 'read'): 504 stream = data 505 elif hasattr(data, '__iter__'): 506 stream = IterStreamer(data, encoding=encoding) # type: ignore 507 else: 508 raise TypeError("Unsupported data type: {}".format(type(data))) 509 return _upload_file_helper( # type: ignore 510 self, 511 stream, 512 length, 513 metadata, 514 content_settings, 515 validate_content, 516 timeout, 517 max_concurrency, 518 self._config, 519 file_attributes=file_attributes, 520 file_creation_time=file_creation_time, 521 file_last_write_time=file_last_write_time, 522 file_permission=file_permission, 523 file_permission_key=permission_key, 524 **kwargs) 525 526 @distributed_trace 527 def start_copy_from_url(self, source_url, **kwargs): 528 # type: (str, Any) -> Any 529 """Initiates the copying of data from a source URL into the file 530 referenced by the client. 531 532 The status of this copy operation can be found using the `get_properties` 533 method. 534 535 :param str source_url: 536 Specifies the URL of the source file. 537 :keyword str file_permission: 538 If specified the permission (security descriptor) shall be set for the directory/file. 539 This value can be set to "source" to copy the security descriptor from the source file. 540 Otherwise if set, this value will be used to override the source value. If not set, permission value 541 is inherited from the parent directory of the target file. This setting can be 542 used if Permission size is <= 8KB, otherwise permission_key shall be used. 543 If SDDL is specified as input, it must have owner, group and dacl. 544 Note: Only one of the file_permission or permission_key should be specified. 545 546 .. versionadded:: 12.1.0 547 This parameter was introduced in API version '2019-07-07'. 548 549 :keyword str permission_key: 550 Key of the permission to be set for the directory/file. 551 This value can be set to "source" to copy the security descriptor from the source file. 552 Otherwise if set, this value will be used to override the source value. If not set, permission value 553 is inherited from the parent directory of the target file. 554 Note: Only one of the file_permission or permission_key should be specified. 555 556 .. versionadded:: 12.1.0 557 This parameter was introduced in API version '2019-07-07'. 558 559 :keyword file_attributes: 560 This value can be set to "source" to copy file attributes from the source file to the target file, 561 or to clear all attributes, it can be set to "None". Otherwise it can be set to a list of attributes 562 to set on the target file. If this is not set, the default value is "Archive". 563 564 .. versionadded:: 12.1.0 565 This parameter was introduced in API version '2019-07-07'. 566 567 :paramtype file_attributes: str or :class:`~azure.storage.fileshare.NTFSAttributes` 568 :keyword file_creation_time: 569 This value can be set to "source" to copy the creation time from the source file to the target file, 570 or a datetime to set as creation time on the target file. This could also be a string in ISO 8601 format. 571 If this is not set, creation time will be set to the date time value of the creation 572 (or when it was overwritten) of the target file by copy engine. 573 574 .. versionadded:: 12.1.0 575 This parameter was introduced in API version '2019-07-07'. 576 577 :paramtype file_creation_time: str or ~datetime.datetime 578 :keyword file_last_write_time: 579 This value can be set to "source" to copy the last write time from the source file to the target file, or 580 a datetime to set as the last write time on the target file. This could also be a string in ISO 8601 format. 581 If this is not set, value will be the last write time to the file by the copy engine. 582 583 .. versionadded:: 12.1.0 584 This parameter was introduced in API version '2019-07-07'. 585 586 :paramtype file_last_write_time: str or ~datetime.datetime 587 :keyword bool ignore_read_only: 588 Specifies the option to overwrite the target file if it already exists and has read-only attribute set. 589 590 .. versionadded:: 12.1.0 591 This parameter was introduced in API version '2019-07-07'. 592 593 :keyword bool set_archive_attribute: 594 Specifies the option to set the archive attribute on the target file. 595 True means the archive attribute will be set on the target file despite attribute 596 overrides or the source file state. 597 598 .. versionadded:: 12.1.0 599 This parameter was introduced in API version '2019-07-07'. 600 601 :keyword metadata: 602 Name-value pairs associated with the file as metadata. 603 :type metadata: dict(str, str) 604 :keyword lease: 605 Required if the file has an active lease. Value can be a ShareLeaseClient object 606 or the lease ID as a string. 607 608 .. versionadded:: 12.1.0 609 610 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 611 :keyword int timeout: 612 The timeout parameter is expressed in seconds. 613 :rtype: dict(str, Any) 614 615 .. admonition:: Example: 616 617 .. literalinclude:: ../samples/file_samples_client.py 618 :start-after: [START copy_file_from_url] 619 :end-before: [END copy_file_from_url] 620 :language: python 621 :dedent: 12 622 :caption: Copy a file from a URL 623 """ 624 metadata = kwargs.pop('metadata', None) 625 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 626 timeout = kwargs.pop('timeout', None) 627 headers = kwargs.pop('headers', {}) 628 headers.update(add_metadata_headers(metadata)) 629 kwargs.update(get_smb_properties(kwargs)) 630 try: 631 return self._client.file.start_copy( 632 source_url, 633 metadata=metadata, 634 lease_access_conditions=access_conditions, 635 headers=headers, 636 cls=return_response_headers, 637 timeout=timeout, 638 **kwargs) 639 except HttpResponseError as error: 640 process_storage_error(error) 641 642 def abort_copy(self, copy_id, **kwargs): 643 # type: (Union[str, FileProperties], Any) -> None 644 """Abort an ongoing copy operation. 645 646 This will leave a destination file with zero length and full metadata. 647 This will raise an error if the copy operation has already ended. 648 649 :param copy_id: 650 The copy operation to abort. This can be either an ID, or an 651 instance of FileProperties. 652 :type copy_id: str or ~azure.storage.fileshare.FileProperties 653 :keyword lease: 654 Required if the file has an active lease. Value can be a ShareLeaseClient object 655 or the lease ID as a string. 656 657 .. versionadded:: 12.1.0 658 659 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 660 :keyword int timeout: 661 The timeout parameter is expressed in seconds. 662 :rtype: None 663 """ 664 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 665 timeout = kwargs.pop('timeout', None) 666 try: 667 copy_id = copy_id.copy.id 668 except AttributeError: 669 try: 670 copy_id = copy_id['copy_id'] 671 except TypeError: 672 pass 673 try: 674 self._client.file.abort_copy(copy_id=copy_id, 675 lease_access_conditions=access_conditions, 676 timeout=timeout, **kwargs) 677 except HttpResponseError as error: 678 process_storage_error(error) 679 680 @distributed_trace 681 def download_file( 682 self, offset=None, # type: Optional[int] 683 length=None, # type: Optional[int] 684 **kwargs 685 ): 686 # type: (...) -> Iterable[bytes] 687 """Downloads a file to a stream with automatic chunking. 688 689 :param int offset: 690 Start of byte range to use for downloading a section of the file. 691 Must be set if length is provided. 692 :param int length: 693 Number of bytes to read from the stream. This is optional, but 694 should be supplied for optimal performance. 695 :keyword int max_concurrency: 696 Maximum number of parallel connections to use. 697 :keyword bool validate_content: 698 If true, calculates an MD5 hash for each chunk of the file. The storage 699 service checks the hash of the content that has arrived with the hash 700 that was sent. This is primarily valuable for detecting bitflips on 701 the wire if using http instead of https as https (the default) will 702 already validate. Note that this MD5 hash is not stored with the 703 file. Also note that if enabled, the memory-efficient upload algorithm 704 will not be used, because computing the MD5 hash requires buffering 705 entire blocks, and doing so defeats the purpose of the memory-efficient algorithm. 706 :keyword lease: 707 Required if the file has an active lease. Value can be a ShareLeaseClient object 708 or the lease ID as a string. 709 710 .. versionadded:: 12.1.0 711 712 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 713 :keyword int timeout: 714 The timeout parameter is expressed in seconds. 715 :returns: A iterable data generator (stream) 716 717 .. admonition:: Example: 718 719 .. literalinclude:: ../samples/file_samples_client.py 720 :start-after: [START download_file] 721 :end-before: [END download_file] 722 :language: python 723 :dedent: 12 724 :caption: Download a file. 725 """ 726 if self.require_encryption or (self.key_encryption_key is not None): 727 raise ValueError("Encryption not supported.") 728 if length is not None and offset is None: 729 raise ValueError("Offset value must not be None if length is set.") 730 731 range_end = None 732 if length is not None: 733 range_end = offset + length - 1 # Service actually uses an end-range inclusive index 734 735 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 736 737 return StorageStreamDownloader( 738 client=self._client.file, 739 config=self._config, 740 start_range=offset, 741 end_range=range_end, 742 encryption_options=None, 743 name=self.file_name, 744 path='/'.join(self.file_path), 745 share=self.share_name, 746 lease_access_conditions=access_conditions, 747 cls=deserialize_file_stream, 748 **kwargs) 749 750 @distributed_trace 751 def delete_file(self, **kwargs): 752 # type: (Any) -> None 753 """Marks the specified file for deletion. The file is 754 later deleted during garbage collection. 755 756 :keyword lease: 757 Required if the file has an active lease. Value can be a ShareLeaseClient object 758 or the lease ID as a string. 759 760 .. versionadded:: 12.1.0 761 762 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 763 :keyword int timeout: 764 The timeout parameter is expressed in seconds. 765 :rtype: None 766 767 .. admonition:: Example: 768 769 .. literalinclude:: ../samples/file_samples_client.py 770 :start-after: [START delete_file] 771 :end-before: [END delete_file] 772 :language: python 773 :dedent: 12 774 :caption: Delete a file. 775 """ 776 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 777 timeout = kwargs.pop('timeout', None) 778 try: 779 self._client.file.delete(lease_access_conditions=access_conditions, timeout=timeout, **kwargs) 780 except HttpResponseError as error: 781 process_storage_error(error) 782 783 @distributed_trace 784 def get_file_properties(self, **kwargs): 785 # type: (Any) -> FileProperties 786 """Returns all user-defined metadata, standard HTTP properties, and 787 system properties for the file. 788 789 :keyword lease: 790 Required if the file has an active lease. Value can be a ShareLeaseClient object 791 or the lease ID as a string. 792 793 .. versionadded:: 12.1.0 794 795 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 796 :keyword int timeout: 797 The timeout parameter is expressed in seconds. 798 :returns: FileProperties 799 :rtype: ~azure.storage.fileshare.FileProperties 800 """ 801 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 802 timeout = kwargs.pop('timeout', None) 803 try: 804 file_props = self._client.file.get_properties( 805 sharesnapshot=self.snapshot, 806 lease_access_conditions=access_conditions, 807 timeout=timeout, 808 cls=deserialize_file_properties, 809 **kwargs) 810 except HttpResponseError as error: 811 process_storage_error(error) 812 file_props.name = self.file_name 813 file_props.share = self.share_name 814 file_props.snapshot = self.snapshot 815 file_props.path = '/'.join(self.file_path) 816 return file_props # type: ignore 817 818 @distributed_trace 819 def set_http_headers(self, content_settings, # type: ContentSettings 820 file_attributes="preserve", # type: Union[str, NTFSAttributes] 821 file_creation_time="preserve", # type: Union[str, datetime] 822 file_last_write_time="preserve", # type: Union[str, datetime] 823 file_permission=None, # type: Optional[str] 824 permission_key=None, # type: Optional[str] 825 **kwargs # type: Any 826 ): 827 # type: (...) -> Dict[str, Any] 828 """Sets HTTP headers on the file. 829 830 :param ~azure.storage.fileshare.ContentSettings content_settings: 831 ContentSettings object used to set file properties. Used to set content type, encoding, 832 language, disposition, md5, and cache control. 833 :param file_attributes: 834 The file system attributes for files and directories. 835 If not set, indicates preservation of existing values. 836 Here is an example for when the var type is str: 'Temporary|Archive' 837 :type file_attributes: str or :class:`~azure.storage.fileshare.NTFSAttributes` 838 :param file_creation_time: Creation time for the file 839 Default value: Preserve. 840 :type file_creation_time: str or ~datetime.datetime 841 :param file_last_write_time: Last write time for the file 842 Default value: Preserve. 843 :type file_last_write_time: str or ~datetime.datetime 844 :param file_permission: If specified the permission (security 845 descriptor) shall be set for the directory/file. This header can be 846 used if Permission size is <= 8KB, else x-ms-file-permission-key 847 header shall be used. Default value: Inherit. If SDDL is specified as 848 input, it must have owner, group and dacl. Note: Only one of the 849 x-ms-file-permission or x-ms-file-permission-key should be specified. 850 :type file_permission: str 851 :param permission_key: Key of the permission to be set for the 852 directory/file. Note: Only one of the x-ms-file-permission or 853 x-ms-file-permission-key should be specified. 854 :type permission_key: str 855 :keyword lease: 856 Required if the file has an active lease. Value can be a ShareLeaseClient object 857 or the lease ID as a string. 858 859 .. versionadded:: 12.1.0 860 861 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 862 :keyword int timeout: 863 The timeout parameter is expressed in seconds. 864 :returns: File-updated property dict (Etag and last modified). 865 :rtype: dict(str, Any) 866 """ 867 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 868 timeout = kwargs.pop('timeout', None) 869 file_content_length = kwargs.pop('size', None) 870 file_http_headers = FileHTTPHeaders( 871 file_cache_control=content_settings.cache_control, 872 file_content_type=content_settings.content_type, 873 file_content_md5=bytearray(content_settings.content_md5) if content_settings.content_md5 else None, 874 file_content_encoding=content_settings.content_encoding, 875 file_content_language=content_settings.content_language, 876 file_content_disposition=content_settings.content_disposition 877 ) 878 file_permission = _get_file_permission(file_permission, permission_key, 'preserve') 879 try: 880 return self._client.file.set_http_headers( # type: ignore 881 file_content_length=file_content_length, 882 file_http_headers=file_http_headers, 883 file_attributes=_str(file_attributes), 884 file_creation_time=_datetime_to_str(file_creation_time), 885 file_last_write_time=_datetime_to_str(file_last_write_time), 886 file_permission=file_permission, 887 file_permission_key=permission_key, 888 lease_access_conditions=access_conditions, 889 timeout=timeout, 890 cls=return_response_headers, 891 **kwargs) 892 except HttpResponseError as error: 893 process_storage_error(error) 894 895 @distributed_trace 896 def set_file_metadata(self, metadata=None, **kwargs): 897 # type: (Optional[Dict[str, Any]], Any) -> Dict[str, Any] 898 """Sets user-defined metadata for the specified file as one or more 899 name-value pairs. 900 901 Each call to this operation replaces all existing metadata 902 attached to the file. To remove all metadata from the file, 903 call this operation with no metadata dict. 904 905 :param metadata: 906 Name-value pairs associated with the file as metadata. 907 :type metadata: dict(str, str) 908 :keyword lease: 909 Required if the file has an active lease. Value can be a ShareLeaseClient object 910 or the lease ID as a string. 911 912 .. versionadded:: 12.1.0 913 914 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 915 :keyword int timeout: 916 The timeout parameter is expressed in seconds. 917 :returns: File-updated property dict (Etag and last modified). 918 :rtype: dict(str, Any) 919 """ 920 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 921 timeout = kwargs.pop('timeout', None) 922 headers = kwargs.pop('headers', {}) 923 headers.update(add_metadata_headers(metadata)) # type: ignore 924 try: 925 return self._client.file.set_metadata( # type: ignore 926 timeout=timeout, 927 cls=return_response_headers, 928 headers=headers, 929 metadata=metadata, 930 lease_access_conditions=access_conditions, 931 **kwargs) 932 except HttpResponseError as error: 933 process_storage_error(error) 934 935 @distributed_trace 936 def upload_range( # type: ignore 937 self, data, # type: bytes 938 offset, # type: int 939 length, # type: int 940 **kwargs 941 ): 942 # type: (...) -> Dict[str, Any] 943 """Upload a range of bytes to a file. 944 945 :param bytes data: 946 The data to upload. 947 :param int offset: 948 Start of byte range to use for uploading a section of the file. 949 The range can be up to 4 MB in size. 950 :param int length: 951 Number of bytes to use for uploading a section of the file. 952 The range can be up to 4 MB in size. 953 :keyword bool validate_content: 954 If true, calculates an MD5 hash of the page content. The storage 955 service checks the hash of the content that has arrived 956 with the hash that was sent. This is primarily valuable for detecting 957 bitflips on the wire if using http instead of https as https (the default) 958 will already validate. Note that this MD5 hash is not stored with the 959 file. 960 :keyword lease: 961 Required if the file has an active lease. Value can be a ShareLeaseClient object 962 or the lease ID as a string. 963 964 .. versionadded:: 12.1.0 965 966 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 967 :keyword int timeout: 968 The timeout parameter is expressed in seconds. 969 :keyword str encoding: 970 Defaults to UTF-8. 971 :returns: File-updated property dict (Etag and last modified). 972 :rtype: Dict[str, Any] 973 """ 974 validate_content = kwargs.pop('validate_content', False) 975 timeout = kwargs.pop('timeout', None) 976 encoding = kwargs.pop('encoding', 'UTF-8') 977 if self.require_encryption or (self.key_encryption_key is not None): 978 raise ValueError("Encryption not supported.") 979 if isinstance(data, six.text_type): 980 data = data.encode(encoding) 981 982 end_range = offset + length - 1 # Reformat to an inclusive range index 983 content_range = 'bytes={0}-{1}'.format(offset, end_range) 984 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 985 try: 986 return self._client.file.upload_range( # type: ignore 987 range=content_range, 988 content_length=length, 989 optionalbody=data, 990 timeout=timeout, 991 validate_content=validate_content, 992 lease_access_conditions=access_conditions, 993 cls=return_response_headers, 994 **kwargs) 995 except HttpResponseError as error: 996 process_storage_error(error) 997 998 @staticmethod 999 def _upload_range_from_url_options(source_url, # type: str 1000 offset, # type: int 1001 length, # type: int 1002 source_offset, # type: int 1003 **kwargs # type: Any 1004 ): 1005 # type: (...) -> Dict[str, Any] 1006 1007 if offset is None: 1008 raise ValueError("offset must be provided.") 1009 if length is None: 1010 raise ValueError("length must be provided.") 1011 if source_offset is None: 1012 raise ValueError("source_offset must be provided.") 1013 1014 # Format range 1015 end_range = offset + length - 1 1016 destination_range = 'bytes={0}-{1}'.format(offset, end_range) 1017 source_range = 'bytes={0}-{1}'.format(source_offset, source_offset + length - 1) 1018 1019 source_mod_conditions = get_source_conditions(kwargs) 1020 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 1021 1022 options = { 1023 'copy_source': source_url, 1024 'content_length': 0, 1025 'source_range': source_range, 1026 'range': destination_range, 1027 'source_modified_access_conditions': source_mod_conditions, 1028 'lease_access_conditions': access_conditions, 1029 'timeout': kwargs.pop('timeout', None), 1030 'cls': return_response_headers} 1031 options.update(kwargs) 1032 return options 1033 1034 @distributed_trace 1035 def upload_range_from_url(self, source_url, 1036 offset, 1037 length, 1038 source_offset, 1039 **kwargs 1040 ): 1041 # type: (str, int, int, int, **Any) -> Dict[str, Any] 1042 """ 1043 Writes the bytes from one Azure File endpoint into the specified range of another Azure File endpoint. 1044 1045 :param int offset: 1046 Start of byte range to use for updating a section of the file. 1047 The range can be up to 4 MB in size. 1048 :param int length: 1049 Number of bytes to use for updating a section of the file. 1050 The range can be up to 4 MB in size. 1051 :param str source_url: 1052 A URL of up to 2 KB in length that specifies an Azure file or blob. 1053 The value should be URL-encoded as it would appear in a request URI. 1054 If the source is in another account, the source must either be public 1055 or must be authenticated via a shared access signature. If the source 1056 is public, no authentication is required. 1057 Examples: 1058 https://myaccount.file.core.windows.net/myshare/mydir/myfile 1059 https://otheraccount.file.core.windows.net/myshare/mydir/myfile?sastoken 1060 :param int source_offset: 1061 This indicates the start of the range of bytes(inclusive) that has to be taken from the copy source. 1062 The service will read the same number of bytes as the destination range (length-offset). 1063 :keyword ~datetime.datetime source_if_modified_since: 1064 A DateTime value. Azure expects the date value passed in to be UTC. 1065 If timezone is included, any non-UTC datetimes will be converted to UTC. 1066 If a date is passed in without timezone info, it is assumed to be UTC. 1067 Specify this conditional header to copy the blob only if the source 1068 blob has been modified since the specified date/time. 1069 :keyword ~datetime.datetime source_if_unmodified_since: 1070 A DateTime value. Azure expects the date value passed in to be UTC. 1071 If timezone is included, any non-UTC datetimes will be converted to UTC. 1072 If a date is passed in without timezone info, it is assumed to be UTC. 1073 Specify this conditional header to copy the blob only if the source blob 1074 has not been modified since the specified date/time. 1075 :keyword str source_etag: 1076 The source ETag value, or the wildcard character (*). Used to check if the resource has changed, 1077 and act according to the condition specified by the `match_condition` parameter. 1078 :keyword ~azure.core.MatchConditions source_match_condition: 1079 The source match condition to use upon the etag. 1080 :keyword lease: 1081 Required if the file has an active lease. Value can be a ShareLeaseClient object 1082 or the lease ID as a string. 1083 1084 .. versionadded:: 12.1.0 1085 1086 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 1087 :keyword int timeout: 1088 The timeout parameter is expressed in seconds. 1089 """ 1090 options = self._upload_range_from_url_options( 1091 source_url=source_url, 1092 offset=offset, 1093 length=length, 1094 source_offset=source_offset, 1095 **kwargs 1096 ) 1097 try: 1098 return self._client.file.upload_range_from_url(**options) # type: ignore 1099 except HttpResponseError as error: 1100 process_storage_error(error) 1101 1102 def _get_ranges_options( # type: ignore 1103 self, offset=None, # type: Optional[int] 1104 length=None, # type: Optional[int] 1105 previous_sharesnapshot=None, # type: Optional[Union[str, Dict[str, Any]]] 1106 **kwargs 1107 ): 1108 # type: (...) -> Dict[str, Any] 1109 if self.require_encryption or (self.key_encryption_key is not None): 1110 raise ValueError("Unsupported method for encryption.") 1111 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 1112 1113 content_range = None 1114 if offset is not None: 1115 if length is not None: 1116 end_range = offset + length - 1 # Reformat to an inclusive range index 1117 content_range = 'bytes={0}-{1}'.format(offset, end_range) 1118 else: 1119 content_range = 'bytes={0}-'.format(offset) 1120 options = { 1121 'sharesnapshot': self.snapshot, 1122 'lease_access_conditions': access_conditions, 1123 'timeout': kwargs.pop('timeout', None), 1124 'range': content_range} 1125 if previous_sharesnapshot: 1126 try: 1127 options['prevsharesnapshot'] = previous_sharesnapshot.snapshot # type: ignore 1128 except AttributeError: 1129 try: 1130 options['prevsharesnapshot'] = previous_sharesnapshot['snapshot'] # type: ignore 1131 except TypeError: 1132 options['prevsharesnapshot'] = previous_sharesnapshot 1133 options.update(kwargs) 1134 return options 1135 1136 @distributed_trace 1137 def get_ranges( # type: ignore 1138 self, offset=None, # type: Optional[int] 1139 length=None, # type: Optional[int] 1140 **kwargs # type: Any 1141 ): 1142 # type: (...) -> List[Dict[str, int]] 1143 """Returns the list of valid page ranges for a file or snapshot 1144 of a file. 1145 1146 :param int offset: 1147 Specifies the start offset of bytes over which to get ranges. 1148 :param int length: 1149 Number of bytes to use over which to get ranges. 1150 :keyword lease: 1151 Required if the file has an active lease. Value can be a ShareLeaseClient object 1152 or the lease ID as a string. 1153 1154 .. versionadded:: 12.1.0 1155 1156 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 1157 :keyword int timeout: 1158 The timeout parameter is expressed in seconds. 1159 :returns: 1160 A list of valid ranges. 1161 :rtype: List[dict[str, int]] 1162 """ 1163 options = self._get_ranges_options( 1164 offset=offset, 1165 length=length, 1166 **kwargs) 1167 try: 1168 ranges = self._client.file.get_range_list(**options) 1169 except HttpResponseError as error: 1170 process_storage_error(error) 1171 return [{'start': file_range.start, 'end': file_range.end} for file_range in ranges.ranges] 1172 1173 def get_ranges_diff( # type: ignore 1174 self, 1175 previous_sharesnapshot, # type: Union[str, Dict[str, Any]] 1176 offset=None, # type: Optional[int] 1177 length=None, # type: Optional[int] 1178 **kwargs # type: Any 1179 ): 1180 # type: (...) -> Tuple[List[Dict[str, int]], List[Dict[str, int]]] 1181 """Returns the list of valid page ranges for a file or snapshot 1182 of a file. 1183 1184 .. versionadded:: 12.6.0 1185 1186 :param int offset: 1187 Specifies the start offset of bytes over which to get ranges. 1188 :param int length: 1189 Number of bytes to use over which to get ranges. 1190 :param str previous_sharesnapshot: 1191 The snapshot diff parameter that contains an opaque DateTime value that 1192 specifies a previous file snapshot to be compared 1193 against a more recent snapshot or the current file. 1194 :keyword lease: 1195 Required if the file has an active lease. Value can be a ShareLeaseClient object 1196 or the lease ID as a string. 1197 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 1198 :keyword int timeout: 1199 The timeout parameter is expressed in seconds. 1200 :returns: 1201 A tuple of two lists of file ranges as dictionaries with 'start' and 'end' keys. 1202 The first element are filled file ranges, the 2nd element is cleared file ranges. 1203 :rtype: tuple(list(dict(str, str), list(dict(str, str)) 1204 """ 1205 options = self._get_ranges_options( 1206 offset=offset, 1207 length=length, 1208 previous_sharesnapshot=previous_sharesnapshot, 1209 **kwargs) 1210 try: 1211 ranges = self._client.file.get_range_list(**options) 1212 except HttpResponseError as error: 1213 process_storage_error(error) 1214 return get_file_ranges_result(ranges) 1215 1216 @distributed_trace 1217 def clear_range( # type: ignore 1218 self, offset, # type: int 1219 length, # type: int 1220 **kwargs 1221 ): 1222 # type: (...) -> Dict[str, Any] 1223 """Clears the specified range and releases the space used in storage for 1224 that range. 1225 1226 :param int offset: 1227 Start of byte range to use for clearing a section of the file. 1228 The range can be up to 4 MB in size. 1229 :param int length: 1230 Number of bytes to use for clearing a section of the file. 1231 The range can be up to 4 MB in size. 1232 :keyword lease: 1233 Required if the file has an active lease. Value can be a ShareLeaseClient object 1234 or the lease ID as a string. 1235 1236 .. versionadded:: 12.1.0 1237 1238 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 1239 :keyword int timeout: 1240 The timeout parameter is expressed in seconds. 1241 :returns: File-updated property dict (Etag and last modified). 1242 :rtype: Dict[str, Any] 1243 """ 1244 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 1245 timeout = kwargs.pop('timeout', None) 1246 if self.require_encryption or (self.key_encryption_key is not None): 1247 raise ValueError("Unsupported method for encryption.") 1248 1249 if offset is None or offset % 512 != 0: 1250 raise ValueError("offset must be an integer that aligns with 512 bytes file size") 1251 if length is None or length % 512 != 0: 1252 raise ValueError("length must be an integer that aligns with 512 bytes file size") 1253 end_range = length + offset - 1 # Reformat to an inclusive range index 1254 content_range = 'bytes={0}-{1}'.format(offset, end_range) 1255 try: 1256 return self._client.file.upload_range( # type: ignore 1257 timeout=timeout, 1258 cls=return_response_headers, 1259 content_length=0, 1260 optionalbody=None, 1261 file_range_write="clear", 1262 range=content_range, 1263 lease_access_conditions=access_conditions, 1264 **kwargs) 1265 except HttpResponseError as error: 1266 process_storage_error(error) 1267 1268 @distributed_trace 1269 def resize_file(self, size, **kwargs): 1270 # type: (int, Any) -> Dict[str, Any] 1271 """Resizes a file to the specified size. 1272 1273 :param int size: 1274 Size to resize file to (in bytes) 1275 :keyword lease: 1276 Required if the file has an active lease. Value can be a ShareLeaseClient object 1277 or the lease ID as a string. 1278 1279 .. versionadded:: 12.1.0 1280 1281 :paramtype lease: ~azure.storage.fileshare.ShareLeaseClient or str 1282 :keyword int timeout: 1283 The timeout parameter is expressed in seconds. 1284 :returns: File-updated property dict (Etag and last modified). 1285 :rtype: Dict[str, Any] 1286 """ 1287 access_conditions = get_access_conditions(kwargs.pop('lease', None)) 1288 timeout = kwargs.pop('timeout', None) 1289 try: 1290 return self._client.file.set_http_headers( # type: ignore 1291 file_content_length=size, 1292 file_attributes="preserve", 1293 file_creation_time="preserve", 1294 file_last_write_time="preserve", 1295 file_permission="preserve", 1296 lease_access_conditions=access_conditions, 1297 cls=return_response_headers, 1298 timeout=timeout, 1299 **kwargs) 1300 except HttpResponseError as error: 1301 process_storage_error(error) 1302 1303 @distributed_trace 1304 def list_handles(self, **kwargs): 1305 # type: (Any) -> ItemPaged[Handle] 1306 """Lists handles for file. 1307 1308 :keyword int timeout: 1309 The timeout parameter is expressed in seconds. 1310 :returns: An auto-paging iterable of HandleItem 1311 :rtype: ~azure.core.paging.ItemPaged[~azure.storage.fileshare.HandleItem] 1312 """ 1313 timeout = kwargs.pop('timeout', None) 1314 results_per_page = kwargs.pop('results_per_page', None) 1315 command = functools.partial( 1316 self._client.file.list_handles, 1317 sharesnapshot=self.snapshot, 1318 timeout=timeout, 1319 **kwargs) 1320 return ItemPaged( 1321 command, results_per_page=results_per_page, 1322 page_iterator_class=HandlesPaged) 1323 1324 @distributed_trace 1325 def close_handle(self, handle, **kwargs): 1326 # type: (Union[str, HandleItem], Any) -> Dict[str, int] 1327 """Close an open file handle. 1328 1329 :param handle: 1330 A specific handle to close. 1331 :type handle: str or ~azure.storage.fileshare.Handle 1332 :keyword int timeout: 1333 The timeout parameter is expressed in seconds. 1334 :returns: 1335 The number of handles closed (this may be 0 if the specified handle was not found) 1336 and the number of handles failed to close in a dict. 1337 :rtype: dict[str, int] 1338 """ 1339 try: 1340 handle_id = handle.id # type: ignore 1341 except AttributeError: 1342 handle_id = handle 1343 if handle_id == '*': 1344 raise ValueError("Handle ID '*' is not supported. Use 'close_all_handles' instead.") 1345 try: 1346 response = self._client.file.force_close_handles( 1347 handle_id, 1348 marker=None, 1349 sharesnapshot=self.snapshot, 1350 cls=return_response_headers, 1351 **kwargs 1352 ) 1353 return { 1354 'closed_handles_count': response.get('number_of_handles_closed', 0), 1355 'failed_handles_count': response.get('number_of_handles_failed', 0) 1356 } 1357 except HttpResponseError as error: 1358 process_storage_error(error) 1359 1360 @distributed_trace 1361 def close_all_handles(self, **kwargs): 1362 # type: (Any) -> Dict[str, int] 1363 """Close any open file handles. 1364 1365 This operation will block until the service has closed all open handles. 1366 1367 :keyword int timeout: 1368 The timeout parameter is expressed in seconds. 1369 :returns: The number of handles closed (this may be 0 if the specified handle was not found) 1370 and the number of handles failed to close in a dict. 1371 :rtype: dict[str, int] 1372 """ 1373 timeout = kwargs.pop('timeout', None) 1374 start_time = time.time() 1375 1376 try_close = True 1377 continuation_token = None 1378 total_closed = 0 1379 total_failed = 0 1380 while try_close: 1381 try: 1382 response = self._client.file.force_close_handles( 1383 handle_id='*', 1384 timeout=timeout, 1385 marker=continuation_token, 1386 sharesnapshot=self.snapshot, 1387 cls=return_response_headers, 1388 **kwargs 1389 ) 1390 except HttpResponseError as error: 1391 process_storage_error(error) 1392 continuation_token = response.get('marker') 1393 try_close = bool(continuation_token) 1394 total_closed += response.get('number_of_handles_closed', 0) 1395 total_failed += response.get('number_of_handles_failed', 0) 1396 if timeout: 1397 timeout = max(0, timeout - (time.time() - start_time)) 1398 return { 1399 'closed_handles_count': total_closed, 1400 'failed_handles_count': total_failed 1401 } 1402