Skip to content

Commit

Permalink
FEAT: Directive working with pydantic
Browse files Browse the repository at this point in the history
  • Loading branch information
daquintero committed Jan 12, 2024
1 parent 10b508e commit fb95d8a
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 11 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ When we have multiple classes, and multiple inherited classes, it is hard to kee

This extension aims to do that.

### Objectives

- Improve the API template for `autodoc` which I don't think is good enough.
- For each class, such as `tidy3d` complex classes, properly generate a nicer template of the parameters and methods that extends how autodoc works.
- Generate an index of each API directive that can be easily searched by corresponding themes like the `sphinx_book_theme`.
- For inherited classes, have the option to easily define the methods and parameters that we want to generate documentation for.
- Have the option to generate custom HTML relevant to each class in a way defined by the class docstring.
- Make sure the docstrings don't interfere with help messages such as ipython, etc. (Maybe explore how to compile relevant docstrings into the help messages).

**API Docstrings Improvements**

When we have highly complex classes that have multiple inherited parameters, it is desired to have a clear definition of each specific class and its parameters, types and defaults. Whilst this can be done manually for an individual class or method, as a project increases in complexity, it is desired to understand the relevant docstrings for each class and method.

## Usage

The idea is that we can use the `autoflex` directive to improve the data structure generated during the `autosummary` process instead of the `automodule` or `autoclass` directive.

Basic Example:

```rst
.. autoflex:: somepackage.MyClass
```





## Links

Expand Down
151 changes: 149 additions & 2 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ packages = [

[tool.poetry.dependencies]
python = ">=3.9"
docutils = ">=0.16,<1.0"
pydantic = {version="^2"}
sphinx = {version="^7"}


# Testing and linting
pytest = {version="*", optional=true}
mypy = {version="*", optional=true}
Expand Down
2 changes: 2 additions & 0 deletions sphinxcontrib/autoflex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from typing import Any, Dict
from .install import install_verification
from .directives import AutoFlexDirective

__version__ = "0.0.1"
__author__ = "Dario Quintero Dominguez"
Expand All @@ -19,6 +20,7 @@
def setup(app) -> Dict[str, Any]:
"""Add icon node to the sphinx builder."""
install_verification()
app.add_directive("autoflex", AutoFlexDirective)
# load the icon node/role
# app.add_node(icon_node, **_NODE_VISITORS) # type: ignore
# app.add_role("icon", Icon())
Expand Down
36 changes: 36 additions & 0 deletions sphinxcontrib/autoflex/directives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pydantic
from docutils.parsers import rst
from typing import Any


class AutoFlexDirective(pydantic.BaseModel, rst.Directive):
"""
Extension of the ``.. automodule::`` directive.
Usage
-----
.. code::
.. autoflex:: my_package.MyClass
"""

# Directive information
name: str = "autoflex"
arguments: Any = None
options: Any = None
content: Any = None
lineno: Any = None
content_offset: Any = None
block_text: Any = None
state: Any = None
state_machine: Any = None
reporter: Any = None

required_arguments: int = 0
optional_arguments: int = 0

def run(self):
print("AutoFlex directive running.")
print(self.name)
83 changes: 83 additions & 0 deletions sphinxcontrib/autoflex/parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
This is what we had before in terms of our parameter extraction.
Other relevant references are:
- autodoc_pydantic parameter visualisation.
"""


# @classmethod
# def generate_docstring(cls) -> str:
# """Generates a docstring for a Tidy3D mode and saves it to the __doc__ of the class."""
#
# # store the docstring in here
# doc = ""
#
# # if the model already has a docstring, get the first lines and save the rest
# original_docstrings = []
# if cls.__doc__:
# original_docstrings = cls.__doc__.split("\n\n")
# class_description = original_docstrings.pop(0)
# doc += class_description
# original_docstrings = "\n\n".join(original_docstrings)
#
# # create the list of parameters (arguments) for the model
# doc += "\n\n Parameters\n ----------\n"
# for field_name, field in cls.__fields__.items():
# # ignore the type tag
# if field_name == TYPE_TAG_STR:
# continue
#
# # get data type
# data_type = field._type_display()
#
# # get default values
# default_val = field.get_default()
# if "=" in str(default_val):
# # handle cases where default values are pydantic models
# default_val = f"{default_val.__class__.__name__}({default_val})"
# default_val = (", ").join(default_val.split(" "))
# default_val = default_val.replace("=", "")
#
# # make first line: name : type = default
# default_str = "" if field.required else f" = {default_val}"
# doc += f"\n\t\t``{field_name}``: Attribute: :attr:`{field_name}` \n"
# doc += "\n\t\t\t .. list-table::"
# doc += "\n\t\t\t\t:widths: 20 80\n"
# doc += "\n\t\t\t\t* - ``Type``"
# doc += f"\n\t\t\t\t - *{data_type}*"
# doc += "\n\t\t\t\t* - ``Default``"
# doc += f"\n\t\t\t\t - {default_str}"
#
# # get field metadata
# field_info = field.field_info
# # doc += " "
#
# # add units (if present)
# units = field_info.extra.get("units")
# if units is not None:
# if isinstance(units, (tuple, list)):
# unitstr = "("
# for unit in units:
# unitstr += str(unit)
# unitstr += ", "
# unitstr = unitstr[:-2]
# unitstr += ")"
# else:
# unitstr = units
# doc += "\n\t\t\t\t* - ``Units``"
# doc += f"\n\t\t\t\t - `{unitstr}`"
#
# # add description
# description_str = field_info.description
# if description_str is not None:
# doc += "\n\t\t\t\t* - ``Description``"
# doc += f"\n\t\t\t\t - {description_str}\n"
#
# # add in remaining things in the docs
# if original_docstrings:
# doc += "\n"
# doc += original_docstrings
#
# doc += "\n"
# cls.__doc__ = doc
9 changes: 0 additions & 9 deletions tests/conftest.py

This file was deleted.

3 changes: 3 additions & 0 deletions tests/test_directive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def test_import():
from sphinxcontrib.autoflex import AutoFlexDirective
AutoFlexDirective()
4 changes: 4 additions & 0 deletions tests/test_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

def test_import():
import sphinxcontrib.autoflex
assert sphinxcontrib.autoflex.install_verification() == True

0 comments on commit fb95d8a

Please sign in to comment.