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
    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
 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)))
    def make_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
            is_required = key in requireds
            property_ = properties[key]

            annotation = self.get_annotation_from_definition(
                property_, is_required=is_required)
            value = self.get_data_value(key,
                                        property_,
                                        is_required=is_required)

            # Build assign expression
            attribute = ast.AnnAssign(
                target=ast.Attribute(value=ast.Name(id="self"), attr=key),
                annotation=annotation,
                value=value,
                simple=0,
            )

            # 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=ast.Subscript(
                        value=ast.Name(id="Optional"),
                        slice=ast.Index(value=ast.Name(id="Dict"))),
                ),
            ],
            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