27
27
#
28
28
29
29
# stdlib
30
+ import re
30
31
from typing import Any , Dict , Iterator , List , Mapping , MutableMapping , Optional
31
32
32
33
# 3rd party
43
44
__version__ : str = "0.1.0"
44
45
__email__ :
str = "[email protected] "
45
46
46
- __all__ = ["SphinxConfig" , "ProjectParser" ]
47
+ __all__ = ["SphinxConfig" , "ProjectParser" , "PoetryProjectParser" ]
47
48
48
49
49
50
class SphinxConfig (Mapping [str , Any ]):
@@ -55,6 +56,9 @@ class SphinxConfig(Mapping[str, Any]):
55
56
The variables parsed from the ``[tool.sphinx-pyproject]`` table will be added to this namespace.
56
57
By default, or if explicitly :py:obj:`None`, this does not happen.
57
58
:no-default globalns:
59
+ :param style: Either ``pep621`` (default), or ``poetry`` to read configuration from the ``[tool.poetry]`` table.
60
+ :no-default style:
61
+
58
62
59
63
.. autosummary-widths:: 1/4
60
64
"""
@@ -122,15 +126,19 @@ def __init__(
122
126
pyproject_file : PathLike = "../pyproject.toml" ,
123
127
* ,
124
128
globalns : Optional [MutableMapping ] = None ,
129
+ style : str = "pep621" ,
125
130
):
126
131
127
132
pyproject_file = PathPlus (pyproject_file ).abspath ()
128
133
config = dom_toml .load (pyproject_file , decoder = TomlPureDecoder )
129
134
130
- if "project" not in config :
131
- raise BadConfigError (f"No 'project' table found in { pyproject_file .as_posix ()} " )
135
+ parser_cls = project_parser_styles .get (style )
136
+ if parser_cls is None :
137
+ styles = ", " .join (project_parser_styles )
138
+ raise ValueError (f"'style' argument must be one of: { styles } " )
132
139
133
- pep621_config = ProjectParser ().parse (config ["project" ])
140
+ namespace = parser_cls .get_namespace (pyproject_file , config )
141
+ pep621_config = parser_cls ().parse (namespace )
134
142
135
143
for key in ("name" , "version" , "description" ):
136
144
if key not in pep621_config :
@@ -191,6 +199,22 @@ class ProjectParser(AbstractConfigParser):
191
199
.. autosummary-widths:: 7/16
192
200
"""
193
201
202
+ @staticmethod
203
+ def get_namespace (filename : PathPlus , config : Dict [str , TOML_TYPES ]) -> Dict [str , TOML_TYPES ]:
204
+ """
205
+ Returns the ``[project]`` table in a ``project.toml`` file.
206
+
207
+ :param filename: The filename the TOML data was read from. Used in error messages.
208
+ :param config: The data from the TOML file.
209
+
210
+ .. versionadded:: 0.2.0
211
+ """
212
+
213
+ if "project" not in config :
214
+ raise BadConfigError (f"No 'project' table found in { filename .as_posix ()} " )
215
+
216
+ return config ["project" ]
217
+
194
218
def parse_name (self , config : Dict [str , TOML_TYPES ]) -> str :
195
219
"""
196
220
Parse the :pep621:`name` key.
@@ -273,10 +297,57 @@ def parse(
273
297
:param config:
274
298
:param set_defaults: Has no effect in this class.
275
299
"""
276
-
277
300
if "authors" in config :
278
301
config ["author" ] = config .pop ("authors" )
279
302
elif "maintainers" in config :
280
303
config ["author" ] = config .pop ("maintainers" )
281
304
282
305
return super ().parse (config )
306
+
307
+
308
+ class PoetryProjectParser (ProjectParser ):
309
+ """
310
+ Parser for poetry metadata from ``pyproject.toml``.
311
+
312
+ .. versionadded:: 0.2.0
313
+ """
314
+
315
+ @staticmethod
316
+ def get_namespace (filename : PathPlus , config : Dict [str , TOML_TYPES ]) -> Dict [str , TOML_TYPES ]:
317
+ """
318
+ Returns the ``[tool.poetry]`` table in a ``project.toml`` file.
319
+
320
+ :param filename: The filename the TOML data was read from. Used in error messages.
321
+ :param config: The data from the TOML file.
322
+ """
323
+
324
+ result = config .get ("tool" , {}).get ("poetry" )
325
+ if result is None :
326
+ raise BadConfigError (f"No 'tool.poetry' table found in { filename .as_posix ()} " )
327
+
328
+ return result
329
+
330
+ @staticmethod
331
+ def parse_author (config : Dict [str , TOML_TYPES ]) -> str :
332
+ """
333
+ Parse poetry's authors key.
334
+
335
+ :param config: The unparsed TOML config for the ``[tool.poetry]`` table.
336
+ """
337
+
338
+ pep621_style_authors : List [Dict [str , str ]] = []
339
+
340
+ for author in config ["author" ]:
341
+ match = re .match (r"(?P<name>.*)<(?P<email>.*)>" , author )
342
+ if match :
343
+ name = match .group ("name" ).strip ()
344
+ email = match .group ("email" ).strip ()
345
+ pep621_style_authors .append ({"name" : name , "email" : email })
346
+
347
+ return ProjectParser .parse_author ({"author" : pep621_style_authors })
348
+
349
+
350
+ project_parser_styles = {
351
+ "pep621" : ProjectParser ,
352
+ "poetry" : PoetryProjectParser ,
353
+ }
0 commit comments