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  from googleapiclient import _helpers as util
     69
     70
     71 -class Schemas(object):
     72      """Schemas for an API."""
     73
     74 -    def __init__(self, discovery):
     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          # Cache of pretty printed schemas.
     84          self.pretty = {}
     85
     86      @util.positional(2)
     87 -    def _prettyPrintByName(self, name, seen=None, dent=0):
     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              # Do not fall into an infinite loop over recursive definitions.
    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
    116 -    def prettyPrintByName(self, name):
    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          # Return with trailing comma and newline removed.
    127          return self._prettyPrintByName(name, seen=[], dent=1)[:-2]
    128
    129      @util.positional(2)
    130 -    def _prettyPrintSchema(self, schema, seen=None, dent=0):
    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
    147 -    def prettyPrintSchema(self, schema):
    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          # Return with trailing comma and newline removed.
    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
    169
    170 -class _SchemaToStruct(object):
    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          # The result of this parsing kept as list of strings.
    184          self.value = []
    185
    186          # The final value of the parsing.
    187          self.string = None
    188
    189          # The parsed JSON schema.
    190          self.schema = schema
    191
    192          # Indentation level.
    193          self.dent = dent
    194
    195          # Method that when called returns a prototype object for the schema with
    196          # the given name.
    197          self.from_cache = None
    198
    199          # List of names of schema already seen while parsing.
    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
    210 -    def emitBegin(self, text):
    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
    218 -    def emitEnd(self, text, comment):
    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
    234 -    def indent(self):
    235          """Increase indentation level."""
    236          self.dent += 1
    237
    238 -    def undent(self):
    239          """Decrease indentation level."""
    240          self.dent -= 1
    241
    242 -    def _to_str_impl(self, schema):
    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