Repository: linkml/linkml

Issue:
docgen.py no-mergeimports creates incorrect docs
**Describe the bug**

The script `docgen.py` generates incorrect `index.md` by populating merged imports even though `--no-mergeimports` should be respected.

The code  fails to generate <parentclass>.md pages because `--no-mergeimports` is respected (but should be ignored) for everything except index.md.


**To reproduce**
Create a parent schema.
Create a subschema and import parent schema.
Run `docgen.py --no-mergeimports subschema.yaml` and `make serve` and notice how parent classes/slots/types/subsets are included in index.md

**Expected behavior**
Index.md should list subschema classes/types/slots/subsets only not everything.

**Additional context**
Add any other context about the problem here.

The parameter `self.mergeimports` should be passed to lines 660, 672, 684, 696, 708 to fix `index.md` and should not be passed to lines 196, 205, 189, 215, 224, 234 to ensure <class>.md is generated for referenced parent classes (i.e. self.mergeimports must be ignored).

Discussion:

Patch:
diff --git a/linkml/generators/docgen.py b/linkml/generators/docgen.py
index 7cd853cb..8ab2978e 100644
--- a/linkml/generators/docgen.py
+++ b/linkml/generators/docgen.py
@@ -186,14 +186,14 @@ class DocGenerator(Generator):
             )
             return
         template = self._get_template("schema")
-        for schema_name in sv.imports_closure(imports=self.mergeimports):
+        for schema_name in sv.imports_closure():
             imported_schema = sv.schema_map.get(schema_name)
             out_str = template.render(
                 gen=self, schema=imported_schema, schemaview=sv, **template_vars
             )
             self._write(out_str, directory, imported_schema.name)
         template = self._get_template("class")
-        for cn, c in sv.all_classes(imports=self.mergeimports).items():
+        for cn, c in sv.all_classes().items():
             if self._is_external(c):
                 continue
             n = self.name(c)
@@ -202,7 +202,7 @@ class DocGenerator(Generator):
             )
             self._write(out_str, directory, n)
         template = self._get_template("slot")
-        for sn, s in sv.all_slots(imports=self.mergeimports).items():
+        for sn, s in sv.all_slots().items():
             if self._is_external(s):
                 continue
             n = self.name(s)
@@ -212,7 +212,7 @@ class DocGenerator(Generator):
             )
             self._write(out_str, directory, n)
         template = self._get_template("enum")
-        for en, e in sv.all_enums(imports=self.mergeimports).items():
+        for en, e in sv.all_enums().items():
             if self._is_external(e):
                 continue
             n = self.name(e)
@@ -221,7 +221,7 @@ class DocGenerator(Generator):
             )
             self._write(out_str, directory, n)
         template = self._get_template("type")
-        for tn, t in sv.all_types(imports=self.mergeimports).items():
+        for tn, t in sv.all_types().items():
             if self._exclude_type(t):
                 continue
             n = self.name(t)
@@ -231,7 +231,7 @@ class DocGenerator(Generator):
             )
             self._write(out_str, directory, n)
         template = self._get_template("subset")
-        for _, s in sv.all_subsets(imports=self.mergeimports).items():
+        for _, s in sv.all_subsets().items():
             if self._is_external(c):
                 continue
             n = self.name(s)
@@ -657,7 +657,7 @@ class DocGenerator(Generator):
         Ensures rank is non-null
         :return: iterator
         """
-        elts = self.schemaview.all_classes().values()
+        elts = self.schemaview.all_classes(imports=self.mergeimports).values()
         _ensure_ranked(elts)
         for e in elts:
             yield e
@@ -669,7 +669,7 @@ class DocGenerator(Generator):
         Ensures rank is non-null
         :return: iterator
         """
-        elts = self.schemaview.all_slots().values()
+        elts = self.schemaview.all_slots(imports=self.mergeimports).values()
         _ensure_ranked(elts)
         for e in elts:
             yield e
@@ -681,7 +681,7 @@ class DocGenerator(Generator):
         Ensures rank is non-null
         :return: iterator
         """
-        elts = self.schemaview.all_types().values()
+        elts = self.schemaview.all_types(imports=self.mergeimports).values()
         _ensure_ranked(elts)
         for e in elts:
             yield e
@@ -693,7 +693,7 @@ class DocGenerator(Generator):
         Ensures rank is non-null
         :return: iterator
         """
-        elts = self.schemaview.all_enums().values()
+        elts = self.schemaview.all_enums(imports=self.mergeimports).values()
         _ensure_ranked(elts)
         for e in elts:
             yield e
@@ -705,7 +705,7 @@ class DocGenerator(Generator):
         Ensures rank is non-null
         :return: iterator
         """
-        elts = self.schemaview.all_subsets().values()
+        elts = self.schemaview.all_subsets(imports=self.mergeimports).values()
         _ensure_ranked(elts)
         for e in elts:
             yield e
@@ -729,7 +729,7 @@ class DocGenerator(Generator):
         :return: tuples (depth: int, cls: ClassDefinitionName)
         """
         sv = self.schemaview
-        roots = sv.class_roots(mixins=False)
+        roots = sv.class_roots(mixins=False, imports=self.mergeimports)
 
         # by default the classes are sorted alphabetically
         roots = sorted(roots, key=str.casefold, reverse=True)
@@ -743,7 +743,7 @@ class DocGenerator(Generator):
             depth, class_name = stack.pop()
             yield depth, class_name
             children = sorted(
-                sv.class_children(class_name=class_name, mixins=False),
+                sv.class_children(class_name=class_name, mixins=False, imports=self.mergeimports),
                 key=str.casefold,
                 reverse=True,
             )


Test patch:
diff --git a/tests/test_generators/test_docgen.py b/tests/test_generators/test_docgen.py
index 390e4e0c..7764f94a 100644
--- a/tests/test_generators/test_docgen.py
+++ b/tests/test_generators/test_docgen.py
@@ -17,6 +17,7 @@ LATEX_DIR = env.expected_path("kitchen_sink_tex")
 MD_DIR = env.expected_path("kitchen_sink_md")
 META_MD_DIR = env.expected_path("meta_md")
 MD_DIR2 = env.expected_path("kitchen_sink_md2")
+MD_DIR3 = env.expected_path("kitchen_sink_md3")
 HTML_DIR = env.expected_path("kitchen_sink_html")
 EXAMPLE_DIR = env.input_path("examples")
 
@@ -301,6 +302,40 @@ class DocGeneratorTestCase(unittest.TestCase):
             "Person.md",
             "Example: Person",
             after="## Examples",)
+        
+    def test_docgen_no_mergeimports(self):
+        """Tests when imported schemas are not folded into main schema"""
+        gen = DocGenerator(SCHEMA, mergeimports=False, no_types_dir=True)
+        md = gen.serialize(directory=MD_DIR3)
+
+        assert_mdfile_contains(
+            "index.md", 
+            "| [Address](Address.md) |  |", 
+            after="## Classes", 
+            outdir=MD_DIR3
+        )
+        
+        assert_mdfile_does_not_contain(
+            "index.md", 
+            "| [Activity](Activity.md) | a provence-generating activity |", 
+            after="## Classes", 
+            outdir=MD_DIR3
+        )
+
+        assert_mdfile_does_not_contain(
+            "index.md", 
+            "| [acted_on_behalf_of](acted_on_behalf_of.md) |  |", 
+            after="## Slots", 
+            outdir=MD_DIR3
+        )
+
+        assert_mdfile_does_not_contain(
+            "index.md", 
+            "| [AgeInYearsType](AgeInYearsType.md) |  |", 
+            after="## Types", 
+            outdir=MD_DIR3
+        )
+
 
     def test_docgen_rank_ordering(self):
         """Tests overriding default order"""
@@ -461,6 +496,25 @@ class DocGeneratorTestCase(unittest.TestCase):
                            (1, 'FamilialRelationship'), (0, 'WithLocation')]
                            
         self.assertCountEqual(actual_result, expected_result)
+
+    def test_class_hierarchy_as_tuples_no_mergeimports(self):
+        """Test to ensure that imported schema classes are not generated
+        even in the method that hierarchically lists classes on index page
+        when mergeimports=False.
+        """
+        tdir = env.input_path("docgen_html_templates")
+        gen = DocGenerator(
+            SCHEMA,
+            mergeimports=False,
+            no_types_dir=True,
+            template_directory=tdir,
+            format="html",
+        )
+        actual_result = gen.class_hierarchy_as_tuples()
+        actual_result = list(actual_result)
+
+        self.assertNotIn(actual_result, (0, 'activity'))
+        self.assertNotIn(actual_result, (0, 'agent'))
         
     def test_fetch_slots_of_class(self):
         tdir = env.input_path('docgen_html_templates')