Repository: tefra/xsdata

Issue:
Multiple WSDL files on the generator path will conflate the WSDL definitions in a single file
When there are multiple WSDL files in the generator path, the bindings get created correctly, but they all end up in the same file instead of being in distinct files (i.e.: `wsdl_1.py` and `wsdl_2.py`)

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=832)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=832&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=832&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=832&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=832&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=832&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=832&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=832&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=832&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=832&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=832&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=832&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=832&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=832) 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=832&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=832&metric=new_duplicated_lines_density&view=list)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tefra/xsdata/pull/832?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
Patch coverage: **`100.00`**% and no project coverage change.
> Comparison is base [(`ed6927a`)](https://app.codecov.io/gh/tefra/xsdata/commit/ed6927acaa41448792d8bcf129eb9f105be61ea7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) 99.96% compared to head [(`996ca43`)](https://app.codecov.io/gh/tefra/xsdata/pull/832?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           @@
##             main     #832   +/-   ##
=======================================
  Coverage   99.96%   99.96%           
=======================================
  Files         104      104           
  Lines        9262     9270    +8     
  Branches     2072     2072           
=======================================
+ Hits         9259     9267    +8     
  Partials        3        3           
```


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


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tefra/xsdata/pull/832?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).


Patch:
diff --git a/xsdata/codegen/mappers/definitions.py b/xsdata/codegen/mappers/definitions.py
index 4eec6630..be823d44 100644
--- a/xsdata/codegen/mappers/definitions.py
+++ b/xsdata/codegen/mappers/definitions.py
@@ -110,13 +110,13 @@ class DefinitionsMapper:
                 message_type = message_class.name.split("_")[-1]
                 attrs.append(cls.build_attr(message_type, message_class.qname))
 
-        assert definitions.location is not None
+        assert binding_operation.location is not None
 
         yield Class(
             qname=namespaces.build_qname(definitions.target_namespace, name),
             status=Status.FLATTENED,
             tag=type(binding_operation).__name__,
-            location=definitions.location,
+            location=binding_operation.location,
             ns_map=binding_operation.ns_map,
             attrs=attrs,
         )
@@ -210,13 +210,13 @@ class DefinitionsMapper:
         """Step 6.1: Build Envelope class for the given binding message with
         attributes from the port type message."""
 
-        assert definitions.location is not None
+        assert binding_message.location is not None
 
         target = Class(
             qname=namespaces.build_qname(definitions.target_namespace, name),
             meta_name="Envelope",
             tag=Tag.BINDING_MESSAGE,
-            location=definitions.location,
+            location=binding_message.location,
             ns_map=binding_message.ns_map,
             namespace=namespace,
         )
@@ -253,14 +253,14 @@ class DefinitionsMapper:
         ns_map = definition_message.ns_map.copy()
         source_namespace = ns_map.get(prefix)
 
-        assert definitions.location is not None
+        assert port_type_message.location is not None
 
         return Class(
             qname=namespaces.build_qname(source_namespace, name),
             namespace=source_namespace,
             status=Status.RAW,
             tag=Tag.ELEMENT,
-            location=definitions.location,
+            location=port_type_message.location,
             ns_map=ns_map,
             attrs=list(cls.build_parts_attributes(definition_message.parts, ns_map)),
         )
diff --git a/xsdata/codegen/parsers/definitions.py b/xsdata/codegen/parsers/definitions.py
index 5c286f62..2e66a220 100644
--- a/xsdata/codegen/parsers/definitions.py
+++ b/xsdata/codegen/parsers/definitions.py
@@ -1,7 +1,12 @@
 from dataclasses import dataclass
+from typing import Any
+from typing import List
+from typing import Optional
 
 from xsdata.codegen.parsers.schema import SchemaParser
 from xsdata.formats.bindings import T
+from xsdata.formats.dataclass.parsers.bases import Parsed
+from xsdata.formats.dataclass.parsers.mixins import XmlNode
 from xsdata.models import wsdl
 
 
@@ -10,9 +15,21 @@ class DefinitionsParser(SchemaParser):
     """A simple parser to convert a wsdl to an easy to handle data structure
     based on dataclasses."""
 
-    def end_definitions(self, obj: T):
-        """Normalize various properties for the schema and it's children."""
-        if isinstance(obj, wsdl.Definitions) and self.location:
+    def end(
+        self,
+        queue: List[XmlNode],
+        objects: List[Parsed],
+        qname: str,
+        text: Optional[str],
+        tail: Optional[str],
+    ) -> Any:
+        """Override parent method to set element location."""
+        obj = super().end(queue, objects, qname, text, tail)
+        if isinstance(obj, wsdl.WsdlElement):
             obj.location = self.location
-            for imp in obj.imports:
-                imp.location = self.resolve_path(imp.location)
+
+        return obj
+
+    def end_import(self, obj: T):
+        if isinstance(obj, wsdl.Import) and self.location:
+            obj.location = self.resolve_path(obj.location)
diff --git a/xsdata/models/wsdl.py b/xsdata/models/wsdl.py
index 023c583e..3df87a6e 100644
--- a/xsdata/models/wsdl.py
+++ b/xsdata/models/wsdl.py
@@ -32,10 +32,13 @@ class WsdlElement:
     """
     :param name:
     :param documentation:
+    :param location:
+    :param ns_map
     """
 
     name: str = attribute()
     documentation: Optional[Documentation] = element()
+    location: Optional[str] = field(default=None, metadata={"type": "Ignore"})
     ns_map: Dict[str, str] = field(
         default_factory=dict, init=False, metadata={"type": "Ignore"}
     )
@@ -207,7 +210,6 @@ class Definitions(ExtensibleElement):
     port_types: List[PortType] = array_element(name="portType")
     bindings: List[Binding] = array_element(name="binding")
     services: List[Service] = array_element(name="service")
-    location: Optional[str] = field(default=None)
 
     @property
     def schemas(self):


Test patch:
diff --git a/tests/codegen/mappers/test_definitions.py b/tests/codegen/mappers/test_definitions.py
index 16488a32..f6c49ae9 100644
--- a/tests/codegen/mappers/test_definitions.py
+++ b/tests/codegen/mappers/test_definitions.py
@@ -158,8 +158,8 @@ class DefinitionsMapperTests(FactoryTestCase):
     def test_map_binding_operation(
         self, mock_operation_namespace, mock_map_binding_operation_messages
     ):
-        definitions = Definitions(location="foo.wsdl", target_namespace="xsdata")
-        operation = BindingOperation(name="Add")
+        definitions = Definitions(target_namespace="xsdata")
+        operation = BindingOperation(name="Add", location="foo.wsdl")
         operation.ns_map["foo"] = "bar"
         port_operation = PortTypeOperation()
         config = {"a": "one", "b": "two", "style": "rpc"}
@@ -333,14 +333,17 @@ class DefinitionsMapperTests(FactoryTestCase):
         name = "some_operation_bindings"
         style = "document"
         namespace = "xsdata"
-        definitions = Definitions(location="foo.wsdl", target_namespace="bar")
-        port_type_message = PortTypeMessage(message="some_operation")
+        definitions = Definitions(target_namespace="bar")
+        port_type_message = PortTypeMessage(
+            message="some_operation", location="foo.wsdl"
+        )
         binding_message = BindingMessage(
             extended=[
                 AnyElement(qname="body"),
                 AnyElement(qname="header"),
                 AnyElement(qname="header"),
-            ]
+            ],
+            location="foo.wsdl",
         )
         binding_message.ns_map["foo"] = "bar"
 
@@ -415,14 +418,17 @@ class DefinitionsMapperTests(FactoryTestCase):
         name = "some_operation_bindings"
         style = "rpc"
         namespace = "xsdata"
-        definitions = Definitions(location="foo.wsdl", target_namespace="bar")
-        port_type_message = PortTypeMessage(message="some_operation")
+        definitions = Definitions(target_namespace="bar")
+        port_type_message = PortTypeMessage(
+            message="some_operation", location="foo.wsdl"
+        )
         binding_message = BindingMessage(
             extended=[
                 AnyElement(qname="body", attributes={"namespace": "bodyns"}),
                 AnyElement(qname="header"),
                 AnyElement(qname="header"),
-            ]
+            ],
+            location="foo.wsdl",
         )
         binding_message.ns_map["foo"] = "bar"
 
@@ -705,10 +711,8 @@ class DefinitionsMapperTests(FactoryTestCase):
     def test_build_message_class(self, mock_create_message_attributes):
         message = Message(name="bar", parts=[Part()])
         message.ns_map["foo"] = "bar"
-        definitions = Definitions(
-            messages=[message], target_namespace="xsdata", location="foo.wsdl"
-        )
-        port_type_message = PortTypeMessage(message="foo:bar")
+        definitions = Definitions(messages=[message], target_namespace="xsdata")
+        port_type_message = PortTypeMessage(message="foo:bar", location="foo.wsdl")
 
         attrs = AttrFactory.list(2)
         mock_create_message_attributes.return_value = attrs
diff --git a/tests/codegen/parsers/test_definitions.py b/tests/codegen/parsers/test_definitions.py
index 292ad388..6b071043 100644
--- a/tests/codegen/parsers/test_definitions.py
+++ b/tests/codegen/parsers/test_definitions.py
@@ -13,7 +13,7 @@ class DefinitionsParserTests(TestCase):
 
     def test_complete(self):
         path = fixtures_dir.joinpath("calculator/services.wsdl").resolve()
-        parser = DefinitionsParser()
+        parser = DefinitionsParser(location="here.wsdl")
         definitions = parser.from_path(path, Definitions)
 
         self.assertIsInstance(definitions, Definitions)
@@ -22,20 +22,15 @@ class DefinitionsParserTests(TestCase):
         self.assertEqual(1, len(definitions.port_types))
         self.assertEqual(1, len(definitions.types.schemas))
         self.assertEqual(8, len(definitions.messages))
+        self.assertEqual(parser.location, definitions.bindings[0].location)
 
-    def test_end_definitions(self):
-        parser = DefinitionsParser()
-        definitions = Definitions(
-            imports=[Import(location="../foo.xsd"), Import(location="bar.xsd")]
-        )
+    def test_end_import(self):
+        parser = DefinitionsParser(location="foo/bar.wsdl")
+        imp = Import(location="../hello/foo.wsdl")
 
-        parser.end_definitions(definitions)
-        self.assertEqual("bar.xsd", definitions.imports[1].location)
+        parser.end_import(imp)
+        self.assertEqual("hello/foo.wsdl", imp.location)
 
-        parser.location = "file://a/b/services/parent.wsdl"
-        parser.end_definitions(definitions)
-        self.assertEqual("file://a/b/foo.xsd", definitions.imports[0].location)
-        self.assertEqual("file://a/b/services/bar.xsd", definitions.imports[1].location)
-
-        # Update only Definitions instances
-        parser.end_definitions("foo")
+        parser.location = None
+        parser.end_import(imp)
+        self.assertEqual("hello/foo.wsdl", imp.location)