Skip to content

Conversation

@sayalaruano
Copy link
Collaborator

@sayalaruano sayalaruano commented Aug 25, 2025

Summary

Create a base plotly schema to store parameters shared across multiple plot types, ensuring consistency and reducing code repetition. The Pydantic models were updated accordingly.

List of changes proposed in this PR (pull-request)

  • Created src/vuecore/schemas/plotly_base.py base plotly Pydantic model
  • Update bar, box, line, and scatter Pydantic models with this change
  • Add autodocand autodoc_pydantic options on the docs conf.py file to avoid duplicate attributes and document the Pydantic schemas on the Sphinx docs.
  • Add the document_pydant_params decorator in the src/vuecore/utils/docs_utils.py file to add Pydantic model parameters to a function's docstrings. This was done to show the kwargs on the user-facing functions.
  • Createdtest_docs_utils.py file to test the decorator and helper functions

… pydantic models to store parameters shared across multiple plot types
…tending from PlotlyBaseConfig to reduce code repetition
…tending from PlotlyBaseConfig to reduce code repetition
…extending from PlotlyBaseConfig to reduce code repetition
…odel, extending from PlotlyBaseConfig to reduce code repetition
@sayalaruano sayalaruano requested a review from enryH August 25, 2025 14:52
@enryH
Copy link
Collaborator

enryH commented Aug 27, 2025

ok. this change now leads to hiding the parameters from the main function:

>>> create_bar_plot?
create_bar_plot(
    data: pandas.core.frame.DataFrame,
    engine: vuecore.constants.EngineType = <EngineType.PLOTLY: 'plotly'>,
    file_path: str = None,
    **kwargs,
) -> Any
Docstring:
Creates, styles, and optionally saves a bar plot using the specified engine.

This function serves as the main entry point for users to generate bar plots.
It validates the provided configuration against the BarConfig schema,
retrieves the appropriate plotting builder and saver functions based on the
selected engine, builds the plot, and optionally saves it to a file.

Parameters
----------
data : pd.DataFrame
    The DataFrame containing the data to be plotted. Each row represents
    an observation, and columns correspond to variables.
engine : EngineType, optional
    The plotting engine to use for rendering the plot.
    Defaults to `EngineType.PLOTLY`.
file_path : str, optional
    If provided, the path where the final plot will be saved.
    The file format is automatically inferred from the file extension
    (e.g., '.html', '.png', '.jpeg', '.svg'). Defaults to None, meaning
    the plot will not be saved.
**kwargs
    Keyword arguments for plot configuration. These arguments are
    validated against the `BarConfig` Pydantic model. Refer to
    `vuecore.schemas.basic.bar.BarConfig` for all available
    options and their descriptions.

Returns
-------
Any
    The final plot object returned by the selected engine.
    For Plotly, this will typically be a `plotly.graph_objects.Figure`.
    The exact type depends on the chosen engine.

Raises
------
pydantic.ValidationError
    If the provided keyword arguments do not conform to the `BarConfig` schema.
    e.g., a required parameter is missing or a value has an incorrect type.
ValueError
    Raised by the plotting engine (e.g., Plotly Express) if a
    column specified in the configuration (e.g., 'x', 'y', 'color') is
    not found in the provided DataFrame.

Examples
--------
For detailed examples and usage, please refer to the documentation:

* **Jupyter Notebook:** `docs/api_examples/bar_plot.ipynb` -
https://vuecore.readthedocs.io/en/latest/api_examples/bar_plot.html
* **Python Script:** `docs/api_examples/bar_plot.py` -
https://github.com/Multiomics-Analytics-Group/vuecore/blob/main/docs/api_examples/bar_plot.py

And if i check the BarConfig, I see that not all parameters are defined in that docstring.

>>> import vuecore
>>> vuecore.schemas.basic.bar.BarConfig?
Init signature:
vuecore.schemas.basic.bar.BarConfig(
    *,
    x: Optional[str] = None,
    y: Optional[str] = None,
    color: Optional[str] = None,
    hover_name: Optional[str] = None,
    hover_data: List[str] = [],
    facet_row: Optional[str] = None,
    facet_col: Optional[str] = None,
    labels: Optional[Dict[str, str]] = None,
    color_discrete_map: Optional[Dict[str, str]] = None,
    category_orders: Optional[Dict[str, List[str]]] = None,
    log_x: bool = False,
    log_y: bool = False,
    range_x: Optional[List[float]] = None,
    range_y: Optional[List[float]] = None,
    title: str = 'Plotly Plot',
    x_title: Optional[str] = None,
    y_title: Optional[str] = None,
    subtitle: Optional[str] = None,
    template: str = 'plotly_white',
    width: Optional[int] = 800,
    height: Optional[int] = 600,
    pattern_shape: Optional[str] = None,
    text: Optional[str] = None,
    error_x: Optional[str] = None,
    error_y: Optional[str] = None,
    pattern_shape_map: Optional[Dict[str, str]] = None,
    opacity: float = 0.8,
    orientation: str = 'v',
    barmode: str = 'relative',
    **extra_data: Any,
) -> None
Docstring:     
Pydantic model for validating and managing bar plot configurations,
which extends PlotlyBaseConfig.

This model serves as a curated API for the most relevant parameters
for bar plots, closely aligned with the `plotly.express.bar` API
(https://plotly.com/python-api-reference/generated/plotly.express.bar.html).

This model includes the most relevant parameters for data mapping, styling,
and layout. It ensures that user-provided configurations are type-safe and
adhere to the expected structure. The plotting function handles parameters
defined here, and also accepts additional Plotly keyword arguments,
forwarding them to the appropriate `plotly.express.bar` or
`plotly.graph_objects.Figure` call.

Attributes
----------
-----Data Mapping-----
pattern_shape : Optional[str]
    Column to assign pattern shapes to bars.
text : Optional[str]
    Column for adding text labels to bars.
error_x : Optional[str]
    Column for sizing x-axis error bars.
error_y : Optional[str]
    Column for sizing y-axis error bars.
pattern_shape_map : Optional[Dict[str, str]]
    Specific pattern shape mappings for values in the `pattern_shape` column.

-----Styling and Layout-----
opacity : float
    Overall opacity of markers (0-1).
orientation: str
    Orientation of the bars ('v' for vertical, 'h' for horizontal).
barmode : str
    Mode for grouping bars ('group', 'overlay', 'relative').
Init docstring:
Create a new model by parsing and validating input data from keyword arguments.

Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
validated to form a valid model.

`self` is explicitly positional-only to allow `self` as a field name.

My feeling is that then in the example (link users' might get confused where the
extra parameters come from:

# Generate the basic bar plot
bar_plot_basic = create_bar_plot(
    data=bar_plot_basic_df,
    x="Sample",
    y="Genera_Count",
    title="Genera Count by Sample Type",
    file_path=file_path_basic_png,  
)

Matplotlib has the same issue using the kwargs as I mentioned and there is only the option to patch the docstring on the fly with the docstrings from the base schemas (on top of my head), which hopefully is not too tricky. What do you think?

from pydantic import BaseModel, Field, ConfigDict, model_validator


class PlotlyBaseConfig(BaseModel):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could take the docstring from the base class, split it at Attributes and then attach the rest before you add new parameters in any children configurations

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing it. Yes, it's a good idea to update the docstrings of the child configs with the parameters of the base class. Will work on it tmr and commit it in this PR.

@enryH
Copy link
Collaborator

enryH commented Aug 27, 2025

And interestingly the plots subpackge is not listed in the API reference, but it is available in the build documentation. I gues you will need to manually add these entries for automatically created functions at runtime:

image

@sayalaruano
Copy link
Collaborator Author

Hey @enryH! I added the code to combine the child and parent docstirngs, check it out when you have time

@enryH
Copy link
Collaborator

enryH commented Sep 8, 2025

ScatterConfig has now duplicated entries and some extra hypens it seems

https://vuecore--31.org.readthedocs.build/en/31/reference/vuecore.schemas.basic.scatter.html#vuecore.schemas.basic.scatter.ScatterConfig

And what about create_scatter? Do you think we can add the attributes to the docstring there? (as this is the entry point for the user, no?)

from typing import Type


def combine_docstrings(cls: Type) -> Type:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add an test for that one.

This function finds the `Attributes` section in the parent class's docstring
and prepends it to the `Attributes` section of the decorated child class.
This ensures all inherited parameters are properly documented for runtime
isnpection (e.g., using `help()` or `?`).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isnpection (e.g., using `help()` or `?`).
inspection (e.g., using `help()` or `?`).

@sayalaruano
Copy link
Collaborator Author

Hey @enryH! I figured out how to solve the issues with the duplicated attributes and add the kwargs to the user-facing functions. I think that now everything is working as expected, but check it out just in case.

@sayalaruano sayalaruano merged commit 4029150 into main Sep 12, 2025
7 checks passed
@sayalaruano sayalaruano deleted the base-schema branch September 12, 2025 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants