Repository: tefra/xsdata

Issue:
Signal for circular references
Some past tickets were about 'resolving' circular references. How easy would it be to get a list of circular references from the XSD?

Discussion:

Patch:
diff --git a/xsdata/cli.py b/xsdata/cli.py
index af9e40d0..1f98d1a9 100644
--- a/xsdata/cli.py
+++ b/xsdata/cli.py
@@ -117,6 +117,7 @@ def download(source: str, output: str):
 @click.option("-c", "--config", default=".xsdata.xml", help="Project configuration")
 @click.option("-pp", "--print", is_flag=True, default=False, help="Print output")
 @click.option("--cache", is_flag=True, default=False, help="Cache sources loading")
+@click.option("--debug", is_flag=True, default=False, help="Show debug messages")
 @model_options(GeneratorOutput)
 def generate(**kwargs: Any):
     """
@@ -126,6 +127,11 @@ def generate(**kwargs: Any):
     The input source can be either a filepath, uri or a directory
     containing xml, json, xsd and wsdl files.
     """
+
+    debug = kwargs.pop("debug")
+    if debug:
+        logger.setLevel(logging.DEBUG)
+
     source = kwargs.pop("source")
     stdout = kwargs.pop("print")
     cache = kwargs.pop("cache")
diff --git a/xsdata/codegen/handlers/flatten_class_extensions.py b/xsdata/codegen/handlers/flatten_class_extensions.py
index b8aad125..beb37e9d 100644
--- a/xsdata/codegen/handlers/flatten_class_extensions.py
+++ b/xsdata/codegen/handlers/flatten_class_extensions.py
@@ -157,7 +157,6 @@ class FlattenClassExtensions(RelativeHandlerInterface):
             ClassUtils.copy_attributes(source, target, ext)
         else:
             ext.type.reference = id(source)
-            logger.debug("Ignore extension: %s", ext.type.name)
 
     def find_dependency(self, attr_type: AttrType) -> Optional[Class]:
         """
diff --git a/xsdata/codegen/handlers/process_attributes_types.py b/xsdata/codegen/handlers/process_attributes_types.py
index b26ab32a..5ea6ccae 100644
--- a/xsdata/codegen/handlers/process_attributes_types.py
+++ b/xsdata/codegen/handlers/process_attributes_types.py
@@ -196,6 +196,9 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
         attr_type.reference = id(source)
         attr_type.circular = self.is_circular_dependency(source, target, set())
 
+        if attr_type.circular:
+            logger.debug("Possible circular reference %s, %s", target.name, source.name)
+
     def is_circular_dependency(self, source: Class, target: Class, seen: Set) -> bool:
         """Check if any source dependencies recursively match the target
         class."""
diff --git a/xsdata/utils/click.py b/xsdata/utils/click.py
index f9a962cb..d5227441 100644
--- a/xsdata/utils/click.py
+++ b/xsdata/utils/click.py
@@ -111,7 +111,7 @@ class LogFormatter(logging.Formatter):
             level = record.levelname.lower()
             msg = record.getMessage()
             if level in self.colors:
-                prefix = click.style(f"{level}:", **self.colors[level])
+                prefix = click.style(f"{level}", **self.colors[level])
                 msg = f"{prefix}: {msg}"
             return msg
 


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 7e9fb6d0..e49cc871 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,3 +1,4 @@
+import logging
 import platform
 import sys
 import tempfile
@@ -14,6 +15,7 @@ from xsdata.cli import resolve_source
 from xsdata.codegen.transformer import SchemaTransformer
 from xsdata.codegen.writer import CodeWriter
 from xsdata.formats.dataclass.generator import DataclassGenerator
+from xsdata.logger import logger
 from xsdata.models.config import GeneratorConfig
 from xsdata.models.config import StructureStyle
 from xsdata.utils.downloader import Downloader
@@ -86,6 +88,12 @@ class CliTests(TestCase):
         self.assertEqual([source.as_uri()], mock_process.call_args[0][0])
         self.assertTrue(mock_init.call_args[1]["print"])
 
+    @mock.patch.object(SchemaTransformer, "process")
+    @mock.patch.object(SchemaTransformer, "__init__", return_value=None)
+    def test_generate_with_debug_mode(self, *args):
+        self.runner.invoke(cli, ["foo.xsd", "--package", "foo", "--debug"])
+        self.assertEqual(logging.DEBUG, logger.level)
+
     @mock.patch("xsdata.cli.logger.info")
     def test_init_config(self, mock_info):
         output = tempfile.mktemp()