Repository: tefra/xsdata

Issue:
Problem in typing with self-referencing classes
Problem:
When converting AUTOSAR_00049_COMPACT.xsd (https://www.autosar.org/fileadmin/user_upload/standards/foundation/20-11/AUTOSAR_TR_XMLSchemaSupplement.zip), some classes have references to self. In Python <= 3.9, this is seemingly not supported by the 'typing' module (https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class).

Example to reproduce error:
- use xsdata to convert AUTOSAR_00049_COMPACT.xsd

- try to execute the following program
```
from autosar_00048_compact import Autosar

if __name__ == '__main__':
    ar = Autosar()
    print(ar)
```
- The program fails with "NameError: name 'SlOverviewParagraph' is not defined"

Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=VULNERABILITY)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=505&resolved=false&types=SECURITY_HOTSPOT) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=505&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=505&resolved=false&types=SECURITY_HOTSPOT)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=505&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=505) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=505&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=505&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
> Merging [#505](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (8411764) into [master](https://codecov.io/gh/tefra/xsdata/commit/82a1085021f8c46ca9fb1a94c65f6901ac5be1a5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (82a1085) will **decrease** coverage by `0.06%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/505/graphs/tree.svg?width=650&height=150&src=pr&token=YzDDLtywvl&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)

```diff
@@             Coverage Diff             @@
##            master     #505      +/-   ##
===========================================
- Coverage   100.00%   99.93%   -0.07%     
===========================================
  Files           84       84              
  Lines         7406     7406              
  Branches      1297     1297              
===========================================
- Hits          7406     7401       -5     
- Misses           0        3       +3     
- Partials         0        2       +2     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) | Coverage Δ | |
|---|---|---|
| [xsdata/codegen/handlers/attribute\_type.py](https://codecov.io/gh/tefra/xsdata/pull/505/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2NvZGVnZW4vaGFuZGxlcnMvYXR0cmlidXRlX3R5cGUucHk=) | `100.00% <100.00%> (ø)` | |
| [xsdata/utils/hooks.py](https://codecov.io/gh/tefra/xsdata/pull/505/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL3V0aWxzL2hvb2tzLnB5) | `81.81% <0.00%> (-18.19%)` | :arrow_down: |
| [xsdata/formats/dataclass/typing.py](https://codecov.io/gh/tefra/xsdata/pull/505/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3R5cGluZy5weQ==) | `97.02% <0.00%> (-2.98%)` | :arrow_down: |
| [xsdata/codegen/handlers/class\_name\_conflict.py](https://codecov.io/gh/tefra/xsdata/pull/505/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2NvZGVnZW4vaGFuZGxlcnMvY2xhc3NfbmFtZV9jb25mbGljdC5weQ==) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou). Last update [82a1085...8411764](https://codecov.io/gh/tefra/xsdata/pull/505?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).


Patch:
diff --git a/xsdata/codegen/handlers/attribute_type.py b/xsdata/codegen/handlers/attribute_type.py
index d242e907..9bfd86da 100644
--- a/xsdata/codegen/handlers/attribute_type.py
+++ b/xsdata/codegen/handlers/attribute_type.py
@@ -13,6 +13,7 @@ from xsdata.codegen.models import Status
 from xsdata.codegen.utils import ClassUtils
 from xsdata.logger import logger
 from xsdata.models.enums import DataType
+from xsdata.models.enums import Tag
 from xsdata.utils import collections
 
 
@@ -69,15 +70,16 @@ class AttributeTypeHandler(ContainerHandlerInterface):
         """
         Find dependency for the given attribute and tag.
 
-        Avoid conflicts by search in order:
-            1. Non element/complexType
-            2. Non abstract
-            3. anything
+        Avoid conflicts by selecting any matching type by qname and preferably:
+            1. Match the candidate object tag
+            2. Match element again complexType
+            3. Match non element and complexType
+            4. Anything
         """
         conditions = (
             lambda obj: obj.tag == tag,
+            lambda obj: tag == Tag.ELEMENT and obj.tag == Tag.COMPLEX_TYPE,
             lambda obj: not obj.is_complex,
-            lambda x: not x.abstract,
             lambda x: True,
         )
 


Test patch:
diff --git a/tests/codegen/handlers/test_attribute_type.py b/tests/codegen/handlers/test_attribute_type.py
index 717398bf..836a1f56 100644
--- a/tests/codegen/handlers/test_attribute_type.py
+++ b/tests/codegen/handlers/test_attribute_type.py
@@ -320,25 +320,30 @@ class AttributeTypeHandlerTests(FactoryTestCase):
     def test_find_dependency(self):
         attr_type = AttrTypeFactory.create(qname="a")
 
-        self.assertIsNone(self.processor.find_dependency(attr_type, Tag.ELEMENT))
+        element = ClassFactory.create(qname="a", tag=Tag.ELEMENT)
+        complex_type = ClassFactory.create(qname="a", tag=Tag.COMPLEX_TYPE)
+        simple_type = ClassFactory.create(qname="a", tag=Tag.SIMPLE_TYPE)
 
-        abstract = ClassFactory.create(qname="a", tag=Tag.COMPLEX_TYPE, abstract=True)
-        self.processor.container.add(abstract)
-        self.assertEqual(
-            abstract, self.processor.find_dependency(attr_type, Tag.ELEMENT)
-        )
+        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        self.assertIsNone(actual)
+
+        self.processor.container.add(simple_type)
+        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        self.assertEqual(simple_type, actual)
+
+        self.processor.container.add(complex_type)
+        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        self.assertEqual(complex_type, actual)
 
-        element = ClassFactory.create(qname="a", tag=Tag.ELEMENT)
         self.processor.container.add(element)
-        self.assertEqual(
-            element, self.processor.find_dependency(attr_type, Tag.SIMPLE_TYPE)
-        )
+        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        self.assertEqual(element, actual)
 
-        simple = ClassFactory.create(qname="a", tag=Tag.SIMPLE_TYPE)
-        self.processor.container.add(simple)
-        self.assertEqual(
-            simple, self.processor.find_dependency(attr_type, Tag.SIMPLE_TYPE)
-        )
+        actual = self.processor.find_dependency(attr_type, Tag.SIMPLE_TYPE)
+        self.assertEqual(simple_type, actual)
+
+        actual = self.processor.find_dependency(attr_type, Tag.EXTENSION)
+        self.assertEqual(simple_type, actual)
 
     @mock.patch.object(Class, "dependencies")
     def test_cached_dependencies(self, mock_class_dependencies):