Skip to content

Commit d813fa2

Browse files
authored
{0.6.1} Fix paging issue in filedatalake (#44)
* update filedatalake * version * changelog
1 parent 529e0e9 commit d813fa2

19 files changed

+364
-149
lines changed

README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Handles multi-API versions of Azure Storage Data Plane originally from https://g
1717

1818
Change Log
1919
----------
20+
0.6.1
21+
+++++
22+
* Support undelete filesystem
23+
* Fix minor issues in filedatalake
24+
2025
0.6.0
2126
+++++
2227
* blob(12.7.1): Support v2020-04-08

azure/multiapi/storagev2/filedatalake/v2020_02_10/_data_lake_directory_client.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6+
from typing import Any
7+
68
try:
79
from urllib.parse import quote, unquote
810
except ImportError:
@@ -11,7 +13,7 @@
1113
from ._deserialize import deserialize_dir_properties
1214
from ._shared.base_client import TransportWrapper, parse_connection_str
1315
from ._data_lake_file_client import DataLakeFileClient
14-
from ._models import DirectoryProperties
16+
from ._models import DirectoryProperties, FileProperties
1517
from ._path_client import PathClient
1618

1719

@@ -241,9 +243,19 @@ def get_directory_properties(self, **kwargs):
241243
"""
242244
return self._get_path_properties(cls=deserialize_dir_properties, **kwargs) # pylint: disable=protected-access
243245

244-
def rename_directory(self, new_name, # type: str
245-
**kwargs):
246-
# type: (**Any) -> DataLakeDirectoryClient
246+
def exists(self, **kwargs):
247+
# type: (**Any) -> bool
248+
"""
249+
Returns True if a directory exists and returns False otherwise.
250+
251+
:kwarg int timeout:
252+
The timeout parameter is expressed in seconds.
253+
:returns: boolean
254+
"""
255+
return self._exists(**kwargs)
256+
257+
def rename_directory(self, new_name, **kwargs):
258+
# type: (str, **Any) -> DataLakeDirectoryClient
247259
"""
248260
Rename the source directory.
249261
@@ -502,12 +514,12 @@ def get_file_client(self, file # type: Union[FileProperties, str]
502514
or an instance of FileProperties. eg. directory/subdirectory/file
503515
:type file: str or ~azure.storage.filedatalake.FileProperties
504516
:returns: A DataLakeFileClient.
505-
:rtype: ~azure.storage.filedatalake..DataLakeFileClient
517+
:rtype: ~azure.storage.filedatalake.DataLakeFileClient
506518
"""
507519
try:
508-
file_path = file.name
520+
file_path = file.get('name')
509521
except AttributeError:
510-
file_path = self.path_name + '/' + file
522+
file_path = self.path_name + '/' + str(file)
511523

512524
_pipeline = Pipeline(
513525
transport=TransportWrapper(self._pipeline._transport), # pylint: disable = protected-access
@@ -535,9 +547,9 @@ def get_sub_directory_client(self, sub_directory # type: Union[DirectoryPropert
535547
:rtype: ~azure.storage.filedatalake.DataLakeDirectoryClient
536548
"""
537549
try:
538-
subdir_path = sub_directory.name
550+
subdir_path = sub_directory.get('name')
539551
except AttributeError:
540-
subdir_path = self.path_name + '/' + sub_directory
552+
subdir_path = self.path_name + '/' + str(sub_directory)
541553

542554
_pipeline = Pipeline(
543555
transport=TransportWrapper(self._pipeline._transport), # pylint: disable = protected-access

azure/multiapi/storagev2/filedatalake/v2020_02_10/_data_lake_file_client.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
from io import BytesIO
7+
from typing import Any
78

89
try:
910
from urllib.parse import quote, unquote
@@ -596,9 +597,19 @@ def download_file(self, offset=None, length=None, **kwargs):
596597
downloader = self._blob_client.download_blob(offset=offset, length=length, **kwargs)
597598
return StorageStreamDownloader(downloader)
598599

599-
def rename_file(self, new_name, # type: str
600-
**kwargs):
601-
# type: (**Any) -> DataLakeFileClient
600+
def exists(self, **kwargs):
601+
# type: (**Any) -> bool
602+
"""
603+
Returns True if a file exists and returns False otherwise.
604+
605+
:kwarg int timeout:
606+
The timeout parameter is expressed in seconds.
607+
:returns: boolean
608+
"""
609+
return self._exists(**kwargs)
610+
611+
def rename_file(self, new_name, **kwargs):
612+
# type: (str, **Any) -> DataLakeFileClient
602613
"""
603614
Rename the source file.
604615

azure/multiapi/storagev2/filedatalake/v2020_02_10/_data_lake_service_client.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6+
from typing import Any
67

78
try:
89
from urllib.parse import urlparse
@@ -92,17 +93,19 @@ def __init__(
9293
# ADLS doesn't support secondary endpoint, make sure it's empty
9394
self._hosts[LocationMode.SECONDARY] = ""
9495

96+
def __enter__(self):
97+
self._blob_service_client.__enter__()
98+
return self
99+
95100
def __exit__(self, *args):
96101
self._blob_service_client.close()
97-
super(DataLakeServiceClient, self).__exit__(*args)
98102

99103
def close(self):
100104
# type: () -> None
101105
""" This method is to close the sockets opened by the client.
102106
It need not be used when using with a context manager.
103107
"""
104108
self._blob_service_client.close()
105-
self.__exit__()
106109

107110
def _format_url(self, hostname):
108111
"""Format the endpoint URL according to hostname
@@ -195,6 +198,10 @@ def list_file_systems(self, name_starts_with=None, # type: Optional[str]
195198
call. If the request does not specify the server will return up to 5,000 items per page.
196199
:keyword int timeout:
197200
The timeout parameter is expressed in seconds.
201+
:keyword bool include_deleted:
202+
Specifies that deleted file systems to be returned in the response. This is for file system restore enabled
203+
account. The default value is `False`.
204+
.. versionadded:: 12.3.0
198205
:returns: An iterable (auto-paging) of FileSystemProperties.
199206
:rtype: ~azure.core.paging.ItemPaged[~azure.storage.filedatalake.FileSystemProperties]
200207
@@ -250,6 +257,55 @@ def create_file_system(self, file_system, # type: Union[FileSystemProperties, s
250257
file_system_client.create_file_system(metadata=metadata, public_access=public_access, **kwargs)
251258
return file_system_client
252259

260+
def _rename_file_system(self, name, new_name, **kwargs):
261+
# type: (str, str, **Any) -> FileSystemClient
262+
"""Renames a filesystem.
263+
264+
Operation is successful only if the source filesystem exists.
265+
266+
:param str name:
267+
The name of the filesystem to rename.
268+
:param str new_name:
269+
The new filesystem name the user wants to rename to.
270+
:keyword lease:
271+
Specify this to perform only if the lease ID given
272+
matches the active lease ID of the source filesystem.
273+
:paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
274+
:keyword int timeout:
275+
The timeout parameter is expressed in seconds.
276+
:rtype: ~azure.storage.filedatalake.FileSystemClient
277+
"""
278+
self._blob_service_client._rename_container(name, new_name, **kwargs) # pylint: disable=protected-access
279+
renamed_file_system = self.get_file_system_client(new_name)
280+
return renamed_file_system
281+
282+
def undelete_file_system(self, name, deleted_version, **kwargs):
283+
# type: (str, str, **Any) -> FileSystemClient
284+
"""Restores soft-deleted filesystem.
285+
286+
Operation will only be successful if used within the specified number of days
287+
set in the delete retention policy.
288+
289+
.. versionadded:: 12.3.0
290+
This operation was introduced in API version '2019-12-12'.
291+
292+
:param str name:
293+
Specifies the name of the deleted filesystem to restore.
294+
:param str deleted_version:
295+
Specifies the version of the deleted filesystem to restore.
296+
:keyword str new_name:
297+
The new name for the deleted filesystem to be restored to.
298+
If not specified "name" will be used as the restored filesystem name.
299+
:keyword int timeout:
300+
The timeout parameter is expressed in seconds.
301+
:rtype: ~azure.storage.filedatalake.FileSystemClient
302+
"""
303+
new_name = kwargs.pop('new_name', None)
304+
file_system = self.get_file_system_client(new_name or name)
305+
self._blob_service_client.undelete_container(
306+
name, deleted_version, new_name=new_name, **kwargs) # pylint: disable=protected-access
307+
return file_system
308+
253309
def delete_file_system(self, file_system, # type: Union[FileSystemProperties, str]
254310
**kwargs):
255311
# type: (...) -> FileSystemClient

azure/multiapi/storagev2/filedatalake/v2020_02_10/_deserialize.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from azure.core.pipeline.policies import ContentDecodePolicy
1313
from azure.core.exceptions import HttpResponseError, DecodeError, ResourceModifiedError, ClientAuthenticationError, \
1414
ResourceNotFoundError, ResourceExistsError
15-
from ._models import FileProperties, DirectoryProperties, LeaseProperties
15+
from ._models import FileProperties, DirectoryProperties, LeaseProperties, PathProperties
1616
from ._shared.models import StorageErrorCode
1717

1818
if TYPE_CHECKING:
@@ -44,6 +44,10 @@ def deserialize_file_properties(response, obj, headers):
4444
return file_properties
4545

4646

47+
def deserialize_path_properties(path_list):
48+
return [PathProperties._from_generated(path) for path in path_list] # pylint: disable=protected-access
49+
50+
4751
def from_blob_properties(blob_properties):
4852
file_props = FileProperties()
4953
file_props.name = blob_properties.name

azure/multiapi/storagev2/filedatalake/v2020_02_10/_file_system_client.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
import functools
6+
from typing import Optional, Any
77

88
try:
99
from urllib.parse import urlparse, quote
@@ -17,12 +17,12 @@
1717
from azure.multiapi.storagev2.blob.v2020_02_10 import ContainerClient
1818
from ._shared.base_client import TransportWrapper, StorageAccountHostsMixin, parse_query, parse_connection_str
1919
from ._serialize import convert_dfs_url_to_blob_url
20-
from ._models import LocationMode, FileSystemProperties, PublicAccess
21-
from ._list_paths_helper import PathPropertiesPaged
20+
from ._models import LocationMode, FileSystemProperties, PublicAccess, FileProperties, DirectoryProperties
2221
from ._data_lake_file_client import DataLakeFileClient
2322
from ._data_lake_directory_client import DataLakeDirectoryClient
2423
from ._data_lake_lease import DataLakeLeaseClient
2524
from ._generated import AzureDataLakeStorageRESTAPI
25+
from ._deserialize import deserialize_path_properties
2626

2727

2828
class FileSystemClient(StorageAccountHostsMixin):
@@ -247,6 +247,42 @@ def create_file_system(self, metadata=None, # type: Optional[Dict[str, str]]
247247
public_access=public_access,
248248
**kwargs)
249249

250+
def exists(self, **kwargs):
251+
# type: (**Any) -> bool
252+
"""
253+
Returns True if a file system exists and returns False otherwise.
254+
255+
:kwarg int timeout:
256+
The timeout parameter is expressed in seconds.
257+
:returns: boolean
258+
"""
259+
return self._container_client.exists(**kwargs)
260+
261+
def _rename_file_system(self, new_name, **kwargs):
262+
# type: (str, **Any) -> FileSystemClient
263+
"""Renames a filesystem.
264+
265+
Operation is successful only if the source filesystem exists.
266+
267+
:param str new_name:
268+
The new filesystem name the user wants to rename to.
269+
:keyword lease:
270+
Specify this to perform only if the lease ID given
271+
matches the active lease ID of the source filesystem.
272+
:paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
273+
:keyword int timeout:
274+
The timeout parameter is expressed in seconds.
275+
:rtype: ~azure.storage.filedatalake.FileSystemClient
276+
"""
277+
self._container_client._rename_container(new_name, **kwargs) # pylint: disable=protected-access
278+
renamed_file_system = FileSystemClient(
279+
"{}://{}".format(self.scheme, self.primary_hostname), file_system_name=new_name,
280+
credential=self._raw_credential, api_version=self.api_version, _configuration=self._config,
281+
_pipeline=self._pipeline, _location_mode=self._location_mode, _hosts=self._hosts,
282+
require_encryption=self.require_encryption, key_encryption_key=self.key_encryption_key,
283+
key_resolver_function=self.key_resolver_function)
284+
return renamed_file_system
285+
250286
def delete_file_system(self, **kwargs):
251287
# type: (Any) -> None
252288
"""Marks the specified file system for deletion.
@@ -463,14 +499,13 @@ def get_paths(self, path=None, # type: Optional[str]
463499
:caption: List the paths in the file system.
464500
"""
465501
timeout = kwargs.pop('timeout', None)
466-
command = functools.partial(
467-
self._client.file_system.list_paths,
502+
return self._client.file_system.list_paths(
503+
recursive=recursive,
504+
max_results=max_results,
468505
path=path,
469506
timeout=timeout,
507+
cls=deserialize_path_properties,
470508
**kwargs)
471-
return ItemPaged(
472-
command, recursive, path=path, max_results=max_results,
473-
page_iterator_class=PathPropertiesPaged, **kwargs)
474509

475510
def create_directory(self, directory, # type: Union[DirectoryProperties, str]
476511
metadata=None, # type: Optional[Dict[str, str]]
@@ -738,9 +773,9 @@ def get_directory_client(self, directory # type: Union[DirectoryProperties, str
738773
:caption: Getting the directory client to interact with a specific directory.
739774
"""
740775
try:
741-
directory_name = directory.name
776+
directory_name = directory.get('name')
742777
except AttributeError:
743-
directory_name = directory
778+
directory_name = str(directory)
744779
_pipeline = Pipeline(
745780
transport=TransportWrapper(self._pipeline._transport), # pylint: disable = protected-access
746781
policies=self._pipeline._impl_policies # pylint: disable = protected-access
@@ -778,9 +813,9 @@ def get_file_client(self, file_path # type: Union[FileProperties, str]
778813
:caption: Getting the file client to interact with a specific file.
779814
"""
780815
try:
781-
file_path = file_path.name
816+
file_path = file_path.get('name')
782817
except AttributeError:
783-
pass
818+
file_path = str(file_path)
784819
_pipeline = Pipeline(
785820
transport=TransportWrapper(self._pipeline._transport), # pylint: disable = protected-access
786821
policies=self._pipeline._impl_policies # pylint: disable = protected-access

azure/multiapi/storagev2/filedatalake/v2020_02_10/_generated/aio/operations/_file_system_operations.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,8 @@ def list_paths(
438438
error_map.update(kwargs.pop('error_map', {}))
439439
accept = "application/json"
440440

441-
def prepare_request(next_link=None):
441+
# TODO: change this once continuation/next_link autorest PR is merged
442+
def prepare_request(next_link=None, cont_token=None):
442443
# Construct headers
443444
header_parameters = {} # type: Dict[str, Any]
444445
if request_id_parameter is not None:
@@ -458,8 +459,9 @@ def prepare_request(next_link=None):
458459
query_parameters['resource'] = self._serialize.query("self._config.resource", self._config.resource, 'str')
459460
if timeout is not None:
460461
query_parameters['timeout'] = self._serialize.query("timeout", timeout, 'int', minimum=0)
461-
if continuation is not None:
462-
query_parameters['continuation'] = self._serialize.query("continuation", continuation, 'str')
462+
# TODO: change this once continuation/next_link autorest PR is merged
463+
if cont_token is not None:
464+
query_parameters['continuation'] = self._serialize.query("continuation", cont_token, 'str')
463465
if path is not None:
464466
query_parameters['directory'] = self._serialize.query("path", path, 'str')
465467
query_parameters['recursive'] = self._serialize.query("recursive", recursive, 'bool')
@@ -480,14 +482,21 @@ def prepare_request(next_link=None):
480482
return request
481483

482484
async def extract_data(pipeline_response):
485+
# TODO: change this once continuation/next_link autorest PR is merged
486+
try:
487+
cont_token = pipeline_response.http_response.headers['x-ms-continuation']
488+
except KeyError:
489+
cont_token = None
483490
deserialized = self._deserialize('PathList', pipeline_response)
484491
list_of_elem = deserialized.paths
485492
if cls:
486493
list_of_elem = cls(list_of_elem)
487-
return None, AsyncList(list_of_elem)
494+
return cont_token, AsyncList(list_of_elem)
488495

489-
async def get_next(next_link=None):
490-
request = prepare_request(next_link)
496+
# TODO: change this once continuation/next_link autorest PR is merged
497+
async def get_next(cont_token=None):
498+
cont_token = cont_token if not continuation else continuation
499+
request = prepare_request(cont_token=cont_token)
491500

492501
pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs)
493502
response = pipeline_response.http_response

0 commit comments

Comments
 (0)