Esempio n. 1
0
 def _visited_union(self, results: Sequence[JsonSchema]) -> JsonSchema:
     if len(results) == 1:
         return results[0]
     elif any(alt == {} for alt in results):
         return JsonSchema()
     elif all(alt.keys() == {"type"} for alt in results):
         types: Any = chain.from_iterable(
             [res["type"]] if isinstance(res["type"], (
                 str, JsonType)) else res["type"] for res in results)
         return json_schema(type=list(types))
     elif (len(results) == 2 and all("type" in res for res in results) and {
             "type": "null"
     } in results):
         for result in results:
             if result != {"type": "null"}:
                 types = result["type"]
                 if isinstance(types, (str, JsonType)):
                     types = [types]
                 if "null" not in types:
                     result = JsonSchema({
                         **result, "type": [*types, "null"]
                     })
                 return result
         else:
             raise NotImplementedError
     else:
         return json_schema(anyOf=results)
Esempio n. 2
0
 def _union_result(self, results: Iterable[JsonSchema]) -> JsonSchema:
     results = list(results)
     if len(results) == 1:
         return results[0]
     elif all(alt.keys() == {"type"} for alt in results):
         types = chain.from_iterable(
             [res["type"]] if isinstance(res["type"], JsonType
                                         ) else res["type"]
             for res in results)
         return json_schema(type=list(types))
     elif (len(results) == 2 and all("type" in res for res in results) and {
             "type": "null"
     } in results):
         for result in results:
             if result != {"type": "null"}:
                 types = result["type"]
                 if isinstance(types, str):
                     types = [types]  # type: ignore
                 if "null" not in types:
                     result = JsonSchema({
                         **result, "type": [*types, "null"]
                     })
                 return result
         else:
             raise NotImplementedError()
     else:
         return json_schema(anyOf=results)
Esempio n. 3
0
 def literal(self, values: Sequence[Any]) -> JsonSchema:
     if not values:
         raise TypeError("Empty Literal")
     types = {JsonType.from_type(type(v)) for v in literal_values(values)}
     # Mypy issue
     type_: Any = types.pop() if len(types) == 1 else types
     if len(values) == 1:
         return json_schema(type=type_, const=values[0])
     else:
         return json_schema(type=type_, enum=values)
Esempio n. 4
0
 def literal(self, values: Sequence[Any]) -> JsonSchema:
     if not values:
         raise TypeError("Empty Literal")
     types = sorted(
         set(
             JsonType.from_type(type(v.value if isinstance(v, Enum) else v))
             for v in values))
     # Mypy issue
     type_: Any = types[0] if len(types) == 1 else types
     if len(values) == 1:
         return json_schema(type=type_, const=values[0])
     else:
         return json_schema(type=type_, enum=values)
Esempio n. 5
0
 def object(self, tp: AnyType, fields: Sequence[ObjectField]) -> JsonSchema:
     cls = get_origin_or_type(tp)
     flattened_schemas: List[JsonSchema] = []
     pattern_properties = {}
     additional_properties: Union[bool,
                                  JsonSchema] = self.additional_properties
     properties = {}
     required = []
     for field in fields:
         if field.flattened:
             self._check_flattened_schema(cls, field)
             flattened_schemas.append(self.visit_field(field))
         elif field.pattern_properties is not None:
             if field.pattern_properties is ...:
                 pattern = infer_pattern(field.type,
                                         self.default_conversion)
             else:
                 assert isinstance(field.pattern_properties, Pattern)
                 pattern = field.pattern_properties
             pattern_properties[pattern] = self._properties_schema(field)
         elif field.additional_properties:
             additional_properties = self._properties_schema(field)
         else:
             alias = self.aliaser(field.alias)
             if is_typed_dict(cls):
                 is_required = field.required
             else:
                 is_required = self._field_required(field)
             properties[alias] = self.visit_field(field, is_required)
             if is_required:
                 required.append(alias)
     alias_by_names = {f.name: f.alias for f in fields}.__getitem__
     dependent_required = get_dependent_required(cls)
     result = json_schema(
         type=JsonType.OBJECT,
         properties=properties,
         required=required,
         additionalProperties=additional_properties,
         patternProperties=pattern_properties,
         dependentRequired=OrderedDict(
             (alias_by_names(f),
              sorted(map(alias_by_names, dependent_required[f])))
             for f in sorted(dependent_required, key=alias_by_names)),
     )
     if flattened_schemas:
         result = json_schema(
             type=JsonType.OBJECT,
             allOf=[result, *flattened_schemas],
             unevaluatedProperties=False,
         )
     return result
Esempio n. 6
0
 def mapping(self, cls: Type[Mapping], key_type: AnyType,
             value_type: AnyType) -> JsonSchema:
     with context_setter(self):
         self._ignore_first_ref = True
         key = self.visit(key_type)
     if key["type"] != JsonType.STRING:
         raise ValueError("Mapping types must string-convertible key")
     value = self.visit(value_type)
     if "pattern" in key:
         return json_schema(type=JsonType.OBJECT,
                            patternProperties={key["pattern"]: value})
     else:
         return json_schema(type=JsonType.OBJECT,
                            additionalProperties=value)
Esempio n. 7
0
 def tuple(self, types: Sequence[AnyType]) -> JsonSchema:
     return json_schema(
         type=JsonType.ARRAY,
         items=[self.visit(cls) for cls in types],
         minItems=len(types),
         maxItems=len(types),
     )
Esempio n. 8
0
 def collection(self, cls: Type[Collection],
                value_type: AnyType) -> JsonSchema:
     return json_schema(
         type=JsonType.ARRAY,
         items=self.visit(value_type),
         uniqueItems=issubclass(cls, AbstractSet),
     )
Esempio n. 9
0
 def collection(self, cls: Type[Iterable],
                value_type: AnyType) -> JsonSchema:
     self._check_constraints(ArrayConstraints)
     return json_schema(
         type=JsonType.ARRAY,
         items=self.visit(value_type),
         uniqueItems=issubclass(cls, AbstractSet),
     )
Esempio n. 10
0
 def typed_dict(self, cls: Type, keys: Mapping[str, AnyType],
                total: bool) -> JsonSchema:
     self._check_constraints(ObjectConstraints)
     return json_schema(
         type=JsonType.OBJECT,
         properties={key: self.visit(tp)
                     for key, tp in keys.items()},
         required=list(keys) if total else [],
     )
Esempio n. 11
0
 def mapping(
     self,
     cls: Type[Mapping],
     key_type: AnyType,
     value_type: AnyType,
 ) -> JsonSchema:
     self._check_constraints(ObjectConstraints)
     with self._without_ref():
         key = self.visit(key_type)
     if key["type"] != JsonType.STRING:
         raise ValueError("Mapping types must string-convertible key")
     value = self.visit(value_type)
     if "pattern" in key:
         return json_schema(type=JsonType.OBJECT,
                            patternProperties={key["pattern"]: value})
     else:
         return json_schema(type=JsonType.OBJECT,
                            additionalProperties=value)
Esempio n. 12
0
 def tuple(self, types: Sequence[AnyType]) -> JsonSchema:
     self._check_constraints(ArrayConstraints)
     if self._schema is not None and self._schema.constraints is not None:
         assert isinstance(self._schema.constraints, ArrayConstraints)
         if (self._schema.constraints.max_items is not None
                 or self._schema.constraints.min_items is not None):
             raise TypeError(
                 "Tuple cannot have min_items/max_items constraints")
     return json_schema(
         type=JsonType.ARRAY,
         items=[self.visit(cls) for cls in types],
         minItems=len(types),
         maxItems=len(types),
     )
Esempio n. 13
0
 def named_tuple(
     self,
     cls: Type[Tuple],
     types: Mapping[str, AnyType],
     defaults: Mapping[str, Any],
 ) -> JsonSchema:
     self._check_constraints(ObjectConstraints)
     return json_schema(
         type=JsonType.OBJECT,
         properties={
             self.aliaser(key): self.visit(key)
             for key, cls in types.items()
         },
         required=sorted(types.keys() - defaults.keys()),
         additionalProperties=settings.additional_properties,
     )
Esempio n. 14
0
 def dataclass(
     self,
     cls: Type,
     types: Mapping[str, AnyType],
     fields: Sequence[Field],
     init_vars: Sequence[Field],
 ) -> JsonSchema:
     assert is_dataclass(cls)
     self._check_constraints(ObjectConstraints)
     properties = {}
     required: List[str] = []
     merged_schemas = []
     pattern_properties = {}
     additional_properties: Union[
         bool, JsonSchema] = settings.additional_properties
     for field in get_fields(fields, init_vars, self.operation):
         metadata = check_metadata(field)
         field_type = types[field.name]
         if MERGED_METADATA in metadata:
             self._check_merged_schema(cls, field, field_type)
             merged_schemas.append(self.visit_field(field, field_type))
         elif PROPERTIES_METADATA in metadata:
             pattern = metadata[PROPERTIES_METADATA]
             properties_schema = self._properties_schema(field, field_type)
             if pattern is None:
                 additional_properties = properties_schema
             elif pattern is ...:
                 pattern_properties[infer_pattern(
                     field_type)] = properties_schema
             else:
                 pattern_properties[pattern] = properties_schema
         else:
             alias = self.aliaser(get_alias(field))
             properties[alias] = json_schema(
                 readOnly=not field.init,
                 writeOnly=field in init_vars,
                 **self.visit_field(field, field_type),
             )
             if is_required(field):
                 required.append(alias)
     if self.operation == Operation.SERIALIZATION:
         for name, resolver in get_serialized_resolvers(cls).items():
             with self._replace_conversions(resolver.conversions):
                 properties[self.aliaser(name)] = json_schema(
                     readOnly=True,
                     **self.visit_with_schema(resolver.return_type,
                                              resolver.schema),
                 )
     dependent_required = {
         self.aliaser(get_alias(field)):
         sorted(self.aliaser(get_alias(req)) for req in required_by)
         for field, required_by in get_requirements(
             cls, DependentRequired.requiring, self.operation).items()
     }
     result = json_schema(
         type=JsonType.OBJECT,
         properties=properties,
         required=required,
         additionalProperties=additional_properties,
         patternProperties=pattern_properties,
         dependentRequired=OrderedDict((f, dependent_required[f])
                                       for f in sorted(dependent_required)),
     )
     if merged_schemas:
         result = json_schema(
             type=JsonType.OBJECT,
             allOf=[result, *merged_schemas],
             unevaluatedProperties=False,
         )
     return result