11"""Everything needed to build a CPPython project
22"""
33
4- from dataclasses import dataclass
54from importlib import metadata
5+ from inspect import getmodule
66from logging import Logger
77from pathlib import Path
88from typing import Any
2525 CorePluginData ,
2626 CPPythonGlobalConfiguration ,
2727 CPPythonLocalConfiguration ,
28+ DataPluginT ,
2829 PEP621Configuration ,
2930 ProjectConfiguration ,
3031)
3132
3233
33- @dataclass
34- class GeneratorInformation :
35- """Data that the builder outputs about plugins"""
36-
37- plugin_type : type [Generator ]
38- entry : metadata .EntryPoint
39-
40-
4134class Builder :
4235 """Helper class for building CPPython projects"""
4336
@@ -126,11 +119,40 @@ def extract_scm_version(self, path: Path) -> str:
126119
127120 return version
128121
129- def find_generator (self , core_data : CoreData ) -> GeneratorInformation :
122+ def find_generators (self ) -> list [ type [ Generator ]] :
130123 """_summary_
131124
132- Args:
133- core_data: _description_
125+ Raises:
126+ PluginError: _description_
127+
128+ Returns:
129+ _description_
130+ """
131+
132+ group_name = "generator"
133+ plugin_types : list [type [Generator ]] = []
134+
135+ # Filter entries by type
136+ for entry_point in list (metadata .entry_points (group = f"cppython.{ group_name } " )):
137+ loaded_type = entry_point .load ()
138+ if not issubclass (loaded_type , Generator ):
139+ self .logger .warning (
140+ f"Found incompatible plugin. The '{ resolve_name (loaded_type )} ' plugin must be an instance of"
141+ f" '{ group_name } '"
142+ )
143+ else :
144+ self .logger .warning (
145+ f"{ group_name } plugin found: { resolve_name (loaded_type )} from { getmodule (loaded_type )} "
146+ )
147+ plugin_types .append (loaded_type )
148+
149+ if not plugin_types :
150+ raise PluginError (f"No { group_name } plugin was found" )
151+
152+ return plugin_types
153+
154+ def find_providers (self ) -> list [type [Provider ]]:
155+ """_summary_
134156
135157 Raises:
136158 PluginError: _description_
@@ -139,59 +161,113 @@ def find_generator(self, core_data: CoreData) -> GeneratorInformation:
139161 _description_
140162 """
141163
142- group = "Generator "
143- generator_info : list [tuple [ type [Generator ], metadata . EntryPoint ]] = []
164+ group_name = "provider "
165+ plugin_types : list [type [Provider ]] = []
144166
145- # Filter entries
146- for entry_point in list (metadata .entry_points (group = f"cppython.{ group } " )):
147- plugin_type = entry_point .load ()
148- if not issubclass (plugin_type , Generator ):
167+ # Filter entries by type
168+ for entry_point in list (metadata .entry_points (group = f"cppython.{ group_name } " )):
169+ loaded_type = entry_point .load ()
170+ if not issubclass (loaded_type , Provider ):
149171 self .logger .warning (
150- f"Found incompatible plugin. The '{ type ( plugin_type ). __name__ } ' plugin must be an instance of"
151- f" '{ group } '"
172+ f"Found incompatible plugin. The '{ resolve_name ( loaded_type ) } ' plugin must be an instance of"
173+ f" '{ group_name } '"
152174 )
153175 else :
154- self .logger .warning ("Generator plugin found: %s" , resolve_name (plugin_type ))
155- generator_info .append ((plugin_type , entry_point ))
156-
157- if not generator_info :
158- raise PluginError ("No generator plugin was found" )
159-
160- # Lookup the requested generator if given
161- supported_plugin_info = None
162- if core_data .cppython_data .generator_name is not None :
163- for plugin_type , entry in generator_info :
164- if resolve_name (plugin_type ) == core_data .cppython_data .generator_name :
165- supported_plugin_info = plugin_type , entry
166- break
167-
168- # Try and deduce generator
169- if supported_plugin_info is None :
170- for plugin_type , entry in generator_info :
171- if plugin_type .supported (core_data .project_data .pyproject_file .parent ):
172- supported_plugin_info = plugin_type , entry
173- break
176+ self .logger .warning (
177+ f"{ group_name } plugin found: { resolve_name (loaded_type )} from { getmodule (loaded_type )} "
178+ )
179+ plugin_types .append (loaded_type )
180+
181+ if not plugin_types :
182+ raise PluginError (f"No { group_name } plugin was found" )
183+
184+ return plugin_types
185+
186+ def filter_plugins (
187+ self , plugin_types : list [type [DataPluginT ]], directory : Path , pinned_name : str | None , group_name : str
188+ ) -> list [type [DataPluginT ]]:
189+ """Finds and filters data plugins
190+
191+ Args:
192+ plugin_types: The plugin type to lookup
193+ directory: The data to query support for the filtered plugins
194+ pinned_name: The configuration name
195+ group_name: The group name
196+
197+ Raises:
198+ PluginError: Raised if no plugins can be found
199+
200+ Returns:
201+ The list of applicable plugins
202+ """
203+
204+ # Lookup the requested plugin if given
205+ if pinned_name is not None :
206+ for loaded_type in plugin_types :
207+ if resolve_name (loaded_type ) == pinned_name :
208+ self .logger .warning (
209+ f"Using { group_name } plugin: { resolve_name (loaded_type )} from { getmodule (loaded_type )} "
210+ )
211+ return [loaded_type ]
212+
213+ self .logger .warning (f"'{ group_name } _name' was empty. Trying to deduce { group_name } s" )
214+
215+ supported_types : list [type [DataPluginT ]] = []
216+
217+ # Deduce types
218+ for loaded_type in plugin_types :
219+ if loaded_type .supported (directory ):
220+ self .logger .warning (
221+ f"A { group_name } plugin is supported: { resolve_name (loaded_type )} from { getmodule (loaded_type )} "
222+ )
223+ supported_types .append (loaded_type )
174224
175225 # Fail
176- if supported_plugin_info is None :
177- raise PluginError (
178- "The 'generator_name' was empty and no generator could be deduced from the root directory."
179- )
226+ if supported_types is None :
227+ raise PluginError (f"No { group_name } could be deduced from the root directory." )
228+
229+ return supported_types
230+
231+ def solve (
232+ self , generator_types : list [type [Generator ]], provider_types : list [type [Provider ]]
233+ ) -> tuple [type [Generator ], type [Provider ]]:
234+ """_summary_
235+
236+ Args:
237+ generator_types: _description_
238+ provider_types: _description_
239+
240+ Raises:
241+ PluginError: _description_
242+
243+ Returns:
244+ _description_
245+ """
246+
247+ combos : list [tuple [type [Generator ], type [Provider ]]] = []
180248
181- supported_plugin_type , supported_plugin_entry = supported_plugin_info
182- self .logger .warning ("Using generator plugin: '%s'" , resolve_name (supported_plugin_type ))
249+ for generator_type in generator_types :
250+ sync_types = generator_type .sync_types ()
251+ for provider_type in provider_types :
252+ for sync_type in sync_types :
253+ if provider_type .supported_sync_type (sync_type ):
254+ combos .append ((generator_type , provider_type ))
255+ break
183256
184- return GeneratorInformation (plugin_type = supported_plugin_type , entry = supported_plugin_entry )
257+ if not combos :
258+ raise PluginError ("No provider that supports a given generator could be deduced" )
259+
260+ return combos [0 ]
185261
186262 def create_generator (
187- self , core_data : CoreData , generator_configuration : dict [str , Any ], plugin_info : GeneratorInformation
263+ self , core_data : CoreData , generator_configuration : dict [str , Any ], generator_type : type [ Generator ]
188264 ) -> Generator :
189265 """Creates a generator from input configuration
190266
191267 Args:
192268 core_data: The resolved configuration data
193269 generator_configuration: The generator table of the CPPython configuration data
194- plugin_info : The plugin information
270+ generator_type : The plugin type
195271
196272 Raises:
197273 PluginError: Raised if no viable generator plugin was found
@@ -201,8 +277,7 @@ def create_generator(
201277 """
202278
203279 generator_data = resolve_generator (core_data .project_data )
204-
205- cppython_plugin_data = resolve_cppython_plugin (core_data .cppython_data , plugin_info .plugin_type )
280+ cppython_plugin_data = resolve_cppython_plugin (core_data .cppython_data , generator_type )
206281
207282 core_plugin_data = CorePluginData (
208283 project_data = core_data .project_data ,
@@ -215,66 +290,27 @@ def create_generator(
215290 "The pyproject.toml table 'tool.cppython.generator' does not exist. Sending generator empty data" ,
216291 )
217292
218- return plugin_info . plugin_type (generator_data , core_plugin_data , generator_configuration )
293+ return generator_type (generator_data , core_plugin_data , generator_configuration )
219294
220- def create_provider (self , core_data : CoreData , provider_configuration : dict [str , Any ]) -> Provider :
295+ def create_provider (
296+ self , core_data : CoreData , provider_configuration : dict [str , Any ], provider_type : type [Provider ]
297+ ) -> Provider :
221298 """Creates Providers from input data
222299
223300 Args:
224301 core_data: The resolved configuration data
225302 provider_configuration: The provider data table
303+ provider_type: The type to instantiate
226304
227305 Raises:
228- PluginError: Raised if no viable generator plugin was found
306+ PluginError: Raised if no viable provider plugin was found
229307
230308 Returns:
231309 A constructed provider plugins
232310 """
233311
234- group = "Provider"
235-
236- entries = list (metadata .entry_points (group = f"cppython.{ group } " ))
237- provider_info : list [type [Provider ]] = []
238-
239- # Filter entries
240- for entry_point in entries :
241- plugin_type = entry_point .load ()
242- if not issubclass (plugin_type , Provider ):
243- self .logger .warning (
244- f"Found incompatible plugin. The '{ resolve_name (plugin_type )} ' plugin must be an instance of"
245- f" '{ group } '"
246- )
247- else :
248- self .logger .warning ("Provider plugin found: %s" , resolve_name (plugin_type ))
249- provider_info .append (plugin_type )
250-
251- if not provider_info :
252- raise PluginError ("No provider_types plugin was found" )
253-
254- # Lookup the requested provider if given
255- supported_plugin_type : type [Provider ] | None = None
256- if core_data .cppython_data .provider_name is not None :
257- for plugin_type in provider_info :
258- if resolve_name (plugin_type ) == core_data .cppython_data .provider_name :
259- supported_plugin_type = plugin_type
260- break
261-
262- # Try and deduce provider
263- if supported_plugin_type is None :
264- for plugin_type in provider_info :
265- if plugin_type .supported (core_data .project_data .pyproject_file .parent ):
266- supported_plugin_type = plugin_type
267- break
268-
269- # Fail
270- if supported_plugin_type is None :
271- raise PluginError ("The 'provider_name' was empty and no provider could be deduced from the root directory." )
272-
273- self .logger .warning ("Using provider plugin: '%s'" , resolve_name (supported_plugin_type ))
274-
275- provider_data = resolve_provider (core_data .project_data , core_data .cppython_data )
276-
277- cppython_plugin_data = resolve_cppython_plugin (core_data .cppython_data , supported_plugin_type )
312+ provider_data = resolve_provider (core_data .project_data )
313+ cppython_plugin_data = resolve_cppython_plugin (core_data .cppython_data , provider_type )
278314
279315 core_plugin_data = CorePluginData (
280316 project_data = core_data .project_data ,
@@ -287,4 +323,4 @@ def create_provider(self, core_data: CoreData, provider_configuration: dict[str,
287323 "The pyproject.toml table 'tool.cppython.provider' does not exist. Sending provider empty data" ,
288324 )
289325
290- return supported_plugin_type (provider_data , core_plugin_data , provider_configuration )
326+ return provider_type (provider_data , core_plugin_data , provider_configuration )
0 commit comments