323 |
323 |
324 Args: |
324 Args: |
325 # a base64-encoded primary key, generated by Key.__str__ |
325 # a base64-encoded primary key, generated by Key.__str__ |
326 encoded: str |
326 encoded: str |
327 """ |
327 """ |
|
328 self._str = None |
328 if encoded is not None: |
329 if encoded is not None: |
329 if not isinstance(encoded, basestring): |
330 if not isinstance(encoded, basestring): |
330 try: |
331 try: |
331 repr_encoded = repr(encoded) |
332 repr_encoded = repr(encoded) |
332 except: |
333 except: |
337 try: |
338 try: |
338 modulo = len(encoded) % 4 |
339 modulo = len(encoded) % 4 |
339 if modulo != 0: |
340 if modulo != 0: |
340 encoded += ('=' * (4 - modulo)) |
341 encoded += ('=' * (4 - modulo)) |
341 |
342 |
342 encoded_pb = base64.urlsafe_b64decode(str(encoded)) |
343 self._str = str(encoded) |
|
344 encoded_pb = base64.urlsafe_b64decode(self._str) |
343 self.__reference = entity_pb.Reference(encoded_pb) |
345 self.__reference = entity_pb.Reference(encoded_pb) |
344 assert self.__reference.IsInitialized() |
346 assert self.__reference.IsInitialized() |
|
347 |
|
348 self._str = self._str.rstrip('=') |
345 |
349 |
346 except (AssertionError, TypeError), e: |
350 except (AssertionError, TypeError), e: |
347 raise datastore_errors.BadKeyError( |
351 raise datastore_errors.BadKeyError( |
348 'Invalid string key %s. Details: %s' % (encoded, e)) |
352 'Invalid string key %s. Details: %s' % (encoded, e)) |
349 except Exception, e: |
353 except Exception, e: |
352 else: |
356 else: |
353 raise |
357 raise |
354 else: |
358 else: |
355 self.__reference = entity_pb.Reference() |
359 self.__reference = entity_pb.Reference() |
356 |
360 |
357 def to_path(self): |
361 def to_path(self, _default_id=None): |
358 """Construct the "path" of this key as a list. |
362 """Construct the "path" of this key as a list. |
359 |
363 |
360 Returns: |
364 Returns: |
361 A list [kind_1, id_or_name_1, ..., kind_n, id_or_name_n] of the key path. |
365 A list [kind_1, id_or_name_1, ..., kind_n, id_or_name_n] of the key path. |
362 |
366 |
363 Raises: |
367 Raises: |
364 datastore_errors.BadKeyError if this key does not have a valid path. |
368 datastore_errors.BadKeyError if this key does not have a valid path. |
365 """ |
369 """ |
|
370 |
366 path = [] |
371 path = [] |
367 for path_element in self.__reference.path().element_list(): |
372 for path_element in self.__reference.path().element_list(): |
368 path.append(path_element.type().decode('utf-8')) |
373 path.append(path_element.type().decode('utf-8')) |
369 if path_element.has_name(): |
374 if path_element.has_name(): |
370 path.append(path_element.name().decode('utf-8')) |
375 path.append(path_element.name().decode('utf-8')) |
371 elif path_element.has_id(): |
376 elif path_element.has_id(): |
372 path.append(path_element.id()) |
377 path.append(path_element.id()) |
|
378 elif _default_id is not None: |
|
379 path.append(_default_id) |
373 else: |
380 else: |
374 raise datastore_errors.BadKeyError('Incomplete key found in to_path') |
381 raise datastore_errors.BadKeyError('Incomplete key found in to_path') |
375 return path |
382 return path |
376 |
383 |
377 @staticmethod |
384 @staticmethod |
629 the entity's id or name. |
636 the entity's id or name. |
630 |
637 |
631 Returns: |
638 Returns: |
632 string |
639 string |
633 """ |
640 """ |
634 if (self.has_id_or_name()): |
641 if self._str is None: |
635 encoded = base64.urlsafe_b64encode(self.__reference.Encode()) |
642 if (self.has_id_or_name()): |
636 return encoded.replace('=', '') |
643 encoded = base64.urlsafe_b64encode(self.__reference.Encode()) |
637 else: |
644 self._str = encoded.replace('=', '') |
638 raise datastore_errors.BadKeyError( |
645 else: |
639 'Cannot string encode an incomplete key!\n%s' % self.__reference) |
646 raise datastore_errors.BadKeyError( |
|
647 'Cannot string encode an incomplete key!\n%s' % self.__reference) |
|
648 return self._str |
|
649 |
640 |
650 |
641 def __repr__(self): |
651 def __repr__(self): |
642 """Returns an eval()able string representation of this key. |
652 """Returns an eval()able string representation of this key. |
643 |
653 |
644 Returns a Python string of the form 'datastore_types.Key.from_path(...)' |
654 Returns a Python string of the form 'datastore_types.Key.from_path(...)' |
673 Positive if self is greater than "other" |
683 Positive if self is greater than "other" |
674 """ |
684 """ |
675 if not isinstance(other, Key): |
685 if not isinstance(other, Key): |
676 return -2 |
686 return -2 |
677 |
687 |
678 self_args = [] |
688 self_args = [self.__reference.app()] |
679 other_args = [] |
689 self_args += self.to_path(_default_id=0) |
680 |
690 |
681 self_args.append(self.__reference.app()) |
691 other_args = [other.__reference.app()] |
682 other_args.append(other.__reference.app()) |
692 other_args += other.to_path(_default_id=0) |
683 |
|
684 for elem in self.__reference.path().element_list(): |
|
685 self_args.append(elem.type()) |
|
686 if elem.has_name(): |
|
687 self_args.append(elem.name()) |
|
688 else: |
|
689 self_args.append(elem.id()) |
|
690 |
|
691 for elem in other.__reference.path().element_list(): |
|
692 other_args.append(elem.type()) |
|
693 if elem.has_name(): |
|
694 other_args.append(elem.name()) |
|
695 else: |
|
696 other_args.append(elem.id()) |
|
697 |
693 |
698 for self_component, other_component in zip(self_args, other_args): |
694 for self_component, other_component in zip(self_args, other_args): |
699 comparison = cmp(self_component, other_component) |
695 comparison = cmp(self_component, other_component) |
700 if comparison != 0: |
696 if comparison != 0: |
701 return comparison |
697 return comparison |
709 dictionary keys. |
705 dictionary keys. |
710 |
706 |
711 Returns: |
707 Returns: |
712 int |
708 int |
713 """ |
709 """ |
714 return hash(self.__str__()) |
710 args = self.to_path(_default_id=0) |
|
711 args.append(self.__reference.app()) |
|
712 return hash(type(args)) ^ hash(tuple(args)) |
715 |
713 |
716 |
714 |
717 class Category(unicode): |
715 class Category(unicode): |
718 """A tag, ie a descriptive word or phrase. Entities may be tagged by users, |
716 """A tag, ie a descriptive word or phrase. Entities may be tagged by users, |
719 and later returned by a queries for that tag. Tags can also be used for |
717 and later returned by a queries for that tag. Tags can also be used for |
898 address = None |
896 address = None |
899 |
897 |
900 def __init__(self, protocol, address=None): |
898 def __init__(self, protocol, address=None): |
901 if address is None: |
899 if address is None: |
902 try: |
900 try: |
903 split = protocol.split(' ') |
901 split = protocol.split(' ', 1) |
904 protocol, address = split |
902 protocol, address = split |
905 except (AttributeError, ValueError): |
903 except (AttributeError, ValueError): |
906 raise datastore_errors.BadValueError( |
904 raise datastore_errors.BadValueError( |
907 'Expected string of format "protocol address"; received %s' % |
905 'Expected string of format "protocol address"; received %s' % |
908 str(protocol)) |
906 (protocol,)) |
909 |
907 |
910 ValidateString(address, 'address') |
908 ValidateString(address, 'address') |
911 if protocol not in self.PROTOCOLS: |
909 if protocol not in self.PROTOCOLS: |
912 Link(protocol) |
910 Link(protocol) |
913 |
911 |