@@ -654,6 +654,36 @@ def build_editable(self, directory: Path, verbose: bool = False) -> pathlib.Path
654654 return wheel_file
655655
656656
657+ def _validate_pyproject_config (pyproject : Dict [str , Any ]) -> Dict [str , Any ]:
658+
659+ def _table (scheme : Dict [str , Callable [[Any , str ], Any ]]) -> Callable [[Any , str ], Dict [str , Any ]]:
660+ def func (value : Any , name : str ) -> Dict [str , Any ]:
661+ if not isinstance (value , dict ):
662+ raise ConfigError (f'configuration entry "{ name } " must be a table' )
663+ table = {}
664+ for key , val in value .items ():
665+ check = scheme .get (key )
666+ if check is None :
667+ raise ConfigError (f'unknown configuration entry "{ name } .{ key } "' )
668+ table [key ] = check (val , f'{ name } .{ key } ' )
669+ return table
670+ return func
671+
672+ def _strings (value : Any , name : str ) -> List [str ]:
673+ if not isinstance (value , list ) or not all (isinstance (x , str ) for x in value ):
674+ raise ConfigError (f'configuration entry "{ name } " must be a list of strings' )
675+ return value
676+
677+ scheme = _table ({
678+ 'args' : _table ({
679+ name : _strings for name in _MESON_ARGS_KEYS
680+ })
681+ })
682+
683+ table = pyproject .get ('tool' , {}).get ('meson-python' , {})
684+ return scheme (table , 'tool.meson-python' )
685+
686+
657687class Project ():
658688 """Meson project wrapper to generate Python artifacts."""
659689
@@ -662,7 +692,7 @@ class Project():
662692 ]
663693 _metadata : Optional [pyproject_metadata .StandardMetadata ]
664694
665- def __init__ ( # noqa: C901
695+ def __init__ (
666696 self ,
667697 source_dir : Path ,
668698 working_dir : Path ,
@@ -712,11 +742,13 @@ def __init__( # noqa: C901
712742 self ._meson_cross_file .write_text (cross_file_data )
713743 self ._meson_args ['setup' ].extend (('--cross-file' , os .fspath (self ._meson_cross_file )))
714744
715- # load config -- PEP 621 support is optional
716- self ._config = tomllib .loads (self ._source_dir .joinpath ('pyproject.toml' ).read_text ())
717- self ._pep621 = 'project' in self ._config
745+ # load pyproject.toml
746+ pyproject = tomllib .loads (self ._source_dir .joinpath ('pyproject.toml' ).read_text ())
747+
748+ # package metadata
749+ self ._pep621 = 'project' in pyproject :
718750 if self .pep621 :
719- self ._metadata = pyproject_metadata .StandardMetadata .from_pyproject (self . _config , self ._source_dir )
751+ self ._metadata = pyproject_metadata .StandardMetadata .from_pyproject (pyproject , self ._source_dir )
720752 else :
721753 print (
722754 '{yellow}{bold}! Using Meson to generate the project metadata '
@@ -727,14 +759,10 @@ def __init__( # noqa: C901
727759 if self ._metadata :
728760 self ._validate_metadata ()
729761
730- # load meson args
731- for key in self ._get_config_key ('args' ):
732- self ._meson_args [key ].extend (self ._get_config_key (f'args.{ key } ' ))
733- # XXX: We should validate the user args to make sure they don't conflict with ours.
734-
735- self ._check_for_unknown_config_keys ({
736- 'args' : _MESON_ARGS_KEYS ,
737- })
762+ # load meson args from pyproject.toml
763+ pyproject_config = _validate_pyproject_config (pyproject )
764+ for key , value in pyproject_config .get ('args' , {}).items ():
765+ self ._meson_args [key ].extend (value )
738766
739767 # meson arguments from the command line take precedence over
740768 # arguments from the configuration file thus are added later
@@ -768,14 +796,6 @@ def __init__( # noqa: C901
768796 if self ._metadata and 'version' in self ._metadata .dynamic :
769797 self ._metadata .version = self .version
770798
771- def _get_config_key (self , key : str ) -> Any :
772- value : Any = self ._config
773- for part in f'tool.meson-python.{ key } ' .split ('.' ):
774- if not isinstance (value , Mapping ):
775- raise ConfigError (f'Configuration entry "tool.meson-python.{ key } " should be a TOML table not { type (value )} ' )
776- value = value .get (part , {})
777- return value
778-
779799 def _run (self , cmd : Sequence [str ]) -> None :
780800 """Invoke a subprocess."""
781801 print ('{cyan}{bold}+ {}{reset}' .format (' ' .join (cmd ), ** _STYLES ))
@@ -834,17 +854,6 @@ def _validate_metadata(self) -> None:
834854 f'expected { self ._metadata .requires_python } '
835855 )
836856
837- def _check_for_unknown_config_keys (self , valid_args : Mapping [str , Collection [str ]]) -> None :
838- config = self ._config .get ('tool' , {}).get ('meson-python' , {})
839-
840- for key , valid_subkeys in config .items ():
841- if key not in valid_args :
842- raise ConfigError (f'Unknown configuration key "tool.meson-python.{ key } "' )
843-
844- for subkey in valid_args [key ]:
845- if subkey not in valid_subkeys :
846- raise ConfigError (f'Unknown configuration key "tool.meson-python.{ key } .{ subkey } "' )
847-
848857 @cached_property
849858 def _wheel_builder (self ) -> _WheelBuilder :
850859 return _WheelBuilder (
0 commit comments