Skip to content

Commit

Permalink
1.1.0 - naming improvements and added utilties
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Gaultney committed May 28, 2020
1 parent e07a044 commit b0f32d0
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 9 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### 1.1.0

- Added `require_index` utility to `dynamodb.query`
- New, clearer name `page` to replace `From` in `dynamodb.query`. The
previous name remains but is a deprecated alias.
- Made `dynamodb.batch_get.items_only` a bit more generic.

### 1.0.3

Fixed install_requires for Python > 3.6
Expand Down
39 changes: 39 additions & 0 deletions examples/ddb_batch_get.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python
"""This little example script only supports BatchGet on tables with
simple (non-composite) keys (i.e., the base index is HASH only, not
HASH+RANGE) for the sake of keeping the CLI manageable.
However, BatchGetItem itself supports HASH+RANGE keys just fine, where
a key would look something like `dict(activity_group='XOi',
id='job-1234')`.
"""
import argparse
from pprint import pprint

import boto3

from xoto3.dynamodb.batch_get import BatchGetItem, items_only


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("table_name")
parser.add_argument("ids", nargs="+")
parser.add_argument("--key-name", default="id", help="the name of your hash key attribute")
args = parser.parse_args()

table = boto3.resource("dynamodb").Table(args.table_name)

for item in items_only( # we don't care about keys, nor items that aren't found
BatchGetItem(
table,
# make a proper ItemKey (a dict) for each of the things you're looking to get
({args.key_name: id} for id in args.ids),
# this is a memory-efficient generator but you can pass a list or tuple of dicts too
)
):
pprint(item)


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion xoto3/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""xoto3"""
__version__ = "1.0.3"
__version__ = "1.1.0"
__author__ = "Peter Gaultney"
__author_email__ = "[email protected]"
14 changes: 8 additions & 6 deletions xoto3/dynamodb/batch_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def BatchGetItem(
KeyItemPairs, e.g. `KeyItemPair(key={'id': 'petros'}, val={'id':
'petros', 'age': 88})`.
If all you want is the non-empty items, wrap this call in the
If all you want is the non-empty (existing) items, wrap this call in the
provided `items_only` utility.
"""
Expand Down Expand Up @@ -243,13 +243,15 @@ def _kv_tuple_to_key(kv_tuple, key_names):
return {key_names[i]: kv_tuple[i] for i in range(len(key_names))}


def items_only(key_item_pairs: ty.Iterable[KeyItemPair]) -> ty.Iterable[Item]:
"""Use with BatchGetItemsByKeys if you just want the items that were
def items_only(
key_item_pairs: ty.Iterable[ty.Union[KeyItemPair, KeyTupleItemPair]]
) -> ty.Iterable[Item]:
"""Use with BatchGetItem if you just want the items that were
found instead of the full iterable of all the keys you requested
alongside their respective item or empty dict if the item wasn't
found.
"""
for kip in key_item_pairs:
if kip.item:
yield kip.item
for key, item in key_item_pairs:
if item:
yield item
25 changes: 24 additions & 1 deletion xoto3/dynamodb/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from .types import Index, KeyAttributeType, TableQuery
from .utils.index import hash_key_name, range_key_name
from .utils.index import find_index, require_index # noqa # included only for cleaner imports


def single_partition(index: Index, partition_value: KeyAttributeType) -> TableQuery:
Expand Down Expand Up @@ -54,13 +55,35 @@ def tx_query(query: TableQuery) -> TableQuery:
return tx_query


def From(last_evaluated_key: dict):
def page(last_evaluated_key: dict):
"""Resume a query on the page represented by the LastEvaluatedKey you
previously received.
Note that there are pagination utilities in `paginate` if you don't
actually need to maintain this state (e.g., across client calls in a
RESTful service) and simply want to iterate through all the results.
"""

def tx_query(query: TableQuery) -> TableQuery:
return dict(query, ExclusiveStartKey=last_evaluated_key) if last_evaluated_key else query

return tx_query


From = page
"""Deprecated name - prefer 'page'
The name From overlaps with SQL parlance about selecting a table,
which is absolutely not what we're doing here. This was intended
as shorthand for 'starting from', but even that overlaps with the
concepts of 'greater than or equal' or 'less than or equal' for a
range query.
'page' makes it clearer, hopefully, that what is in view is
specifically a pagination of a previous query.
"""


def within_range(
index: Index, *, gte: Optional[KeyAttributeType] = None, lte: Optional[KeyAttributeType] = None
):
Expand Down
2 changes: 1 addition & 1 deletion xoto3/dynamodb/update/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .builders import build_update # noqa
from .diff import build_update_diff # noqa
from .diff import build_update_diff, select_attributes_for_set_and_remove # noqa
from .core import UpdateItem, DiffedUpdateItem # noqa
from .versioned import versioned_diffed_update_item, VersionedUpdateFailure # noqa
7 changes: 7 additions & 0 deletions xoto3/dynamodb/utils/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,10 @@ def find_index(table: TableResource, hash_key: str, range_key: str) -> Optional[
if hash_key_name(index) == hash_key and range_key_name(index) == range_key:
return index
return None


def require_index(table: TableResource, hash_key: str, range_key: str) -> Index:
"""Raises if the index is not found. A common pattern."""
index = find_index(table, hash_key, range_key)
assert index, f"Index ({hash_key}, {range_key}) was not found in table {table.name}"
return index

0 comments on commit b0f32d0

Please sign in to comment.