Repository: matplotlib/matplotlib
Pull request: Fix get_constrained_layout_pads

## PR summary
- fix non-existing function `get_info()`
- complete docs

closes #25963

## PR checklist
<!-- Please mark any checkboxes that do not apply to this PR as [N/A].-->

- [x] "closes #0000" is in the body of the PR description to [link the related issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
- [x] new and changed code is [tested](https://matplotlib.org/devdocs/devel/testing.html)
- [N/A] *Plotting related* features are demonstrated in an [example](https://matplotlib.org/devdocs/devel/documenting_mpl.html#writing-examples-and-tutorials)
- [N/A] *New Features* and *API Changes* are noted with a [directive and release note](https://matplotlib.org/devdocs/devel/coding_guide.html#new-features-and-api-changes)
- [x] Documentation complies with [general](https://matplotlib.org/devdocs/devel/documenting_mpl.html#writing-rest-pages) and [docstring](https://matplotlib.org/devdocs/devel/documenting_mpl.html#writing-docstrings) guidelines

<!--
Thank you so much for your PR!  To help us review your contribution, please
consider the following points:

- A development guide is available at https://matplotlib.org/devdocs/devel/index.html.

- Help with git and github is available at https://matplotlib.org/devdocs/devel/development_workflow.html

- Create a separate branch for your changes and open the PR from this branch. Please avoid working on `main`.

- The PR title should summarize the changes, for example "Raise ValueError on
  non-numeric input to set_xlim".  Avoid non-descriptive titles such as
  "Addresses issue #8576".

- The summary should provide at least 1-2 sentences describing the pull request
  in detail (Why is this change required?  What problem does it solve?) and
  link to any relevant issues.

- If you are contributing fixes to docstrings, please pay attention to
  https://matplotlib.org/stable/devel/documenting_mpl.html#formatting-conventions.  In particular,
  note the difference between using single backquotes, double backquotes, and
  asterisks in the markup.

We understand that PRs can sometimes be overwhelming, especially as the
reviews start coming in.  Please let us know if the reviews are unclear or
the recommended next step seems overly demanding, if you would like help in
addressing a reviewer's comments, or if you have been waiting too long to hear
back on your PR.
-->


Problem statement:
[Bug]: fig.get_constrained_layout_pads() raises AttributeError
### Bug summary

`fig.get_constrained_layout_pads()` fails because the LayoutEngine has no function `get_info()`.

### Code for reproduction

```python
import matplotlib.pyplot as plt

fig = plt.figure(layout='constrained')
print(fig.get_constrained_layout_pads())
```


### Actual outcome

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\programs\mambaforge\lib\site-packages\matplotlib\_api\deprecation.py", line 200, in wrapper
    return func(*args, **kwargs)
  File "c:\programs\mambaforge\lib\site-packages\matplotlib\figure.py", line 2876, in get_constrained_layout_pads
    info = self.get_layout_engine().get_info()
AttributeError: 'ConstrainedLayoutEngine' object has no attribute 'get_info'
```

### Expected outcome

`(0.04167, 0.04167, 0.02, 0.02)`

### Additional information

_No response_

### Operating system

_No response_

### Matplotlib Version

3.7.1

### Matplotlib Backend

_No response_

### Python version

3.10.11

### Jupyter version

_No response_

### Installation

conda

Hints from issue discussion:

Commit message: Fix get_constrained_layout_pads()

- fix non-existing function get_info()
- complete docs

Patch to review:
diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index 39517e14d0e3..08bb483d606b 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -2872,7 +2872,7 @@ def get_constrained_layout_pads(self, relative=False):
         """
         if not isinstance(self.get_layout_engine(), ConstrainedLayoutEngine):
             return None, None, None, None
-        info = self.get_layout_engine().get_info()
+        info = self.get_layout_engine().get()
         w_pad = info['w_pad']
         h_pad = info['h_pad']
         wspace = info['wspace']
diff --git a/lib/matplotlib/layout_engine.py b/lib/matplotlib/layout_engine.py
index 1989f89684f1..d751059f4e09 100644
--- a/lib/matplotlib/layout_engine.py
+++ b/lib/matplotlib/layout_engine.py
@@ -64,6 +64,9 @@ def __init__(self, **kwargs):
         self._params = {}
 
     def set(self, **kwargs):
+        """
+        Set the parameters for the layout engine.
+        """
         raise NotImplementedError
 
     @property
@@ -120,6 +123,9 @@ def __init__(self, adjust_compatible, colorbar_gridspec, **kwargs):
         super().__init__(**kwargs)
 
     def execute(self, fig):
+        """
+        Do nothing.
+        """
         return
 
 
@@ -138,7 +144,7 @@ def __init__(self, *, pad=1.08, h_pad=None, w_pad=None,
 
         Parameters
         ----------
-        pad : float, 1.08
+        pad : float, default: 1.08
             Padding between the figure edge and the edges of subplots, as a
             fraction of the font size.
         h_pad, w_pad : float
@@ -182,6 +188,21 @@ def execute(self, fig):
             fig.subplots_adjust(**kwargs)
 
     def set(self, *, pad=None, w_pad=None, h_pad=None, rect=None):
+        """
+        Set the pads for tight_layout.
+
+        Parameters
+        ----------
+        pad : float
+            Padding between the figure edge and the edges of subplots, as a
+            fraction of the font size.
+        w_pad, h_pad : float
+            Padding (width/height) between edges of adjacent subplots.
+            Defaults to *pad*.
+        rect : tuple (left, bottom, right, top)
+            rectangle in normalized figure coordinates that the subplots
+            (including labels) will fit into.
+        """
         for td in self.set.__kwdefaults__:
             if locals()[td] is not None:
                 self._params[td] = locals()[td]
diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py
index a795a3fbe3f5..06d594c4ea1a 100644
--- a/lib/matplotlib/tests/test_figure.py
+++ b/lib/matplotlib/tests/test_figure.py
@@ -1603,3 +1603,11 @@ def test_savefig_metadata(fmt):
 def test_savefig_metadata_error(fmt):
     with pytest.raises(ValueError, match="metadata not supported"):
         Figure().savefig(io.BytesIO(), format=fmt, metadata={})
+
+
+def test_get_constrained_layout_pads():
+    expected = (0.01, 0.02, 0.03, 0.04)
+    params = dict(zip(['w_pad', 'h_pad', 'wspace', 'hspace'], expected))
+    fig = plt.figure(layout=mpl.layout_engine.ConstrainedLayoutEngine(**params))
+    with pytest.warns(PendingDeprecationWarning, match="will be deprecated"):
+        assert fig.get_constrained_layout_pads() == expected


Code review comments:
diff_hunk: @@ -1603,3 +1603,11 @@ def test_savefig_metadata(fmt):
 def test_savefig_metadata_error(fmt):
     with pytest.raises(ValueError, match="metadata not supported"):
         Figure().savefig(io.BytesIO(), format=fmt, metadata={})
+
+
+def test_get_constrained_layout_pads():
+    expected = (0.01, 0.02, 0.03, 0.04)
+    params = dict(zip(['w_pad', 'h_pad', 'wspace', 'hspace'], expected))
path: lib/matplotlib/tests/test_figure.py
text: @user1:
The inverse seems clearer to me:
```suggestion
    params = {'w_pad': 0.01, 'h_pad': 0.02, 'wspace': 0.03, 'hspace': 0.04}
    expected = [*params.values()]
```

@author:
@user1 I'm neutral on this: my understanding of unit testing is that expected values should be immediate values, not derived ones, because we want to test the function under test and not the code for obtaining the expected value. So on second thought the canonical solution in my opinion would have been

```
def test_get_constrained_layout_pads():
    params = {'w_pad': 0.01, 'h_pad': 0.02, 'wspace': 0.03, 'hspace': 0.04}
    fig = plt.figure(layout=mpl.layout_engine.ConstrainedLayoutEngine(**params))
    with pytest.warns(PendingDeprecationWarning, match="will be deprecated"):
        assert fig.get_constrained_layout_pads() == (0.01, 0.02, 0.03, 0.04)
```

(but you were too quick to merge, no problem)

@user1:
I would have been okay with that too, but I think it's probably not too big of a deal to make a whole PR for it now.