Skip to content

Commit 1a74dea

Browse files
authored
add IntegerSetAttribute (#52)
adding an IntegerSetAttribute
1 parent fe416dd commit 1a74dea

7 files changed

+79
-4
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ repos:
2323
- id: debug-statements
2424
- id: end-of-file-fixer
2525
- id: trailing-whitespace
26-
- repo: https://gitlab.com/pycqa/flake8
26+
- repo: https://github.com/pycqa/flake8
2727
rev: 3.9.2
2828
hooks:
2929
- id: flake8

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ This Python 3 library contains compound and high-level PynamoDB attributes:
22

33
- `FloatAttribute` – same as `NumberAttribute` but whose value is typed as `float`
44
- `IntegerAttribute` – same as `NumberAttribute` but whose value is typed as `int` (rather than `float`)
5+
- `IntegerSetAttribute` – same as `NumberSetAttribute` but whose value is typed as `int` (rather than `float`)
56
- `UnicodeDelimitedTupleAttribute` - a delimiter-separated value, useful for storing composite keys
67
- `UnicodeEnumAttribute` - serializes a string-valued `Enum` into a Unicode (`S`-typed) attribute
78
- `UnicodeProtobufEnumAttribute` - serializes a Protobuf enum into a Unicode (`S`-typed) attribute

pynamodb_attributes/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .integer import IntegerAttribute
33
from .integer_date import IntegerDateAttribute
44
from .integer_enum import IntegerEnumAttribute
5+
from .integer_set import IntegerSetAttribute
56
from .timedelta import TimedeltaAttribute
67
from .timedelta import TimedeltaMsAttribute
78
from .timedelta import TimedeltaUsAttribute
@@ -16,6 +17,7 @@
1617
__all__ = [
1718
"FloatAttribute",
1819
"IntegerAttribute",
20+
"IntegerSetAttribute",
1921
"IntegerDateAttribute",
2022
"IntegerEnumAttribute",
2123
"UnicodeDelimitedTupleAttribute",

pynamodb_attributes/integer_set.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Set
2+
3+
from pynamodb.attributes import Attribute
4+
from pynamodb.attributes import NumberSetAttribute
5+
6+
7+
class IntegerSetAttribute(Attribute[Set[int]]):
8+
"""
9+
Unlike NumberSetAttribute, this attribute has its type hinted as 'Set[int]'.
10+
"""
11+
12+
attr_type = NumberSetAttribute.attr_type
13+
null = NumberSetAttribute.null
14+
serialize = NumberSetAttribute.serialize # type: ignore
15+
deserialize = NumberSetAttribute.deserialize # type: ignore

setup.cfg

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ universal = 1
44
[metadata]
55
license_file = LICENSE
66
name = pynamodb-attributes
7-
version = 0.4.0
7+
version = 0.5.0
88
description = Common attributes for PynamoDB
99
long_description = file:README.md
1010
long_description_content_type = text/markdown
@@ -35,8 +35,8 @@ ignore = E203
3535
addopts = --cov=pynamodb_attributes --cov-report=term-missing:skip-covered --cov-report=xml --cov-report=html -vvv
3636
env =
3737
# We don't need real AWS access in unit tests
38-
D:AWS_ACCESS_KEY_ID=mock_aws_access_key_id
39-
D:AWS_SECRET_ACCESS_KEY=mock_aws_secret_access_key
38+
D:AWS_ACCESS_KEY_ID=1
39+
D:AWS_SECRET_ACCESS_KEY=1
4040

4141
[coverage:run]
4242
branch = True

tests/integer_set_attribute_test.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from typing import Set
2+
3+
import pytest
4+
from pynamodb.attributes import UnicodeAttribute
5+
from pynamodb.models import Model
6+
from typing_extensions import assert_type
7+
8+
from pynamodb_attributes import IntegerSetAttribute
9+
from tests.connection import _connection
10+
from tests.meta import dynamodb_table_meta
11+
12+
13+
class MyModel(Model):
14+
Meta = dynamodb_table_meta(__name__)
15+
16+
key = UnicodeAttribute(hash_key=True)
17+
value = IntegerSetAttribute(null=True)
18+
19+
20+
assert_type(MyModel.value, IntegerSetAttribute)
21+
assert_type(MyModel().value, Set[int])
22+
23+
24+
@pytest.fixture(scope="module", autouse=True)
25+
def create_table():
26+
MyModel.create_table()
27+
28+
29+
def test_serialization_non_null(uuid_key):
30+
model = MyModel()
31+
model.key = uuid_key
32+
model.value = {456, 789}
33+
model.save()
34+
35+
# verify underlying storage
36+
item = _connection(MyModel).get_item(uuid_key)
37+
assert item["Item"]["value"] == {"NS": ["456", "789"]}
38+
39+
# verify deserialization
40+
model = MyModel.get(uuid_key)
41+
assert model.value == {456, 789}
42+
43+
44+
def test_serialization_null(uuid_key):
45+
model = MyModel()
46+
model.key = uuid_key
47+
model.value = None
48+
model.save()
49+
50+
# verify underlying storage
51+
item = _connection(MyModel).get_item(uuid_key)
52+
assert "value" not in item["Item"]
53+
54+
# verify deserialization
55+
model = MyModel.get(uuid_key)
56+
assert model.value is None

tests/meta.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
def dynamodb_table_meta(table_suffix):
55
class Meta:
6+
region = os.getenv("DYNAMODB_REGION", "us-east-1")
67
host = os.getenv("DYNAMODB_URL", "http://localhost:8000")
78
table_name = f"pynamodb-attributes-{table_suffix}"
89
read_capacity_units = 10

0 commit comments

Comments
 (0)