Repository: tefra/xsdata
Pull request #480 (2021-05-04 12:52:06)

Fix various typing issues

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=480&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=480&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480) 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=480&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=480&metric=new_duplicated_lines_density&view=list)


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=480&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=480&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480) 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=480&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=480&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) Report
> Merging [#480](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) (dd70714) into [master](https://codecov.io/gh/tefra/xsdata/commit/e84edd8d60980f1fb5069bf593b0367be735b448?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) (e84edd8) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/480/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+T)](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T)

```diff
@@            Coverage Diff            @@
##            master      #480   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           83        83           
  Lines         7274      7286   +12     
  Branches      1303      1307    +4     
=========================================
+ Hits          7274      7286   +12     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) | Coverage Δ | |
|---|---|---|
| [xsdata/codegen/mappers/schema.py](https://codecov.io/gh/tefra/xsdata/pull/480/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T#diff-eHNkYXRhL2NvZGVnZW4vbWFwcGVycy9zY2hlbWEucHk=) | `100.00% <100.00%> (ø)` | |
| [xsdata/codegen/models.py](https://codecov.io/gh/tefra/xsdata/pull/480/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T#diff-eHNkYXRhL2NvZGVnZW4vbW9kZWxzLnB5) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/converter.py](https://codecov.io/gh/tefra/xsdata/pull/480/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T#diff-eHNkYXRhL2Zvcm1hdHMvY29udmVydGVyLnB5) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/filters.py](https://codecov.io/gh/tefra/xsdata/pull/480/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL2ZpbHRlcnMucHk=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T).
> **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+T)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T). Last update [e84edd8...dd70714](https://codecov.io/gh/tefra/xsdata/pull/480?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T). 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+T).

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=480&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=480&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480) 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=480&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=480&metric=new_duplicated_lines_density&view=list)


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=480&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=480&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=480&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=480&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=480&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=480&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=480) 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=480&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=480&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/xsdata/codegen/handlers/attribute_overrides.py b/xsdata/codegen/handlers/attribute_overrides.py
index 6d804dd3..e82c7472 100644
--- a/xsdata/codegen/handlers/attribute_overrides.py
+++ b/xsdata/codegen/handlers/attribute_overrides.py
@@ -1,3 +1,4 @@
+import sys
 from dataclasses import dataclass
 
 from xsdata.codegen.mixins import ContainerInterface
@@ -50,21 +51,19 @@ class AttributeOverridesHandler(HandlerInterface):
         for extension in source.extensions:
             self.process_extension(target, extension)
 
-    def validate_override(self, target: Class, attr: Attr, source_attr: Attr):
-        choices = self.container.config.output.compound_fields
-        with_occurrences = not all(
-            (choices, attr.restrictions.choice, source_attr.restrictions.choice)
-        )
+    @classmethod
+    def validate_override(cls, target: Class, attr: Attr, source_attr: Attr):
+        if attr.is_list and not source_attr.is_list:
+            # Hack much??? idk but Optional[str] can't override List[str]
+            source_attr.restrictions.max_occurs = sys.maxsize
 
         if (
             attr.default == source_attr.default
             and attr.fixed == source_attr.fixed
             and attr.mixed == source_attr.mixed
-            and attr.restrictions.is_compatible(
-                source_attr.restrictions, with_occurrences
-            )
+            and attr.restrictions == source_attr.restrictions
         ):
-            target.attrs.remove(attr)
+            ClassUtils.remove_attribute(target, attr)
 
     @classmethod
     def resolve_conflict(cls, attr: Attr, source_attr: Attr):
diff --git a/xsdata/codegen/mappers/schema.py b/xsdata/codegen/mappers/schema.py
index ff8ab260..21b5e10d 100644
--- a/xsdata/codegen/mappers/schema.py
+++ b/xsdata/codegen/mappers/schema.py
@@ -217,7 +217,7 @@ class SchemaMapper:
         if obj.class_name in (Tag.ELEMENT, Tag.ANY, Tag.GROUP):
             restrictions.merge(parent_restrictions)
 
-        if restrictions.prohibited:
+        if restrictions.is_prohibited:
             return
 
         name = obj.real_name
diff --git a/xsdata/codegen/models.py b/xsdata/codegen/models.py
index f6d8dd09..3e6bd470 100644
--- a/xsdata/codegen/models.py
+++ b/xsdata/codegen/models.py
@@ -2,7 +2,6 @@ import sys
 from dataclasses import asdict
 from dataclasses import dataclass
 from dataclasses import field
-from dataclasses import fields
 from dataclasses import replace
 from enum import IntEnum
 from typing import Any
@@ -18,9 +17,7 @@ from xsdata.models.enums import DataType
 from xsdata.models.enums import QNames
 from xsdata.models.enums import Tag
 from xsdata.models.mixins import ElementBase
-from xsdata.utils.namespaces import build_qname
-from xsdata.utils.namespaces import local_name
-from xsdata.utils.namespaces import target_uri
+from xsdata.utils import namespaces
 
 xml_type_map = {
     Tag.ANY: XmlType.WILDCARD,
@@ -63,8 +60,8 @@ class Restrictions:
 
     required: Optional[bool] = field(default=None)
     prohibited: Optional[bool] = field(default=None)
-    min_occurs: Optional[int] = field(default=None)
-    max_occurs: Optional[int] = field(default=None)
+    min_occurs: Optional[int] = field(default=None, compare=False)
+    max_occurs: Optional[int] = field(default=None, compare=False)
     min_exclusive: Optional[str] = field(default=None)
     min_inclusive: Optional[str] = field(default=None)
     min_length: Optional[int] = field(default=None)
@@ -81,7 +78,7 @@ class Restrictions:
     sequential: Optional[bool] = field(default=None)
     tokens: Optional[bool] = field(default=None)
     format: Optional[str] = field(default=None)
-    choice: Optional[str] = field(default=None)
+    choice: Optional[str] = field(default=None, compare=False)
 
     @property
     def is_list(self) -> bool:
@@ -93,17 +90,9 @@ class Restrictions:
         """Return true if min occurs property equals zero."""
         return self.min_occurs == 0
 
-    def is_compatible(self, other: "Restrictions", with_occurrences: bool) -> bool:
-        if with_occurrences:
-            ignore = ["choice"]
-        else:
-            ignore = ["min_occurs", "max_occurs", "choice"]
-
-        return all(
-            getattr(self, f.name) == getattr(other, f.name)
-            for f in fields(self)
-            if f.name not in ignore
-        )
+    @property
+    def is_prohibited(self) -> bool:
+        return self.prohibited or self.max_occurs == 0
 
     def merge(self, source: "Restrictions"):
         """Update properties from another instance."""
@@ -212,7 +201,7 @@ class AttrType:
     @property
     def name(self) -> str:
         """Shortcut for qname local name."""
-        return local_name(self.qname)
+        return namespaces.local_name(self.qname)
 
     @property
     def is_dependency(self) -> bool:
@@ -317,7 +306,7 @@ class Attr:
     def is_xsi_type(self) -> bool:
         """Return whether this attribute qualified name is equal to
         xsi:type."""
-        return QNames.XSI_TYPE == build_qname(self.namespace, self.name)
+        return QNames.XSI_TYPE == namespaces.build_qname(self.namespace, self.name)
 
     @property
     def is_tokens(self) -> bool:
@@ -433,11 +422,11 @@ class Class:
     @property
     def name(self) -> str:
         """Shortcut for qname local name."""
-        return local_name(self.qname)
+        return namespaces.local_name(self.qname)
 
     @property
     def target_namespace(self) -> Optional[str]:
-        return target_uri(self.qname)
+        return namespaces.target_uri(self.qname)
 
     @property
     def has_suffix_attr(self) -> bool:
diff --git a/xsdata/codegen/utils.py b/xsdata/codegen/utils.py
index adbafa1f..9075c0e5 100644
--- a/xsdata/codegen/utils.py
+++ b/xsdata/codegen/utils.py
@@ -10,15 +10,39 @@ from xsdata.codegen.models import Extension
 from xsdata.codegen.models import Restrictions
 from xsdata.exceptions import CodeGenerationError
 from xsdata.utils import collections
+from xsdata.utils import namespaces
 from xsdata.utils import text
-from xsdata.utils.namespaces import build_qname
-from xsdata.utils.namespaces import clean_uri
-from xsdata.utils.namespaces import split_qname
 
 
 class ClassUtils:
     """General reusable utils methods that didn't fit anywhere else."""
 
+    @classmethod
+    def remove_attribute(cls, target: Class, attr: Attr):
+        """Remove the given attr from the target class and clean inner
+        classes."""
+        target.attrs.remove(attr)
+
+        cls.clean_inner_classes(target)
+
+    @classmethod
+    def clean_inner_classes(cls, target: Class):
+        """Check if there are orphan inner classes and remove them."""
+        for inner in list(target.inner):
+            if cls.is_orphan_inner(target, inner):
+                target.inner.remove(inner)
+
+    @classmethod
+    def is_orphan_inner(cls, target: Class, inner: Class) -> bool:
+        """Check if there is at least once valid attr reference to the given
+        inner class."""
+        for attr in target.attrs:
+            for attr_type in attr.types:
+                if attr_type.forward and attr_type.qname == inner.qname:
+                    return False
+
+        return True
+
     @classmethod
     def copy_attributes(cls, source: Class, target: Class, extension: Extension):
         """
@@ -113,8 +137,9 @@ class ClassUtils:
 
             # Simple type, update the name
             if clone.name == "@value":
-                namespace, _ = split_qname(clone.qname)
-                clone.qname = attr_type.qname = build_qname(namespace, attr.name)
+                namespace, _ = namespaces.split_qname(clone.qname)
+                qname = namespaces.build_qname(namespace, attr.name)
+                clone.qname = attr_type.qname = qname
 
             target.inner.append(clone)
 
@@ -205,7 +230,7 @@ class ClassUtils:
         if a.tag == b.tag and (a.namespace or b.namespace):
             change = b if b.namespace else a
             assert change.namespace is not None
-            change.name = f"{clean_uri(change.namespace)}_{change.name}"
+            change.name = f"{namespaces.clean_uri(change.namespace)}_{change.name}"
         else:
             change = b if b.is_attribute else a
             change.name = f"{change.name}_{change.tag}"


Test patch:
diff --git a/tests/codegen/handlers/test_attribute_overrides.py b/tests/codegen/handlers/test_attribute_overrides.py
index 61a15005..c963518d 100644
--- a/tests/codegen/handlers/test_attribute_overrides.py
+++ b/tests/codegen/handlers/test_attribute_overrides.py
@@ -1,3 +1,4 @@
+import sys
 from unittest import mock
 
 from xsdata.codegen.container import ClassContainer
@@ -76,21 +77,33 @@ class AttributeOverridesHandlerTests(FactoryTestCase):
         self.processor.validate_override(target, attr_a, attr_b)
         self.assertEqual(1, len(target.attrs))
 
-        # occurs doesn't match
+        # restrictions except choice, min/max occurs don't match
         attr_b.fixed = attr_a.fixed
-        attr_a.restrictions.min_occurs = 2
+        attr_a.restrictions.max_length = 10
         self.processor.validate_override(target, attr_a, attr_b)
         self.assertEqual(1, len(target.attrs))
 
-        self.container.config.output.compound_fields = True
-        self.processor.validate_override(target, attr_a, attr_b)
-        self.assertEqual(1, len(target.attrs))
+        # Restrictions are compatible again
+        attr_b.restrictions.max_length = attr_a.restrictions.max_length
+
+        attr_a.restrictions.min_occurs = 1
+        attr_a.restrictions.max_occurs = 2
+        attr_a.restrictions.choice = 3
 
-        # Occurs is compatible again
-        attr_b.restrictions.min_occurs = 2
+        attr_b.restrictions.min_occurs = 4
+        attr_b.restrictions.max_occurs = 5
+        attr_b.restrictions.choice = 6
         self.processor.validate_override(target, attr_a, attr_b)
         self.assertEqual(0, len(target.attrs))
 
+        target.attrs.append(attr_a)
+        attr_a.restrictions.min_occurs = None
+        attr_a.restrictions.max_occurs = 10
+        attr_b.restrictions.min_occurs = None
+        attr_b.restrictions.max_occurs = None
+        self.processor.validate_override(target, attr_a, attr_b)
+        self.assertEqual(sys.maxsize, attr_b.restrictions.max_occurs)
+
     def test_resolve_conflicts(self):
         a = AttrFactory.create(name="foo", tag=Tag.ATTRIBUTE)
         b = a.clone()
diff --git a/tests/codegen/models/test_restrictions.py b/tests/codegen/models/test_restrictions.py
index 11a3fd43..8b2584c9 100644
--- a/tests/codegen/models/test_restrictions.py
+++ b/tests/codegen/models/test_restrictions.py
@@ -36,6 +36,11 @@ class RestrictionsTests(TestCase):
         restrictions.max_occurs = 2
         self.assertTrue(restrictions.is_list)
 
+    def test_property_is_prohibited(self):
+        self.assertFalse(Restrictions().is_prohibited)
+        self.assertTrue(Restrictions(prohibited=True).is_prohibited)
+        self.assertTrue(Restrictions(max_occurs=0).is_prohibited)
+
     def test_merge(self):
         source = Restrictions(min_length=2, max_length=10, format="base16")
         target = Restrictions(min_length=1, pattern=r"[A-Z]")
@@ -116,19 +121,3 @@ class RestrictionsTests(TestCase):
 
         self.assertEqual(clone, restrictions)
         self.assertIsNot(clone, restrictions)
-
-    def test_is_compatible(self):
-        a = self.restrictions
-        b = a.clone()
-
-        a.choice = "112"
-        b.choice = "112"
-        self.assertTrue(a.is_compatible(b, True))
-        self.assertTrue(b.is_compatible(a, True))
-
-        a.min_occurs += 1
-        a.max_occurs += 1
-        self.assertFalse(a.is_compatible(b, True))
-        self.assertFalse(b.is_compatible(a, True))
-        self.assertTrue(a.is_compatible(b, False))
-        self.assertTrue(b.is_compatible(a, False))
diff --git a/tests/codegen/test_utils.py b/tests/codegen/test_utils.py
index 34499c0a..aa9b7459 100644
--- a/tests/codegen/test_utils.py
+++ b/tests/codegen/test_utils.py
@@ -15,6 +15,50 @@ from xsdata.utils.testing import FactoryTestCase
 
 
 class ClassUtilsTests(FactoryTestCase):
+    @mock.patch.object(ClassUtils, "clean_inner_classes")
+    def test_remove_attribute(self, mock_clean_inner_classes):
+
+        target = ClassFactory.elements(1)
+        attr = target.attrs[0]
+
+        ClassUtils.remove_attribute(target, attr)
+        self.assertEqual(0, len(target.attrs))
+
+        mock_clean_inner_classes.assert_called_once_with(target)
+
+    @mock.patch.object(ClassUtils, "is_orphan_inner")
+    def test_clean_inner_classes(self, mock_is_orphan_inner):
+        mock_is_orphan_inner.side_effect = [True, False, True]
+
+        target = ClassFactory.create()
+        target.inner.extend(ClassFactory.list(3))
+        survivor = target.inner[1]
+
+        ClassUtils.clean_inner_classes(target)
+
+        self.assertEqual(1, len(target.inner))
+        self.assertEqual(survivor, target.inner[0])
+
+    def test_is_orphan_inner(self):
+
+        inner = ClassFactory.create(qname="thug")
+        target = ClassFactory.create(
+            attrs=[
+                AttrFactory.create(
+                    types=[
+                        AttrTypeFactory.create(qname="bar"),
+                        AttrTypeFactory.create(qname="foo"),
+                    ]
+                )
+            ]
+        )
+        self.assertTrue(ClassUtils.is_orphan_inner(target, inner))
+
+        target.attrs[0].types[1].qname = "thug"
+        target.attrs[0].types[1].forward = True
+
+        self.assertFalse(ClassUtils.is_orphan_inner(target, inner))
+
     @mock.patch.object(ClassUtils, "copy_inner_classes")
     @mock.patch.object(ClassUtils, "clone_attribute")
     def test_copy_attributes(self, mock_clone_attribute, mock_copy_inner_classes):