App Engine Python SDK version 1.9.12
[gae.git] / python / google / appengine / ext / ndb / key.py
blob31247848e5d51a9725865481216ae19f2438f3cd
1 """The Key class, and associated utilities.
3 A Key encapsulates the following pieces of information, which together
4 uniquely designate a (possible) entity in the App Engine datastore:
6 - an application id (a string)
7 - a namespace (a string)
8 - a list of one or more (kind, id) pairs where kind is a string and id
9 is either a string or an integer.
11 The application id must always be part of the key, but since most
12 applications can only access their own entities, it defaults to the
13 current application id and you rarely need to worry about it. It must
14 not be empty.
16 The namespace designates a top-level partition of the key space for a
17 particular application. If you've never heard of namespaces, you can
18 safely ignore this feature.
20 Most of the action is in the (kind, id) pairs. A key must have at
21 least one (kind, id) pair. The last (kind, id) pair gives the kind
22 and the id of the entity that the key refers to, the others merely
23 specify a 'parent key'.
25 The kind is a string giving the name of the model class used to
26 represent the entity. (In more traditional databases this would be
27 the table name.) A model class is a Python class derived from
28 ndb.Model; see the documentation for ndb/model.py. Only the class
29 name itself is used as the kind. This means all your model classes
30 must be uniquely named within one application. You can override this
31 on a per-class basis.
33 The id is either a string or an integer. When the id is a string, the
34 application is in control of how it assigns ids: For example, if you
35 could use an email address as the id for Account entities.
37 To use integer ids, you must let the datastore choose a unique id for
38 an entity when it is first inserted into the datastore. You can set
39 the id to None to represent the key for an entity that hasn't yet been
40 inserted into the datastore. The final key (including the assigned
41 id) will be returned after the entity is successfully inserted into
42 the datastore.
44 A key for which the id of the last (kind, id) pair is set to None is
45 called an incomplete key. Such keys can only be used to insert
46 entities into the datastore.
48 A key with exactly one (kind, id) pair is called a top level key or a
49 root key. Top level keys are also used as entity groups, which play a
50 role in transaction management.
52 If there is more than one (kind, id) pair, all but the last pair
53 represent the 'ancestor path', also known as the key of the 'parent
54 entity'.
56 Other constraints:
58 - Kinds and string ids must not be empty and must be at most 500 bytes
59 long (after UTF-8 encoding, if given as Python unicode objects).
60 NOTE: This is defined as a module level constant _MAX_KEYPART_BYTES.
62 - Integer ids must be at least 1 and less than 2**63.
64 For more info about namespaces, see
65 http://code.google.com/appengine/docs/python/multitenancy/overview.html.
66 The namespace defaults to the 'default namespace' selected by the
67 namespace manager. To explicitly select the empty namespace pass
68 namespace=''.
69 """
71 __author__ = 'guido@google.com (Guido van Rossum)'
73 import base64
74 import os
76 from .google_imports import datastore_errors
77 from .google_imports import datastore_types
78 from .google_imports import namespace_manager
79 from .google_imports import entity_pb
81 from . import utils
83 __all__ = ['Key']
85 _MAX_LONG = 2L ** 63 # Use 2L, see issue 65. http://goo.gl/ELczz
86 _MAX_KEYPART_BYTES = 500
89 class Key(object):
90 """An immutable datastore key.
92 For flexibility and convenience, multiple constructor signatures are
93 supported.
95 The primary way to construct a key is using positional arguments:
96 - Key(kind1, id1, kind2, id2, ...).
98 This is shorthand for either of the following two longer forms:
99 - Key(pairs=[(kind1, id1), (kind2, id2), ...])
100 - Key(flat=[kind1, id1, kind2, id2, ...])
102 Either of the above constructor forms can additionally pass in another
103 key using parent=<key>. The (kind, id) pairs of the parent key are
104 inserted before the (kind, id) pairs passed explicitly.
106 You can also construct a Key from a 'url-safe' encoded string:
107 - Key(urlsafe=<string>)
109 For esoteric purposes the following constructors exist:
110 - Key(reference=<reference>) -- passing in a low-level Reference object
111 - Key(serialized=<string>) -- passing in a serialized low-level Reference
112 - Key(<dict>) -- for unpickling, the same as Key(**<dict>)
114 The 'url-safe' string is really a websafe-base64-encoded serialized
115 Reference, but it's best to think of it as just an opaque unique
116 string.
118 Additional constructor keyword arguments:
119 - app=<string> -- specify the application id
120 - namespace=<string> -- specify the namespace
122 If a Reference is passed (using one of reference, serialized or
123 urlsafe), the args and namespace keywords must match what is already
124 present in the Reference (after decoding if necessary). The parent
125 keyword cannot be combined with a Reference in any form.
128 Keys are immutable, which means that a Key object cannot be modified
129 once it has been created. This is enforced by the implementation as
130 well as Python allows.
132 For access to the contents of a key, the following methods and
133 operations are supported:
135 - repr(key), str(key) -- return a string representation resembling
136 the shortest constructor form, omitting the app and namespace
137 unless they differ from the default value.
139 - key1 == key2, key1 != key2 -- comparison for equality between Keys.
141 - hash(key) -- a hash value sufficient for storing Keys in a dict.
143 - key.pairs() -- a tuple of (kind, id) pairs.
145 - key.flat() -- a tuple of flattened kind and id values, i.e.
146 (kind1, id1, kind2, id2, ...).
148 - key.app() -- the application id.
150 - key.id() -- the string or integer id in the last (kind, id) pair,
151 or None if the key is incomplete.
153 - key.string_id() -- the string id in the last (kind, id) pair,
154 or None if the key has an integer id or is incomplete.
156 - key.integer_id() -- the integer id in the last (kind, id) pair,
157 or None if the key has a string id or is incomplete.
159 - key.namespace() -- the namespace.
161 - key.kind() -- a shortcut for key.pairs()[-1][0].
163 - key.parent() -- a Key constructed from all but the last (kind, id)
164 pairs.
166 - key.urlsafe() -- a websafe-base64-encoded serialized Reference.
168 - key.serialized() -- a serialized Reference.
170 - key.reference() -- a Reference object. The caller promises not to
171 mutate it.
173 Keys also support interaction with the datastore; these methods are
174 the only ones that engage in any kind of I/O activity. For Future
175 objects, see the document for ndb/tasklets.py.
177 - key.get() -- return the entity for the Key.
179 - key.get_async() -- return a Future whose eventual result is
180 the entity for the Key.
182 - key.delete() -- delete the entity for the Key.
184 - key.delete_async() -- asynchronously delete the entity for the Key.
186 Keys may be pickled.
188 Subclassing Key is best avoided; it would be hard to get right.
191 __slots__ = ['__reference', '__pairs', '__app', '__namespace']
193 def __new__(cls, *_args, **kwargs):
194 """Constructor. See the class docstring for arguments."""
195 if _args:
196 if len(_args) == 1 and isinstance(_args[0], dict):
197 if kwargs:
198 raise TypeError('Key() takes no keyword arguments when a dict is the '
199 'the first and only non-keyword argument (for '
200 'unpickling).')
201 kwargs = _args[0]
202 else:
203 if 'flat' in kwargs:
204 raise TypeError('Key() with positional arguments '
205 'cannot accept flat as a keyword argument.')
206 kwargs['flat'] = _args
207 self = super(Key, cls).__new__(cls)
208 # Either __reference or (__pairs, __app, __namespace) must be set.
209 # Either one fully specifies a key; if both are set they must be
210 # consistent with each other.
211 if 'reference' in kwargs or 'serialized' in kwargs or 'urlsafe' in kwargs:
212 self.__reference = _ConstructReference(cls, **kwargs)
213 self.__pairs = None
214 self.__app = None
215 self.__namespace = None
216 elif 'pairs' in kwargs or 'flat' in kwargs:
217 self.__reference = None
218 (self.__pairs,
219 self.__app,
220 self.__namespace) = self._parse_from_args(**kwargs)
221 else:
222 raise TypeError('Key() cannot create a Key instance without arguments.')
223 return self
225 @staticmethod
226 def _parse_from_args(pairs=None, flat=None, app=None, namespace=None,
227 parent=None):
228 if flat:
229 if pairs is not None:
230 raise TypeError('Key() cannot accept both flat and pairs arguments.')
231 if len(flat) % 2:
232 raise ValueError('Key() must have an even number of positional '
233 'arguments.')
234 pairs = [(flat[i], flat[i + 1]) for i in xrange(0, len(flat), 2)]
235 else:
236 pairs = list(pairs)
237 if not pairs:
238 raise TypeError('Key must consist of at least one pair.')
239 for i, (kind, id) in enumerate(pairs):
240 if isinstance(id, unicode):
241 id = id.encode('utf8')
242 elif id is None:
243 if i + 1 < len(pairs):
244 raise datastore_errors.BadArgumentError(
245 'Incomplete Key entry must be last')
246 else:
247 if not isinstance(id, (int, long, str)):
248 raise TypeError('Key id must be a string or a number; received %r' %
250 if isinstance(kind, type):
251 kind = kind._get_kind()
252 if isinstance(kind, unicode):
253 kind = kind.encode('utf8')
254 if not isinstance(kind, str):
255 raise TypeError('Key kind must be a string or Model class; '
256 'received %r' % kind)
257 if not id:
258 id = None
259 pairs[i] = (kind, id)
260 if parent is not None:
261 if not isinstance(parent, Key):
262 raise datastore_errors.BadValueError(
263 'Expected Key instance, got %r' % parent)
264 if not parent.id():
265 raise datastore_errors.BadArgumentError(
266 'Parent cannot have incomplete key')
267 pairs[:0] = parent.pairs()
268 if app:
269 if app != parent.app():
270 raise ValueError('Cannot specify a different app %r '
271 'than the parent app %r' %
272 (app, parent.app()))
273 else:
274 app = parent.app()
275 if namespace is not None:
276 if namespace != parent.namespace():
277 raise ValueError('Cannot specify a different namespace %r '
278 'than the parent namespace %r' %
279 (namespace, parent.namespace()))
280 else:
281 namespace = parent.namespace()
282 if not app:
283 app = _DefaultAppId()
284 if namespace is None:
285 namespace = _DefaultNamespace()
286 return tuple(pairs), app, namespace
288 def __repr__(self):
289 """String representation, used by str() and repr().
291 We produce a short string that conveys all relevant information,
292 suppressing app and namespace when they are equal to the default.
294 # TODO: Instead of "Key('Foo', 1)" perhaps return "Key(Foo, 1)" ?
295 args = []
296 for item in self.flat():
297 if not item:
298 args.append('None')
299 elif isinstance(item, basestring):
300 if not isinstance(item, str):
301 raise TypeError('Key item is not an 8-bit string %r' % item)
302 args.append(repr(item))
303 else:
304 args.append(str(item))
305 if self.app() != _DefaultAppId():
306 args.append('app=%r' % self.app())
307 if self.namespace() != _DefaultNamespace():
308 args.append('namespace=%r' % self.namespace())
309 return 'Key(%s)' % ', '.join(args)
311 __str__ = __repr__
313 def __hash__(self):
314 """Hash value, for use in dict lookups."""
315 # This ignores app and namespace, which is fine since hash()
316 # doesn't need to return a unique value -- it only needs to ensure
317 # that the hashes of equal keys are equal, not the other way
318 # around.
319 return hash(tuple(self.pairs()))
321 def __eq__(self, other):
322 """Equality comparison operation."""
323 # This does not use __tuple() because it is usually enough to
324 # compare pairs(), and we're performance-conscious here.
325 if not isinstance(other, Key):
326 return NotImplemented
327 return (tuple(self.pairs()) == tuple(other.pairs()) and
328 self.app() == other.app() and
329 self.namespace() == other.namespace())
331 def __ne__(self, other):
332 """The opposite of __eq__."""
333 if not isinstance(other, Key):
334 return NotImplemented
335 return not self.__eq__(other)
337 def __tuple(self):
338 """Helper to return an orderable tuple."""
339 return (self.app(), self.namespace(), self.pairs())
341 def __lt__(self, other):
342 """Less than ordering."""
343 if not isinstance(other, Key):
344 return NotImplemented
345 return self.__tuple() < other.__tuple()
347 def __le__(self, other):
348 """Less than or equal ordering."""
349 if not isinstance(other, Key):
350 return NotImplemented
351 return self.__tuple() <= other.__tuple()
353 def __gt__(self, other):
354 """Greater than ordering."""
355 if not isinstance(other, Key):
356 return NotImplemented
357 return self.__tuple() > other.__tuple()
359 def __ge__(self, other):
360 """Greater than or equal ordering."""
361 if not isinstance(other, Key):
362 return NotImplemented
363 return self.__tuple() >= other.__tuple()
365 def __getstate__(self):
366 """Private API used for pickling."""
367 return ({'pairs': list(self.pairs()),
368 'app': self.app(),
369 'namespace': self.namespace()},)
371 def __setstate__(self, state):
372 """Private API used for pickling."""
373 if len(state) != 1:
374 raise TypeError('Invalid state length, expected 1; received %i' %
375 len(state))
376 kwargs = state[0]
377 if not isinstance(kwargs, dict):
378 raise TypeError('Key accepts a dict of keyword arguments as state; '
379 'received %r' % kwargs)
380 self.__reference = None
381 self.__pairs = kwargs['pairs']
382 self.__app = kwargs['app']
383 self.__namespace = kwargs['namespace']
385 def __getnewargs__(self):
386 """Private API used for pickling."""
387 return ({'pairs': tuple(self.pairs()),
388 'app': self.app(),
389 'namespace': self.namespace()},)
391 def parent(self):
392 """Return a Key constructed from all but the last (kind, id) pairs.
394 If there is only one (kind, id) pair, return None.
396 pairs = self.pairs()
397 if len(pairs) <= 1:
398 return None
399 return Key(pairs=pairs[:-1], app=self.app(), namespace=self.namespace())
401 def root(self):
402 """Return the root key. This is either self or the highest parent."""
403 pairs = self.pairs()
404 if len(pairs) <= 1:
405 return self
406 return Key(pairs=pairs[:1], app=self.app(), namespace=self.namespace())
408 def namespace(self):
409 """Return the namespace."""
410 if self.__namespace is None:
411 self.__namespace = self.__reference.name_space()
412 return self.__namespace
414 def app(self):
415 """Return the application id."""
416 if self.__app is None:
417 self.__app = self.__reference.app()
418 return self.__app
420 def id(self):
421 """Return the string or integer id in the last (kind, id) pair, if any.
423 Returns:
424 A string or integer id, or None if the key is incomplete.
426 if self.__pairs:
427 return self.__pairs[-1][1]
428 elem = self.__reference.path().element(-1)
429 return elem.name() or elem.id() or None
431 def string_id(self):
432 """Return the string id in the last (kind, id) pair, if any.
434 Returns:
435 A string id, or None if the key has an integer id or is incomplete.
437 if self.__reference is None:
438 id = self.id()
439 if not isinstance(id, basestring):
440 id = None
441 return id
442 elem = self.__reference.path().element(-1)
443 return elem.name() or None
445 def integer_id(self):
446 """Return the integer id in the last (kind, id) pair, if any.
448 Returns:
449 An integer id, or None if the key has a string id or is incomplete.
451 if self.__reference is None:
452 id = self.id()
453 if not isinstance(id, (int, long)):
454 id = None
455 return id
456 elem = self.__reference.path().element(-1)
457 return elem.id() or None
459 def pairs(self):
460 """Return a tuple of (kind, id) pairs."""
461 pairs = self.__pairs
462 if pairs is None:
463 pairs = []
464 for elem in self.__reference.path().element_list():
465 kind = elem.type()
466 if elem.has_id():
467 id_or_name = elem.id()
468 else:
469 id_or_name = elem.name()
470 if not id_or_name:
471 id_or_name = None
472 tup = (kind, id_or_name)
473 pairs.append(tup)
474 self.__pairs = pairs = tuple(pairs)
475 return pairs
477 def flat(self):
478 """Return a tuple of alternating kind and id values."""
479 flat = []
480 for kind, id in self.pairs():
481 flat.append(kind)
482 flat.append(id)
483 return tuple(flat)
485 def kind(self):
486 """Return the kind of the entity referenced.
488 This is the kind from the last (kind, id) pair.
490 if self.__pairs:
491 return self.__pairs[-1][0]
492 return self.__reference.path().element(-1).type()
494 def reference(self):
495 """Return the Reference object for this Key.
497 This is a entity_pb.Reference instance -- a protocol buffer class
498 used by the lower-level API to the datastore.
500 NOTE: The caller should not mutate the return value.
502 if self.__reference is None:
503 self.__reference = _ConstructReference(self.__class__,
504 pairs=self.__pairs,
505 app=self.__app,
506 namespace=self.__namespace)
507 return self.__reference
509 def serialized(self):
510 """Return a serialized Reference object for this Key."""
511 return self.reference().Encode()
513 def urlsafe(self):
514 """Return a url-safe string encoding this Key's Reference.
516 This string is compatible with other APIs and languages and with
517 the strings used to represent Keys in GQL and in the App Engine
518 Admin Console.
520 # This is 3-4x faster than urlsafe_b64decode()
521 urlsafe = base64.b64encode(self.reference().Encode())
522 return urlsafe.rstrip('=').replace('+', '-').replace('/', '_')
524 # Datastore API using the default context.
525 # These use local import since otherwise they'd be recursive imports.
527 def get(self, **ctx_options):
528 """Synchronously get the entity for this Key.
530 Return None if there is no such entity.
532 return self.get_async(**ctx_options).get_result()
534 def get_async(self, **ctx_options):
535 """Return a Future whose result is the entity for this Key.
537 If no such entity exists, a Future is still returned, and the
538 Future's eventual return result be None.
540 from . import model, tasklets
541 ctx = tasklets.get_context()
542 cls = model.Model._kind_map.get(self.kind())
543 if cls:
544 cls._pre_get_hook(self)
545 fut = ctx.get(self, **ctx_options)
546 if cls:
547 post_hook = cls._post_get_hook
548 if not cls._is_default_hook(model.Model._default_post_get_hook,
549 post_hook):
550 fut.add_immediate_callback(post_hook, self, fut)
551 return fut
553 def delete(self, **ctx_options):
554 """Synchronously delete the entity for this Key.
556 This is a no-op if no such entity exists.
558 return self.delete_async(**ctx_options).get_result()
560 def delete_async(self, **ctx_options):
561 """Schedule deletion of the entity for this Key.
563 This returns a Future, whose result becomes available once the
564 deletion is complete. If no such entity exists, a Future is still
565 returned. In all cases the Future's result is None (i.e. there is
566 no way to tell whether the entity existed or not).
568 from . import tasklets, model
569 ctx = tasklets.get_context()
570 cls = model.Model._kind_map.get(self.kind())
571 if cls:
572 cls._pre_delete_hook(self)
573 fut = ctx.delete(self, **ctx_options)
574 if cls:
575 post_hook = cls._post_delete_hook
576 if not cls._is_default_hook(model.Model._default_post_delete_hook,
577 post_hook):
578 fut.add_immediate_callback(post_hook, self, fut)
579 return fut
581 @classmethod
582 def from_old_key(cls, old_key):
583 return cls(urlsafe=str(old_key))
585 def to_old_key(self):
586 return datastore_types.Key(encoded=self.urlsafe())
589 # The remaining functions in this module are private.
590 # TODO: Conform to PEP 8 naming, e.g. _construct_reference() etc.
592 @utils.positional(1)
593 def _ConstructReference(cls, pairs=None, flat=None,
594 reference=None, serialized=None, urlsafe=None,
595 app=None, namespace=None, parent=None):
596 """Construct a Reference; the signature is the same as for Key."""
597 if cls is not Key:
598 raise TypeError('Cannot construct Key reference on non-Key class; '
599 'received %r' % cls)
600 if (bool(pairs) + bool(flat) + bool(reference) + bool(serialized) +
601 bool(urlsafe)) != 1:
602 raise TypeError('Cannot construct Key reference from incompatible keyword '
603 'arguments.')
604 if flat or pairs:
605 if flat:
606 if len(flat) % 2:
607 raise TypeError('_ConstructReference() must have an even number of '
608 'positional arguments.')
609 pairs = [(flat[i], flat[i + 1]) for i in xrange(0, len(flat), 2)]
610 elif parent is not None:
611 pairs = list(pairs)
612 if not pairs:
613 raise TypeError('Key references must consist of at least one pair.')
614 if parent is not None:
615 if not isinstance(parent, Key):
616 raise datastore_errors.BadValueError(
617 'Expected Key instance, got %r' % parent)
618 pairs[:0] = parent.pairs()
619 if app:
620 if app != parent.app():
621 raise ValueError('Cannot specify a different app %r '
622 'than the parent app %r' %
623 (app, parent.app()))
624 else:
625 app = parent.app()
626 if namespace is not None:
627 if namespace != parent.namespace():
628 raise ValueError('Cannot specify a different namespace %r '
629 'than the parent namespace %r' %
630 (namespace, parent.namespace()))
631 else:
632 namespace = parent.namespace()
633 reference = _ReferenceFromPairs(pairs, app=app, namespace=namespace)
634 else:
635 if parent is not None:
636 raise TypeError('Key reference cannot be constructed when the parent '
637 'argument is combined with either reference, serialized '
638 'or urlsafe arguments.')
639 if urlsafe:
640 serialized = _DecodeUrlSafe(urlsafe)
641 if serialized:
642 reference = _ReferenceFromSerialized(serialized)
643 if not reference.path().element_size():
644 raise RuntimeError('Key reference has no path or elements (%r, %r, %r).'
645 % (urlsafe, serialized, str(reference)))
646 # TODO: ensure that each element has a type and either an id or a name
647 if not serialized:
648 reference = _ReferenceFromReference(reference)
649 # You needn't specify app= or namespace= together with reference=,
650 # serialized= or urlsafe=, but if you do, their values must match
651 # what is already in the reference.
652 if app is not None:
653 if app != reference.app():
654 raise RuntimeError('Key reference constructed uses a different app %r '
655 'than the one specified %r' %
656 (reference.app(), app))
657 if namespace is not None:
658 if namespace != reference.name_space():
659 raise RuntimeError('Key reference constructed uses a different '
660 'namespace %r than the one specified %r' %
661 (reference.name_space(), namespace))
662 return reference
665 def _ReferenceFromPairs(pairs, reference=None, app=None, namespace=None):
666 """Construct a Reference from a list of pairs.
668 If a Reference is passed in as the second argument, it is modified
669 in place. The app and namespace are set from the corresponding
670 keyword arguments, with the customary defaults.
672 if reference is None:
673 reference = entity_pb.Reference()
674 path = reference.mutable_path()
675 last = False
676 for kind, idorname in pairs:
677 if last:
678 raise datastore_errors.BadArgumentError(
679 'Incomplete Key entry must be last')
680 t = type(kind)
681 if t is str:
682 pass
683 elif t is unicode:
684 kind = kind.encode('utf8')
685 else:
686 if issubclass(t, type):
687 # Late import to avoid cycles.
688 from .model import Model
689 modelclass = kind
690 if not issubclass(modelclass, Model):
691 raise TypeError('Key kind must be either a string or subclass of '
692 'Model; received %r' % modelclass)
693 kind = modelclass._get_kind()
694 t = type(kind)
695 if t is str:
696 pass
697 elif t is unicode:
698 kind = kind.encode('utf8')
699 elif issubclass(t, str):
700 pass
701 elif issubclass(t, unicode):
702 kind = kind.encode('utf8')
703 else:
704 raise TypeError('Key kind must be either a string or subclass of Model;'
705 ' received %r' % kind)
706 if not (1 <= len(kind) <= _MAX_KEYPART_BYTES):
707 raise ValueError('Key kind string must be a non-empty string up to %i'
708 'bytes; received %s' %
709 (_MAX_KEYPART_BYTES, kind))
710 elem = path.add_element()
711 elem.set_type(kind)
712 t = type(idorname)
713 if t is int or t is long:
714 if not (1 <= idorname < _MAX_LONG):
715 raise ValueError('Key id number is too long; received %i' % idorname)
716 elem.set_id(idorname)
717 elif t is str:
718 if not (1 <= len(idorname) <= _MAX_KEYPART_BYTES):
719 raise ValueError('Key name strings must be non-empty strings up to %i '
720 'bytes; received %s' %
721 (_MAX_KEYPART_BYTES, idorname))
722 elem.set_name(idorname)
723 elif t is unicode:
724 idorname = idorname.encode('utf8')
725 if not (1 <= len(idorname) <= _MAX_KEYPART_BYTES):
726 raise ValueError('Key name unicode strings must be non-empty strings up'
727 ' to %i bytes; received %s' %
728 (_MAX_KEYPART_BYTES, idorname))
729 elem.set_name(idorname)
730 elif idorname is None:
731 last = True
732 elif issubclass(t, (int, long)):
733 if not (1 <= idorname < _MAX_LONG):
734 raise ValueError('Key id number is too long; received %i' % idorname)
735 elem.set_id(idorname)
736 elif issubclass(t, basestring):
737 if issubclass(t, unicode):
738 idorname = idorname.encode('utf8')
739 if not (1 <= len(idorname) <= _MAX_KEYPART_BYTES):
740 raise ValueError('Key name strings must be non-empty strings up to %i '
741 'bytes; received %s' % (_MAX_KEYPART_BYTES, idorname))
742 elem.set_name(idorname)
743 else:
744 raise TypeError('id must be either a numeric id or a string name; '
745 'received %r' % idorname)
746 # An empty app id means to use the default app id.
747 if not app:
748 app = _DefaultAppId()
749 # Always set the app id, since it is mandatory.
750 reference.set_app(app)
751 # An empty namespace overrides the default namespace.
752 if namespace is None:
753 namespace = _DefaultNamespace()
754 # Only set the namespace if it is not empty.
755 if namespace:
756 reference.set_name_space(namespace)
757 return reference
760 def _ReferenceFromReference(reference):
761 """Copy a Reference."""
762 new_reference = entity_pb.Reference()
763 new_reference.CopyFrom(reference)
764 return new_reference
767 def _ReferenceFromSerialized(serialized):
768 """Construct a Reference from a serialized Reference."""
769 if not isinstance(serialized, basestring):
770 raise TypeError('serialized must be a string; received %r' % serialized)
771 elif isinstance(serialized, unicode):
772 serialized = serialized.encode('utf8')
773 return entity_pb.Reference(serialized)
776 def _DecodeUrlSafe(urlsafe):
777 """Decode a url-safe base64-encoded string.
779 This returns the decoded string.
781 if not isinstance(urlsafe, basestring):
782 raise TypeError('urlsafe must be a string; received %r' % urlsafe)
783 if isinstance(urlsafe, unicode):
784 urlsafe = urlsafe.encode('utf8')
785 mod = len(urlsafe) % 4
786 if mod:
787 urlsafe += '=' * (4 - mod)
788 # This is 3-4x faster than urlsafe_b64decode()
789 return base64.b64decode(urlsafe.replace('-', '+').replace('_', '/'))
792 def _DefaultAppId():
793 """Return the default application id.
795 This is taken from the APPLICATION_ID environment variable.
797 return os.getenv('APPLICATION_ID', '_')
800 def _DefaultNamespace():
801 """Return the default namespace.
803 This is taken from the namespace manager.
805 return namespace_manager.get_namespace()