tefra / xsdata-pydantic

Naive XML & JSON Bindings for python pydantic classes!
https://xsdata-pydantic.readthedocs.io/
MIT License
52 stars 10 forks source link

Field ordering issue in generated output #12

Closed ialarmedalien closed 7 months ago

ialarmedalien commented 2 years ago

Attempting to dump the JSONschema from the pydantic class generated by xsdata-pydantic results in a type error due to field ordering: "TypeError: non-default argument 'name_identifier' follows default argument"

To reproduce:

Source file: https://github.com/datacite/schema/blob/master/source/meta/kernel-4.4/metadata.xsd (plus associated files)

xsdata kernel-4.4/metadata.xsd

then run the following python code:

>>> import pydantic
>>> import generated.metadata as md
>>> print(pydantic.schema_json_of(md.Resource, title="Resource", indent=2))

Output:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pydantic/tools.py", line 92, in pydantic.tools.schema_json_of
  File "pydantic/tools.py", line 30, in pydantic.tools._get_parsing_type
  File "pydantic/main.py", line 1026, in pydantic.main.create_model
  File "pydantic/main.py", line 198, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/fields.py", line 506, in pydantic.fields.ModelField.infer
  File "pydantic/fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 557, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 831, in pydantic.fields.ModelField.populate_validators
  File "pydantic/validators.py", line 725, in find_validators
  File "pydantic/dataclasses.py", line 479, in make_dataclass_validator
    value = f'{default_name}()'
  File "pydantic/dataclasses.py", line 231, in pydantic.dataclasses.dataclass
    def __repr__(self):
  File "pydantic/dataclasses.py", line 224, in pydantic.dataclasses.dataclass.wrap

  File "pydantic/dataclasses.py", line 336, in pydantic.dataclasses._add_pydantic_validation_attributes
    f'eq={self.eq!r},'
  File "pydantic/dataclasses.py", line 391, in pydantic.dataclasses.create_pydantic_model_from_dataclass
    # This function's logic is copied from "recursive_repr" function in
  File "pydantic/main.py", line 1026, in pydantic.main.create_model
  File "pydantic/main.py", line 198, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/fields.py", line 506, in pydantic.fields.ModelField.infer
  File "pydantic/fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 557, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 831, in pydantic.fields.ModelField.populate_validators
  File "pydantic/validators.py", line 725, in find_validators
  File "pydantic/dataclasses.py", line 479, in make_dataclass_validator
    value = f'{default_name}()'
  File "pydantic/dataclasses.py", line 231, in pydantic.dataclasses.dataclass
    def __repr__(self):
  File "pydantic/dataclasses.py", line 224, in pydantic.dataclasses.dataclass.wrap

  File "pydantic/dataclasses.py", line 336, in pydantic.dataclasses._add_pydantic_validation_attributes
    f'eq={self.eq!r},'
  File "pydantic/dataclasses.py", line 391, in pydantic.dataclasses.create_pydantic_model_from_dataclass
    # This function's logic is copied from "recursive_repr" function in
  File "pydantic/main.py", line 1026, in pydantic.main.create_model
  File "pydantic/main.py", line 198, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/fields.py", line 506, in pydantic.fields.ModelField.infer
  File "pydantic/fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 552, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 758, in pydantic.fields.ModelField._type_analysis
  File "pydantic/fields.py", line 808, in pydantic.fields.ModelField._create_sub_type
  File "pydantic/fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 557, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 831, in pydantic.fields.ModelField.populate_validators
  File "pydantic/validators.py", line 725, in find_validators
  File "pydantic/dataclasses.py", line 479, in make_dataclass_validator
    value = f'{default_name}()'
  File "pydantic/dataclasses.py", line 231, in pydantic.dataclasses.dataclass
    def __repr__(self):
  File "pydantic/dataclasses.py", line 207, in pydantic.dataclasses.dataclass.wrap

  File "/usr/local/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 1185, in dataclass
    return wrap(cls)
  File "/usr/local/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 1176, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
  File "/usr/local/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 1025, in _process_class
    _init_fn(all_init_fields,
  File "/usr/local/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 546, in _init_fn
    raise TypeError(f'non-default argument {f.name!r} '
TypeError: non-default argument 'name_identifier' follows default argument
tefra commented 2 years ago

I am getting a bit different errors,

First of all use the xsdata --output pydantic command, I am not sure if the schema_json_of works with normal dataclasses.

Then I get this error

File "/Users/chris/projects/xsdata-pydantic/scripts.py", line 6, in <module>
    print(pydantic.schema_json_of(md.Resource, title="Resource", indent=2))
  File "pydantic/tools.py", line 92, in pydantic.tools.schema_json_of
  File "pydantic/main.py", line 675, in pydantic.main.BaseModel.schema_json
  File "pydantic/main.py", line 664, in pydantic.main.BaseModel.schema
  File "pydantic/schema.py", line 186, in pydantic.schema.model_schema
  File "pydantic/schema.py", line 580, in pydantic.schema.model_process_schema
  File "pydantic/schema.py", line 621, in pydantic.schema.model_type_schema
  File "pydantic/schema.py", line 254, in pydantic.schema.field_schema
  File "pydantic/schema.py", line 526, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 924, in pydantic.schema.field_singleton_schema
  File "pydantic/schema.py", line 580, in pydantic.schema.model_process_schema
  File "pydantic/schema.py", line 621, in pydantic.schema.model_type_schema
  File "pydantic/schema.py", line 254, in pydantic.schema.field_schema
  File "pydantic/schema.py", line 526, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 924, in pydantic.schema.field_singleton_schema
  File "pydantic/schema.py", line 580, in pydantic.schema.model_process_schema
  File "pydantic/schema.py", line 621, in pydantic.schema.model_type_schema
  File "pydantic/schema.py", line 254, in pydantic.schema.field_schema
  File "pydantic/schema.py", line 461, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 847, in pydantic.schema.field_singleton_schema
  File "pydantic/schema.py", line 698, in pydantic.schema.field_singleton_sub_fields_schema
  File "pydantic/schema.py", line 526, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 921, in pydantic.schema.field_singleton_schema
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class

Which is know because of this https://github.com/pydantic/pydantic/issues/3695 we have to tell xsdata to unnest all inner classes.

xsdata --unnest-classes --output pydantic

But then I am getting this

Traceback (most recent call last):
  File "/Users/chris/projects/xsdata-pydantic/scripts.py", line 6, in <module>
    print(pydantic.schema_json_of(md.Resource, title="Resource", indent=2))
  File "pydantic/tools.py", line 92, in pydantic.tools.schema_json_of
  File "pydantic/main.py", line 674, in pydantic.main.BaseModel.schema_json
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 201, in encode
    chunks = list(chunks)
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 431, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  [Previous line repeated 2 more times]
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 325, in _iterencode_list
    yield from chunks
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/json/encoder.py", line 438, in _iterencode
    o = _default(o)
  File "pydantic/json.py", line 80, in pydantic.json.pydantic_encoder
  File "/Users/chris/.pyenv/versions/3.10.5/lib/python3.10/dataclasses.py", line 1238, in asdict
    raise TypeError("asdict() should be called on dataclass instances")
TypeError: asdict() should be called on dataclass instances

Which as far as I can tell it tries to pass the Meta class for some reason.

This sounds like a pydantic issue to be honest

tefra commented 7 months ago

Not sure if this is still applicable, I am hoping with the upcoming release, this is also resolved