diff --git a/sdk/storage/azure-storage-file-share/CHANGELOG.md b/sdk/storage/azure-storage-file-share/CHANGELOG.md index 1d22c6452247..7ba4b19a2eb3 100644 --- a/sdk/storage/azure-storage-file-share/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-share/CHANGELOG.md @@ -3,6 +3,10 @@ ## 12.26.0b1 (Unreleased) ### Features Added +- Added support for the keyword `file_property_semantics` in `ShareClient`'s `create_directory` and `DirectoryClient`'s +`create_directory` APIs, which specifies permissions to be configured upon directory creation. +- Added support for the keyword `data` to `FileClient`'s `create_file` API, which specifies the +optional initial data to be uploaded (up to 4MB). ## 12.25.0b1 (2026-01-27) diff --git a/sdk/storage/azure-storage-file-share/assets.json b/sdk/storage/azure-storage-file-share/assets.json index 5d09afd5ce09..992936aafc04 100644 --- a/sdk/storage/azure-storage-file-share/assets.json +++ b/sdk/storage/azure-storage-file-share/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/storage/azure-storage-file-share", - "Tag": "python/storage/azure-storage-file-share_c5fe7fb7b7" + "Tag": "python/storage/azure-storage-file-share_b02a740ab3" } diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py index 223b7fbf6360..5191ff9392ab 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py @@ -346,6 +346,16 @@ def create_directory(self, **kwargs: Any) -> Dict[str, Any]: NFS only. The owning group of the directory. :keyword str file_mode: NFS only. The file mode of the directory. + :keyword file_property_semantics: + SMB only. Specifies permissions to be configured. Default value is None. + If not specified or None is passed, New will be the default. Possible values are: + + New - forcefully add the ARCHIVE attribute flag and alter the permissions specified in + x-ms-file-permission to inherit missing permissions from the parent. + + Restore - apply changes without further modification. + + :paramtype file_property_semantics: Optional[Literal["New", "Restore"]] :keyword int timeout: Sets the server-side timeout for the operation in seconds. For more details see https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-file-service-operations. diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.pyi b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.pyi index ba76bd85b530..2f5e9f679c58 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.pyi +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.pyi @@ -121,6 +121,7 @@ class ShareDirectoryClient(StorageAccountHostsMixin): owner: Optional[str] = None, group: Optional[str] = None, file_mode: Optional[str] = None, + file_property_semantics: Optional[Literal["New", "Restore"]] = None, timeout: Optional[int] = None, **kwargs: Any ) -> Dict[str, Any]: ... diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py index ed032562a768..c4ac3901d8c1 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py @@ -436,6 +436,18 @@ def create_file( NFS only. The owning group of the file. :keyword str file_mode: NFS only. The file mode of the file. + :keyword file_property_semantics: + SMB only. Specifies permissions to be configured. Default value is None. + If not specified or None is passed, New will be the default. Possible values are: + + New - forcefully add the ARCHIVE attribute flag and alter the permissions specified in + x-ms-file-permission to inherit missing permissions from the parent. + + Restore - apply changes without further modification. + + :paramtype file_property_semantics: Optional[Literal["New", "Restore"]] + :keyword data: Optional initial data to upload, up to 4MB. + :paramtype data: bytes :keyword int timeout: Sets the server-side timeout for the operation in seconds. For more details see https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-file-service-operations. diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.pyi b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.pyi index 3fc1fb454d86..269a088f36f5 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.pyi +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.pyi @@ -132,6 +132,8 @@ class ShareFileClient(StorageAccountHostsMixin): owner: Optional[str] = None, group: Optional[str] = None, file_mode: Optional[str] = None, + file_property_semantics: Optional[Literal["New", "Restore"]] = None, + data: Optional[bytes] = None, timeout: Optional[int] = None, **kwargs: Any ) -> Dict[str, Any]: ... diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py index 1330b66fcf9e..f5815256d02a 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py @@ -982,6 +982,16 @@ def create_directory(self, directory_name: str, **kwargs: Any) -> ShareDirectory NFS only. The owning group of the directory. :keyword str file_mode: NFS only. The file mode of the directory. + :keyword file_property_semantics: + SMB only. Specifies permissions to be configured. Default value is None. + If not specified or None is passed, New will be the default. Possible values are: + + New - forcefully add the ARCHIVE attribute flag and alter the permissions specified in + x-ms-file-permission to inherit missing permissions from the parent. + + Restore - apply changes without further modification. + + :paramtype file_property_semantics: Optional[Literal["New", "Restore"]] :keyword int timeout: Sets the server-side timeout for the operation in seconds. For more details see https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-file-service-operations. diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.pyi b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.pyi index ae8d73ad95e1..a5c105bae781 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.pyi +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.pyi @@ -227,6 +227,7 @@ class ShareClient(StorageAccountHostsMixin): owner: Optional[str] = None, group: Optional[str] = None, file_mode: Optional[str] = None, + file_property_semantics: Optional[Literal["New", "Restore"]] = None, timeout: Optional[int] = None, **kwargs: Any ) -> ShareDirectoryClient: ... diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py index 4fc04bb91d26..b4fb7ff46333 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py @@ -352,6 +352,16 @@ async def create_directory(self, **kwargs: Any) -> Dict[str, Any]: NFS only. The owning group of the directory. :keyword str file_mode: NFS only. The file mode of the directory. + :keyword file_property_semantics: + SMB only. Specifies permissions to be configured. Default value is None. + If not specified or None is passed, New will be the default. Possible values are: + + New - forcefully add the ARCHIVE attribute flag and alter the permissions specified in + x-ms-file-permission to inherit missing permissions from the parent. + + Restore - apply changes without further modification. + + :paramtype file_property_semantics: Optional[Literal["New", "Restore"]] :keyword int timeout: Sets the server-side timeout for the operation in seconds. For more details see https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-file-service-operations. diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.pyi b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.pyi index 70e0078868e0..ee32af55d216 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.pyi +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.pyi @@ -126,6 +126,7 @@ class ShareDirectoryClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMix owner: Optional[str] = None, group: Optional[str] = None, file_mode: Optional[str] = None, + file_property_semantics: Optional[Literal["New", "Restore"]] = None, timeout: Optional[int] = None, **kwargs: Any ) -> Dict[str, Any]: ... diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py index 75d77c35cc1f..de0e661b82e3 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py @@ -431,6 +431,18 @@ async def create_file( NFS only. The owning group of the file. :keyword str file_mode: NFS only. The file mode of the file. + :keyword file_property_semantics: + SMB only. Specifies permissions to be configured. Default value is None. + If not specified or None is passed, New will be the default. Possible values are: + + New - forcefully add the ARCHIVE attribute flag and alter the permissions specified in + x-ms-file-permission to inherit missing permissions from the parent. + + Restore - apply changes without further modification. + + :paramtype file_property_semantics: Optional[Literal["New", "Restore"]] + :keyword data: Optional initial data to upload, up to 4MB. + :paramtype data: bytes :keyword int timeout: Sets the server-side timeout for the operation in seconds. For more details see https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-file-service-operations. diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.pyi b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.pyi index 3218ca795360..3ba625b8149d 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.pyi +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.pyi @@ -133,6 +133,8 @@ class ShareFileClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin): owner: Optional[str] = None, group: Optional[str] = None, file_mode: Optional[str] = None, + file_property_semantics: Optional[Literal["New", "Restore"]] = None, + data: Optional[bytes] = None, timeout: Optional[int] = None, **kwargs: Any ) -> Dict[str, Any]: ... diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py index 2d2cdaf0a3f0..8f6be194dc5c 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py @@ -978,6 +978,16 @@ async def create_directory(self, directory_name: str, **kwargs: Any) -> ShareDir NFS only. The owning group of the directory. :keyword str file_mode: NFS only. The file mode of the directory. + :keyword file_property_semantics: + SMB only. Specifies permissions to be configured. Default value is None. + If not specified or None is passed, New will be the default. Possible values are: + + New - forcefully add the ARCHIVE attribute flag and alter the permissions specified in + x-ms-file-permission to inherit missing permissions from the parent. + + Restore - apply changes without further modification. + + :paramtype file_property_semantics: Optional[Literal["New", "Restore"]] :keyword int timeout: Sets the server-side timeout for the operation in seconds. For more details see https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-file-service-operations. diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.pyi b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.pyi index 98c078e74da8..e03fec9e8d7e 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.pyi +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.pyi @@ -232,6 +232,7 @@ class ShareClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin): # t owner: Optional[str] = None, group: Optional[str] = None, file_mode: Optional[str] = None, + file_property_semantics: Optional[Literal["New", "Restore"]] = None, timeout: Optional[int] = None, **kwargs: Any ) -> ShareDirectoryClient: ... diff --git a/sdk/storage/azure-storage-file-share/tests/test_directory.py b/sdk/storage/azure-storage-file-share/tests/test_directory.py index d5e59e77beef..118d9f04f002 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_directory.py +++ b/sdk/storage/azure-storage-file-share/tests/test_directory.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +import os import unittest from datetime import datetime, timedelta, timezone @@ -1477,6 +1478,29 @@ def test_file_permission_format_directory(self, **kwargs): new_directory_client.delete_directory() + @FileSharePreparer() + @recorded_by_proxy + def test_create_directory_semantics(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup(storage_account_name, storage_account_key) + share_client = self.fsc.get_share_client(self.share_name) + + directory = share_client.create_directory('dir1', file_property_semantics=None) + props = directory.get_directory_properties() + assert props is not None + + directory = share_client.create_directory('dir2', file_property_semantics='New') + props = directory.get_directory_properties() + assert props is not None + + directory = share_client.create_directory( + 'dir3', file_property_semantics='Restore', file_permission=TEST_FILE_PERMISSIONS + ) + props = directory.get_directory_properties() + assert props is not None + # ------------------------------------------------------------------------------ if __name__ == '__main__': unittest.main() diff --git a/sdk/storage/azure-storage-file-share/tests/test_directory_async.py b/sdk/storage/azure-storage-file-share/tests/test_directory_async.py index aab82f8447bd..23a0f3845df2 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_directory_async.py +++ b/sdk/storage/azure-storage-file-share/tests/test_directory_async.py @@ -5,8 +5,9 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- -import unittest import asyncio +import os +import unittest from datetime import datetime, timedelta, timezone import pytest @@ -1580,4 +1581,27 @@ async def test_file_permission_format_directory(self, **kwargs): await new_directory_client.delete_directory() + @FileSharePreparer() + @recorded_by_proxy_async + async def test_create_directory_semantics(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + await self._setup(storage_account_name, storage_account_key) + share_client = self.fsc.get_share_client(self.share_name) + + directory = await share_client.create_directory('dir1', file_property_semantics=None) + props = await directory.get_directory_properties() + assert props is not None + + directory = await share_client.create_directory('dir2', file_property_semantics='New') + props = await directory.get_directory_properties() + assert props is not None + + directory = await share_client.create_directory( + 'dir3', file_property_semantics='Restore', file_permission=TEST_FILE_PERMISSIONS + ) + props = await directory.get_directory_properties() + assert props is not None + # ------------------------------------------------------------------------------ diff --git a/sdk/storage/azure-storage-file-share/tests/test_file.py b/sdk/storage/azure-storage-file-share/tests/test_file.py index 1224a52dbbd8..a247c636c57d 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_file.py +++ b/sdk/storage/azure-storage-file-share/tests/test_file.py @@ -4015,4 +4015,64 @@ def test_download_file_no_decompress_chunks(self, **kwargs): result = file_client.download_file(decompress=False).readall() assert result == compressed_data + @FileSharePreparer() + @recorded_by_proxy + def test_create_file_semantics(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup(storage_account_name, storage_account_key) + storage_account_key = storage_account_key.secret + file_name = self._get_file_reference() + + file1 = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file1", + credential=storage_account_key + ) + file1.create_file(1024, file_property_semantics=None) + props = file1.get_file_properties() + assert props is not None + + file2 = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file2", + credential=storage_account_key + ) + file2.create_file(1024, file_property_semantics="New") + props = file2.get_file_properties() + assert props is not None + + file3 = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file2", + credential=storage_account_key + ) + file3.create_file(1024, file_property_semantics="Restore", file_permission=TEST_FILE_PERMISSIONS) + props = file3.get_file_properties() + assert props is not None + + @FileSharePreparer() + @recorded_by_proxy + def test_create_file_with_data(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup(storage_account_name, storage_account_key) + file_name = self._get_file_reference() + file_client = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file", + credential=storage_account_key.secret + ) + size = 1024 + data = b"abc" * size + file_client.create_file(len(data), data=data) + downloaded_data = file_client.download_file().readall() + assert downloaded_data == data + # ------------------------------------------------------------------------------ diff --git a/sdk/storage/azure-storage-file-share/tests/test_file_async.py b/sdk/storage/azure-storage-file-share/tests/test_file_async.py index 6d4d0d56426d..0829506f841e 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_file_async.py +++ b/sdk/storage/azure-storage-file-share/tests/test_file_async.py @@ -3,7 +3,6 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- - import asyncio import base64 import os @@ -4141,3 +4140,66 @@ async def test_download_file_no_decompress_chunks(self, **kwargs): result = await (await file_client.download_file(decompress=False)).readall() assert result == compressed_data + + @FileSharePreparer() + @recorded_by_proxy_async + async def test_create_file_semantics(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup(storage_account_name, storage_account_key) + await self._setup_share(storage_account_name, storage_account_key) + storage_account_key = storage_account_key.secret + file_name = self._get_file_reference() + + file1 = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file1", + credential=storage_account_key + ) + await file1.create_file(1024, file_property_semantics=None) + props = await file1.get_file_properties() + assert props is not None + + file2 = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file2", + credential=storage_account_key + ) + await file2.create_file(1024, file_property_semantics="New") + props = await file2.get_file_properties() + assert props is not None + + file3 = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file2", + credential=storage_account_key + ) + await file3.create_file(1024, file_property_semantics="Restore", file_permission=TEST_FILE_PERMISSIONS) + props = await file3.get_file_properties() + assert props is not None + + @FileSharePreparer() + @recorded_by_proxy_async + async def test_create_file_with_data(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup(storage_account_name, storage_account_key) + await self._setup_share(storage_account_name, storage_account_key) + + file_name = self._get_file_reference() + file_client = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_name + "file", + credential=storage_account_key.secret + ) + size = 1024 + data = b"abc" * size + await file_client.create_file(len(data), data=data) + downloaded_data = await (await file_client.download_file()).readall() + assert downloaded_data == data