1#!/usr/bin/env python 2"""An abstract for a collection of key_range.KeyRange objects.""" 3 4 5 6from google.appengine.ext import key_range 7from mapreduce import namespace_range 8 9 10__all__ = [ 11 "KeyRangesFactory", 12 "KeyRanges"] 13 14# pylint: disable=g-bad-name 15 16 17class KeyRangesFactory(object): 18 """Factory for KeyRanges.""" 19 20 @classmethod 21 def create_from_list(cls, list_of_key_ranges): 22 """Create a KeyRanges object. 23 24 Args: 25 list_of_key_ranges: a list of key_range.KeyRange object. 26 27 Returns: 28 A _KeyRanges object. 29 """ 30 return _KeyRangesFromList(list_of_key_ranges) 31 32 @classmethod 33 def create_from_ns_range(cls, ns_range): 34 """Create a KeyRanges object. 35 36 Args: 37 ns_range: a namespace_range.NameSpace Range object. 38 39 Returns: 40 A _KeyRanges object. 41 """ 42 return _KeyRangesFromNSRange(ns_range) 43 44 @classmethod 45 def from_json(cls, json): 46 """Deserialize from json. 47 48 Args: 49 json: a dict of json compatible fields. 50 51 Returns: 52 a KeyRanges object. 53 54 Raises: 55 ValueError: if the json is invalid. 56 """ 57 if json["name"] in _KEYRANGES_CLASSES: 58 return _KEYRANGES_CLASSES[json["name"]].from_json(json) 59 raise ValueError("Invalid json %s", json) 60 61 62class KeyRanges(object): 63 """An abstraction for a collection of key_range.KeyRange objects.""" 64 65 def __iter__(self): 66 return self 67 68 def next(self): 69 """Iterator iteraface.""" 70 raise NotImplementedError() 71 72 def to_json(self): 73 return {"name": self.__class__.__name__} 74 75 @classmethod 76 def from_json(cls): 77 raise NotImplementedError() 78 79 def __eq__(self): 80 raise NotImplementedError() 81 82 def __str__(self): 83 raise NotImplementedError() 84 85 86class _KeyRangesFromList(KeyRanges): 87 """Create KeyRanges from a list.""" 88 89 def __init__(self, list_of_key_ranges): 90 self._key_ranges = list_of_key_ranges 91 92 def __eq__(self, other): 93 if not isinstance(other, self.__class__): 94 return False 95 return self._key_ranges == other._key_ranges 96 97 def next(self): 98 if self._key_ranges: 99 return self._key_ranges.pop() 100 raise StopIteration() 101 102 def __str__(self): 103 if len(self._key_ranges) == 1: 104 return "Single KeyRange %s" % (self._key_ranges[0]) 105 if self._key_ranges: 106 return "From %s to %s" % (self._key_ranges[0], self._key_ranges[-1]) 107 return "Empty KeyRange." 108 109 def to_json(self): 110 json = super(_KeyRangesFromList, self).to_json() 111 json.update( 112 {"list_of_key_ranges": [kr.to_json() for kr in self._key_ranges]}) 113 return json 114 115 @classmethod 116 def from_json(cls, json): 117 return cls( 118 [key_range.KeyRange.from_json(kr) for kr in json["list_of_key_ranges"]]) 119 120 121class _KeyRangesFromNSRange(KeyRanges): 122 """Create KeyRanges from a namespace range.""" 123 124 def __init__(self, ns_range): 125 """Init.""" 126 self._ns_range = ns_range 127 if self._ns_range is not None: 128 self._iter = iter(self._ns_range) 129 self._last_ns = None 130 131 def __eq__(self, other): 132 if not isinstance(other, self.__class__): 133 return False 134 return self._ns_range == other._ns_range 135 136 def __str__(self): 137 return str(self._ns_range) 138 139 def next(self): 140 if self._ns_range is None: 141 raise StopIteration() 142 143 self._last_ns = self._iter.next() 144 current_ns_range = self._ns_range 145 if self._last_ns == self._ns_range.namespace_end: 146 self._ns_range = None 147 return key_range.KeyRange(namespace=self._last_ns, 148 _app=current_ns_range.app) 149 150 def to_json(self): 151 json = super(_KeyRangesFromNSRange, self).to_json() 152 ns_range = self._ns_range 153 if self._ns_range is not None and self._last_ns is not None: 154 ns_range = ns_range.with_start_after(self._last_ns) 155 if ns_range is not None: 156 json.update({"ns_range": ns_range.to_json_object()}) 157 return json 158 159 @classmethod 160 def from_json(cls, json): 161 if "ns_range" in json: 162 return cls( 163 namespace_range.NamespaceRange.from_json_object(json["ns_range"])) 164 else: 165 return cls(None) 166 167 168_KEYRANGES_CLASSES = { 169 _KeyRangesFromList.__name__: _KeyRangesFromList, 170 _KeyRangesFromNSRange.__name__: _KeyRangesFromNSRange 171} 172