Skip to content

Commit

Permalink
Type inference (#12980)
Browse files Browse the repository at this point in the history
* adding type hinting, changed tests to have INT64 as default for integers, not INT32

* added test for entity type, need to verify this works in python2

* fixed some silly linting issues

* using six to check for string and binary types

* adding six.integer_types as well

* modifying entity inference

* prefixing string with `u` to ensure string type matching

* unicoded strings in tests

* linting fixes

* addressing izzys nits
  • Loading branch information
seankane-msft authored Aug 17, 2020
1 parent 6aa1b6a commit d9ad885
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ def _deserialize_table_creation(response, _, headers):


def _from_entity_binary(value):
return EntityProperty(EdmType.BINARY, _decode_base64_to_bytes(value))
return EntityProperty(_decode_base64_to_bytes(value))


def _from_entity_int32(value):
return EntityProperty(EdmType.INT32, int(value))
return EntityProperty(int(value))


zero = datetime.timedelta(0) # same as 00:00
Expand Down
35 changes: 31 additions & 4 deletions sdk/tables/azure-data-tables/azure/data/tables/_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# license information.
# --------------------------------------------------------------------------
from enum import Enum
from datetime import datetime
from uuid import UUID
import six

from ._error import _ERROR_ATTRIBUTE_MISSING

Expand Down Expand Up @@ -75,17 +78,41 @@ class EntityProperty(object):
"""

def __init__(self,
type=None, # type: Union[str,EdmType] # pylint:disable=W0622
value=None # type: Any
):
value=None, # type: Any
type=None, # type: Union[str,EdmType] # pylint:disable=W0622
):
"""
Represents an Azure Table. Returned by list_tables.
:param Union[str, EdmType] type: The type of the property.
:param Any value: The value of the property.
"""
self.type = type
self.value = value
if type is not None:
self.type = type
elif isinstance(value, six.text_type):
try:
self.value = UUID(value)
self.type = EdmType.GUID
except ValueError:
self.type = EdmType.STRING
elif isinstance(value, six.binary_type):
self.type = EdmType.BINARY
elif isinstance(value, bool):
self.type = EdmType.BOOLEAN
elif isinstance(value, six.integer_types):
self.type = EdmType.INT64
elif isinstance(value, datetime):
self.type = EdmType.DATETIME
elif isinstance(value, float):
self.type = EdmType.DOUBLE
else:
raise ValueError(
"""Type of {} could not be inferred. Acceptable types are bytes, int, uuid.UUID,
datetime, string, int32, int64, float, and boolean. Refer to
azure.data.tables.EdmType for more information.
""".format(value)
)


class EdmType(str, Enum):
Expand Down
66 changes: 47 additions & 19 deletions sdk/tables/azure-data-tables/tests/test_table_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from azure.core import MatchConditions
from azure.core.exceptions import (
ResourceExistsError)
from azure.data.tables import EdmType, TableEntity, EntityProperty

from _shared.testcase import GlobalStorageAccountPreparer, TableTestCase, LogCaptured

Expand Down Expand Up @@ -78,7 +79,7 @@ def _create_random_entity_dict(self, pk=None, rk=None):
'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()),
'birthday': datetime(1970, 10, 4, tzinfo=tzutc()),
'binary': b'binary',
'other': EntityProperty(EdmType.INT32, 20),
'other': EntityProperty(20, EdmType.INT32),
'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833')
}
return Entity(**properties)
Expand Down Expand Up @@ -148,6 +149,33 @@ def _assert_updated_entity(self, entity):
self.assertIsInstance(entity.timestamp, datetime)

#--Test cases for batch ---------------------------------------------

def test_inferred_types(self):
# Arrange
# Act
entity = TableEntity()
entity.PartitionKey = '003'
entity.RowKey = 'batch_all_operations_together-1'
entity.test = EntityProperty(True)
entity.test2 = EntityProperty(b'abcdef')
entity.test3 = EntityProperty(u'c9da6455-213d-42c9-9a79-3e9149a57833')
entity.test4 = EntityProperty(datetime(1973, 10, 4, tzinfo=tzutc()))
entity.test5 = EntityProperty(u"stringystring")
entity.test6 = EntityProperty(3.14159)
entity.test7 = EntityProperty(100)
entity.test8 = EntityProperty(10, EdmType.INT32)

# Assert
self.assertEqual(entity.test.type, EdmType.BOOLEAN)
self.assertEqual(entity.test2.type, EdmType.BINARY)
self.assertEqual(entity.test3.type, EdmType.GUID)
self.assertEqual(entity.test4.type, EdmType.DATETIME)
self.assertEqual(entity.test5.type, EdmType.STRING)
self.assertEqual(entity.test6.type, EdmType.DOUBLE)
self.assertEqual(entity.test7.type, EdmType.INT64)
self.assertEqual(entity.test8.type, EdmType.INT32)


@pytest.mark.skip("pending")
@GlobalStorageAccountPreparer()
def test_batch_insert(self, resource_group, location, storage_account, storage_account_key):
Expand All @@ -158,10 +186,10 @@ def test_batch_insert(self, resource_group, location, storage_account, storage_a
entity = Entity()
entity.PartitionKey = '001'
entity.RowKey = 'batch_insert'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()

batch = self.table.create_batch()
Expand All @@ -185,10 +213,10 @@ def test_batch_update(self, resource_group, location, storage_account, storage_a
entity = Entity()
entity.PartitionKey = '001'
entity.RowKey = 'batch_update'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()
self.table.create_item(entity)

Expand Down Expand Up @@ -218,10 +246,10 @@ def test_batch_merge(self, resource_group, location, storage_account, storage_ac
entity = Entity()
entity.PartitionKey = '001'
entity.RowKey = 'batch_merge'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()
self.table.create_item(entity)

Expand Down Expand Up @@ -313,7 +341,7 @@ def test_batch_insert_replace(self, resource_group, location, storage_account, s
entity.test = True
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()

batch = self.table.create_batch()
Expand Down Expand Up @@ -343,7 +371,7 @@ def test_batch_insert_merge(self, resource_group, location, storage_account, sto
entity.test = True
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()

batch = self.table.create_batch()
Expand All @@ -370,10 +398,10 @@ def test_batch_delete(self, resource_group, location, storage_account, storage_a
entity = Entity()
entity.PartitionKey = '001'
entity.RowKey = 'batch_delete'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()
self.table.create_item(entity)

Expand All @@ -399,10 +427,10 @@ def test_batch_inserts(self, resource_group, location, storage_account, storage_
# Act
entity = Entity()
entity.PartitionKey = 'batch_inserts'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)

batch = self.table.create_batch()
for i in range(100):
Expand All @@ -428,10 +456,10 @@ def test_batch_all_operations_together(self, resource_group, location, storage_a
entity = Entity()
entity.PartitionKey = '003'
entity.RowKey = 'batch_all_operations_together-1'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()
self.table.create_item(entity)
entity.RowKey = 'batch_all_operations_together-2'
Expand Down Expand Up @@ -476,10 +504,10 @@ def test_batch_all_operations_together_context_manager(self, resource_group, loc
entity = Entity()
entity.PartitionKey = '003'
entity.RowKey = 'batch_all_operations_together-1'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()
self.table.create_item(entity)
entity.RowKey = 'batch_all_operations_together-2'
Expand Down Expand Up @@ -525,10 +553,10 @@ def test_batch_reuse(self, resource_group, location, storage_account, storage_ac
entity = Entity()
entity.PartitionKey = '003'
entity.RowKey = 'batch_all_operations_together-1'
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'value'
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime.utcnow()

batch = TableBatchClient()
Expand Down
22 changes: 11 additions & 11 deletions sdk/tables/azure-data-tables/tests/test_table_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def _create_random_entity_dict(self, pk=None, rk=None):
'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()),
'birthday': datetime(1970, 10, 4, tzinfo=tzutc()),
'binary': b'binary',
'other': EntityProperty(type=EdmType.INT32, value=20),
'other': EntityProperty(value=20, type=EdmType.INT32),
'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833')
}
return TableEntity(**properties)
Expand Down Expand Up @@ -160,7 +160,7 @@ def _assert_default_entity(self, entity, headers=None):
self.assertEqual(entity['birthday'], datetime(1970, 10, 4, tzinfo=tzutc()))
self.assertEqual(entity['binary'].value, b'binary')
self.assertIsInstance(entity['other'], EntityProperty)
self.assertEqual(entity['other'].type, EdmType.INT32)
self.assertEqual(entity['other'].type, EdmType.INT64)
self.assertEqual(entity['other'].value, 20)
self.assertEqual(entity['clsid'], uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833'))
# self.assertTrue('metadata' in entity.odata)
Expand Down Expand Up @@ -188,7 +188,7 @@ def _assert_default_entity_json_full_metadata(self, entity, headers=None):
self.assertEqual(entity['birthday'], datetime(1970, 10, 4, tzinfo=tzutc()))
self.assertEqual(entity['binary'].value, b'binary')
self.assertIsInstance(entity['other'], EntityProperty)
self.assertEqual(entity['other'].type, EdmType.INT32)
self.assertEqual(entity['other'].type, EdmType.INT64)
self.assertEqual(entity['other'].value, 20)
self.assertEqual(entity['clsid'], uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833'))
# self.assertTrue('metadata' in entity.odata)
Expand Down Expand Up @@ -222,7 +222,7 @@ def _assert_default_entity_json_no_metadata(self, entity, headers=None):
self.assertTrue(entity['birthday'].endswith('00Z'))
self.assertEqual(entity['binary'], b64encode(b'binary').decode('utf-8'))
self.assertIsInstance(entity['other'], EntityProperty)
self.assertEqual(entity['other'].type, EdmType.INT32)
self.assertEqual(entity['other'].type, EdmType.INT64)
self.assertEqual(entity['other'].value, 20)
self.assertEqual(entity['clsid'], 'c9da6455-213d-42c9-9a79-3e9149a57833')
# self.assertIsNone(entity.odata)
Expand Down Expand Up @@ -273,7 +273,7 @@ def _assert_merged_entity(self, entity):
self.assertEqual(entity.Birthday, datetime(1973, 10, 4, tzinfo=tzutc()))
self.assertEqual(entity.birthday, datetime(1991, 10, 4, tzinfo=tzutc()))
self.assertIsInstance(entity.other, EntityProperty)
self.assertEqual(entity.other.type, EdmType.INT32)
self.assertEqual(entity.other.type, EdmType.INT64)
self.assertEqual(entity.other.value, 20)
self.assertIsInstance(entity.clsid, uuid.UUID)
self.assertEqual(str(entity.clsid), 'c9da6455-213d-42c9-9a79-3e9149a57833')
Expand Down Expand Up @@ -417,13 +417,13 @@ def test_insert_entity_with_large_int32_value_throws(self, resource_group, locat
try:
# Act
dict32 = self._create_random_base_entity_dict()
dict32['large'] = EntityProperty(EdmType.INT32, 2 ** 31)
dict32['large'] = EntityProperty(2 ** 31, EdmType.INT32)

# Assert
with self.assertRaises(TypeError):
self.table.create_entity(entity=dict32)

dict32['large'] = EntityProperty(EdmType.INT32, -(2 ** 31 + 1))
dict32['large'] = EntityProperty(-(2 ** 31 + 1), EdmType.INT32)
with self.assertRaises(TypeError):
self.table.create_entity(entity=dict32)
finally:
Expand All @@ -438,13 +438,13 @@ def test_insert_entity_with_large_int64_value_throws(self, resource_group, locat
try:
# Act
dict64 = self._create_random_base_entity_dict()
dict64['large'] = EntityProperty(EdmType.INT64, 2 ** 63)
dict64['large'] = EntityProperty(2 ** 63)

# Assert
with self.assertRaises(TypeError):
self.table.create_entity(entity=dict64)

dict64['large'] = EntityProperty(EdmType.INT64, -(2 ** 63 + 1))
dict64['large'] = EntityProperty(-(2 ** 63 + 1))
with self.assertRaises(TypeError):
self.table.create_entity(entity=dict64)
finally:
Expand Down Expand Up @@ -1304,10 +1304,10 @@ def test_query_entities_large(self, resource_group, location, storage_account, s
entity = Entity()
entity.PartitionKey = 'large'
entity.RowKey = 'batch{0}-item{1}'.format(j, i)
entity.test = EntityProperty(EdmType.BOOLEAN, 'true')
entity.test = EntityProperty(True)
entity.test2 = 'hello world;' * 100
entity.test3 = 3
entity.test4 = EntityProperty(EdmType.INT64, '1234567890')
entity.test4 = EntityProperty(1234567890)
entity.test5 = datetime(2016, 12, 31, 11, 59, 59, 0)
batch.create_entity(entity)
self.ts.commit_batch(table_name, batch)
Expand Down
Loading

0 comments on commit d9ad885

Please sign in to comment.