66
77import sys
88
9- from contextlib import suppress
9+ from dataclasses import dataclass
10+ from enum import Enum
1011from typing import Any , NoReturn , TextIO
1112
1213from hatch_fancy_pypi_readme .exceptions import ConfigurationError
1516from ._config import load_and_validate_config
1617
1718
19+ class Backend (Enum ):
20+ AUTO = "auto"
21+ HATCHLING = "hatchling"
22+ PDM_BACKEND = "pdm.backend"
23+
24+
25+ @dataclass
26+ class BackendSettings :
27+ pyproject_key : str
28+ use_hatch_toml : bool
29+
30+
31+ _BACKEND_SETTINGS = {
32+ Backend .HATCHLING : BackendSettings (
33+ pyproject_key = "tool.hatch.metadata.hooks.fancy-pypi-readme" ,
34+ use_hatch_toml = True ,
35+ ),
36+ Backend .PDM_BACKEND : BackendSettings (
37+ pyproject_key = "tool.pdm.build.hooks.fancy-pypi-readme" ,
38+ use_hatch_toml = False ,
39+ ),
40+ }
41+
42+
43+ _HATCH_TOML_KEY = "metadata.hooks.fancy-pypi-readme"
44+
45+
1846def cli_run (
19- pyproject : dict [str , Any ], hatch_toml : dict [str , Any ], out : TextIO
47+ pyproject : dict [str , Any ],
48+ hatch_toml : dict [str , Any ],
49+ out : TextIO ,
50+ backend : Backend = Backend .AUTO ,
2051) -> None :
2152 """
2253 Best-effort verify config and print resulting PyPI readme.
2354 """
24- is_dynamic = False
25- with suppress ( KeyError ):
26- is_dynamic = "readme" in pyproject [ "project" ][ "dynamic" ]
55+ if backend is Backend . AUTO :
56+ backend = _dwim_backend ( pyproject )
57+ settings = _BACKEND_SETTINGS [ backend ]
2758
28- if not is_dynamic :
59+ if "readme" not in _get_dotted ( pyproject , "project.dynamic" , ()) :
2960 _fail ("You must add 'readme' to 'project.dynamic'." )
3061
31- find_config = _find_config
32- with suppress (KeyError ):
33- build_backend = pyproject ["build-system" ]["build-backend" ]
34- if build_backend == "pdm.backend" :
35- find_config = _find_config_pdm
36-
37- cfg = find_config (pyproject , hatch_toml )
62+ config_data = _get_dotted (pyproject , settings .pyproject_key )
63+ config_key = settings .pyproject_key
64+ config_source = f"`[{ settings .pyproject_key } ]` in pyproject.toml"
65+
66+ if settings .use_hatch_toml :
67+ hatch_toml_config = _get_dotted (hatch_toml , _HATCH_TOML_KEY )
68+ config_source += f" or `[{ _HATCH_TOML_KEY } ]` in hatch.toml"
69+ if hatch_toml_config and config_data :
70+ _fail (
71+ "Both pyproject.toml and hatch.toml contain "
72+ "hatch-fancy-pypi-readme configuration."
73+ )
74+ if hatch_toml_config is not None :
75+ config_data = hatch_toml_config
76+ config_key = _HATCH_TOML_KEY
77+
78+ if config_data is None :
79+ _fail (f"Missing configuration ({ config_source } )" )
3880
3981 try :
40- config = load_and_validate_config (cfg )
82+ config = load_and_validate_config (config_data , base = f" { config_key } ." )
4183 except ConfigurationError as e :
4284 _fail (
4385 "Configuration has errors:\n \n "
@@ -47,73 +89,25 @@ def cli_run(
4789 print (build_text (config .fragments , config .substitutions ), file = out )
4890
4991
50- def _fail (msg : str ) -> NoReturn :
51- print (msg , file = sys .stderr )
52- sys .exit (1 )
53-
54-
55- def _find_config (
56- pyproject : dict [str , Any ], hatch_toml : dict [str , Any ]
57- ) -> dict [str , Any ]:
58- """Find fancy-pypi-readme configuration table.
59-
60- This find the configuration from either pyproject.toml or hatch.toml data,
61- with table names as used when running under ``hatchling``.
62-
63- """
64- pyproject_config = _get_table (
65- pyproject , "tool.hatch.metadata.hooks.fancy-pypi-readme"
66- )
67- hatch_toml_config = _get_table (
68- hatch_toml , "metadata.hooks.fancy-pypi-readme"
69- )
70- if hatch_toml_config and pyproject_config :
71- _fail (
72- "Both pyproject.toml and hatch.toml contain "
73- "hatch-fancy-pypi-readme configuration."
74- )
75- if hatch_toml_config is not None :
76- return hatch_toml_config
77- if pyproject_config is None :
78- _fail (
79- "Missing configuration "
80- "(`[tool.hatch.metadata.hooks.fancy-pypi-readme]` in"
81- " pyproject.toml or `[metadata.hooks.fancy-pypi-readme]`"
82- " in hatch.toml)"
83- )
84- return pyproject_config
85-
86-
87- def _find_config_pdm (
88- pyproject : dict [str , Any ], hatch_toml : dict [str , Any ]
89- ) -> dict [str , Any ]:
90- """Find fancy-pypi-readme configuration table, PDM version.
92+ def _get_dotted (
93+ data : dict [str , Any ], dotted_key : str , default : Any = None
94+ ) -> Any :
95+ try :
96+ for key in dotted_key .split ("." ):
97+ data = data [key ]
98+ except KeyError :
99+ return default
100+ return data
91101
92- This finds the configuration from pyproject.toml assuming we are
93- running under ``pdm-backend``.
94- """
95- if _get_table (hatch_toml , "metadata.hooks.fancy-pypi-readme" ):
96- _fail (
97- "Configuration in hatch.toml is ignored "
98- "when using pdm-backend as the build backend."
99- )
100- cfg = _get_table (pyproject , "tool.pdm.build.hooks.fancy-pypi-readme" )
101- if cfg is None :
102- _fail (
103- "Missing configuration "
104- "(`[tool.pdm.build.hooks.fancy-pypi-readme]` in pyproject.toml)"
105- )
106- return cfg
107102
103+ def _dwim_backend (pyproject : dict [str , Any ]) -> Backend :
104+ """Guess backend from pyproject.toml."""
105+ build_backend = _get_dotted (pyproject , "build-system.build-backend" )
106+ if build_backend == "pdm.backend" :
107+ return Backend .PDM_BACKEND
108+ return Backend .HATCHLING
108109
109- def _get_table (data : dict [str , Any ], key : str ) -> dict [str , Any ] | None :
110- """Get config data from dotted key.
111110
112- Returns ``None`` if no data exists at the given dotted key.
113- """
114- try :
115- for part in key .split ("." ):
116- data = data [part ]
117- except KeyError :
118- return None
119- return data
111+ def _fail (msg : str ) -> NoReturn :
112+ print (msg , file = sys .stderr )
113+ sys .exit (1 )
0 commit comments