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