Exemplo n.º 1
0
    def construct_dec_post_load(schema):
        name = class_name(schema.name)
        name_lower = name.lower()

        fn_args = ast.arguments(
            args=[
                ast.arg(arg="self", annotation=None),
                ast.arg(arg=name_lower, annotation=None)
            ],
            vararg=None,
            kwonlyargs=[],
            kw_defaults=[],
            kwarg=None,
            defaults=[],
        )

        fn_body = [
            ast.Return(value=ast.Call(func=ast.Name(id=name),
                                      args=[ast.Name(id=name_lower)],
                                      keywords=[]))
        ]

        # By convention, the helper loading function is called make_class
        return ast.FunctionDef(
            name="make_" + name_lower,
            args=fn_args,
            body=fn_body,
            decorator_list=[ast.Name(id="post_load")],
            returns=None,
        )
Exemplo n.º 2
0
    def get_klass_constructor(self, properties: PropertiesType,
                              requireds: RequiredType) -> ast.FunctionDef:
        # Prepare body
        fn_body = []

        # Default value for `data` argument
        if len(properties) > 0:
            fn_body.append(
                ast.Assign(
                    targets=[ast.Name(id="data")],
                    value=ast.BoolOp(op=ast.Or(),
                                     values=[
                                         ast.Name(id="data"),
                                         ast.Dict(keys=[], values=[])
                                     ]),
                ))

        for key in sorted(properties.keys()):
            # Get default value
            property_ = properties[key]
            is_required = key in requireds

            value = self.get_member_value(key,
                                          property_,
                                          is_required=is_required)

            # Build assign expression
            attribute = ast.Assign(
                targets=[ast.Attribute(value=ast.Name(id="self"), attr=key)],
                value=value)

            # Add to body
            fn_body.append(attribute)

        # Bundle function arguments and keywords
        fn_arguments = ast.arguments(
            args=[
                ast.arg(arg="self", annotation=None),
                ast.arg(arg="data", annotation=None)
            ],
            vararg=None,
            kwarg=None,
            kwonlyargs=[],
            kw_defaults=[],
            defaults=[ast.NameConstant(value=None)],
        )

        # Generate class constructor
        fn_init = ast.FunctionDef(name="__init__",
                                  args=fn_arguments,
                                  body=fn_body,
                                  decorator_list=[],
                                  returns=None)

        # Return constructor
        return fn_init
Exemplo n.º 3
0
    def map_property_as_array(self, property_: PropertyType,
                              value: ast.AST) -> ast.AST:
        """
        If array and `items` has `$ref` wrap it into a list comprehension and map array's elements
        """
        # Exit early if doesn't needs to wrap
        items = property_.get("items")

        if isinstance(items, list):
            # We don't support tuple definition yet
            raise NotImplementedError(
                "Tuple items for type 'array' not supported: {}".format(
                    property_))

        elif isinstance(items, dict):
            if "oneOf" in items:
                ref = items["oneOf"][0].get("$ref")

                if ref is None:
                    return value

                # Don't wrap if type is a primitive
                ref = self.definitions[ref]

                if self.definition_is_primitive_alias(ref):
                    return value

                # Wrap value
                ref_title = ref["title"]

                return ast.ListComp(
                    elt=ast.Call(
                        func=ast.Name(id=ref_title),
                        args=[ast.Name(id="v")],
                        keywords=[],
                        starargs=None,
                        kwargs=None,
                    ),
                    generators=[
                        ast.comprehension(target=ast.Name(id="v"),
                                          iter=value,
                                          ifs=[],
                                          is_async=0)
                    ],
                )

        return value
Exemplo n.º 4
0
 def get_key_from_data_object(self, key: str) -> ast.Call:
     return ast.Call(
         func=ast.Attribute(value=ast.Name(id="data"), attr="get"),
         args=[ast.Str(s=key)],
         keywords=[],
         starargs=None,
         kwargs=None,
     )
Exemplo n.º 5
0
 def get_nested_type(self, prop):
     """
     Scrap the definition from the reference string
     """
     nested_schema_name = search("#/definitions/(.*)$", prop["$ref"])
     attr_type = nested_schema_name.group(1)
     attr_args = [ast.Name(id=upper_first_letter(attr_type) + "Schema")]
     return "Dict", attr_args
Exemplo n.º 6
0
 def _make_field(self, field: str, args: List, keywords: List) -> ast.Call:
     return ast.Call(
         func=ast.Attribute(value=ast.Name(id="fields_"), attr=field),
         args=args,
         keywords=keywords,
         starargs=None,
         kwargs=None,
     )
Exemplo n.º 7
0
    def map_property_as_array(self, property_: PropertyType,
                              value: ast.AST) -> ast.ListComp:
        """
        If array has `items` as 'object' type with `$ref`
        wrap it into a list comprehension and map array's elements
        """
        # Exit early if doesn't needs to wrap
        items = property_.get("items")

        if isinstance(items, dict):
            one_of = items.get("oneOf")
            ref = one_of[0]["$ref"] if one_of else None
        elif isinstance(items, list):
            raise NotImplementedError(
                "Tuple for 'array' non supported: {}".format(property_))
        else:
            ref = None

        if property_.get("type") != "array" or ref is None:
            return value

        # Don't wrap if type is a primitive
        ref = self.definitions[ref]

        if self.definition_is_primitive_alias(ref):
            return value

        # Wrap value
        ref_title = ref["title"]

        return ast.ListComp(
            elt=ast.Call(
                func=ast.Name(id=ref_title),
                args=[ast.Name(id="v")],
                keywords=[],
                starargs=None,
                kwargs=None,
            ),
            generators=[
                ast.comprehension(target=ast.Name(id="v"),
                                  iter=value,
                                  ifs=[],
                                  is_async=0)
            ],
        )
Exemplo n.º 8
0
    def get_partial_annotation_from_definition(
            self, property_: PropertyType) -> ast.AST:
        # Map property type to annotation
        property_type = property_["type"]

        # ...map object
        if property_type == "object":
            if "oneOf" in property_:
                ref = self.definitions[property_["oneOf"][0]["$ref"]]

                if self.definition_is_primitive_alias(ref):
                    annotation = self.get_partial_annotation_from_definition(
                        ref)
                else:
                    annotation = ast.Name(id=ref["title"])
            elif "additionalProperties" in property_:
                object_name = self.definitions[
                    property_["additionalProperties"]["$ref"]]["title"]

                annotation = ast.Subscript(
                    value=ast.Name(id="Dict"),
                    slice=ast.Index(value=ast.Tuple(
                        elts=[ast.Name(id="str"),
                              ast.Name(id=object_name)])),
                )
            else:
                annotation = ast.Name(id="Dict")

        # ... map array
        elif property_type == "array":
            items = property_.get("items")

            if isinstance(items, dict):

                item_annotation = self.get_partial_annotation_from_definition(
                    items)
            elif isinstance(items, list):
                raise NotImplementedError(
                    "Tuple for 'array' is not supported: {}".format(property_))
            else:
                item_annotation = ast.Name(id="Any")

            annotation = ast.Subscript(value=ast.Name(id="List"),
                                       slice=ast.Index(value=item_annotation))

        # ... map scalar
        else:
            python_type = PYTHON_ANNOTATION_MAP[property_["type"]]

            annotation = ast.Name(id=python_type)

        # Return
        return annotation
Exemplo n.º 9
0
    def get_annotation_from_definition(self,
                                       property_: PropertyType,
                                       is_required: bool = False) -> ast.AST:
        annotation = self.get_partial_annotation_from_definition(property_)

        # Make annotation optional if no default value
        if not is_required and "default" not in property_:
            annotation = ast.Subscript(value=ast.Name(id="Optional"),
                                       slice=ast.Index(value=annotation))

        # Return
        return annotation
Exemplo n.º 10
0
    def _get_dict_comprehension_for_property(self, key, property_):
        # key, value
        comp_key = ast.Name(id="k")
        comp_value = ast.Call(
            func=ast.Name(id="Value"),
            args=[ast.Name(id="v")],
            keywords=[],
            starargs=None,
            kwargs=None,
        )

        generator = ast.comprehension(
            target=ast.Tuple(elts=[ast.Name(
                id="k"), ast.Name(id="v")]),
            iter=ast.Call(
                func=ast.Attribute(
                    value=self.get_key_with_default_for_data_object(
                        key, property_),
                    attr="iteritems",
                ),
                args=[],
                keywords=[],
                starargs=None,
                kwargs=None,
            ),
            ifs=[],
            is_async=0,
        )

        # Dit comprehension
        dict_comp = ast.DictComp(key=comp_key,
                                 value=comp_value,
                                 generators=[generator])

        # Return node
        return dict_comp
Exemplo n.º 11
0
    def get_klass_constructor(self, properties, required):
        # Prepare body
        body = []

        for key in sorted(properties.keys()):
            # Get default value
            property_ = properties[key]
            value = self._get_member_value(key, property_, required)

            # Build assign expression
            attribute = ast.Assign(targets=[ast.Name(id=key)], value=value)

            # Add to body
            body.append(attribute)

        # Return constructor
        return body
Exemplo n.º 12
0
    def _map_property_if_array(self, property_: PropertyType,
                               value: ast.AST) -> ast.AST:
        """
        If array and `items` has `$ref` wrap it into a list comprehension and map array's elements
        """
        # Exit early if doesn't needs to wrap
        property_items = property_.get("items", {})

        if isinstance(property_items, dict) and "oneOf" in property_items:
            refs = (i.get("$ref") for i in property_items["oneOf"])
            refs = [r for r in refs if r is not None]
        elif isinstance(property_items, list):
            refs = [
                i["$ref"] for i in property_.get("items", []) if "$ref" in i
            ]
        else:
            refs = []

        if property_.get("type") != "array":  # or len(refs) == 0:
            return value

        if len(refs) == 0:
            return self._set_item_type_scalar(property_, value)

        # Only support one custom type for now
        if len(refs) > 1:
            raise NotImplementedError(
                "{}: we only support one $ref per array".format(self))

        # Don't wrap if type is a primitive
        ref = self.definitions[refs[0]]

        if self.definition_is_primitive_alias(ref):
            return self._set_item_type_scalar(property_, value)

        # Where the array type references a definition, make a nested field with
        # the type of the item schema
        ref_title = upper_first_letter(ref["title"])
        for node in ast.walk(value):
            if isinstance(node, ast.Call):
                x = self._make_field("Nested",
                                     [ast.Name(id=ref_title + "Schema")], [])
                node.args = [x] + node.args

        return value
Exemplo n.º 13
0
    def klass(self, definition):
        # Build class properties
        class_body = []
        properties = definition.get("properties")

        if properties:
            required = definition.get("required", ())
            class_body.extend(self.get_klass_constructor(properties, required))
        else:
            class_body.append(ast.Pass())

        # Create class definition
        class_def = ast.ClassDef(
            name=upper_first_letter(definition["title"]) + "Schema",
            bases=[ast.Name(id="Schema")],
            body=class_body,
            decorator_list=[],
            keywords=[],
        )

        # Add to module's body
        return class_def
Exemplo n.º 14
0
    def coerce_to_nested_object(self, property_: PropertyType,
                                value: ast.AST) -> ast.AST:
        if "oneOf" in property_:
            ref = property_["oneOf"][0].get("$ref")

            if ref is not None:
                # Don't wrap if type is a primitive
                ref = self.definitions[ref]

                if not self.definition_is_primitive_alias(ref):
                    ref_title = ref["title"]

                    value = ast.IfExp(
                        test=ast.Compare(
                            left=value,
                            ops=[ast.Is()],
                            comparators=[ast.NameConstant(value=None)]),
                        body=ast.NameConstant(value=None),
                        orelse=ast.Call(func=ast.Name(id=ref_title),
                                        args=[value],
                                        keywords=[]),
                    )

        return value
Exemplo n.º 15
0
 def slice_key_from_data_object(self, key: str) -> ast.Subscript:
     return ast.Subscript(value=ast.Name(id="data"),
                          slice=ast.Index(value=ast.Str(s=key)))