From 3484351250e2bd43bac7f90bd05f803396a30670 Mon Sep 17 00:00:00 2001
From: Markus Scheidgen <markus.scheidgen@gmail.com>
Date: Sat, 28 Sep 2019 12:28:09 +0200
Subject: [PATCH] Added package with actual sub sections.

---
 nomad/metainfo/metainfo.py | 49 ++++++++++++++++++++++++++++++++++++--
 tests/test_metainfo.py     | 12 ++++++++--
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/nomad/metainfo/metainfo.py b/nomad/metainfo/metainfo.py
index 79a15ce85d..58a8918c91 100644
--- a/nomad/metainfo/metainfo.py
+++ b/nomad/metainfo/metainfo.py
@@ -293,7 +293,7 @@ class MObject(metaclass=MObjectMeta):
     @classmethod
     def __init_section_cls__(cls):
         # only works after bootstrapping, since functionality is still missing
-        if not all([hasattr(__module__, cls) for cls in ['Quantity', 'Section', 'sub_section']]):
+        if not all([hasattr(__module__, cls) for cls in ['Quantity', 'Section', 'Package', 'Category', 'sub_section']]):
             return
 
         # ensure that the m_section is defined
@@ -329,6 +329,11 @@ class MObject(metaclass=MObjectMeta):
                 if attr.section_def.name is None:
                     attr.section_def.name = inflection.camelize(name)
 
+        # add section cls' section to the module's package
+        module_name = cls.__module__
+        pkg = Package.from_module(module_name)
+        pkg.m_add_sub_section(cls.m_section)
+
     @staticmethod
     def m_type_check(definition: 'Quantity', value: Any, check_item: bool = False):
         """Checks if the value fits the given quantity in type and shape; raises
@@ -399,6 +404,27 @@ class MObject(metaclass=MObjectMeta):
 
         return section
 
+    def m_sub_sections(self, definition: SectionDef) -> List[MObjectBound]:
+        """Returns all sub sections for the given section definition
+
+        Args:
+            definition: The definition of the section.
+
+        Raises:
+            KeyError: If the definition is not for a sub section
+        """
+        section_def = self._resolve_section(definition)
+
+        m_data_value = self.m_data.get(section_def.name, None)
+
+        if m_data_value is None:
+            return []
+
+        if section_def.repeats:
+            return m_data_value
+        else:
+            return [m_data_value]
+
     def m_sub_section(self, definition: SectionDef, parent_index: int = -1) -> MObjectBound:
         """Returns the sub section for the given section definition and possible
            parent_index (for repeatable sections).
@@ -837,7 +863,21 @@ class Section(Definition):
 
 
 class Package(Definition):
-    pass
+
+    @staticmethod
+    def from_module(module_name: str):
+        module = sys.modules[module_name]
+
+        pkg: 'Package' = getattr(module, 'm_package', None)
+        if pkg is None:
+            pkg = Package()
+            setattr(module, 'm_package', pkg)
+
+        pkg.name = module_name
+        if pkg.description is None and module.__doc__ is not None:
+            pkg.description = inspect.cleandoc(module.__doc__)
+
+        return pkg
 
 
 class sub_section:
@@ -880,6 +920,11 @@ class Category(Definition):
     In the old meta-info this was known as `abstract types`.
     """
 
+    def __init__(self, module_name, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        Package.from_module(module_name).m_add_sub_section(self)
+
     @cached_property
     def definitions(self) -> Iterable[Definition]:
         """ All definitions that are directly or indirectly in this category. """
diff --git a/tests/test_metainfo.py b/tests/test_metainfo.py
index c4d94116dd..be47636923 100644
--- a/tests/test_metainfo.py
+++ b/tests/test_metainfo.py
@@ -15,7 +15,7 @@
 import pytest
 import numpy as np
 
-from nomad.metainfo.metainfo import MObject, Section, Quantity, Definition, Category, sub_section
+from nomad.metainfo.metainfo import MObject, Section, Quantity, Definition, Category, Package, sub_section
 
 
 def assert_section_def(section_def: Section):
@@ -79,8 +79,10 @@ class TestPureReflection:
         assert getattr(obj, 'test_quantity') == 'test_value'
 
 
+m_package = Package(description='package doc')
+
 material_defining = Category(
-    name='material_defining',
+    __name__, name='material_defining',
     description='Quantities that add to what constitutes a different material.')
 
 
@@ -167,6 +169,12 @@ class TestM2:
         assert material_defining in System.atom_label.categories
         assert System.atom_label in material_defining.definitions
 
+    def test_package(self):
+        assert m_package.name == __name__
+        assert m_package.description is not None
+        assert len(m_package.m_sub_sections(Section)) == 3
+        assert len(m_package.m_sub_sections(Category)) == 1
+
 
 class TestM1:
     """ Test for meta-info instances. """
-- 
GitLab