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()