11from __future__ import annotations
22
3- import argparse
4- from collections .abc import Callable , Mapping
53from typing import TYPE_CHECKING , Any , Protocol
64
75from mdformat ._compat import importlib_metadata
86
97if TYPE_CHECKING :
8+ import argparse
9+ from collections .abc import Callable , Mapping
10+
1011 from markdown_it import MarkdownIt
1112
1213 from mdformat .renderer .typing import Postprocess , Render
@@ -31,13 +32,6 @@ def _load_entrypoints(
3132 return loaded_ifaces , dist_versions
3233
3334
34- CODEFORMATTERS : Mapping [str , Callable [[str , str ], str ]]
35- _CODEFORMATTER_DISTS : Mapping [str , tuple [str , list [str ]]]
36- CODEFORMATTERS , _CODEFORMATTER_DISTS = _load_entrypoints (
37- importlib_metadata .entry_points (group = "mdformat.codeformatter" )
38- )
39-
40-
4135class ParserExtensionInterface (Protocol ):
4236 """An interface for parser extension plugins."""
4337
@@ -86,8 +80,37 @@ def update_mdit(mdit: MarkdownIt) -> None:
8680 """Update the parser, e.g. by adding a plugin: `mdit.use(myplugin)`"""
8781
8882
83+ CODEFORMATTERS : Mapping [str , Callable [[str , str ], str ]]
84+ _CODEFORMATTER_DISTS : Mapping [str , tuple [str , list [str ]]]
8985PARSER_EXTENSIONS : Mapping [str , ParserExtensionInterface ]
9086_PARSER_EXTENSION_DISTS : Mapping [str , tuple [str , list [str ]]]
91- PARSER_EXTENSIONS , _PARSER_EXTENSION_DISTS = _load_entrypoints (
92- importlib_metadata .entry_points (group = "mdformat.parser_extension" )
93- )
87+
88+
89+ def __getattr__ (name : str ) -> Mapping [str , Any ]:
90+ """Attribute getter fallback.
91+
92+ Used to lazy load CODEFORMATTERS and PARSER_EXTENSIONS. It'd
93+ probably be more readable to use `@functools.cache` decorated
94+ functions, but `__getattr__` is used now for back compatibility.
95+ """
96+ if name in {"CODEFORMATTERS" , "_CODEFORMATTER_DISTS" }:
97+ formatters , formatter_dists = _load_entrypoints (
98+ importlib_metadata .entry_points (group = "mdformat.codeformatter" )
99+ )
100+ # Cache the values in this module for next time, so that `__getattr__`
101+ # is only called once per `name`.
102+ global CODEFORMATTERS , _CODEFORMATTER_DISTS
103+ CODEFORMATTERS = formatters
104+ _CODEFORMATTER_DISTS = formatter_dists
105+ return formatters if name == "CODEFORMATTERS" else formatter_dists
106+ if name in {"PARSER_EXTENSIONS" , "_PARSER_EXTENSION_DISTS" }:
107+ extensions , extension_dists = _load_entrypoints (
108+ importlib_metadata .entry_points (group = "mdformat.parser_extension" )
109+ )
110+ # Cache the value in this module for next time, so that `__getattr__`
111+ # is only called once per `name`.
112+ global PARSER_EXTENSIONS , _PARSER_EXTENSION_DISTS
113+ PARSER_EXTENSIONS = extensions
114+ _PARSER_EXTENSION_DISTS = extension_dists
115+ return extensions if name == "PARSER_EXTENSIONS" else extension_dists
116+ raise AttributeError (f"module { __name__ !r} has no attribute { name !r} " )
0 commit comments