Repository: tefra/xsdata

Issue:
Conflicting Enum values leading to attribute error on reference
First off, thanks for the awesome library.  The structure is lovely and I'm very much enjoying using it.

I came across an issue in one of the XSD files I'm trying to process, wherein two enumerations conflict if handled with case-insensitivity (e.g. `value = "MW"` vs `value = "mW"`).

Here is a minimal `example.xsd`  

```xml
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="2">

    <xsd:complexType name="LightSource">
        <xsd:attribute name="PowerUnit" use="optional" default="mW" type="UnitsPower"></xsd:attribute>
    </xsd:complexType>

    <xsd:simpleType name="UnitsPower">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value = "MW"></xsd:enumeration>
            <xsd:enumeration value = "mW"></xsd:enumeration>
        </xsd:restriction>
    </xsd:simpleType>

</xsd:schema>
```

and the result after running `xsdata generate xsdata generate example.xsd`:

```py
from dataclasses import dataclass, field
from enum import Enum

class UnitsPower(Enum):
    MW = "MW"
    M_W_1 = "mW"


@dataclass
class LightSource:
    power_unit: UnitsPower = field(
        default=UnitsPower.M_W,
        metadata={
            "name": "PowerUnit",
            "type": "Attribute",
        }
    )
```


Note the incorrect `default=UnitsPower.M_W` in the dataclass.  Which, is correctly caught by mypy:

```bash
$ mypy generated
generated/power.py:13: error: "Type[UnitsPower]" has no attribute "M_W"  [attr-defined]
Found 1 error in 1 file (checked 2 source files)
```

xsdata: version 23.5
macos
python 3.11

Thanks!
(PS.  Happy to fix this myself if you'd like to roughly guide me to where either enums are generated or looked up in refs)

Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=806)

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

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=806) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=806&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=806&metric=new_duplicated_lines_density&view=list)


tlambert03: yeah... breaks the expectations set in `RenameDuplicateAttributesTests.test_process` ... So, I guess my question would be whether this is an issue with the enum naming itself, or in the referencing of the enum in the class default?

for example (from #805):
```python
from dataclasses import dataclass, field
from enum import Enum

class UnitsPower(Enum):
    MW = "MW"
    M_W_1 = "mW"


@dataclass
class LightSource:
    power_unit: UnitsPower = field(
        default=UnitsPower.M_W,
        metadata={
            "name": "PowerUnit",
            "type": "Attribute",
        }
    )
```

should I be trying to convert the enum `UnitsPower.M_W_1` -> `UnitsPower.M_W`, or  `LightSource.power_unit` default from `UnitsPower.M_W` -> `UnitsPower.M_W_1`?
tlambert03: a little more debugging...

after `transformer.py` has called `classes = self.analyze_classes(self.classes)`  The attrs for the class defining the enum have been deduped:

```py
[
    Class(
        qname='UnitsPower',
        attrs=[
            Attr(
                tag='Enumeration',
                name='MW',
                local_name='MW',
                ...
            ),
            Attr(
                tag='Enumeration',
                name='mW_1',
                local_name='mW',
```

but the `@enum`  value in the default of the referring attribute doesn't seem to have gotten the memo:

```py
    Class(
        qname='LightSource',
        attrs=[
            Attr(
                tag='Attribute',
                name='PowerUnit',
                local_name='PowerUnit',
                default='@enum@UnitsPower::mW',
```
tlambert03: moving `RenameDuplicateAttributes` to right before `SanitizeAttributesDefaultValue` seems to have fixed it!

tests are all passing but let me know if maybe I'm missing some unexpected side effect here.
(note @tefra  that I had to change the expectation in the test to match the new order). 

The code output by my test case is now correct:

```py
class UnitsPower(Enum):
    MW = "MW"
    M_W_1 = "mW"


@dataclass
class LightSource:
    power_unit: UnitsPower = field(
        default=UnitsPower.M_W_1,
        metadata={
            "name": "PowerUnit",
            "type": "Attribute",
        }
    )
```
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tefra/xsdata/pull/806?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
Patch and project coverage have no change.
> Comparison is base [(`11dbca8`)](https://app.codecov.io/gh/tefra/xsdata/commit/11dbca80c6e59f5235f283cb69e3df3fb87e0cb8?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) 99.96% compared to head [(`041fa3b`)](https://app.codecov.io/gh/tefra/xsdata/pull/806?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) 99.96%.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #806   +/-   ##
=======================================
  Coverage   99.96%   99.96%           
=======================================
  Files         104      104           
  Lines        9244     9244           
  Branches     2067     2067           
=======================================
  Hits         9241     9241           
  Partials        3        3           
```


| [Impacted Files](https://app.codecov.io/gh/tefra/xsdata/pull/806?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) | Coverage Δ | |
|---|---|---|
| [xsdata/codegen/container.py](https://app.codecov.io/gh/tefra/xsdata/pull/806?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2NvZGVnZW4vY29udGFpbmVyLnB5) | `100.00% <ø> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tefra/xsdata/pull/806?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).

tlambert03: happy to add a test for this if you tell me at what level it should be tested (i.e. full fixture or low level unit)

Patch:
diff --git a/xsdata/codegen/container.py b/xsdata/codegen/container.py
index 617ecfef..aed8b4b1 100644
--- a/xsdata/codegen/container.py
+++ b/xsdata/codegen/container.py
@@ -69,11 +69,11 @@ class ClassContainer(ContainerInterface):
             ],
             Steps.SANITIZE: [
                 ResetAttributeSequences(),
+                RenameDuplicateAttributes(),
                 SanitizeAttributesDefaultValue(self),
             ],
             Steps.RESOLVE: [
                 ValidateAttributesOverrides(self),
-                RenameDuplicateAttributes(),
             ],
             Steps.FINALIZE: [
                 VacuumInnerClasses(),


Test patch:
diff --git a/tests/codegen/test_container.py b/tests/codegen/test_container.py
index 890579ad..ed0207aa 100644
--- a/tests/codegen/test_container.py
+++ b/tests/codegen/test_container.py
@@ -51,8 +51,12 @@ class ClassContainerTests(FactoryTestCase):
                 "MergeAttributes",
                 "ProcessMixedContentClass",
             ],
-            30: ["ResetAttributeSequences", "SanitizeAttributesDefaultValue"],
-            40: ["ValidateAttributesOverrides", "RenameDuplicateAttributes"],
+            30: [
+                "ResetAttributeSequences",
+                "RenameDuplicateAttributes",
+                "SanitizeAttributesDefaultValue",
+            ],
+            40: ["ValidateAttributesOverrides"],
             50: [
                 "VacuumInnerClasses",
                 "CreateCompoundFields",