Source code for gcloud.datastore.key
import copy
from itertools import izip
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
from gcloud.datastore.dataset import Dataset
[docs]class Key(object):
# TODO: Determine if this really should be immutable.
"""
An immutable representation of a datastore Key.
"""
def __init__(self, dataset=None, namespace=None, path=None):
self._dataset = dataset
self._namespace = namespace
self._path = path or [{'kind': ''}]
def _clone(self):
"""Duplicates the Key.
We make a shallow copy of the :class:`gcloud.datastore.dataset.Dataset`
because it holds a reference an authenticated connection,
which we don't want to lose.
"""
clone = copy.deepcopy(self)
clone._dataset = self._dataset # Make a shallow copy of the Dataset.
return clone
@classmethod
[docs] def from_protobuf(cls, pb, dataset=None):
path = []
for element in pb.path_element:
element_dict = {'kind': element.kind}
if element.HasField('id'):
element_dict['id'] = element.id
elif element.HasField('name'):
element_dict['name'] = element.name
path.append(element_dict)
if not dataset:
dataset = Dataset(id=pb.partition_id.dataset_id)
return cls(path=path, dataset=dataset)
[docs] def to_protobuf(self):
key = datastore_pb.Key()
# Technically a dataset is required to do anything with the key,
# but we shouldn't throw a cryptic error if one isn't provided
# in the initializer.
if self.dataset():
# Apparently 's~' is a prefix for High-Replication and is necessary here.
# Another valid preflix is 'e~' indicating EU datacenters.
dataset_id = self.dataset().id()
if dataset_id:
if dataset_id[:2] not in ['s~', 'e~']:
dataset_id = 's~' + dataset_id
key.partition_id.dataset_id = dataset_id
if self._namespace:
key.partition_id.namespace = self._namespace
for item in self.path():
element = key.path_element.add()
if 'kind' in item:
element.kind = item['kind']
if 'id' in item:
element.id = item['id']
if 'name' in item:
element.name = item['name']
return key
@classmethod
[docs] def from_path(cls, *args, **kwargs):
path = []
items = iter(args)
for kind, id_or_name in izip(items, items):
entry = {'kind': kind}
if isinstance(id_or_name, basestring):
entry['name'] = id_or_name
else:
entry['id'] = id_or_name
path.append(entry)
kwargs['path'] = path
return cls(**kwargs)
[docs] def is_partial(self):
return (self.id_or_name() is None)
[docs] def dataset(self, dataset=None):
if dataset:
clone = self._clone()
clone._dataset = dataset
return clone
else:
return self._dataset
[docs] def namespace(self, namespace=None):
if namespace:
clone = self._clone()
clone._namespace = namespace
return clone
else:
return self._namespace
[docs] def path(self, path=None):
if path:
clone = self._clone()
clone._path = path
return clone
else:
return self._path
[docs] def kind(self, kind=None):
if kind:
clone = self._clone()
clone._path[-1]['kind'] = kind
return clone
elif self.path():
return self._path[-1]['kind']
[docs] def id(self, id=None):
if id:
clone = self._clone()
clone._path[-1]['id'] = id
return clone
elif self.path():
return self._path[-1].get('id')
[docs] def name(self, name=None):
if name:
clone = self._clone()
clone._path[-1]['name'] = name
return clone
elif self.path():
return self._path[-1].get('name')
[docs] def id_or_name(self):
return self.id() or self.name()
[docs] def parent(self):
raise NotImplementedError
def __repr__(self):
return '<Key%s>' % self.path()