Repository: tefra/xsdata
Pull request #984 (2024-03-21 11:50:49)

feat: Make rename duplicate attributes reusable
## 📒 Description

> Write a brief description of your PR.

Resolves #xxxx

## 🔗 What I've Done

> Write a description of the steps taken to resolve the issue

## 💬 Comments

> A place to write any comments to the reviewer.

## 🛫 Checklist

- [ ] Updated docs
- [ ] Added unit-tests
- [ ] [Sample tests](https://github.com/tefra/xsdata-samples) pass
- [ ] [W3C tests](https://github.com/tefra/xsdata-w3c-tests) pass


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=984) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=984&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=984&metric=new_accepted_issues&view=list)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=984&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=984&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=984)



Patch:
diff --git a/xsdata/codegen/handlers/rename_duplicate_attributes.py b/xsdata/codegen/handlers/rename_duplicate_attributes.py
index a5b906ad..28f24222 100644
--- a/xsdata/codegen/handlers/rename_duplicate_attributes.py
+++ b/xsdata/codegen/handlers/rename_duplicate_attributes.py
@@ -1,8 +1,6 @@
 from xsdata.codegen.mixins import HandlerInterface
-from xsdata.codegen.models import Attr, Class
+from xsdata.codegen.models import Class
 from xsdata.codegen.utils import ClassUtils
-from xsdata.utils.collections import group_by
-from xsdata.utils.constants import DEFAULT_ATTR_NAME
 
 
 class RenameDuplicateAttributes(HandlerInterface):
@@ -16,14 +14,4 @@ class RenameDuplicateAttributes(HandlerInterface):
         Args:
             target: The target class instance
         """
-        grouped = group_by(target.attrs, key=self._attr_unique_slug)
-        for items in grouped.values():
-            total = len(items)
-            if total == 2 and not items[0].is_enumeration:
-                ClassUtils.rename_attribute_by_preference(*items)
-            elif total > 1:
-                ClassUtils.rename_attributes_by_index(target.attrs, items)
-
-    @staticmethod
-    def _attr_unique_slug(attr: Attr) -> str:
-        return attr.slug or DEFAULT_ATTR_NAME
+        ClassUtils.rename_duplicate_attributes(target)
diff --git a/xsdata/codegen/utils.py b/xsdata/codegen/utils.py
index 2d9e219b..91815ee7 100644
--- a/xsdata/codegen/utils.py
+++ b/xsdata/codegen/utils.py
@@ -383,6 +383,17 @@ class ClassUtils:
         if source.restrictions.sequence is not None:
             target.restrictions.sequence = source.restrictions.sequence
 
+    @classmethod
+    def rename_duplicate_attributes(cls, target: Class):
+        """Find and rename attributes with the same slug."""
+        grouped = collections.group_by(target.attrs, key=get_slug)
+        for items in grouped.values():
+            total = len(items)
+            if total == 2 and not items[0].is_enumeration:
+                cls.rename_attribute_by_preference(*items)
+            elif total > 1:
+                cls.rename_attributes_by_index(target.attrs, items)
+
     @classmethod
     def rename_attribute_by_preference(cls, a: Attr, b: Attr):
         """Decide and rename one of the two given attributes.


Test patch:
diff --git a/tests/codegen/handlers/test_rename_duplicate_attributes.py b/tests/codegen/handlers/test_rename_duplicate_attributes.py
index ab483b18..1a47804a 100644
--- a/tests/codegen/handlers/test_rename_duplicate_attributes.py
+++ b/tests/codegen/handlers/test_rename_duplicate_attributes.py
@@ -1,6 +1,8 @@
+from unittest import mock
+
 from xsdata.codegen.handlers import RenameDuplicateAttributes
-from xsdata.models.enums import Tag
-from xsdata.utils.testing import AttrFactory, ClassFactory, FactoryTestCase
+from xsdata.codegen.utils import ClassUtils
+from xsdata.utils.testing import ClassFactory, FactoryTestCase
 
 
 class RenameDuplicateAttributesTests(FactoryTestCase):
@@ -9,42 +11,9 @@ class RenameDuplicateAttributesTests(FactoryTestCase):
 
         self.processor = RenameDuplicateAttributes()
 
-    def test_process(self):
-        attrs = [
-            AttrFactory.create(name="a", tag=Tag.ELEMENT),
-            AttrFactory.create(name="a", tag=Tag.ATTRIBUTE),
-            AttrFactory.create(name="b", tag=Tag.ATTRIBUTE),
-            AttrFactory.create(name="c", tag=Tag.ATTRIBUTE),
-            AttrFactory.create(name="c", tag=Tag.ELEMENT),
-            AttrFactory.create(name="d", tag=Tag.ELEMENT),
-            AttrFactory.create(name="d", tag=Tag.ELEMENT),
-            AttrFactory.create(name="e", tag=Tag.ELEMENT, namespace="b"),
-            AttrFactory.create(name="e", tag=Tag.ELEMENT),
-            AttrFactory.create(name="f", tag=Tag.ELEMENT),
-            AttrFactory.create(name="f", tag=Tag.ELEMENT, namespace="a"),
-            AttrFactory.create(name="gA", tag=Tag.ENUMERATION),
-            AttrFactory.create(name="g[A]", tag=Tag.ENUMERATION),
-            AttrFactory.create(name="g_a", tag=Tag.ENUMERATION),
-            AttrFactory.create(name="g_a_1", tag=Tag.ENUMERATION),
-        ]
-        target = ClassFactory.create(attrs=attrs)
-
+    @mock.patch.object(ClassUtils, "rename_duplicate_attributes")
+    def test_process(self, mock_rename_duplicate_attributes):
+        target = ClassFactory.create()
         self.processor.process(target)
-        expected = [
-            "a",
-            "a_Attribute",
-            "b",
-            "c_Attribute",
-            "c",
-            "d_Element",
-            "d",
-            "b_e",
-            "e",
-            "f",
-            "a_f",
-            "gA",
-            "g[A]_2",
-            "g_a_3",
-            "g_a_1",
-        ]
-        self.assertEqual(expected, [x.name for x in attrs])
+
+        mock_rename_duplicate_attributes.assert_called_once_with(target)
diff --git a/tests/codegen/test_utils.py b/tests/codegen/test_utils.py
index 2083fb19..d34540a2 100644
--- a/tests/codegen/test_utils.py
+++ b/tests/codegen/test_utils.py
@@ -307,6 +307,46 @@ class ClassUtilsTests(FactoryTestCase):
         self.assertEqual(4, c.restrictions.max_occurs)
         self.assertEqual(1, c.restrictions.sequence)
 
+    def test_rename_duplicate_attributes(self):
+        attrs = [
+            AttrFactory.create(name="a", tag=Tag.ELEMENT),
+            AttrFactory.create(name="a", tag=Tag.ATTRIBUTE),
+            AttrFactory.create(name="b", tag=Tag.ATTRIBUTE),
+            AttrFactory.create(name="c", tag=Tag.ATTRIBUTE),
+            AttrFactory.create(name="c", tag=Tag.ELEMENT),
+            AttrFactory.create(name="d", tag=Tag.ELEMENT),
+            AttrFactory.create(name="d", tag=Tag.ELEMENT),
+            AttrFactory.create(name="e", tag=Tag.ELEMENT, namespace="b"),
+            AttrFactory.create(name="e", tag=Tag.ELEMENT),
+            AttrFactory.create(name="f", tag=Tag.ELEMENT),
+            AttrFactory.create(name="f", tag=Tag.ELEMENT, namespace="a"),
+            AttrFactory.create(name="gA", tag=Tag.ENUMERATION),
+            AttrFactory.create(name="g[A]", tag=Tag.ENUMERATION),
+            AttrFactory.create(name="g_a", tag=Tag.ENUMERATION),
+            AttrFactory.create(name="g_a_1", tag=Tag.ENUMERATION),
+        ]
+        target = ClassFactory.create(attrs=attrs)
+
+        ClassUtils.rename_duplicate_attributes(target)
+        expected = [
+            "a",
+            "a_Attribute",
+            "b",
+            "c_Attribute",
+            "c",
+            "d_Element",
+            "d",
+            "b_e",
+            "e",
+            "f",
+            "a_f",
+            "gA",
+            "g[A]_2",
+            "g_a_3",
+            "g_a_1",
+        ]
+        self.assertEqual(expected, [x.name for x in attrs])
+
     def test_rename_attribute_by_preference(self):
         one = AttrFactory.create(name="a", tag=Tag.ELEMENT)
         two = AttrFactory.create(name="a", tag=Tag.ATTRIBUTE)