Skip to content

Commit b068d40

Browse files
authored
Python: S3 Conditional Requests scenario (#7018)
1 parent 82378ae commit b068d40

File tree

10 files changed

+862
-1
lines changed

10 files changed

+862
-1
lines changed

.doc_gen/metadata/s3_metadata.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ s3_CopyObject:
294294
snippet_tags:
295295
- python.example_code.s3.helper.ObjectWrapper
296296
- python.example_code.s3.CopyObject
297+
- description: Copy an object using a conditional request.
298+
genai: some
299+
snippet_tags:
300+
- python.example_code.s3.helper.S3ConditionalRequests
301+
- python.example_code.s3.CopyObjectConditional
297302
Ruby:
298303
versions:
299304
- sdk_version: 3
@@ -872,6 +877,11 @@ s3_GetObject:
872877
snippet_tags:
873878
- python.example_code.s3.helper.ObjectWrapper
874879
- python.example_code.s3.GetObject
880+
- description: Get an object using a conditional request.
881+
genai: some
882+
snippet_tags:
883+
- python.example_code.s3.helper.S3ConditionalRequests
884+
- python.example_code.s3.GetObjectConditional
875885
JavaScript:
876886
versions:
877887
- sdk_version: 3
@@ -1510,6 +1520,11 @@ s3_PutObject:
15101520
snippet_tags:
15111521
- python.example_code.s3.helper.ObjectWrapper
15121522
- python.example_code.s3.PutObject
1523+
- description: Upload an object using a conditional request.
1524+
genai: some
1525+
snippet_tags:
1526+
- python.example_code.s3.helper.S3ConditionalRequests
1527+
- python.example_code.s3.PutObjectConditional
15131528
Rust:
15141529
versions:
15151530
- sdk_version: 1
@@ -3456,6 +3471,28 @@ s3_Scenario_DeleteAllObjects:
34563471
- javascriptv3/example_code/s3/scenarios/delete-all-objects.js
34573472
services:
34583473
s3: {DeleteObjects, ListObjectsV2}
3474+
s3_Scenario_ConditionalRequests:
3475+
title: Make &S3; conditional requests using an &AWS; SDK.
3476+
title_abbrev: Make conditional requests
3477+
synopsis: add preconditions to &S3; requests.
3478+
category: Scenarios
3479+
languages:
3480+
Python:
3481+
versions:
3482+
- sdk_version: 3
3483+
github: python/example_code/s3/scenarios/conditional_requests
3484+
sdkguide:
3485+
excerpts:
3486+
- description: Run an interactive scenario demonstrating &S3; conditional requests.
3487+
genai: some
3488+
snippet_tags:
3489+
- python.example_code.s3.S3ConditionalRequests.scenario
3490+
- description: A wrapper class that defines the conditional request operations.
3491+
genai: some
3492+
snippet_tags:
3493+
- python.example_code.s3.S3ConditionalRequests.wrapper
3494+
services:
3495+
s3: {GetObject, PutObject, CopyObject}
34593496
s3_Scenario_ExpressBasics:
34603497
title: Learn the basics of Amazon S3 Express One Zone with an &AWS; SDK
34613498
title_abbrev: Learn the basics of S3 Express One Zone

python/example_code/s3/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ functions within the same service.
8888
- [Create an Amazon Textract explorer application](../../cross_service/textract_explorer)
8989
- [Detect entities in text extracted from an image](../../cross_service/textract_comprehend_notebook)
9090
- [Detect objects in images](../../cross_service/photo_analyzer)
91+
- [Make conditional requests](scenarios/conditional_requests/scenario.py)
9192
- [Manage versioned objects in batches with a Lambda function](../../example_code/s3/s3_versioning)
9293
- [Upload or download large files](file_transfer/file_transfer.py)
9394
- [Work with versioned objects](s3_versioning/versioning.py)
@@ -190,6 +191,24 @@ This example shows you how to build an app that uses Amazon Rekognition to detec
190191
<!--custom.scenarios.cross_RekognitionPhotoAnalyzer.start-->
191192
<!--custom.scenarios.cross_RekognitionPhotoAnalyzer.end-->
192193

194+
#### Make conditional requests
195+
196+
This example shows you how to add preconditions to Amazon S3 requests.
197+
198+
199+
<!--custom.scenario_prereqs.s3_Scenario_ConditionalRequests.start-->
200+
<!--custom.scenario_prereqs.s3_Scenario_ConditionalRequests.end-->
201+
202+
Start the example by running the following at a command prompt:
203+
204+
```
205+
python scenarios/conditional_requests/scenario.py
206+
```
207+
208+
209+
<!--custom.scenarios.s3_Scenario_ConditionalRequests.start-->
210+
<!--custom.scenarios.s3_Scenario_ConditionalRequests.end-->
211+
193212
#### Manage versioned objects in batches with a Lambda function
194213

195214
This example shows you how to manage versioned S3 objects in batches with a Lambda function.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
boto3>=1.34.4
1+
boto3>=1.35.49
22
pytest>=7.2.1
33
requests>=2.28.2
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
# Amazon S3 Conditional Requests Feature Scenario for the SDK for Python (boto3)
3+
4+
## Overview
5+
6+
This example demonstrates how to use the AWS SDK for Python (boto3) to work with Amazon Simple Storage Service (Amazon S3) conditional request features. The scenario demonstrates how to add preconditions to S3 operations, and how those operations will succeed or fail based on the conditional requests.
7+
8+
[Amazon S3 Conditional Requests](https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-requests.html) are used to add preconditions to S3 read, copy, or write requests.
9+
10+
## ⚠ Important
11+
12+
- Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/).
13+
- Running the tests might result in charges to your AWS account.
14+
- We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
15+
- This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
16+
17+
## Code examples
18+
19+
### Prerequisites
20+
21+
To run these examples, you need:
22+
23+
- Python 3.x installed.
24+
- Run `python pip install -r requirements.txt`
25+
- AWS credentials configured. For more information, see [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).
26+
27+
### Scenario
28+
29+
This example uses a feature scenario to demonstrate various aspects of S3 conditional requests. The scenario is divided into three stages:
30+
31+
1. **Setup**: Create test buckets and objects.
32+
2. **Conditional Reads and Writes**: Explore S3 conditional requests by listing objects, attempting to read or write with conditional requests, and viewing request results.
33+
3. **Clean**: Delete all objects and buckets.
34+
35+
#### Running the scenario
36+
To run this feature scenario, run the command below from this directory:
37+
38+
```
39+
python scenario.py
40+
```
41+
42+
43+
## Additional resources
44+
45+
- [Amazon S3 Developer Guide](https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-requests.html)
46+
- [Amazon S3 API Reference](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html)
47+
- [boto3 Amazon S3 reference](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html)
48+
49+
---
50+
51+
© Amazon.com, Inc. or its affiliates. All Rights Reserved.
52+
53+
SPDX-License-Identifier: Apache-2.0
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# snippet-start:[python.example_code.s3.S3ConditionalRequests.wrapper]
5+
6+
import boto3
7+
import logging
8+
9+
from botocore.exceptions import ClientError
10+
11+
# Configure logging
12+
logger = logging.getLogger(__name__)
13+
14+
15+
# snippet-start:[python.example_code.s3.helper.S3ConditionalRequests]
16+
class S3ConditionalRequests:
17+
"""Encapsulates S3 conditional request operations."""
18+
19+
def __init__(self, s3_client):
20+
self.s3 = s3_client
21+
22+
@classmethod
23+
def from_client(cls):
24+
"""
25+
Instantiates this class from a Boto3 client.
26+
"""
27+
s3_client = boto3.client("s3")
28+
return cls(s3_client)
29+
30+
# snippet-end:[python.example_code.s3.helper.S3ConditionalRequests]
31+
32+
# snippet-start:[python.example_code.s3.GetObjectConditional]
33+
34+
def get_object_conditional(
35+
self,
36+
object_key: str,
37+
source_bucket: str,
38+
condition_type: str,
39+
condition_value: str,
40+
):
41+
"""
42+
Retrieves an object from Amazon S3 with a conditional request.
43+
44+
:param object_key: The key of the object to retrieve.
45+
:param source_bucket: The source bucket of the object.
46+
:param condition_type: The type of condition: 'IfMatch', 'IfNoneMatch', 'IfModifiedSince', 'IfUnmodifiedSince'.
47+
:param condition_value: The value to use for the condition.
48+
"""
49+
try:
50+
response = self.s3.get_object(
51+
Bucket=source_bucket,
52+
Key=object_key,
53+
**{condition_type: condition_value},
54+
)
55+
sample_bytes = response["Body"].read(20)
56+
print(
57+
f"\tConditional read successful. Here are the first 20 bytes of the object:\n"
58+
)
59+
print(f"\t{sample_bytes}")
60+
except ClientError as e:
61+
error_code = e.response["Error"]["Code"]
62+
if error_code == "PreconditionFailed":
63+
print("\tConditional read failed: Precondition failed")
64+
elif error_code == "304": # Not modified error code.
65+
print("\tConditional read failed: Object not modified")
66+
else:
67+
logger.error(f"Unexpected error: {error_code}")
68+
raise
69+
70+
# snippet-end:[python.example_code.s3.GetObjectConditional]
71+
72+
# snippet-start:[python.example_code.s3.PutObjectConditional]
73+
74+
def put_object_conditional(self, object_key: str, source_bucket: str, data: bytes):
75+
"""
76+
Uploads an object to Amazon S3 with a conditional request. Prevents overwrite
77+
using an IfNoneMatch condition for the object key.
78+
79+
:param object_key: The key of the object to upload.
80+
:param source_bucket: The source bucket of the object.
81+
:param data: The data to upload.
82+
"""
83+
try:
84+
self.s3.put_object(
85+
Bucket=source_bucket, Key=object_key, Body=data, IfNoneMatch="*"
86+
)
87+
print(
88+
f"\tConditional write successful for key {object_key} in bucket {source_bucket}."
89+
)
90+
except ClientError as e:
91+
error_code = e.response["Error"]["Code"]
92+
if error_code == "PreconditionFailed":
93+
print("\tConditional write failed: Precondition failed")
94+
else:
95+
logger.error(f"Unexpected error: {error_code}")
96+
raise
97+
98+
# snippet-end:[python.example_code.s3.PutObjectConditional]
99+
100+
# snippet-start:[python.example_code.s3.CopyObjectConditional]
101+
def copy_object_conditional(
102+
self,
103+
source_key: str,
104+
dest_key: str,
105+
source_bucket: str,
106+
dest_bucket: str,
107+
condition_type: str,
108+
condition_value: str,
109+
):
110+
"""
111+
Copies an object from one Amazon S3 bucket to another with a conditional request.
112+
113+
:param source_key: The key of the source object to copy.
114+
:param dest_key: The key of the destination object.
115+
:param source_bucket: The source bucket of the object.
116+
:param dest_bucket: The destination bucket of the object.
117+
:param condition_type: The type of condition to apply, e.g.
118+
'CopySourceIfMatch', 'CopySourceIfNoneMatch', 'CopySourceIfModifiedSince', 'CopySourceIfUnmodifiedSince'.
119+
:param condition_value: The value to use for the condition.
120+
"""
121+
try:
122+
self.s3.copy_object(
123+
Bucket=dest_bucket,
124+
Key=dest_key,
125+
CopySource={"Bucket": source_bucket, "Key": source_key},
126+
**{condition_type: condition_value},
127+
)
128+
print(
129+
f"\tConditional copy successful for key {dest_key} in bucket {dest_bucket}."
130+
)
131+
except ClientError as e:
132+
error_code = e.response["Error"]["Code"]
133+
if error_code == "PreconditionFailed":
134+
print("\tConditional copy failed: Precondition failed")
135+
elif error_code == "304": # Not modified error code.
136+
print("\tConditional copy failed: Object not modified")
137+
else:
138+
logger.error(f"Unexpected error: {error_code}")
139+
raise
140+
141+
# snippet-end:[python.example_code.s3.CopyObjectConditional]
142+
# snippet-end:[python.example_code.s3.S3ConditionalRequests.wrapper]

0 commit comments

Comments
 (0)