2727#
2828
2929# stdlib
30+ import re
3031from typing import Any , Dict , Iterator , List , Mapping , MutableMapping , Optional
3132
3233# 3rd party
4344__version__ : str = "0.1.0"
4445__email__ : str = "dominic@davis-foster.co.uk"
4546
46- __all__ = ["SphinxConfig" , "ProjectParser" ]
47+ __all__ = ["SphinxConfig" , "ProjectParser" , "PoetryProjectParser" ]
4748
4849
4950class SphinxConfig (Mapping [str , Any ]):
@@ -55,6 +56,9 @@ class SphinxConfig(Mapping[str, Any]):
5556 The variables parsed from the ``[tool.sphinx-pyproject]`` table will be added to this namespace.
5657 By default, or if explicitly :py:obj:`None`, this does not happen.
5758 :no-default globalns:
59+ :param style: Either ``pep621`` (default), or ``poetry`` to read configuration from the ``[tool.poetry]`` table.
60+ :no-default style:
61+
5862
5963 .. autosummary-widths:: 1/4
6064 """
@@ -122,15 +126,19 @@ def __init__(
122126 pyproject_file : PathLike = "../pyproject.toml" ,
123127 * ,
124128 globalns : Optional [MutableMapping ] = None ,
129+ style : str = "pep621" ,
125130 ):
126131
127132 pyproject_file = PathPlus (pyproject_file ).abspath ()
128133 config = dom_toml .load (pyproject_file , decoder = TomlPureDecoder )
129134
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 } " )
132139
133- pep621_config = ProjectParser ().parse (config ["project" ])
140+ namespace = parser_cls .get_namespace (pyproject_file , config )
141+ pep621_config = parser_cls ().parse (namespace )
134142
135143 for key in ("name" , "version" , "description" ):
136144 if key not in pep621_config :
@@ -191,6 +199,22 @@ class ProjectParser(AbstractConfigParser):
191199 .. autosummary-widths:: 7/16
192200 """
193201
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+
194218 def parse_name (self , config : Dict [str , TOML_TYPES ]) -> str :
195219 """
196220 Parse the :pep621:`name` key.
@@ -273,10 +297,57 @@ def parse(
273297 :param config:
274298 :param set_defaults: Has no effect in this class.
275299 """
276-
277300 if "authors" in config :
278301 config ["author" ] = config .pop ("authors" )
279302 elif "maintainers" in config :
280303 config ["author" ] = config .pop ("maintainers" )
281304
282305 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