-
-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
abstract attribut problem #406
Comments
abstract ABC and dataclasses don't play nice together without adding extra code to ensure you can't instantiate an abstract class. do you have a specific use case that you would need this as a feature? |
from abc import ABC, abstractmethod
from typing import List
from dataclasses import dataclass, field
@dataclass
class Animal(ABC):
@abstractmethod
def move(self):
pass
# a = Animal()
# TypeError: Can't instantiate abstract class Animal with abstract methods move
@dataclass
class Cat(Animal):
def move(self):
super().move()
print('Cat moves')
@dataclass
class Dog(Animal):
def move(self):
super().move()
print('Dog moves')
@dataclass
class Animals():
animals: List[Animal] = field(default_factory=list)
def move(self):
for a in self.animals:
a.move()
als = Animals()
als.animals.append(Dog())
als.animals.append(Cat())
als.move()
|
I am pretty sure I tried that in the very beginning and I had encountered an issue, anyway I 'll take another look. |
Yeah this one In [8]: from abc import ABC, abstractmethod
...: from typing import List
...: from dataclasses import dataclass, field
...:
...: @dataclass
...: class Animal(ABC):
...: value: int
...:
...: @dataclass
...: class Cat(Animal):
...: pass
...:
...: @dataclass
...: class Dog(Animal):
...: pass
...:
In [9]:
In [9]: Animal(1)
Out[9]: Animal(value=1) # this one shouldn't work
In [10]: Cat(1)
Out[10]: Cat(value=1) |
ok! Very weird behavior. I haven't tested an abstract class without abstract methods. |
Yeah that's what I am trying to avoid, I don't want to complicate the models but I understand that it's necessary for devs to recognize what models they are not supposed to instantiate. |
I have troubles with abstract classes but I can't share my xsd publicly. <xs:element name="Foo" type="test:Foo"/>
<xs:complexType name="Foo" abstract="true">
....
<xs:complexContent>
<xs:extension base="test:Bar">
....... This is an example very close to what I have and does not work as expected <xsd:schema xmlns:test="urn:MyNamespace" targetNamespace="urn:MyNamespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation>
This example illustrates complex types that are derived from other specified types.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="ItemsType" type="test:ItemsType"/>
<xsd:complexType name="test:ItemsType">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="hat" type="test:ProductType"/>
<xsd:element name="umbrella" type="test:RestrictedProductType"/>
<xsd:element name="shirt" type="test:ShirtType"/>
</xsd:choice>
</xsd:complexType>
<!--Empty Content Type-->
<xsd:element name="ItemType" type="test:ItemType"/>
<xsd:complexType name="test:ItemType" abstract="true">
<xsd:attribute name="routingNum" type="xsd:integer"/>
</xsd:complexType>
<!--Empty Content Extension (with Attribute Extension)-->
<xsd:element name="ProductType" type="test:ProductType"/>
<xsd:complexType name="test:ProductType">
<xsd:complexContent>
<xsd:extension base="test:ItemType">
<xsd:sequence>
<xsd:element name="number" type="xsd:integer"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="effDate" type="xsd:date"/>
<xsd:attribute name="lang" type="xsd:language"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!--Complex Content Restriction-->
<xsd:element name="RestrictedProductType" type="test:RestrictedProductType"/>
<xsd:complexType name="test:RestrictedProductType">
<xsd:complexContent>
<xsd:restriction base="test:ProductType">
<xsd:sequence>
<xsd:element name="number" type="xsd:integer"/>
<xsd:element name="name" type="xsd:token"/>
</xsd:sequence>
<xsd:attribute name="routingNum" type="xsd:short" use="required"/>
<xsd:attribute name="effDate" type="xsd:date" default="1900-01-01"/>
<xsd:attribute name="lang" use="prohibited"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!--Complex Content Extension-->
<xsd:element name="ShirtType" type="test:ShirtType"/>
<xsd:complexType name="test:ShirtType">
<xsd:complexContent>
<xsd:extension base="test:RestrictedProductType">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="size" type="test:SmallSizeType"/>
<xsd:element name="color" type="test:ColorType"/>
</xsd:choice>
<xsd:attribute name="sleeve" type="xsd:integer"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!--Simple Content Extension-->
<xsd:element name="SizeType" type="test:SizeType"/>
<xsd:complexType name="test:SizeType">
<xsd:simpleContent>
<xsd:extension base="xsd:integer">
<xsd:attribute name="system" type="xsd:token"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<!--Simple Content Restriction-->
<xsd:element name="SmallSizeType" type="test:SmallSizeType"/>
<xsd:complexType name="test:SmallSizeType">
<xsd:simpleContent>
<xsd:restriction base="test:SizeType">
<xsd:minInclusive value="2"/>
<xsd:maxInclusive value="6"/>
<xsd:attribute name="system" type="xsd:token"
use="required"/>
</xsd:restriction>
</xsd:simpleContent>
</xsd:complexType>
<xsd:element name="ColorType" type="test:ColorType"/>
<xsd:complexType name="test:ColorType">
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
```
My xsd files are exported from Enterprise Architect.
I tested to put the namespace "test:" in front of the complexType name attribute but it does not work |
Can you be a little more specific please, if you check the generated models with the latest version You can see ProductType extending ItemType, ShirtType extending RestrictedProductType It's not obvious @untereiner what the problem is. You don't have to share your whole xsd just the two basic types if you can the abstract complex type and the extending element. Change names and types if you want, but I need something more concrete to continue the investigation |
Yeah, that's why I modified your example to copycat mine. I added the namespaces. If you test you will see it does not generate the inheritance |
xsdata is pretty forgiving with invalid schemas but it's not a validator, the schema itself fails to parse xmlschema and a couple validators I tried online. |
I was to confident on the Enterprise Architect output. However this one is w3c compliant and the problem remains <xsd:schema xmlns:test="urn:MyNamespace" targetNamespace="urn:MyNamespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation>
This example illustrates complex types that are derived from other specified types.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="ItemsType" type="test:ItemsType"/>
<xsd:complexType name="ItemsType">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="hat" type="test:ProductType"/>
<xsd:element name="umbrella" type="test:RestrictedProductType"/>
<xsd:element name="shirt" type="test:ShirtType"/>
</xsd:choice>
</xsd:complexType>
<!--Empty Content Type-->
<xsd:element name="ItemType" type="test:ItemType"/>
<xsd:complexType name="ItemType" abstract="true">
<xsd:attribute name="routingNum" type="xsd:integer"/>
</xsd:complexType>
<!--Empty Content Extension (with Attribute Extension)-->
<xsd:element name="ProductType" type="test:ProductType"/>
<xsd:complexType name="ProductType">
<xsd:complexContent>
<xsd:extension base="test:ItemType">
<xsd:sequence>
<xsd:element name="number" type="xsd:integer"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="effDate" type="xsd:date"/>
<xsd:attribute name="lang" type="xsd:language"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!--Complex Content Restriction-->
<xsd:element name="RestrictedProductType" type="test:RestrictedProductType"/>
<xsd:complexType name="RestrictedProductType">
<xsd:complexContent>
<xsd:restriction base="test:ProductType">
<xsd:sequence>
<xsd:element name="number" type="xsd:integer"/>
<xsd:element name="name" type="xsd:token"/>
</xsd:sequence>
<xsd:attribute name="routingNum" type="xsd:short" use="required"/>
<xsd:attribute name="effDate" type="xsd:date" default="1900-01-01"/>
<xsd:attribute name="lang" use="prohibited"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!--Complex Content Extension-->
<xsd:element name="ShirtType" type="test:ShirtType"/>
<xsd:complexType name="ShirtType">
<xsd:complexContent>
<xsd:extension base="test:RestrictedProductType">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="size" type="test:SmallSizeType"/>
<xsd:element name="color" type="test:ColorType"/>
</xsd:choice>
<xsd:attribute name="sleeve" type="xsd:integer"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!--Simple Content Extension-->
<xsd:element name="SizeType" type="test:SizeType"/>
<xsd:complexType name="SizeType">
<xsd:simpleContent>
<xsd:extension base="xsd:integer">
<xsd:attribute name="system" type="xsd:token"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<!--Simple Content Restriction-->
<xsd:element name="SmallSizeType" type="test:SmallSizeType"/>
<xsd:complexType name="SmallSizeType">
<xsd:simpleContent>
<xsd:restriction base="test:SizeType">
<xsd:minInclusive value="2"/>
<xsd:maxInclusive value="6"/>
<xsd:attribute name="system" type="xsd:token"
use="required"/>
</xsd:restriction>
</xsd:simpleContent>
</xsd:complexType>
<xsd:element name="ColorType" type="test:ColorType"/>
<xsd:complexType name="ColorType">
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
</xsd:schema> |
Oh now I see you added an element with a matching name to a complexType and that complex type is abstract. Here xsdata handling has always been a little fuzzy, it decides to keep the element and flatten the complex type, although fuzzy its a sane way to proceed other processors like xjc for java fail for schemas like that. How would you suggest to resolve this when an element and a complexType share the same name? |
I have an idea about this to not flatten base classes if there is a name conflict, it might take some time |
I do not control the xsd generation. It is an output of Enterprise Architect. An hint maybe. |
Well ignoring them is too crude, effectively now xsdata ignores/flattens the complexType, also I tried xjc for me it's throwing errors. I am thinking in these cases to mark as base class the complexType with a suffix or something and rename all type occurrences. In our case it would create something like this class ItemsTypeBase:
...
class ItemsType(ItemsTypeBase)
.... |
I was writing that because of this sentence: "On XSD import, by default, Enterprise Architect treats this global element and its bounding ComplexType as a single entity, and creates a single XSDcomplexType stereotyped Class with the same name as the global element, as shown:" class ItemsTypeBase:
hat
...
class ItemsType(ItemsTypeBase):
pass The child will be passed |
give it a try #411 @untereiner It's still wip because it has some side effects I need to verify first but it will avoid flattening duplicate complex types. |
@tefra I'll give it a try in the coming days. Thanks ! |
After a first test:
|
Well that's what happens when you have "dummy" elements like that what did you expect? <xsd:element name="ItemType" type="test:ItemType"/>
<xsd:complexType name="ItemType" abstract="true">
<xsd:attribute name="routingNum" type="xsd:integer"/>
</xsd:complexType> Please provide some samples and what you think would be the proper output, otherwise I don't know how to proceed. |
Yes I try to provide what I am expecting (sort of). |
<xsd:complexType name="AbstractObject" abstract="true">
<xs:sequence>
<xs:element name="Aliases" type="String" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Citation" type="String" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xsd:complexType name="FeatureDictionnary">
<xs:complexContent>
<xs:extension base="AbstractObject">
<xs:sequence>
<xs:element name="Feature" type="Feature" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
I expect to not be able to instantiate it. It is modeled in UML that way class AbstractObject():
aliases: List[str] = field(default_factory=list, metadata...)
citation: Optional[str] = field(default=None, metadata=.....)
class FeatureDictionnary(AbstractObject):
feature: List[Feature] = field( default_factory=list, meta.....) It should not be possible to instantiate and AbstractObject() |
<xsd:element name="ItemType" type="ItemType"/>
<xsd:complexType name="ItemType">
<xsd:attribute name="routingNum" type="xsd:integer"/>
</xsd:complexType> should give (simplified) class ItemType():
routingNum: int |
It does that already v20.2 (and latest master), I hope you have a number 2 use case @dataclass
class ItemType:
routing_num: Optional[int] = field(
default=None,
metadata={
"name": "routingNum",
"type": "Attribute",
"required": True,
}
) |
yes the tiny example works so I do not understand why I don't get a proper type hierarchy with my xsd files |
Can you create an excerpt of the two types that have the issue? There are a couple of cases because of limitation either in dataclasses or python in general that base classes have to be flattened but without any sample I can't say for sure that's your case or if there is an actual bug. If you trust me I won't share anything shared privately contact me at chris[at]komposta.net |
Thanks for sending me the actual xsd files So let's take this schema that demonstrates your case <?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Root" type="Root" />
<xs:complexType name="Root" abstract="true">
<xs:complexContent>
<xs:extension base="AbstractRoot">
<xs:sequence>
<xs:element name="a" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="b" type="xs:string" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="AbstractRoot" type="AbstractRoot" />
<xs:complexType name="AbstractRoot" abstract="true">
<xs:sequence>
<xs:element name="c" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="d" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:schema> This is the output with the latest master @dataclass
class AbstractRoot:
c: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
d: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
@dataclass
class Root:
c: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
d: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
a: Optional[str] = field(
default=None,
metadata={
"type": "Element",
}
)
b: Optional[str] = field(
default=None,
metadata={
"type": "Element",
}
) The generator finds one element and one complex type with the same name, it decides to flatten the complex type and keep the element. Flatten means all it's properties recursively are copied to the element(s). That's where I agree the generator is a bit clunky here. This is the output from the experimental solution from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class AbstractRoot1:
class Meta:
name = "AbstractRoot"
c: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
d: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
@dataclass
class AbstractRoot(AbstractRoot1):
pass
@dataclass
class Root1(AbstractRoot1):
class Meta:
name = "Root"
a: Optional[str] = field(
default=None,
metadata={
"type": "Element",
}
)
b: Optional[str] = field(
default=None,
metadata={
"type": "Element",
}
)
@dataclass
class Root(Root1):
pass The experimental solution will force the complex types with the same names to be renamed but generates all models with the correct hierarchy without flattening any properties or base classes. Which is 100% accurate representation of the xsd types. Currently the renaming module simply auto appends the next available auto increment. If I understand correctly you don't like the generation of the bare classes <xs:element name="Root" type="Root" />
<xs:complexType name="Root" abstract="true"> If that's the case I am thinking to add another layer for the duplicate class name handler, to skip those from generating, let me know if that's really the issue here. |
Thank you @tefra. You pointed out exactly the issue here. Why do not flatten (copying) the from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class AbstractRoot:
class Meta:
name = "AbstractRoot"
c: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
d: List[str] = field(
default_factory=list,
metadata={
"type": "Element",
}
)
@dataclass
class Root(AbstractRoot):
class Meta:
name = "Root"
a: Optional[str] = field(
default=None,
metadata={
"type": "Element",
}
)
b: Optional[str] = field(
default=None,
metadata={
"type": "Element",
}
) |
We can't have to classes with the same name right? So until now the generator has to chose which type to generate, the properties of the type the types that are not generated have to copied to the subclasses. That's how xsdata works in general if you notice only root complex types and elements are converted to classes, all root simple types, attributes, attribute groups etc etc are flattened. Yeah it's not that simple, the process is linear, the generator converts all types to a basic form of class, then based on rules these classes are reduced to avoid duplication and as much noise as possible. So if we have this structure A(B), B(C), C it doesn't just jump from C -> A, since it has decided to flatten class B all its properties including the ones from C are copied to class A. Anyway don't worry about that, if we sort out the strategy when there is an xs:element and xs:complexType everything will fall into place. |
ok! |
Thank you for reporting @untereiner the change is on master and will be included in the next release. |
Hi,
<xs:complexType name="myType" abstract="true">
is not marked as abstract is the generation process. I do not have a minimial example here but I made print outs in the code and I saw that the mixins is called twice for the same complexType. The second time the abstract attribut is False. I know it is not very clear. I will try to clarify it.The text was updated successfully, but these errors were encountered: