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