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