Skip to content

Commit 9fb331e

Browse files
authored
Set expiry (#12642)
* [DataLake][SetExpiry]Set Expiry of DataLake File * address comments * use datalake set_expiry operation * add serialize rfc1123 and fix pylint * fix pylint * remove return type
1 parent 1226370 commit 9fb331e

22 files changed

+606
-152
lines changed

sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ def _download_blob_options(self, offset=None, length=None, **kwargs):
576576
'lease_access_conditions': access_conditions,
577577
'modified_access_conditions': mod_conditions,
578578
'cpk_info': cpk_info,
579-
'cls': deserialize_blob_stream,
579+
'cls': kwargs.pop('cls', None) or deserialize_blob_stream,
580580
'max_concurrency':kwargs.pop('max_concurrency', 1),
581581
'encoding': kwargs.pop('encoding', None),
582582
'timeout': kwargs.pop('timeout', None),
@@ -1038,14 +1038,15 @@ def get_blob_properties(self, **kwargs):
10381038
snapshot=self.snapshot,
10391039
lease_access_conditions=access_conditions,
10401040
modified_access_conditions=mod_conditions,
1041-
cls=deserialize_blob_properties,
1041+
cls=kwargs.pop('cls', None) or deserialize_blob_properties,
10421042
cpk_info=cpk_info,
10431043
**kwargs)
10441044
except StorageErrorException as error:
10451045
process_storage_error(error)
10461046
blob_props.name = self.blob_name
1047-
blob_props.snapshot = self.snapshot
1048-
blob_props.container = self.container_name
1047+
if isinstance(blob_props, BlobProperties):
1048+
blob_props.container = self.container_name
1049+
blob_props.snapshot = self.snapshot
10491050
return blob_props # type: ignore
10501051

10511052
def _set_http_headers_options(self, content_settings=None, **kwargs):

sdk/storage/azure-storage-blob/azure/storage/blob/_generated/aio/operations_async/_blob_operations_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ async def set_expiry(self, expiry_options, timeout=None, request_id=None, expire
11191119
header_parameters['x-ms-client-request-id'] = self._serialize.header("request_id", request_id, 'str')
11201120
header_parameters['x-ms-expiry-option'] = self._serialize.header("expiry_options", expiry_options, 'str')
11211121
if expires_on is not None:
1122-
header_parameters['x-ms-expiry-time'] = self._serialize.header("expires_on", expires_on, 'str')
1122+
header_parameters['x-ms-expiry-time'] = self._serialize.header("expires_on", expires_on, 'rfc-1123')
11231123

11241124
# Construct and send request
11251125
request = self._client.put(url, query_parameters, header_parameters)

sdk/storage/azure-storage-blob/azure/storage/blob/_generated/operations/_blob_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,7 @@ def set_expiry(self, expiry_options, timeout=None, request_id=None, expires_on=N
11181118
header_parameters['x-ms-client-request-id'] = self._serialize.header("request_id", request_id, 'str')
11191119
header_parameters['x-ms-expiry-option'] = self._serialize.header("expiry_options", expiry_options, 'str')
11201120
if expires_on is not None:
1121-
header_parameters['x-ms-expiry-time'] = self._serialize.header("expires_on", expires_on, 'str')
1121+
header_parameters['x-ms-expiry-time'] = self._serialize.header("expires_on", expires_on, 'rfc-1123')
11221122

11231123
# Construct and send request
11241124
request = self._client.put(url, query_parameters, header_parameters)

sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_client_async.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,14 +561,15 @@ async def get_blob_properties(self, **kwargs):
561561
snapshot=self.snapshot,
562562
lease_access_conditions=access_conditions,
563563
modified_access_conditions=mod_conditions,
564-
cls=deserialize_blob_properties,
564+
cls=kwargs.pop('cls', None) or deserialize_blob_properties,
565565
cpk_info=cpk_info,
566566
**kwargs)
567567
except StorageErrorException as error:
568568
process_storage_error(error)
569569
blob_props.name = self.blob_name
570-
blob_props.snapshot = self.snapshot
571-
blob_props.container = self.container_name
570+
if isinstance(blob_props, BlobProperties):
571+
blob_props.container = self.container_name
572+
blob_props.snapshot = self.snapshot
572573
return blob_props # type: ignore
573574

574575
@distributed_trace_async

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
DirectoryProperties,
1919
FileProperties,
2020
PathProperties,
21-
PathPropertiesPaged,
2221
LeaseProperties,
2322
ContentSettings,
2423
AccountSasPermissions,
@@ -38,6 +37,7 @@
3837
AccessControlChangeFailure,
3938
AccessControlChanges,
4039
)
40+
4141
from ._shared_access_signature import generate_account_sas, generate_file_system_sas, generate_directory_sas, \
4242
generate_file_sas
4343

@@ -66,7 +66,6 @@
6666
'DirectoryProperties',
6767
'FileProperties',
6868
'PathProperties',
69-
'PathPropertiesPaged',
7069
'LeaseProperties',
7170
'ContentSettings',
7271
'AccessControlChangeResult',

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_directory_client.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
76
try:
87
from urllib.parse import quote, unquote
98
except ImportError:
109
from urllib2 import quote, unquote # type: ignore
11-
10+
from ._deserialize import deserialize_dir_properties
1211
from ._shared.base_client import parse_connection_str
1312
from ._data_lake_file_client import DataLakeFileClient
1413
from ._models import DirectoryProperties
@@ -236,8 +235,7 @@ def get_directory_properties(self, **kwargs):
236235
:dedent: 4
237236
:caption: Getting the properties for a file/directory.
238237
"""
239-
blob_properties = self._get_path_properties(**kwargs)
240-
return DirectoryProperties._from_blob_properties(blob_properties) # pylint: disable=protected-access
238+
return self._get_path_properties(cls=deserialize_dir_properties, **kwargs) # pylint: disable=protected-access
241239

242240
def rename_directory(self, new_name, # type: str
243241
**kwargs):

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_file_client.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
from ._generated.models import StorageErrorException
2222
from ._download import StorageStreamDownloader
2323
from ._path_client import PathClient
24-
from ._serialize import get_mod_conditions, get_path_http_headers, get_access_conditions, add_metadata_headers
25-
from ._deserialize import process_storage_error
24+
from ._serialize import get_mod_conditions, get_path_http_headers, get_access_conditions, add_metadata_headers, \
25+
convert_datetime_to_rfc1123
26+
from ._deserialize import process_storage_error, deserialize_file_properties
2627
from ._models import FileProperties, DataLakeFileQueryError
2728

2829

@@ -246,8 +247,31 @@ def get_file_properties(self, **kwargs):
246247
:dedent: 4
247248
:caption: Getting the properties for a file.
248249
"""
249-
blob_properties = self._get_path_properties(**kwargs)
250-
return FileProperties._from_blob_properties(blob_properties) # pylint: disable=protected-access
250+
return self._get_path_properties(cls=deserialize_file_properties, **kwargs) # pylint: disable=protected-access
251+
252+
def set_file_expiry(self, expiry_options, # type: str
253+
expires_on=None, # type: Optional[Union[datetime, int]]
254+
**kwargs):
255+
# type: (str, Optional[Union[datetime, int]], **Any) -> None
256+
"""Sets the time a file will expire and be deleted.
257+
258+
:param str expiry_options:
259+
Required. Indicates mode of the expiry time.
260+
Possible values include: 'NeverExpire', 'RelativeToCreation', 'RelativeToNow', 'Absolute'
261+
:param datetime or int expires_on:
262+
The time to set the file to expiry.
263+
When expiry_options is RelativeTo*, expires_on should be an int in milliseconds.
264+
If the type of expires_on is datetime, it should be in UTC time.
265+
:keyword int timeout:
266+
The timeout parameter is expressed in seconds.
267+
:rtype: None
268+
"""
269+
try:
270+
expires_on = convert_datetime_to_rfc1123(expires_on)
271+
except AttributeError:
272+
expires_on = str(expires_on)
273+
self._datalake_client_for_blob_operation.path \
274+
.set_expiry(expiry_options, expires_on=expires_on, **kwargs) # pylint: disable=protected-access
251275

252276
def _upload_options( # pylint:disable=too-many-statements
253277
self, data, # type: Union[Iterable[AnyStr], IO[AnyStr]]

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_deserialize.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +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
1516
from ._shared.models import StorageErrorCode
1617

1718
if TYPE_CHECKING:
@@ -20,6 +21,45 @@
2021
_LOGGER = logging.getLogger(__name__)
2122

2223

24+
def deserialize_dir_properties(response, obj, headers):
25+
metadata = deserialize_metadata(response, obj, headers)
26+
dir_properties = DirectoryProperties(
27+
metadata=metadata,
28+
**headers
29+
)
30+
return dir_properties
31+
32+
33+
def deserialize_file_properties(response, obj, headers):
34+
metadata = deserialize_metadata(response, obj, headers)
35+
file_properties = FileProperties(
36+
metadata=metadata,
37+
**headers
38+
)
39+
if 'Content-Range' in headers:
40+
if 'x-ms-blob-content-md5' in headers:
41+
file_properties.content_settings.content_md5 = headers['x-ms-blob-content-md5']
42+
else:
43+
file_properties.content_settings.content_md5 = None
44+
return file_properties
45+
46+
47+
def from_blob_properties(blob_properties):
48+
file_props = FileProperties()
49+
file_props.name = blob_properties.name
50+
file_props.etag = blob_properties.etag
51+
file_props.deleted = blob_properties.deleted
52+
file_props.metadata = blob_properties.metadata
53+
file_props.lease = blob_properties.lease
54+
file_props.lease.__class__ = LeaseProperties
55+
file_props.last_modified = blob_properties.last_modified
56+
file_props.creation_time = blob_properties.creation_time
57+
file_props.size = blob_properties.size
58+
file_props.deleted_time = blob_properties.deleted_time
59+
file_props.remaining_retention_days = blob_properties.remaining_retention_days
60+
file_props.content_settings = blob_properties.content_settings
61+
return file_props
62+
2363
def normalize_headers(headers):
2464
normalized = {}
2565
for key, value in headers.items():

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_download.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
7-
from ._models import FileProperties
6+
from ._deserialize import from_blob_properties
87

98

109
class StorageStreamDownloader(object):
@@ -23,7 +22,7 @@ class StorageStreamDownloader(object):
2322
def __init__(self, downloader):
2423
self._downloader = downloader
2524
self.name = self._downloader.name
26-
self.properties = FileProperties._from_blob_properties(self._downloader.properties) # pylint: disable=protected-access
25+
self.properties = from_blob_properties(self._downloader.properties) # pylint: disable=protected-access
2726
self.size = self._downloader.size
2827

2928
def __len__(self):

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_file_system_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
from azure.storage.blob import ContainerClient
1717
from ._shared.base_client import StorageAccountHostsMixin, parse_query, parse_connection_str
1818
from ._serialize import convert_dfs_url_to_blob_url
19-
from ._models import LocationMode, FileSystemProperties, PathPropertiesPaged, PublicAccess
19+
from ._models import LocationMode, FileSystemProperties, PublicAccess
20+
from ._list_paths_helper import PathPropertiesPaged
2021
from ._data_lake_file_client import DataLakeFileClient
2122
from ._data_lake_directory_client import DataLakeDirectoryClient
2223
from ._data_lake_lease import DataLakeLeaseClient
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
from azure.core.paging import PageIterator
7+
from ._generated.models import StorageErrorException
8+
from ._models import PathProperties
9+
from ._deserialize import return_headers_and_deserialized_path_list
10+
from ._generated.models import Path
11+
from ._shared.response_handlers import process_storage_error
12+
13+
14+
class PathPropertiesPaged(PageIterator):
15+
"""An Iterable of Path properties.
16+
17+
:ivar str path: Filters the results to return only paths under the specified path.
18+
:ivar int results_per_page: The maximum number of results retrieved per API call.
19+
:ivar str continuation_token: The continuation token to retrieve the next page of results.
20+
:ivar list(~azure.storage.filedatalake.PathProperties) current_page: The current page of listed results.
21+
22+
:param callable command: Function to retrieve the next page of items.
23+
:param str path: Filters the results to return only paths under the specified path.
24+
:param int max_results: The maximum number of psths to retrieve per
25+
call.
26+
:param str continuation_token: An opaque continuation token.
27+
"""
28+
def __init__(
29+
self, command,
30+
recursive,
31+
path=None,
32+
max_results=None,
33+
continuation_token=None,
34+
upn=None):
35+
super(PathPropertiesPaged, self).__init__(
36+
get_next=self._get_next_cb,
37+
extract_data=self._extract_data_cb,
38+
continuation_token=continuation_token or ""
39+
)
40+
self._command = command
41+
self.recursive = recursive
42+
self.results_per_page = max_results
43+
self.path = path
44+
self.upn = upn
45+
self.current_page = None
46+
self.path_list = None
47+
48+
def _get_next_cb(self, continuation_token):
49+
try:
50+
return self._command(
51+
self.recursive,
52+
continuation=continuation_token or None,
53+
path=self.path,
54+
max_results=self.results_per_page,
55+
upn=self.upn,
56+
cls=return_headers_and_deserialized_path_list)
57+
except StorageErrorException as error:
58+
process_storage_error(error)
59+
60+
def _extract_data_cb(self, get_next_return):
61+
self.path_list, self._response = get_next_return
62+
self.current_page = [self._build_item(item) for item in self.path_list]
63+
64+
return self._response['continuation'] or None, self.current_page
65+
66+
@staticmethod
67+
def _build_item(item):
68+
if isinstance(item, PathProperties):
69+
return item
70+
if isinstance(item, Path):
71+
path = PathProperties._from_generated(item) # pylint: disable=protected-access
72+
return path
73+
return item

0 commit comments

Comments
 (0)