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