Skip to content

Commit 7c45a0a

Browse files
committed
Add suppoort for custom formatters via entrypoints.
1 parent d0bd878 commit 7c45a0a

File tree

6 files changed

+221
-12
lines changed

6 files changed

+221
-12
lines changed

formate.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ known_third_party = [
3939
"coverage_pyver_pragma",
4040
"dom_toml",
4141
"domdf_python_tools",
42+
"entrypoints",
4243
"formate",
4344
"importlib_metadata",
4445
"pytest",

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ click>=8.0.1
22
consolekit>=1.2.3
33
dom-toml>=0.5.0
44
domdf-python-tools>=3.0.0
5+
entrypoints>=0.3
56
formate>=0.4.9
67
typing-extensions>=3.10.0.0

snippet_fmt/__init__.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
# 3rd party
4040
import click
41+
import entrypoints # type: ignore
4142
from consolekit.terminal_colours import ColourTrilean, resolve_color_default
4243
from consolekit.utils import coloured_diff
4344
from domdf_python_tools.paths import PathPlus
@@ -73,16 +74,6 @@ class CodeBlockError(NamedTuple):
7374
exc: Exception
7475

7576

76-
_formatters: Dict[str, Formatter] = {
77-
"bash": noformat,
78-
"python": format_python,
79-
"python3": format_python,
80-
"toml": format_toml,
81-
"ini": format_ini,
82-
"json": format_json,
83-
}
84-
85-
8677
class RSTReformatter:
8778
"""
8879
Reformat code snippets in a reStructuredText file.
@@ -110,6 +101,16 @@ def __init__(self, filename: PathLike, config: SnippetFmtConfigDict):
110101
self._reformatted_source: Optional[str] = None
111102
self.errors = []
112103

104+
self._formatters: Dict[str, Formatter] = {
105+
# "bash": noformat,
106+
# "python": format_python,
107+
"python3": format_python,
108+
"toml": format_toml,
109+
"ini": format_ini,
110+
"json": format_json,
111+
}
112+
self.load_extra_formatters()
113+
113114
def run(self) -> bool:
114115
"""
115116
Run the reformatter.
@@ -152,7 +153,8 @@ def process_match(self, match: Match[str]) -> str:
152153

153154
if lang in self.config["languages"]:
154155
lang_config = self.config["languages"][lang]
155-
formatter = _formatters.get(lang.lower(), noformat)
156+
# TODO: show warning if not found and in "strict" mode
157+
formatter = self._formatters.get(lang.lower(), noformat)
156158
else:
157159
lang_config = {}
158160
formatter = noformat
@@ -214,6 +216,17 @@ def _collect_error(self, match: Match[str]) -> Iterator[None]:
214216
except Exception as e:
215217
self.errors.append(CodeBlockError(match.start(), e))
216218

219+
def load_extra_formatters(self):
220+
group = "snippet_fmt.formatters"
221+
222+
for distro_config, _ in entrypoints.iter_files_distros():
223+
if group in distro_config:
224+
for name, epstr in distro_config[group].items():
225+
with contextlib.suppress(entrypoints.BadEntryPoint, ImportError):
226+
# TODO: show warning for bad entry point if verbose, or "strict"?
227+
ep = entrypoints.EntryPoint.from_string(epstr, name)
228+
self._formatters[name] = ep.load()
229+
217230

218231
def reformat_file(
219232
filename: PathLike,

tests/test_snippet_fmt.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from coincidence.params import param
1111
from consolekit.terminal_colours import strip_ansi
1212
from consolekit.testing import CliRunner, Result
13-
from domdf_python_tools.paths import PathPlus, in_directory
13+
from domdf_python_tools.paths import PathPlus, TemporaryPathPlus, in_directory
1414

1515
# this package
1616
from snippet_fmt import SnippetFmtConfigDict, reformat_file
@@ -68,6 +68,26 @@
6868
filenames = pytest.mark.parametrize("filename", [param("example.rst", idx=0)])
6969

7070

71+
@pytest.fixture()
72+
def custom_entry_point(monkeypatch):
73+
with TemporaryPathPlus() as tmpdir:
74+
monkeypatch.syspath_prepend(str(tmpdir))
75+
76+
dist_info = tmpdir / "snippet_fmt_demo-0.0.0.dist-info"
77+
dist_info.maybe_make(parents=True)
78+
(dist_info / "entry_points.txt").write_lines([
79+
"[snippet_fmt.formatters]",
80+
"python3 = snippet_fmt_demo:fake_format",
81+
])
82+
83+
(tmpdir / "snippet_fmt_demo.py").write_lines([
84+
"def fake_format(*args, **kwargs):",
85+
"\treturn 'Hello World'",
86+
])
87+
88+
yield
89+
90+
7191
class TestReformatFile:
7292

7393
@directives
@@ -94,6 +114,31 @@ def test_snippet_fmt(
94114
advanced_file_regression.check_file(tmp_pathplus / filename)
95115
check_out(capsys.readouterr(), tmp_pathplus, advanced_data_regression)
96116

117+
@filenames
118+
def test_snippet_fmt_custom_entry_point(
119+
self,
120+
filename: str,
121+
tmp_pathplus: PathPlus,
122+
advanced_file_regression: AdvancedFileRegressionFixture,
123+
advanced_data_regression: AdvancedDataRegressionFixture,
124+
capsys,
125+
custom_entry_point
126+
):
127+
128+
languages = {"python3": {"reformat": True}}
129+
directives = ["code-block", "code-cell"]
130+
131+
(tmp_pathplus / filename).write_text((source_dir / filename).read_text())
132+
(tmp_pathplus / "formate.toml").write_text((source_dir / "example_formate.toml").read_text())
133+
134+
config: SnippetFmtConfigDict = {"languages": languages, "directives": directives}
135+
136+
with in_directory(tmp_pathplus):
137+
reformat_file(tmp_pathplus / filename, config)
138+
139+
advanced_file_regression.check_file(tmp_pathplus / filename)
140+
check_out(capsys.readouterr(), tmp_pathplus, advanced_data_regression)
141+
97142

98143
class TestCLI:
99144

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
2+
.. code-block:: python
3+
4+
print ( 'Hello World'
5+
6+
)
7+
8+
for item in [a,b,c] :
9+
if item: print(item)
10+
11+
12+
.. code-block:: python3
13+
14+
Hello World
15+
16+
17+
.. code-cell:: python3
18+
:count: 1
19+
20+
Hello World
21+
22+
23+
.. code:: python
24+
25+
print ( 'Hello World'
26+
27+
)
28+
29+
for item in [a,b,c] :
30+
if item: print(item)
31+
32+
33+
.. sourcecode:: python
34+
35+
print ( 'Hello World'
36+
37+
)
38+
39+
for item in [a,b,c] :
40+
if item: print(item)
41+
42+
43+
.. parsed-literal:: python
44+
45+
print ( 'Hello World'
46+
47+
)
48+
49+
for item in [a,b,c] :
50+
if item: print(item)
51+
52+
53+
.. code-block:: toml
54+
55+
[project]
56+
57+
58+
name = 'my-project'
59+
60+
61+
version="1.2.3"
62+
license = {file = "LICENSE"}
63+
64+
65+
.. code-block:: TOML
66+
67+
[project]
68+
69+
70+
name = 'my-project'
71+
72+
73+
version="1.2.3"
74+
license = {file = "LICENSE"}
75+
76+
77+
.. code:: TOML
78+
79+
[project]
80+
81+
82+
name = 'my-project'
83+
84+
85+
version="1.2.3"
86+
license = {
87+
file = "LICENSE",
88+
}
89+
90+
.. sourcecode:: toml
91+
92+
[project]
93+
name = 'my-project
94+
95+
96+
.. code-block:: JSON
97+
98+
{
99+
"key": "value",
100+
"key2": "value2",
101+
}
102+
103+
.. code-block:: json
104+
105+
{"key": "value", "key2": "value2"}
106+
107+
108+
.. code-block:: bash
109+
110+
echo "Hello World"
111+
112+
113+
.. code:: YAML
114+
115+
name: my-project
116+
version: 1.2.3
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
err:
2+
- ''
3+
out:
4+
- "--- .../example.rst\t(original)"
5+
- "+++ .../example.rst\t(reformatted)"
6+
- '@@ -11,23 +11,13 @@'
7+
- ''
8+
- ' .. code-block:: python3'
9+
- ''
10+
- "-\tprint ( 'Hello World'"
11+
- '-'
12+
- "-\t)"
13+
- '-'
14+
- "-\tfor item in [a,b,c] :"
15+
- "-\t\t\t\tif item: print(item)"
16+
- "+\tHello World"
17+
- ''
18+
- ''
19+
- ' .. code-cell:: python3'
20+
- " \t:count: 1"
21+
- ''
22+
- "-\tprint ( 'Hello World'"
23+
- '-'
24+
- "-\t)"
25+
- '-'
26+
- "-\tfor item in [a,b,c] :"
27+
- "-\t\t\t\tif item: print(item)"
28+
- "+\tHello World"
29+
- ''
30+
- ''
31+
- ' .. code:: python'
32+
- ''
33+
- ''

0 commit comments

Comments
 (0)