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