Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tables] Removed TableEntity attribute wrapper #18489

Merged
merged 9 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sdk/tables/azure-data-tables/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## 12.0.0b7 (Unreleased)
**Breaking**
* The `TableEntity` object now acts exclusively like a dictionary, and no longer supports key access via attributes.
* Metadata of an entity is now accessed via `TableEntity.metadata` attribute rather than a method.
* Removed explicit `LinearRetry` and `ExponentialRetry` in favor of keyword parameter.
* Renamed `filter` parameter in query APIs to `query_filter`.
* The `location_mode` attribute on clients is now read-only. This has been added as a keyword parameter to the constructor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ def parse_connection_str(conn_str, credential, keyword_args):
}
except KeyError:
credential = conn_settings.get("sharedaccesssignature")
# if "sharedaccesssignature" in conn_settings:
# credential = AzureSasCredential(conn_settings['sharedaccesssignature'])
Comment on lines +375 to +376
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this wrapper?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm somewhat undecided....
I've left it as a TODO for me to revisit before GA.


primary = conn_settings.get("tableendpoint")
secondary = conn_settings.get("tablesecondaryendpoint")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,6 @@ def _convert_to_entity(entry_element):

# Timestamp is a known property
timestamp = properties.pop("Timestamp", None)
if timestamp:
# entity['Timestamp'] = _from_entity_datetime(timestamp)
entity["Timestamp"] = timestamp

for name, value in properties.items():
mtype = edmtypes.get(name)
Expand Down Expand Up @@ -237,9 +234,8 @@ def _convert_to_entity(entry_element):
etag = odata.get("etag")
if timestamp and not etag:
etag = "W/\"datetime'" + url_quote(timestamp) + "'\""
entity["etag"] = etag

entity._set_metadata() # pylint: disable=protected-access
entity._metadata = {'etag': etag, 'timestamp': timestamp} # pylint: disable=protected-access
return entity


Expand Down
50 changes: 4 additions & 46 deletions sdk/tables/azure-data-tables/azure/data/tables/_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,65 +6,23 @@
from enum import Enum
from typing import Any, Dict, Union, NamedTuple

from ._error import _ERROR_ATTRIBUTE_MISSING


class TableEntity(dict):
"""
An entity object. Can be accessed as a dict or as an obj. The attributes of
the entity will be created dynamically. For example, the following are both
valid::
TableEntity = TableEntity()
TableEntity.a = 'b'
TableEntity['x'] = 'y'
An Entity dictionary with additional metadata

"""
_metadata = None

def _set_metadata(self):
if "Timestamp" in self.keys():
self._metadata = { # pylint: disable=attribute-defined-outside-init
"etag": self.pop("etag"),
"timestamp": self.pop("Timestamp"),
}
else:
self._metadata = {"etag": self.pop("etag")} # pylint: disable=attribute-defined-outside-init

@property
def metadata(self):
# type: (...) -> Dict[str,Any]
# type: () -> Dict[str, Any]
"""Resets metadata to be a part of the entity
:return Dict of entity metadata
:rtype Dict[str, Any]
"""
return self._metadata

def __getattr__(self, name):
"""
:param name:name of entity entry
:type name: str
:return: TableEntity dictionary
:rtype: Dict[str,str]
"""
try:
return self[name]
except KeyError:
raise AttributeError(_ERROR_ATTRIBUTE_MISSING.format("TableEntity", name))

__setattr__ = dict.__setitem__

def __delattr__(self, name):
"""
:param name:name of entity entry
:type name: str
"""
try:
if name is not None:
del self[name]
except KeyError:
raise AttributeError(_ERROR_ATTRIBUTE_MISSING.format("TableEntity", name))

def __dir__(self):
return dir({}) + list(self.keys())


class EdmType(str, Enum):
"""
Expand Down
2 changes: 0 additions & 2 deletions sdk/tables/azure-data-tables/azure/data/tables/_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,8 @@ def _to_str(value):
return _str(value) if value is not None else None


_ERROR_ATTRIBUTE_MISSING = "'{0}' object has no attribute '{1}'"
_ERROR_TYPE_NOT_SUPPORTED = "Type not supported when sending data to the service: {0}."
_ERROR_VALUE_TOO_LARGE = "{0} is too large to be cast to type {1}."
_ERROR_ATTRIBUTE_MISSING = "'{0}' object has no attribute '{1}'"
_ERROR_UNKNOWN = "Unknown error ({0})"
_ERROR_VALUE_NONE = "{0} should not be None."
_ERROR_UNKNOWN_KEY_WRAP_ALGORITHM = "Unknown key wrap algorithm."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ async def create_and_get_entities(self):

# [START get_entity]
# Get Entity by partition and row key
got_entity = await table.get_entity(partition_key=my_entity['PartitionKey'],
row_key=my_entity['RowKey'])
got_entity = await table.get_entity(
partition_key=my_entity['PartitionKey'],
row_key=my_entity['RowKey']
)
print("Received entity: {}".format(got_entity))
# [END get_entity]

Expand Down Expand Up @@ -127,28 +129,28 @@ async def update_entities(self):
print("Inserted entity: {}".format(insert_entity))

# Try merge, and merge since already in table
created.text = "NewMarker"
created['text'] = "NewMarker"
merged_entity = await table.upsert_entity(mode=UpdateMode.MERGE, entity=entity)
print("Merged entity: {}".format(merged_entity))
# [END upsert_entity]

# [START update_entity]
# Update the entity
created.text = "NewMarker"
created['text'] = "NewMarker"
await table.update_entity(mode=UpdateMode.REPLACE, entity=created)

# Get the replaced entity
replaced = await table.get_entity(
partition_key=created.PartitionKey, row_key=created.RowKey)
partition_key=created['PartitionKey'], row_key=created['RowKey'])
print("Replaced entity: {}".format(replaced))

# Merge the entity
replaced.color = "Blue"
replaced['color'] = "Blue"
await table.update_entity(mode=UpdateMode.MERGE, entity=replaced)

# Get the merged entity
merged = await table.get_entity(
partition_key=replaced.PartitionKey, row_key=replaced.RowKey)
partition_key=replaced['PartitionKey'], row_key=replaced['RowKey'])
print("Merged entity: {}".format(merged))
# [END update_entity]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ def create_and_get_entities(self):

# [START get_entity]
# Get Entity by partition and row key
got_entity = table.get_entity(partition_key=my_entity['PartitionKey'],
row_key=my_entity['RowKey'])
got_entity = table.get_entity(
partition_key=my_entity['PartitionKey'],
row_key=my_entity['RowKey']
)
print("Received entity: {}".format(got_entity))
# [END get_entity]

Expand Down Expand Up @@ -122,28 +124,28 @@ def update_entities(self):
print("Inserted entity: {}".format(insert_entity))

# Try merge, and merge since already in table
created.text = "NewMarker"
created['text'] = "NewMarker"
merged_entity = table.upsert_entity(mode=UpdateMode.MERGE, entity=entity)
print("Merged entity: {}".format(merged_entity))
# [END upsert_entity]

# [START update_entity]
# Update the entity
created.text = "NewMarker"
created['text'] = "NewMarker"
table.update_entity(mode=UpdateMode.REPLACE, entity=created)

# Get the replaced entity
replaced = table.get_entity(
partition_key=created.PartitionKey, row_key=created.RowKey)
partition_key=created['PartitionKey'], row_key=created['RowKey'])
print("Replaced entity: {}".format(replaced))

# Merge the entity
replaced.color = "Blue"
replaced['color'] = "Blue"
table.update_entity(mode=UpdateMode.MERGE, entity=replaced)

# Get the merged entity
merged = table.get_entity(
partition_key=replaced.PartitionKey, row_key=replaced.RowKey)
partition_key=replaced['PartitionKey'], row_key=replaced['RowKey'])
print("Merged entity: {}".format(merged))
# [END update_entity]

Expand Down
4 changes: 2 additions & 2 deletions sdk/tables/azure-data-tables/tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,8 @@ def test_account_sas(self, tables_storage_account_name, tables_primary_storage_a

# Assert
assert len(entities) == 2
assert entities[0].text == u'hello'
assert entities[1].text == u'hello'
assert entities[0]['text'] == u'hello'
assert entities[1]['text'] == u'hello'
annatisch marked this conversation as resolved.
Show resolved Hide resolved
finally:
self._delete_table(table=table, ts=tsc)

Expand Down
4 changes: 2 additions & 2 deletions sdk/tables/azure-data-tables/tests/test_table_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,8 @@ async def test_account_sas(self, tables_storage_account_name, tables_primary_sto

# Assert
assert len(entities) == 2
assert entities[0].text == u'hello'
assert entities[1].text == u'hello'
assert entities[0]['text'] == u'hello'
assert entities[1]['text'] == u'hello'
finally:
await self._delete_table(table=table, ts=tsc)

Expand Down
Loading