Package googleapiclient :: Module schema
[hide private]
[frames] | no frames]

Source Code for Module googleapiclient.schema

  1  # Copyright 2014 Google Inc. All Rights Reserved. 
  2  # 
  3  # Licensed under the Apache License, Version 2.0 (the "License"); 
  4  # you may not use this file except in compliance with the License. 
  5  # You may obtain a copy of the License at 
  6  # 
  7  #      http://www.apache.org/licenses/LICENSE-2.0 
  8  # 
  9  # Unless required by applicable law or agreed to in writing, software 
 10  # distributed under the License is distributed on an "AS IS" BASIS, 
 11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 12  # See the License for the specific language governing permissions and 
 13  # limitations under the License. 
 14   
 15  """Schema processing for discovery based APIs 
 16   
 17  Schemas holds an APIs discovery schemas. It can return those schema as 
 18  deserialized JSON objects, or pretty print them as prototype objects that 
 19  conform to the schema. 
 20   
 21  For example, given the schema: 
 22   
 23   schema = \"\"\"{ 
 24     "Foo": { 
 25      "type": "object", 
 26      "properties": { 
 27       "etag": { 
 28        "type": "string", 
 29        "description": "ETag of the collection." 
 30       }, 
 31       "kind": { 
 32        "type": "string", 
 33        "description": "Type of the collection ('calendar#acl').", 
 34        "default": "calendar#acl" 
 35       }, 
 36       "nextPageToken": { 
 37        "type": "string", 
 38        "description": "Token used to access the next 
 39           page of this result. Omitted if no further results are available." 
 40       } 
 41      } 
 42     } 
 43   }\"\"\" 
 44   
 45   s = Schemas(schema) 
 46   print s.prettyPrintByName('Foo') 
 47   
 48   Produces the following output: 
 49   
 50    { 
 51     "nextPageToken": "A String", # Token used to access the 
 52         # next page of this result. Omitted if no further results are available. 
 53     "kind": "A String", # Type of the collection ('calendar#acl'). 
 54     "etag": "A String", # ETag of the collection. 
 55    }, 
 56   
 57  The constructor takes a discovery document in which to look up named schema. 
 58  """ 
 59   
 60  # TODO(jcgregorio) support format, enum, minimum, maximum 
 61   
 62  __author__ = 'jcgregorio@google.com (Joe Gregorio)' 
 63   
 64  import copy 
 65   
 66  from oauth2client import util 
67 68 69 -class Schemas(object):
70 """Schemas for an API.""" 71
72 - def __init__(self, discovery):
73 """Constructor. 74 75 Args: 76 discovery: object, Deserialized discovery document from which we pull 77 out the named schema. 78 """ 79 self.schemas = discovery.get('schemas', {}) 80 81 # Cache of pretty printed schemas. 82 self.pretty = {}
83 84 @util.positional(2)
85 - def _prettyPrintByName(self, name, seen=None, dent=0):
86 """Get pretty printed object prototype from the schema name. 87 88 Args: 89 name: string, Name of schema in the discovery document. 90 seen: list of string, Names of schema already seen. Used to handle 91 recursive definitions. 92 93 Returns: 94 string, A string that contains a prototype object with 95 comments that conforms to the given schema. 96 """ 97 if seen is None: 98 seen = [] 99 100 if name in seen: 101 # Do not fall into an infinite loop over recursive definitions. 102 return '# Object with schema name: %s' % name 103 seen.append(name) 104 105 if name not in self.pretty: 106 self.pretty[name] = _SchemaToStruct(self.schemas[name], 107 seen, dent=dent).to_str(self._prettyPrintByName) 108 109 seen.pop() 110 111 return self.pretty[name]
112
113 - def prettyPrintByName(self, name):
114 """Get pretty printed object prototype from the schema name. 115 116 Args: 117 name: string, Name of schema in the discovery document. 118 119 Returns: 120 string, A string that contains a prototype object with 121 comments that conforms to the given schema. 122 """ 123 # Return with trailing comma and newline removed. 124 return self._prettyPrintByName(name, seen=[], dent=1)[:-2]
125 126 @util.positional(2)
127 - def _prettyPrintSchema(self, schema, seen=None, dent=0):
128 """Get pretty printed object prototype of schema. 129 130 Args: 131 schema: object, Parsed JSON schema. 132 seen: list of string, Names of schema already seen. Used to handle 133 recursive definitions. 134 135 Returns: 136 string, A string that contains a prototype object with 137 comments that conforms to the given schema. 138 """ 139 if seen is None: 140 seen = [] 141 142 return _SchemaToStruct(schema, seen, dent=dent).to_str(self._prettyPrintByName)
143
144 - def prettyPrintSchema(self, schema):
145 """Get pretty printed object prototype of schema. 146 147 Args: 148 schema: object, Parsed JSON schema. 149 150 Returns: 151 string, A string that contains a prototype object with 152 comments that conforms to the given schema. 153 """ 154 # Return with trailing comma and newline removed. 155 return self._prettyPrintSchema(schema, dent=1)[:-2]
156
157 - def get(self, name):
158 """Get deserialized JSON schema from the schema name. 159 160 Args: 161 name: string, Schema name. 162 """ 163 return self.schemas[name]
164
165 166 -class _SchemaToStruct(object):
167 """Convert schema to a prototype object.""" 168 169 @util.positional(3)
170 - def __init__(self, schema, seen, dent=0):
171 """Constructor. 172 173 Args: 174 schema: object, Parsed JSON schema. 175 seen: list, List of names of schema already seen while parsing. Used to 176 handle recursive definitions. 177 dent: int, Initial indentation depth. 178 """ 179 # The result of this parsing kept as list of strings. 180 self.value = [] 181 182 # The final value of the parsing. 183 self.string = None 184 185 # The parsed JSON schema. 186 self.schema = schema 187 188 # Indentation level. 189 self.dent = dent 190 191 # Method that when called returns a prototype object for the schema with 192 # the given name. 193 self.from_cache = None 194 195 # List of names of schema already seen while parsing. 196 self.seen = seen
197
198 - def emit(self, text):
199 """Add text as a line to the output. 200 201 Args: 202 text: string, Text to output. 203 """ 204 self.value.extend([" " * self.dent, text, '\n'])
205
206 - def emitBegin(self, text):
207 """Add text to the output, but with no line terminator. 208 209 Args: 210 text: string, Text to output. 211 """ 212 self.value.extend([" " * self.dent, text])
213
214 - def emitEnd(self, text, comment):
215 """Add text and comment to the output with line terminator. 216 217 Args: 218 text: string, Text to output. 219 comment: string, Python comment. 220 """ 221 if comment: 222 divider = '\n' + ' ' * (self.dent + 2) + '# ' 223 lines = comment.splitlines() 224 lines = [x.rstrip() for x in lines] 225 comment = divider.join(lines) 226 self.value.extend([text, ' # ', comment, '\n']) 227 else: 228 self.value.extend([text, '\n'])
229
230 - def indent(self):
231 """Increase indentation level.""" 232 self.dent += 1
233
234 - def undent(self):
235 """Decrease indentation level.""" 236 self.dent -= 1
237
238 - def _to_str_impl(self, schema):
239 """Prototype object based on the schema, in Python code with comments. 240 241 Args: 242 schema: object, Parsed JSON schema file. 243 244 Returns: 245 Prototype object based on the schema, in Python code with comments. 246 """ 247 stype = schema.get('type') 248 if stype == 'object': 249 self.emitEnd('{', schema.get('description', '')) 250 self.indent() 251 if 'properties' in schema: 252 for pname, pschema in schema.get('properties', {}).iteritems(): 253 self.emitBegin('"%s": ' % pname) 254 self._to_str_impl(pschema) 255 elif 'additionalProperties' in schema: 256 self.emitBegin('"a_key": ') 257 self._to_str_impl(schema['additionalProperties']) 258 self.undent() 259 self.emit('},') 260 elif '$ref' in schema: 261 schemaName = schema['$ref'] 262 description = schema.get('description', '') 263 s = self.from_cache(schemaName, seen=self.seen) 264 parts = s.splitlines() 265 self.emitEnd(parts[0], description) 266 for line in parts[1:]: 267 self.emit(line.rstrip()) 268 elif stype == 'boolean': 269 value = schema.get('default', 'True or False') 270 self.emitEnd('%s,' % str(value), schema.get('description', '')) 271 elif stype == 'string': 272 value = schema.get('default', 'A String') 273 self.emitEnd('"%s",' % str(value), schema.get('description', '')) 274 elif stype == 'integer': 275 value = schema.get('default', '42') 276 self.emitEnd('%s,' % str(value), schema.get('description', '')) 277 elif stype == 'number': 278 value = schema.get('default', '3.14') 279 self.emitEnd('%s,' % str(value), schema.get('description', '')) 280 elif stype == 'null': 281 self.emitEnd('None,', schema.get('description', '')) 282 elif stype == 'any': 283 self.emitEnd('"",', schema.get('description', '')) 284 elif stype == 'array': 285 self.emitEnd('[', schema.get('description')) 286 self.indent() 287 self.emitBegin('') 288 self._to_str_impl(schema['items']) 289 self.undent() 290 self.emit('],') 291 else: 292 self.emit('Unknown type! %s' % stype) 293 self.emitEnd('', '') 294 295 self.string = ''.join(self.value) 296 return self.string
297
298 - def to_str(self, from_cache):
299 """Prototype object based on the schema, in Python code with comments. 300 301 Args: 302 from_cache: callable(name, seen), Callable that retrieves an object 303 prototype for a schema with the given name. Seen is a list of schema 304 names already seen as we recursively descend the schema definition. 305 306 Returns: 307 Prototype object based on the schema, in Python code with comments. 308 The lines of the code will all be properly indented. 309 """ 310 self.from_cache = from_cache 311 return self._to_str_impl(self.schema)
312