Issue 1:
Bug summary

Direct access to top level modules of matplotlib raises AttributeError for most of the documented top-level modules. This was discovered when attempting to refer to module contents for type-hints that are evaluated when a function is defined. This occurs reproducibly where no previous matplotlib module imports have occurred prior to evaluation of the code referencing a module namespaced by matplotlib

Code for reproduction

import matplotlib

def do_something_with_figure(fig: matplotlib.figure.Figure):
pass

print('doing some actual runtime code...')

Actual outcome

Traceback (most recent call last):
File "typehint-mpl.py", line 3, in <module>
def do_something_with_figure(fig: matplotlib.figure.Figure):
File "/usr/local/lib/python3.8/site-packages/matplotlib/_api/__init__.py", line 226, in __getattr__
raise AttributeError(
AttributeError: module 'matplotlib' has no attribute 'figure'

Expected outcome

For this example script, we would obviously expect to reach the print() statement.

Additional information

This appears to be a general issue with most top-level modules. The following attempts to access all top-level modules named in the current help(matplotlib):

$ cat test_modules.py
import matplotlib

MODULES = ['axes', 'figure', 'artist', 'lines', 'patches', 'text', 'image', 'collections',
'colors', 'cm', 'ticker', 'backends']

for module in MODULES:
try:
_ = getattr(matplotlib, module)
print(f'got {module}')
except AttributeError:
print(f'failed to access {module}')
$ python test_modules.py
failed to access axes
failed to access figure
failed to access artist
failed to access lines
failed to access patches
failed to access text
failed to access image
failed to access collections
got colors
got cm
got ticker
failed to access backends

Note - I think this issue is due to some sort of lazy-loading behaviour, there are various ways by which the AttributeError can be avoided. If previous imports such as import matplotlb.pyplot as plt have already occurred before references to matplotlib.xxxx occur this appears ok, and also, while investigating interactively, it appears that simply showing the help() in the python shell unblocks this access:

Python 3.8.16 (default, May 23 2023, 14:26:40)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import matplotlib
>>> matplotlib.figure
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/site-packages/matplotlib/_api/__init__.py", line 226, in __getattr__
raise AttributeError(
AttributeError: module 'matplotlib' has no attribute 'figure'
>>> help(matplotlib)
# q to leave help
>>> matplotlib.figure
<module 'matplotlib.figure' from '/usr/local/lib/python3.8/site-packages/matplotlib/figure.py'>

The above interactive behaviour can be replicated by invoking pydoc.render_doc(); the following version of the code example gives no error:

import pydoc
import matplotlib

_ = pydoc.render_doc(matplotlib)

def do_something_with_figure(fig: matplotlib.figure.Figure):
pass

All examples in this report are with matplotlib installed in python3.8 bullseye docker image via pip as follows:

FROM python:3.8-bullseye

RUN pip install --upgrade pip \
&& pip install matplotlib==3.7.1

ENTRYPOINT ["python"]

Operating system

Observed on Debian buster & bullseye and Ubuntu 18.04, 20.04 & 22.04

Matplotlib Version

Various. Code examples using 3.7.1, with behaviour definitely occurring with 3.5.2, 3.6.2 & 3.6.3

Matplotlib Backend

'agg'

Python version

Various 3.8.x, 3.9.x

Jupyter version

No response

Installation

pip

Issue 2:
I get AttributeError: module 'matplotlib' has no attribute 'dates', when trying to access date functions.

eg. mpl.dates.num2date(date + epoch_adjustment)

version: matplotlib-3.5.1-cp39-cp39-macosx_11_0_arm64.whl