Skip to content

Commit ce2b2ff

Browse files
authored
Builder CoreData Refactor (#105)
1 parent 01166e1 commit ce2b2ff

4 files changed

Lines changed: 189 additions & 125 deletions

File tree

cppython/builder.py

Lines changed: 131 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""Everything needed to build a CPPython project
22
"""
33

4+
import logging
45
from importlib import metadata
56
from inspect import getmodule
67
from logging import Logger
7-
from pathlib import Path
88
from typing import Any
99

10-
from cppython_core.exceptions import ConfigError, PluginError
10+
from cppython_core.exceptions import PluginError
1111
from cppython_core.plugin_schema.generator import Generator
1212
from cppython_core.plugin_schema.provider import Provider
1313
from cppython_core.plugin_schema.scm import SCM
@@ -20,15 +20,17 @@
2020
resolve_pep621,
2121
resolve_project_configuration,
2222
resolve_provider,
23+
resolve_scm,
2324
)
2425
from cppython_core.schema import (
2526
CoreData,
2627
CorePluginData,
2728
CPPythonGlobalConfiguration,
28-
CPPythonLocalConfiguration,
2929
DataPluginT,
30-
PEP621Configuration,
30+
PEP621Data,
3131
ProjectConfiguration,
32+
ProjectData,
33+
PyProject,
3234
)
3335

3436

@@ -38,90 +40,104 @@ class Builder:
3840
def __init__(self, logger: Logger) -> None:
3941
self.logger = logger
4042

41-
def generate_core_data(
42-
self,
43-
configuration: ProjectConfiguration,
44-
pep621_configuration: PEP621Configuration,
45-
cppython_configuration: CPPythonLocalConfiguration,
46-
plugin_build_date: PluginBuildData,
47-
) -> CoreData:
48-
"""Parses and returns resolved data from all configuration sources
43+
def setup_logger(self, project_configuration: ProjectConfiguration) -> None:
44+
"""_summary_
4945
5046
Args:
51-
configuration: Input configuration
52-
pep621_configuration: Project table configuration
53-
cppython_configuration: Tool configuration
54-
plugin_build_date: Build data
47+
project_configuration: _description_
48+
"""
49+
# Default logging levels
50+
levels = [logging.WARNING, logging.INFO, logging.DEBUG]
5551

56-
Raises:
57-
ConfigError: Raised if data cannot be parsed
52+
# Add default output stream
53+
self.logger.addHandler(logging.StreamHandler())
54+
self.logger.setLevel(levels[project_configuration.verbosity])
55+
56+
self.logger.info("Logging setup complete")
57+
58+
def generate_project_data(self, project_configuration: ProjectConfiguration) -> ProjectData:
59+
"""_summary_
60+
61+
Args:
62+
project_configuration: _description_
5863
5964
Returns:
60-
The resolved core object
65+
_description_
6166
"""
6267

63-
global_configuration = CPPythonGlobalConfiguration()
68+
return resolve_project_configuration(project_configuration)
6469

65-
project_data = resolve_project_configuration(configuration)
70+
def generate_data_plugins(self, pyproject: PyProject) -> PluginBuildData:
71+
"""_summary_
6672
67-
try:
68-
pep621_data = resolve_pep621(pep621_configuration, configuration)
73+
Args:
74+
pyproject: _description_
6975
70-
except ConfigError:
71-
configuration.version = self.extract_scm_version(configuration.pyproject_file.parent)
72-
pep621_data = resolve_pep621(pep621_configuration, configuration)
76+
Returns:
77+
_description_
78+
"""
7379

74-
cppython_data = resolve_cppython(cppython_configuration, global_configuration, project_data, plugin_build_date)
80+
raw_generator_plugins = self.find_generators()
81+
generator_plugins = self.filter_plugins(
82+
raw_generator_plugins,
83+
pyproject.tool.cppython.generator_name,
84+
"Generator",
85+
)
7586

76-
return CoreData(project_data=project_data, pep621_data=pep621_data, cppython_data=cppython_data)
87+
raw_provider_plugins = self.find_providers()
88+
provider_plugins = self.filter_plugins(
89+
raw_provider_plugins,
90+
pyproject.tool.cppython.provider_name,
91+
"Provider",
92+
)
7793

78-
def extract_scm_version(self, path: Path) -> str:
79-
"""Locates an available SCM plugin that can report version information about the given path
94+
# Solve the messy interactions between plugins
95+
generator_type, provider_type = self.solve(generator_plugins, provider_plugins)
8096

81-
Args:
82-
path: The directory to query
97+
return PluginBuildData(generator_type=generator_type, provider_type=provider_type)
8398

84-
Raises:
85-
PluginError: If no SCM plugin can be found
99+
def generate_pep621_data(
100+
self, pyproject: PyProject, project_configuration: ProjectConfiguration, scm: SCM | None
101+
) -> PEP621Data:
102+
"""_summary_
103+
104+
Args:
105+
pyproject: _description_
106+
project_configuration: _description_
107+
scm: _description_
86108
87109
Returns:
88-
A version token
110+
_description_
89111
"""
112+
return resolve_pep621(pyproject.project, project_configuration, scm)
90113

91-
group = "SCM"
92-
group_lower = group.lower()
93-
94-
scm_types: list[type[SCM]] = []
114+
def generate_core_data(
115+
self,
116+
project_data: ProjectData,
117+
pyproject: PyProject,
118+
pep621_data: PEP621Data,
119+
plugin_build_date: PluginBuildData,
120+
) -> CoreData:
121+
"""Parses and returns resolved data from all configuration sources
95122
96-
if not (entries := list(metadata.entry_points(group=f"cppython.{group_lower}"))):
97-
raise PluginError("No SCM plugin found")
123+
Args:
124+
project_data: Project data
125+
pyproject: TODO
126+
pep621_data: TODO
127+
plugin_build_date: TODO
98128
99-
# Filter entries
100-
for entry_point in entries:
101-
plugin_type = entry_point.load()
102-
if not issubclass(plugin_type, SCM):
103-
self.logger.warning(
104-
f"Found incompatible plugin. The '{resolve_name(plugin_type)}' plugin must be an instance of"
105-
f" '{group_lower}'"
106-
)
107-
else:
108-
scm_types.append(plugin_type)
129+
Raises:
130+
ConfigError: Raised if data cannot be parsed
109131
110-
# Deduce the SCM repository
111-
plugin = None
112-
for scm_type in scm_types:
113-
scm = scm_type()
114-
if scm.supported(path):
115-
plugin = scm
116-
break
132+
Returns:
133+
The resolved core object
134+
"""
117135

118-
if not plugin:
119-
raise PluginError("No applicable SCM plugin found for the given path")
136+
global_configuration = CPPythonGlobalConfiguration()
120137

121-
if (version := plugin.version(path)) is None:
122-
raise PluginError("Project has no version information")
138+
cppython_data = resolve_cppython(pyproject.tool.cppython, global_configuration, project_data, plugin_build_date)
123139

124-
return version
140+
return CoreData(project_data=project_data, pep621_data=pep621_data, cppython_data=cppython_data)
125141

126142
def find_generators(self) -> list[type[Generator]]:
127143
"""_summary_
@@ -188,13 +204,12 @@ def find_providers(self) -> list[type[Provider]]:
188204
return plugin_types
189205

190206
def filter_plugins(
191-
self, plugin_types: list[type[DataPluginT]], directory: Path, pinned_name: str | None, group_name: str
207+
self, plugin_types: list[type[DataPluginT]], pinned_name: str | None, group_name: str
192208
) -> list[type[DataPluginT]]:
193209
"""Finds and filters data plugins
194210
195211
Args:
196212
plugin_types: The plugin type to lookup
197-
directory: The data to query support for the filtered plugins
198213
pinned_name: The configuration name
199214
group_name: The group name
200215
@@ -220,11 +235,10 @@ def filter_plugins(
220235

221236
# Deduce types
222237
for loaded_type in plugin_types:
223-
if loaded_type.supported(directory):
224-
self.logger.warning(
225-
f"A {group_name} plugin is supported: {resolve_name(loaded_type)} from {getmodule(loaded_type)}"
226-
)
227-
supported_types.append(loaded_type)
238+
self.logger.warning(
239+
f"A {group_name} plugin is supported: {resolve_name(loaded_type)} from {getmodule(loaded_type)}"
240+
)
241+
supported_types.append(loaded_type)
228242

229243
# Fail
230244
if supported_types is None:
@@ -263,6 +277,53 @@ def solve(
263277

264278
return combos[0]
265279

280+
def create_scm(
281+
self,
282+
project_data: ProjectData,
283+
) -> SCM | None:
284+
"""_summary_
285+
286+
Args:
287+
project_data: _description_
288+
289+
Raises:
290+
PluginError: Ya
291+
292+
Returns:
293+
_description_
294+
"""
295+
group = "scm"
296+
path = project_data.pyproject_file.parent
297+
298+
scm_types: list[type[SCM]] = []
299+
300+
if not (entries := list(metadata.entry_points(group=f"cppython.{group}"))):
301+
raise PluginError("No SCM plugin found")
302+
303+
# Filter entries
304+
for entry_point in entries:
305+
plugin_type = entry_point.load()
306+
if not issubclass(plugin_type, SCM):
307+
self.logger.warning(
308+
f"Found incompatible plugin. The '{resolve_name(plugin_type)}' plugin must be an instance of"
309+
f" '{group}'"
310+
)
311+
else:
312+
scm_types.append(plugin_type)
313+
314+
# Deduce the SCM repository
315+
plugin = None
316+
for scm_type in scm_types:
317+
if scm_type.features(path).repository:
318+
scm_data = resolve_scm(project_data)
319+
plugin = scm_type(scm_data)
320+
break
321+
322+
if not plugin:
323+
self.logger.error("No applicable SCM plugin found for the given path")
324+
325+
return plugin
326+
266327
def create_generator(
267328
self, core_data: CoreData, generator_configuration: dict[str, Any], generator_type: type[Generator]
268329
) -> Generator:

cppython/plugins/git.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
from pathlib import Path
44

5-
from cppython_core.plugin_schema.scm import SCM
5+
from cppython_core.plugin_schema.scm import (
6+
SCM,
7+
SCMPluginGroupData,
8+
SupportedSCMFeatures,
9+
)
610
from cppython_core.schema import Information
711
from dulwich.errors import NotGitRepository
812
from dulwich.repo import Repo
@@ -11,23 +15,27 @@
1115
class GitSCM(SCM):
1216
"""Git implementation hooks"""
1317

18+
def __init__(self, group_data: SCMPluginGroupData) -> None:
19+
self.group_data = group_data
20+
1421
@staticmethod
15-
def supported(directory: Path) -> bool:
16-
"""Queries repository status of a path
22+
def features(directory: Path) -> SupportedSCMFeatures:
23+
"""Broadcasts the shared features of the SCM plugin to CPPython
1724
1825
Args:
19-
directory: The input path to query
26+
directory: The root directory where features are evaluated
2027
2128
Returns:
22-
Whether the given path is a repository root
29+
The supported features
2330
"""
2431

32+
is_repository = True
2533
try:
2634
Repo(str(directory))
27-
return True
28-
2935
except NotGitRepository:
30-
return False
36+
is_repository = False
37+
38+
return SupportedSCMFeatures(repository=is_repository)
3139

3240
@staticmethod
3341
def information() -> Information:
@@ -38,11 +46,11 @@ def information() -> Information:
3846
"""
3947
return Information()
4048

41-
def version(self, path: Path) -> str:
49+
def version(self, directory: Path) -> str:
4250
"""Extracts the system's version metadata
4351
4452
Args:
45-
path: The repository path
53+
directory: The repository path
4654
4755
Returns:
4856
The git version

0 commit comments

Comments
 (0)