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  from __future__ import absolute_import 
 60  import six 
 61   
 62  # TODO(jcgregorio) support format, enum, minimum, maximum 
 63   
 64  __author__ = 'jcgregorio@google.com (Joe Gregorio)' 
 65   
 66  import copy 
 67   
 68  # Oauth2client < 3 has the positional helper in 'util', >= 3 has it 
 69  # in '_helpers'. 
 70  try: 
 71    from oauth2client import util 
 72  except ImportError: 
 73    from oauth2client import _helpers as util 
74 75 76 -class Schemas(object):
77 """Schemas for an API.""" 78
79 - def __init__(self, discovery):
80 """Constructor. 81 82 Args: 83 discovery: object, Deserialized discovery document from which we pull 84 out the named schema. 85 """ 86 self.schemas = discovery.get('schemas', {}) 87 88 # Cache of pretty printed schemas. 89 self.pretty = {}
90 91 @util.positional(2)
92 - def _prettyPrintByName(self, name, seen=None, dent=0):
93 """Get pretty printed object prototype from the schema name. 94 95 Args: 96 name: string, Name of schema in the discovery document. 97 seen: list of string, Names of schema already seen. Used to handle 98 recursive definitions. 99 100 Returns: 101 string, A string that contains a prototype object with 102 comments that conforms to the given schema. 103 """ 104 if seen is None: 105 seen = [] 106 107 if name in seen: 108 # Do not fall into an infinite loop over recursive definitions. 109 return '# Object with schema name: %s' % name 110 seen.append(name) 111 112 if name not in self.pretty: 113 self.pretty[name] = _SchemaToStruct(self.schemas[name], 114 seen, dent=dent).to_str(self._prettyPrintByName) 115 116 seen.pop() 117 118 return self.pretty[name]
119
120 - def prettyPrintByName(self, name):
121 """Get pretty printed object prototype from the schema name. 122 123 Args: 124 name: string, Name of schema in the discovery document. 125 126 Returns: 127 string, A string that contains a prototype object with 128 comments that conforms to the given schema. 129 """ 130 # Return with trailing comma and newline removed. 131 return self._prettyPrintByName(name, seen=[], dent=1)[:-2]
132 133 @util.positional(2)
134 - def _prettyPrintSchema(self, schema, seen=None, dent=0):
135 """Get pretty printed object prototype of schema. 136 137 Args: 138 schema: object, Parsed JSON schema. 139 seen: list of string, Names of schema already seen. Used to handle 140 recursive definitions. 141 142 Returns: 143 string, A string that contains a prototype object with 144 comments that conforms to the given schema. 145 """ 146 if seen is None: 147 seen = [] 148 149 return _SchemaToStruct(schema, seen, dent=dent).to_str(self._prettyPrintByName)
150
151 - def prettyPrintSchema(self, schema):
152 """Get pretty printed object prototype of schema. 153 154 Args: 155 schema: object, Parsed JSON schema. 156 157 Returns: 158 string, A string that contains a prototype object with 159 comments that conforms to the given schema. 160 """ 161 # Return with trailing comma and newline removed. 162 return self._prettyPrintSchema(schema, dent=1)[:-2]
163
164 - def get(self, name, default=None):
165 """Get deserialized JSON schema from the schema name. 166 167 Args: 168 name: string, Schema name. 169 default: object, return value if name not found. 170 """ 171 return self.schemas.get(name, default)
172
173 174 -class _SchemaToStruct(object):
175 """Convert schema to a prototype object.""" 176 177 @util.positional(3)
178 - def __init__(self, schema, seen, dent=0):
179 """Constructor. 180 181 Args: 182 schema: object, Parsed JSON schema. 183 seen: list, List of names of schema already seen while parsing. Used to 184 handle recursive definitions. 185 dent: int, Initial indentation depth. 186 """ 187 # The result of this parsing kept as list of strings. 188 self.value = [] 189 190 # The final value of the parsing. 191 self.string = None 192 193 # The parsed JSON schema. 194 self.schema = schema 195 196 # Indentation level. 197 self.dent = dent 198 199 # Method that when called returns a prototype object for the schema with 200 # the given name. 201 self.from_cache = None 202 203 # List of names of schema already seen while parsing. 204 self.seen = seen
205
206 - def emit(self, text):
207 """Add text as a line to the output. 208 209 Args: 210 text: string, Text to output. 211 """ 212 self.value.extend([" " * self.dent, text, '\n'])
213
214 - def emitBegin(self, text):
215 """Add text to the output, but with no line terminator. 216 217 Args: 218 text: string, Text to output. 219 """ 220 self.value.extend([" " * self.dent, text])
221
222 - def emitEnd(self, text, comment):
223 """Add text and comment to the output with line terminator. 224 225 Args: 226 text: string, Text to output. 227 comment: string, Python comment. 228 """ 229 if comment: 230 divider = '\n' + ' ' * (self.dent + 2) + '# ' 231 lines = comment.splitlines() 232 lines = [x.rstrip() for x in lines] 233 comment = divider.join(lines) 234 self.value.extend([text, ' # ', comment, '\n']) 235 else: 236 self.value.extend([text, '\n'])
237
238 - def indent(self):
239 """Increase indentation level.""" 240 self.dent += 1
241
242 - def undent(self):
243 """Decrease indentation level.""" 244 self.dent -= 1
245
246 - def _to_str_impl(self, schema):
247 """Prototype object based on the schema, in Python code with comments. 248 249 Args: 250 schema: object, Parsed JSON schema file. 251 252 Returns: 253 Prototype object based on the schema, in Python code with comments. 254 """ 255 stype = schema.get('type') 256 if stype == 'object': 257 self.emitEnd('{', schema.get('description', '')) 258 self.indent() 259 if 'properties' in schema: 260 for pname, pschema in six.iteritems(schema.get('properties', {})): 261 self.emitBegin('"%s": ' % pname) 262 self._to_str_impl(pschema) 263 elif 'additionalProperties' in schema: 264 self.emitBegin('"a_key": ') 265 self._to_str_impl(schema['additionalProperties']) 266 self.undent() 267 self.emit('},') 268 elif '$ref' in schema: 269 schemaName = schema['$ref'] 270 description = schema.get('description', '') 271 s = self.from_cache(schemaName, seen=self.seen) 272 parts = s.splitlines() 273 self.emitEnd(parts[0], description) 274 for line in parts[1:]: 275 self.emit(line.rstrip()) 276 elif stype == 'boolean': 277 value = schema.get('default', 'True or False') 278 self.emitEnd('%s,' % str(value), schema.get('description', '')) 279 elif stype == 'string': 280 value = schema.get('default', 'A String') 281 self.emitEnd('"%s",' % str(value), schema.get('description', '')) 282 elif stype == 'integer': 283 value = schema.get('default', '42') 284 self.emitEnd('%s,' % str(value), schema.get('description', '')) 285 elif stype == 'number': 286 value = schema.get('default', '3.14') 287 self.emitEnd('%s,' % str(value), schema.get('description', '')) 288 elif stype == 'null': 289 self.emitEnd('None,', schema.get('description', '')) 290 elif stype == 'any': 291 self.emitEnd('"",', schema.get('description', '')) 292 elif stype == 'array': 293 self.emitEnd('[', schema.get('description')) 294 self.indent() 295 self.emitBegin('') 296 self._to_str_impl(schema['items']) 297 self.undent() 298 self.emit('],') 299 else: 300 self.emit('Unknown type! %s' % stype) 301 self.emitEnd('', '') 302 303 self.string = ''.join(self.value) 304 return self.string
305
306 - def to_str(self, from_cache):
307 """Prototype object based on the schema, in Python code with comments. 308 309 Args: 310 from_cache: callable(name, seen), Callable that retrieves an object 311 prototype for a schema with the given name. Seen is a list of schema 312 names already seen as we recursively descend the schema definition. 313 314 Returns: 315 Prototype object based on the schema, in Python code with comments. 316 The lines of the code will all be properly indented. 317 """ 318 self.from_cache = from_cache 319 return self._to_str_impl(self.schema)
320