def test_loading_selected_inherited_members(): """Select specific members, some of them being inherited.""" loader = Loader(inherited_members=True) obj = loader.get_object_documentation( "tests.fixtures.inherited_members.Child", members={"V1", "V2"}) for child_name in ("V1", "V2"): assert child_name in (child.name for child in obj.children)
def test_loading_cached_properties(): """Load cached properties.""" loader = Loader(new_path_syntax=True) obj = loader.get_object_documentation("tests.fixtures.cached_properties:C") assert len(obj.children) == 1 assert obj.children[0].name == obj.children[0].docstring == "aaa" assert "cached" in obj.children[0].properties
def test_loading_module_wrapped_members(): """Load documentation for wrapped function, not wrapper.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.wrapped_objects") assert obj.functions and obj.functions[0].docstring == "My docstring." assert obj.classes and obj.classes[0].methods and obj.classes[0].methods[ 0].docstring == "Hello!"
def test_loading_double_nested_class_attribute(): """Select double-nested-class attribute.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module.TheClass.TheNestedClass.TheDoubleNestedClass.THE_ATTRIBUTE" ) assert obj.docstring == "The attribute 0.3 docstring."
def test_loading_pydantic_inherited_members(): """Select inherited members in Pydantic models.""" loader = Loader(inherited_members=True) obj = loader.get_object_documentation( "tests.fixtures.inherited_members.ChildModel") for child_name in ("a", "b"): assert child_name in (child.name for child in obj.children)
def test_loading_empty_dataclass(): """Handle empty dataclasses.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.dataclass.Empty") assert obj.docstring == "A dataclass without any fields" assert len(obj.attributes) == 0 assert "dataclass" in obj.properties
def test_loading_coroutine_method(): """Load documentation for a coroutine method.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.asyncio.ClassContainingCoroutineMethod.coroutine_method" ) assert "async" in obj.properties
def test_loading_with_filters(): """Select with filters.""" loader = Loader(filters=["!^[A-Z_]+$"]) obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module") for child in obj.children: assert child.name != "THE_ATTRIBUTE"
def test_loading_explicit_members(): """Select members explicitly.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module", members={"TheClass"}) assert len(obj.children) == 1 assert obj.children[0].name == "TheClass"
def test_loading_with_filters_reselection(): """A filter can cancel a previous filter.""" loader = Loader(filters=["![A-Z_]", "[a-z]"]) obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module") assert obj.classes assert obj.classes[0].name == "TheClass"
def test_nested_class(): loader = Loader() obj = loader.get_object_documentation("tests.fixtures.nested_class") assert obj.classes assert obj.classes[0].docstring == "Main docstring." assert obj.classes[0].classes assert obj.classes[0].classes[0].docstring == "Nested docstring."
def test_loading_inherited_members(): """Select inherited members.""" loader = Loader(inherited_members=True) obj = loader.get_object_documentation( "tests.fixtures.inherited_members.Child") for child_name in ("method1", "method2", "V1", "V2"): assert child_name in (child.name for child in obj.children)
def test_loading_members_set_at_import_time(): loader = Loader() obj = loader.get_object_documentation("tests.fixtures.dynamic_members") assert obj.functions assert len(obj.classes) == 1 class_ = obj.classes[0] assert class_.methods
def test_loading_double_nested_class_method(): """Select double-nested class method.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module.TheClass.TheNestedClass.TheDoubleNestedClass.the_method" ) assert obj.docstring == "The method3 docstring."
def test_loading_deep_package(): """Handle deep nesting of packages.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.pkg1.pkg2.pkg3.pkg4.pkg5") assert obj.docstring == "Hello from the abyss." assert obj.path == "tests.fixtures.pkg1.pkg2.pkg3.pkg4.pkg5"
def test_loading_django_model(): """Handle Django models""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.django.Person") assert obj.docstring == "Simple Django Model for a person's information" name_attr = next(attr for attr in obj.attributes if attr.name == "name") assert name_attr.type == CharField assert name_attr.docstring == "Name"
def test_loading_namespace_package(): """Handle native namespace packages.""" loader = Loader() old_paths = list(sys.path) sys.path.append(str(Path(FIXTURES_DIR).resolve())) obj = loader.get_object_documentation("test_namespace.subspace") assert obj.docstring == "The subspace package docstring." assert obj.relative_file_path == f"subspace{os.sep}__init__.py" sys.path = old_paths
def test_inherited_properties_docstrings(): """Load docstrings from parent class for inherited properties.""" loader = Loader(new_path_syntax=True) obj = loader.get_object_documentation( "tests.fixtures.inherited_properties:SubClass.read_only") assert obj.docstring == "SuperClass.read_only docs" obj = loader.get_object_documentation( "tests.fixtures.inherited_properties:SubClass.mutable") assert obj.docstring == "SuperClass.mutable getter docs"
def test_loading_cython_cy_class(): """Handle Cython cdef classes.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.cython.CyClass") expected_docstring = """ A Cython class. Attributes: instance_attribute: The instance attribute docstring.""" assert obj.docstring == textwrap.dedent(expected_docstring).strip()
def test_loading_with_members_and_filters(): """Select members with filters.""" loader = Loader(filters=["!THE"]) obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module", members={"THE_ATTRIBUTE", "TheClass"} ) assert obj.attributes assert obj.attributes[0].name == "THE_ATTRIBUTE" assert obj.classes assert obj.classes[0].name == "TheClass" assert not any(a.name == "THE_ATTRIBUTE" for a in obj.classes[0].attributes)
def test_loading_pydantic_model(): """Handle Pydantic models.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.pydantic.Person") assert obj.docstring == "Simple Pydantic Model for a person's information" assert "pydantic-model" in obj.properties name_attr = next(attr for attr in obj.attributes if attr.name == "name") assert name_attr.type == str assert name_attr.docstring == "The person's name" assert "pydantic-field" in name_attr.properties age_attr = next(attr for attr in obj.attributes if attr.name == "age") assert age_attr.type == int assert age_attr.docstring == "The person's age which must be at minimum 18" assert "pydantic-field" in age_attr.properties
def test_inheriting_typing_NamedTuple(): """See details at [tests.fixtures.inheriting_typing_NamedTuple][].""" loader = Loader() loader.get_object_documentation( "tests.fixtures.inheriting_typing_NamedTuple") if sys.version.startswith("3.8"): assert len(loader.errors) == 1 else: # there are 4 class-attributes, 2 errors (source, signature) per attribute assert len(loader.errors) >= 8 for error in loader.errors[-8:]: assert "itemgetter" in error for error in loader.errors[:-8]: assert "could not get source code" in error
def test_loading_dataclass(): """Handle dataclasses.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.dataclass.Person") assert obj.docstring == "Simple dataclass for a person's information" assert len(obj.attributes) == 2 name_attr = next(attr for attr in obj.attributes if attr.name == "name") assert name_attr.type == str age_attr = next(attr for attr in obj.attributes if attr.name == "age") assert age_attr.type == int assert age_attr.docstring == "Field description." assert "dataclass" in obj.properties not_dataclass = loader.get_object_documentation("tests.fixtures.the_package.the_module.TheClass.TheNestedClass") assert "dataclass" not in not_dataclass.properties
def test_loading_marshmallow_model(): """Handle Marshmallow models.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.marshmallow.Person") assert obj.docstring == "Simple Marshmallow Model for a person's information" assert "marshmallow-model" in obj.properties name_attr = next(attr for attr in obj.attributes if attr.name == "name") assert name_attr.type == fields.Str assert name_attr.docstring == "The person's name" assert "marshmallow-field" in name_attr.properties assert "required" in name_attr.properties age_attr = next(attr for attr in obj.attributes if attr.name == "age") assert age_attr.type == fields.Int assert age_attr.docstring == "The person's age which must be at minimum 18" assert "marshmallow-field" in age_attr.properties
def test_relative_file_path_for_leaf(): """Get the relative file path of a deep object.""" obj = Loader().get_object_documentation("tests.fixtures.pkg1") leaf = obj.children[0].children[0].children[0].children[0] assert leaf.relative_file_path == os.path.join("tests", "fixtures", "pkg1", "pkg2", "pkg3", "pkg4", "pkg5", "__init__.py")
def test_property_docstring(): """Parse a property docstring.""" class_ = Loader().get_object_documentation("tests.fixtures.parsing.docstrings.NotDefinedYet") prop = class_.attributes[0] sections, errors = prop.docstring_sections, prop.docstring_errors assert len(sections) == 2 assert not errors
def test_parse_module_attributes_section(): """Parse attributes section in modules.""" loader = Loader() obj = loader.get_object_documentation("tests.fixtures.docstring_attributes_section") assert len(obj.docstring_sections) == 2 assert not obj.docstring_errors attr_section = obj.docstring_sections[1] assert attr_section.type == Section.Type.ATTRIBUTES assert len(attr_section.value) == 5 expected = [ {"name": "A", "annotation": "int", "description": "Alpha."}, {"name": "B", "annotation": "bytes", "description": "Beta."}, {"name": "C", "annotation": "bool", "description": "Gamma."}, {"name": "D", "annotation": "", "description": "Delta."}, {"name": "E", "annotation": "float", "description": "Epsilon."}, ] assert [serialize_attribute(attr) for attr in attr_section.value] == expected
def test_has_contents(): obj = Loader().get_object_documentation("tests.fixtures.pkg1") assert obj.has_contents() obj = Loader().get_object_documentation("tests.fixtures.__init__") assert not obj.children assert obj.has_contents() # we specified that the root always 'has contents' obj = Loader().get_object_documentation("tests.fixtures.no_contents") assert obj.children assert obj.has_contents assert not obj.children[0].has_contents()
def test_loading_method_without_async_property(): """Load documentation for a method that is not a coroutine.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.the_package.the_module.TheClass.the_method") assert "async" not in obj.properties
def test_loading_coroutine(): """Load documentation for a coroutine.""" loader = Loader() obj = loader.get_object_documentation( "tests.fixtures.asyncio.coroutine_function") assert "async" in obj.properties