awsimple 3.10.0__py3-none-any.whl → 3.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of awsimple might be problematic. Click here for more details.

awsimple/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from .__version__ import __application_name__, __version__, __author__, __title__
2
2
  from .mock import use_moto_mock_env_var, is_mock, use_localstack_env_var, is_using_localstack
3
+ from .exceptions import DynamoDBItemAlreadyExists
3
4
  from .aws import AWSAccess, AWSimpleException, boto_error_to_string
4
5
  from .cache import get_disk_free, get_directory_size, lru_cache_write, CacheAccess, CACHE_DIR_ENV_VAR
5
6
  from .dynamodb import DynamoDBAccess, dict_to_dynamodb, DBItemNotFound, DynamoDBTableNotFound, dynamodb_to_json, dynamodb_to_dict, QuerySelection, DictKey, convert_serializable_special_cases
awsimple/__version__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  __application_name__ = "awsimple"
2
2
  __title__ = __application_name__
3
3
  __author__ = "abel"
4
- __version__ = "3.10.0"
4
+ __version__ = "3.11.0"
5
5
  __author_email__ = "j@abel.co"
6
6
  __url__ = "https://github.com/jamesabel/awsimple"
7
7
  __download_url__ = "https://github.com/jamesabel/awsimple"
awsimple/dynamodb.py CHANGED
@@ -28,7 +28,8 @@ from typeguard import typechecked
28
28
  from dictim import dictim # type: ignore
29
29
  from yasf import sf
30
30
 
31
- from awsimple import CacheAccess, __application_name__, AWSimpleException
31
+ from . import CacheAccess, __application_name__, AWSimpleException
32
+ from .exceptions import DynamoDBItemAlreadyExists
32
33
 
33
34
  # don't require pillow, but convert images with it if it exists
34
35
  pil_exists = False
@@ -219,6 +220,10 @@ def _is_valid_db_pickled_file(file_path: Path, cache_life: Union[float, int, Non
219
220
 
220
221
 
221
222
  class DynamoDBAccess(CacheAccess):
223
+ """
224
+ AWS DynamoDB access
225
+ """
226
+
222
227
  @typechecked()
223
228
  def __init__(self, table_name: Union[str, None] = None, **kwargs):
224
229
  """
@@ -733,6 +738,36 @@ class DynamoDBAccess(CacheAccess):
733
738
  except self.client.exceptions.ResourceNotFoundException:
734
739
  raise DynamoDBTableNotFound(str(self.table_name))
735
740
 
741
+ def put_item_if_not_exists(self, item: dict):
742
+ """
743
+ Put (write) a DynamoDB table dict item if it doesn't already exist. If the item already exists, it is not modified and DynamoDBItemAlreadyExists is raised.
744
+
745
+ :param item: item
746
+ """
747
+ assert self.resource is not None
748
+ primary_partition_key = self.get_primary_partition_key()
749
+ primary_sort_key = self.get_primary_sort_key()
750
+ try:
751
+ table = self.resource.Table(self.table_name)
752
+ if primary_sort_key is None:
753
+ table.put_item(Item=item, ConditionExpression="attribute_not_exists(#pk)", ExpressionAttributeNames={"#pk": primary_partition_key})
754
+ else:
755
+ table.put_item(
756
+ Item=item,
757
+ ConditionExpression="attribute_not_exists(#pk) AND attribute_not_exists(#sk)",
758
+ ExpressionAttributeNames={"#pk": primary_partition_key, "#sk": primary_sort_key},
759
+ )
760
+ self.metadata_table.update_table_mtime()
761
+ already_exists = False
762
+ except self.client.exceptions.ResourceNotFoundException:
763
+ raise DynamoDBTableNotFound(str(self.table_name))
764
+ except self.client.exceptions.ConditionalCheckFailedException:
765
+ already_exists = True
766
+ if already_exists:
767
+ primary_partition_value = str(item.get(primary_partition_key, ""))
768
+ primary_sort_value = str(item.get(primary_sort_key, ""))
769
+ raise DynamoDBItemAlreadyExists(str(self.table_name), primary_partition_value, primary_sort_value)
770
+
736
771
  # cant' do a @typechecked() since optional item requires a single type
737
772
  def get_item(
738
773
  self, partition_key: Union[str, None] = None, partition_value: Union[str, int, None] = None, sort_key: Union[str, None] = None, sort_value: Union[str, int, None] = None
@@ -886,6 +921,9 @@ class DynamoDBAccess(CacheAccess):
886
921
  if len(filtered_row) > 0:
887
922
  filtered_rows.append(filtered_row)
888
923
  field_names = sorted(field_names_set)
924
+ primary_partition_key = self.get_primary_partition_key()
925
+ primary_sort_key = self.get_primary_sort_key()
926
+ filtered_rows.sort(key=lambda x: (x[primary_partition_key], x.get(primary_sort_key))) # sort by primary partition key, then sort key if it exists
889
927
 
890
928
  file_path.parent.mkdir(parents=True, exist_ok=True)
891
929
  with open(file_path, "w", newline="") as csvfile:
@@ -897,7 +935,6 @@ class DynamoDBAccess(CacheAccess):
897
935
  log.info(f"wrote {len(filtered_rows)} rows to {file_path}")
898
936
 
899
937
 
900
-
901
938
  metadata_table_name = f"__{__application_name__}_metadata__"
902
939
 
903
940
 
awsimple/exceptions.py ADDED
@@ -0,0 +1,16 @@
1
+ class AWSimpleExceptionBase(Exception):
2
+ """Base exception for AWSimple errors."""
3
+
4
+ pass
5
+
6
+
7
+ class DynamoDBItemAlreadyExists(AWSimpleExceptionBase):
8
+ """Raised when an item already exists in DynamoDB."""
9
+
10
+ def __init__(self, table_name: str, primary_partition_value: str, primary_sort_value: str):
11
+ if len(primary_sort_value) < 1:
12
+ sort_message = ""
13
+ else:
14
+ sort_message = f" and {primary_sort_value=}"
15
+ message = f"Item with {primary_partition_value=}{sort_message} already exists in table {table_name}"
16
+ super().__init__(message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awsimple
3
- Version: 3.10.0
3
+ Version: 3.11.0
4
4
  Summary: Simple AWS API for S3, DynamoDB, SNS, and SQS
5
5
  Home-page: https://github.com/jamesabel/awsimple
6
6
  Download-URL: https://github.com/jamesabel/awsimple
@@ -1,9 +1,10 @@
1
- awsimple/__init__.py,sha256=yo9COyuJZonzuW_5h2yvsQOtTqnwh8e_iMsMBqQxir0,964
2
- awsimple/__version__.py,sha256=7f2QtdJhuSXcZ0FT7TWbKMnjn0kMNBF-iRvyVI8FxF8,324
1
+ awsimple/__init__.py,sha256=X7MV9cg5V1dYf86tf3TonaU-LMQWenPIW2PnjxoXYB4,1015
2
+ awsimple/__version__.py,sha256=ey8QJTcIT2kFHhegrTZ0d01xX-GQD2kvye8fTm8hdp4,324
3
3
  awsimple/aws.py,sha256=NbRu1v_J5j2-pcefNZ5Xggii3mM9nHpeHMz9L9K9r-U,7653
4
4
  awsimple/cache.py,sha256=_LvPx76215t8KhAJOin6Pe2b4lWpB6kbKpdzgR4FeA4,7206
5
- awsimple/dynamodb.py,sha256=07Qz9j0GB5de5k6-OraDR3YCxTzIJa72jKGu5XWG9aY,40674
5
+ awsimple/dynamodb.py,sha256=FZTk_BWOZYr3wYW7jfIgQOYaKQ_SMRMfozAT1UbMXiM,42662
6
6
  awsimple/dynamodb_miv.py,sha256=4xPxQDYkIM-BVDGyAre6uqwJHsxguEbHbof8ztt-V7g,4645
7
+ awsimple/exceptions.py,sha256=dZQn8BKmZVlIl2-MIT5SAHRcwYK_bjzzWeG1AFbDUEg,609
7
8
  awsimple/logs.py,sha256=s9FhdDFWjfxGCVDx-FNTPgJ-YN1AOAgz4HNxTVfRARE,4108
8
9
  awsimple/mock.py,sha256=eScbnxFF9xAosOAsL-NZgp_P-fezB6StQMkb85Y3TNo,574
9
10
  awsimple/platform.py,sha256=TObvLIVgRGh-Mh4ZCxFfxAmrn8KNMHktr6XkDZX8JYE,376
@@ -12,9 +13,9 @@ awsimple/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
13
  awsimple/s3.py,sha256=ydYLDj51b38lYI6LsJlc4i0PDj2vnWBuSkzdRKcWAUg,23858
13
14
  awsimple/sns.py,sha256=T_FyN8eSmBPo213HOfB3UBlMrvtBK768IaRo_ks-7do,3526
14
15
  awsimple/sqs.py,sha256=9ZY7161CpmYpcxlCFIfW8bvMn9SGl4cgGR79I4MFLDk,17281
15
- awsimple-3.10.0.dist-info/licenses/LICENSE,sha256=d956YAgtDaxgxQmccyUk__EfhORZyBjvmJ8pq6zYTxk,1093
16
- awsimple-3.10.0.dist-info/licenses/LICENSE.txt,sha256=d956YAgtDaxgxQmccyUk__EfhORZyBjvmJ8pq6zYTxk,1093
17
- awsimple-3.10.0.dist-info/METADATA,sha256=u4tpQmqUL8lZXkDPRbKC83mgZ_Sk8fcK5h7EFOdMNxU,6641
18
- awsimple-3.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- awsimple-3.10.0.dist-info/top_level.txt,sha256=mwqCoH_8vAaK6iYioiRbasXmVCQM7AK3grNWOKp2VHE,9
20
- awsimple-3.10.0.dist-info/RECORD,,
16
+ awsimple-3.11.0.dist-info/licenses/LICENSE,sha256=d956YAgtDaxgxQmccyUk__EfhORZyBjvmJ8pq6zYTxk,1093
17
+ awsimple-3.11.0.dist-info/licenses/LICENSE.txt,sha256=d956YAgtDaxgxQmccyUk__EfhORZyBjvmJ8pq6zYTxk,1093
18
+ awsimple-3.11.0.dist-info/METADATA,sha256=eIOptt5qOoQFZMW6qipEzGLbIuBhA46K3JZNqSabPq0,6641
19
+ awsimple-3.11.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ awsimple-3.11.0.dist-info/top_level.txt,sha256=mwqCoH_8vAaK6iYioiRbasXmVCQM7AK3grNWOKp2VHE,9
21
+ awsimple-3.11.0.dist-info/RECORD,,