cppython
Advanced tools
+131
-70
| """Everything needed to build a CPPython project | ||
| """ | ||
| import logging | ||
| from importlib import metadata | ||
| from inspect import getmodule | ||
| from logging import Logger | ||
| from pathlib import Path | ||
| from typing import Any | ||
| from cppython_core.exceptions import ConfigError, PluginError | ||
| from cppython_core.exceptions import PluginError | ||
| from cppython_core.plugin_schema.generator import Generator | ||
@@ -23,2 +23,3 @@ from cppython_core.plugin_schema.provider import Provider | ||
| resolve_provider, | ||
| resolve_scm, | ||
| ) | ||
@@ -29,6 +30,7 @@ from cppython_core.schema import ( | ||
| CPPythonGlobalConfiguration, | ||
| CPPythonLocalConfiguration, | ||
| DataPluginT, | ||
| PEP621Configuration, | ||
| PEP621Data, | ||
| ProjectConfiguration, | ||
| ProjectData, | ||
| PyProject, | ||
| ) | ||
@@ -43,87 +45,101 @@ | ||
| def generate_core_data( | ||
| self, | ||
| configuration: ProjectConfiguration, | ||
| pep621_configuration: PEP621Configuration, | ||
| cppython_configuration: CPPythonLocalConfiguration, | ||
| plugin_build_date: PluginBuildData, | ||
| ) -> CoreData: | ||
| """Parses and returns resolved data from all configuration sources | ||
| def setup_logger(self, project_configuration: ProjectConfiguration) -> None: | ||
| """_summary_ | ||
| Args: | ||
| configuration: Input configuration | ||
| pep621_configuration: Project table configuration | ||
| cppython_configuration: Tool configuration | ||
| plugin_build_date: Build data | ||
| project_configuration: _description_ | ||
| """ | ||
| # Default logging levels | ||
| levels = [logging.WARNING, logging.INFO, logging.DEBUG] | ||
| Raises: | ||
| ConfigError: Raised if data cannot be parsed | ||
| # Add default output stream | ||
| self.logger.addHandler(logging.StreamHandler()) | ||
| self.logger.setLevel(levels[project_configuration.verbosity]) | ||
| self.logger.info("Logging setup complete") | ||
| def generate_project_data(self, project_configuration: ProjectConfiguration) -> ProjectData: | ||
| """_summary_ | ||
| Args: | ||
| project_configuration: _description_ | ||
| Returns: | ||
| The resolved core object | ||
| _description_ | ||
| """ | ||
| global_configuration = CPPythonGlobalConfiguration() | ||
| return resolve_project_configuration(project_configuration) | ||
| project_data = resolve_project_configuration(configuration) | ||
| def generate_data_plugins(self, pyproject: PyProject) -> PluginBuildData: | ||
| """_summary_ | ||
| try: | ||
| pep621_data = resolve_pep621(pep621_configuration, configuration) | ||
| Args: | ||
| pyproject: _description_ | ||
| except ConfigError: | ||
| configuration.version = self.extract_scm_version(configuration.pyproject_file.parent) | ||
| pep621_data = resolve_pep621(pep621_configuration, configuration) | ||
| Returns: | ||
| _description_ | ||
| """ | ||
| cppython_data = resolve_cppython(cppython_configuration, global_configuration, project_data, plugin_build_date) | ||
| raw_generator_plugins = self.find_generators() | ||
| generator_plugins = self.filter_plugins( | ||
| raw_generator_plugins, | ||
| pyproject.tool.cppython.generator_name, | ||
| "Generator", | ||
| ) | ||
| return CoreData(project_data=project_data, pep621_data=pep621_data, cppython_data=cppython_data) | ||
| raw_provider_plugins = self.find_providers() | ||
| provider_plugins = self.filter_plugins( | ||
| raw_provider_plugins, | ||
| pyproject.tool.cppython.provider_name, | ||
| "Provider", | ||
| ) | ||
| def extract_scm_version(self, path: Path) -> str: | ||
| """Locates an available SCM plugin that can report version information about the given path | ||
| # Solve the messy interactions between plugins | ||
| generator_type, provider_type = self.solve(generator_plugins, provider_plugins) | ||
| return PluginBuildData(generator_type=generator_type, provider_type=provider_type) | ||
| def generate_pep621_data( | ||
| self, pyproject: PyProject, project_configuration: ProjectConfiguration, scm: SCM | None | ||
| ) -> PEP621Data: | ||
| """_summary_ | ||
| Args: | ||
| path: The directory to query | ||
| pyproject: _description_ | ||
| project_configuration: _description_ | ||
| scm: _description_ | ||
| Raises: | ||
| PluginError: If no SCM plugin can be found | ||
| Returns: | ||
| A version token | ||
| _description_ | ||
| """ | ||
| return resolve_pep621(pyproject.project, project_configuration, scm) | ||
| group = "SCM" | ||
| group_lower = group.lower() | ||
| def generate_core_data( | ||
| self, | ||
| project_data: ProjectData, | ||
| pyproject: PyProject, | ||
| pep621_data: PEP621Data, | ||
| plugin_build_date: PluginBuildData, | ||
| ) -> CoreData: | ||
| """Parses and returns resolved data from all configuration sources | ||
| scm_types: list[type[SCM]] = [] | ||
| Args: | ||
| project_data: Project data | ||
| pyproject: TODO | ||
| pep621_data: TODO | ||
| plugin_build_date: TODO | ||
| if not (entries := list(metadata.entry_points(group=f"cppython.{group_lower}"))): | ||
| raise PluginError("No SCM plugin found") | ||
| Raises: | ||
| ConfigError: Raised if data cannot be parsed | ||
| # Filter entries | ||
| for entry_point in entries: | ||
| plugin_type = entry_point.load() | ||
| if not issubclass(plugin_type, SCM): | ||
| self.logger.warning( | ||
| f"Found incompatible plugin. The '{resolve_name(plugin_type)}' plugin must be an instance of" | ||
| f" '{group_lower}'" | ||
| ) | ||
| else: | ||
| scm_types.append(plugin_type) | ||
| Returns: | ||
| The resolved core object | ||
| """ | ||
| # Deduce the SCM repository | ||
| plugin = None | ||
| for scm_type in scm_types: | ||
| scm = scm_type() | ||
| if scm.supported(path): | ||
| plugin = scm | ||
| break | ||
| global_configuration = CPPythonGlobalConfiguration() | ||
| if not plugin: | ||
| raise PluginError("No applicable SCM plugin found for the given path") | ||
| cppython_data = resolve_cppython(pyproject.tool.cppython, global_configuration, project_data, plugin_build_date) | ||
| if (version := plugin.version(path)) is None: | ||
| raise PluginError("Project has no version information") | ||
| return CoreData(project_data=project_data, pep621_data=pep621_data, cppython_data=cppython_data) | ||
| return version | ||
| def find_generators(self) -> list[type[Generator]]: | ||
@@ -194,3 +210,3 @@ """_summary_ | ||
| def filter_plugins( | ||
| self, plugin_types: list[type[DataPluginT]], directory: Path, pinned_name: str | None, group_name: str | ||
| self, plugin_types: list[type[DataPluginT]], pinned_name: str | None, group_name: str | ||
| ) -> list[type[DataPluginT]]: | ||
@@ -201,3 +217,2 @@ """Finds and filters data plugins | ||
| plugin_types: The plugin type to lookup | ||
| directory: The data to query support for the filtered plugins | ||
| pinned_name: The configuration name | ||
@@ -228,7 +243,6 @@ group_name: The group name | ||
| for loaded_type in plugin_types: | ||
| if loaded_type.supported(directory): | ||
| self.logger.warning( | ||
| f"A {group_name} plugin is supported: {resolve_name(loaded_type)} from {getmodule(loaded_type)}" | ||
| ) | ||
| supported_types.append(loaded_type) | ||
| self.logger.warning( | ||
| f"A {group_name} plugin is supported: {resolve_name(loaded_type)} from {getmodule(loaded_type)}" | ||
| ) | ||
| supported_types.append(loaded_type) | ||
@@ -272,2 +286,49 @@ # Fail | ||
| def create_scm( | ||
| self, | ||
| project_data: ProjectData, | ||
| ) -> SCM | None: | ||
| """_summary_ | ||
| Args: | ||
| project_data: _description_ | ||
| Raises: | ||
| PluginError: Ya | ||
| Returns: | ||
| _description_ | ||
| """ | ||
| group = "scm" | ||
| path = project_data.pyproject_file.parent | ||
| scm_types: list[type[SCM]] = [] | ||
| if not (entries := list(metadata.entry_points(group=f"cppython.{group}"))): | ||
| raise PluginError("No SCM plugin found") | ||
| # Filter entries | ||
| for entry_point in entries: | ||
| plugin_type = entry_point.load() | ||
| if not issubclass(plugin_type, SCM): | ||
| self.logger.warning( | ||
| f"Found incompatible plugin. The '{resolve_name(plugin_type)}' plugin must be an instance of" | ||
| f" '{group}'" | ||
| ) | ||
| else: | ||
| scm_types.append(plugin_type) | ||
| # Deduce the SCM repository | ||
| plugin = None | ||
| for scm_type in scm_types: | ||
| if scm_type.features(path).repository: | ||
| scm_data = resolve_scm(project_data) | ||
| plugin = scm_type(scm_data) | ||
| break | ||
| if not plugin: | ||
| self.logger.error("No applicable SCM plugin found for the given path") | ||
| return plugin | ||
| def create_generator( | ||
@@ -274,0 +335,0 @@ self, core_data: CoreData, generator_configuration: dict[str, Any], generator_type: type[Generator] |
+18
-10
@@ -5,3 +5,7 @@ """Git SCM plugin""" | ||
| from cppython_core.plugin_schema.scm import SCM | ||
| from cppython_core.plugin_schema.scm import ( | ||
| SCM, | ||
| SCMPluginGroupData, | ||
| SupportedSCMFeatures, | ||
| ) | ||
| from cppython_core.schema import Information | ||
@@ -15,20 +19,24 @@ from dulwich.errors import NotGitRepository | ||
| def __init__(self, group_data: SCMPluginGroupData) -> None: | ||
| self.group_data = group_data | ||
| @staticmethod | ||
| def supported(directory: Path) -> bool: | ||
| """Queries repository status of a path | ||
| def features(directory: Path) -> SupportedSCMFeatures: | ||
| """Broadcasts the shared features of the SCM plugin to CPPython | ||
| Args: | ||
| directory: The input path to query | ||
| directory: The root directory where features are evaluated | ||
| Returns: | ||
| Whether the given path is a repository root | ||
| The supported features | ||
| """ | ||
| is_repository = True | ||
| try: | ||
| Repo(str(directory)) | ||
| return True | ||
| except NotGitRepository: | ||
| return False | ||
| is_repository = False | ||
| return SupportedSCMFeatures(repository=is_repository) | ||
| @staticmethod | ||
@@ -43,7 +51,7 @@ def information() -> Information: | ||
| def version(self, path: Path) -> str: | ||
| def version(self, directory: Path) -> str: | ||
| """Extracts the system's version metadata | ||
| Args: | ||
| path: The repository path | ||
| directory: The repository path | ||
@@ -50,0 +58,0 @@ Returns: |
+32
-37
@@ -9,3 +9,4 @@ """Manages data flow to and from plugins | ||
| from cppython_core.exceptions import ConfigError, PluginError | ||
| from cppython_core.resolution import PluginBuildData, resolve_name | ||
| from cppython_core.plugin_schema.scm import SCM | ||
| from cppython_core.resolution import resolve_name | ||
| from cppython_core.schema import CoreData, Interface, ProjectConfiguration, PyProject | ||
@@ -22,46 +23,29 @@ from pydantic import ValidationError | ||
| def __init__( | ||
| self, configuration: ProjectConfiguration, interface: Interface, pyproject_data: dict[str, Any] | ||
| self, project_configuration: ProjectConfiguration, interface: Interface, pyproject_data: dict[str, Any] | ||
| ) -> None: | ||
| self._enabled = False | ||
| self._interface = interface | ||
| # Default logging levels | ||
| levels = [logging.WARNING, logging.INFO, logging.DEBUG] | ||
| # Add default output stream | ||
| self.logger = logging.getLogger("cppython") | ||
| self.logger.addHandler(logging.StreamHandler()) | ||
| self.logger.setLevel(levels[configuration.verbosity]) | ||
| self.logger.info("Initializing project") | ||
| try: | ||
| pyproject = PyProject(**pyproject_data) | ||
| builder = Builder(self.logger) | ||
| builder.setup_logger(project_configuration) | ||
| raw_generator_plugins = builder.find_generators() | ||
| generator_plugins = builder.filter_plugins( | ||
| raw_generator_plugins, | ||
| configuration.pyproject_file.parent, | ||
| pyproject.tool.cppython.generator_name, | ||
| "Generator", | ||
| ) | ||
| self.logger.info("Initializing project") | ||
| raw_provider_plugins = builder.find_providers() | ||
| provider_plugins = builder.filter_plugins( | ||
| raw_provider_plugins, | ||
| configuration.pyproject_file.parent, | ||
| pyproject.tool.cppython.provider_name, | ||
| "Provider", | ||
| ) | ||
| project_data = builder.generate_project_data(project_configuration) | ||
| self._scm = builder.create_scm(project_data) | ||
| # Solve the messy interactions between plugins | ||
| generator_type, provider_type = builder.solve(generator_plugins, provider_plugins) | ||
| pyproject = PyProject(**pyproject_data) | ||
| pyproject_build_data = PluginBuildData(generator_type=generator_type, provider_type=provider_type) | ||
| plugin_build_data = builder.generate_data_plugins(pyproject) | ||
| # Once the plugins are resolved, the core data is complete and can be generated | ||
| pep621_data = builder.generate_pep621_data(pyproject, project_configuration, self._scm) | ||
| self._core_data = builder.generate_core_data( | ||
| configuration, pyproject.project, pyproject.tool.cppython, pyproject_build_data | ||
| project_data, | ||
| pyproject, | ||
| pep621_data, | ||
| plugin_build_data, | ||
| ) | ||
@@ -71,5 +55,7 @@ | ||
| self._generator = builder.create_generator( | ||
| self.core_data, pyproject.tool.cppython.generator, generator_type | ||
| self._core_data, pyproject.tool.cppython.generator, plugin_build_data.generator_type | ||
| ) | ||
| self._provider = builder.create_provider(self.core_data, pyproject.tool.cppython.provider, provider_type) | ||
| self._provider = builder.create_provider( | ||
| self._core_data, pyproject.tool.cppython.provider, plugin_build_data.provider_type | ||
| ) | ||
@@ -97,10 +83,19 @@ except ConfigError: | ||
| @property | ||
| def core_data(self) -> CoreData: | ||
| def core_data(self) -> CoreData | None: | ||
| """Core data | ||
| Returns: | ||
| Core data | ||
| Core data, if enabled | ||
| """ | ||
| return self._core_data | ||
| return self._core_data if self._enabled else None | ||
| @property | ||
| def scm(self) -> SCM | None: | ||
| """SCM | ||
| Returns: | ||
| SCM, if enabled | ||
| """ | ||
| return self._scm if self._enabled else None | ||
| async def download_provider_tools(self) -> None: | ||
@@ -113,3 +108,3 @@ """Download the provider tooling if required""" | ||
| name = resolve_name(type(self._provider)) | ||
| base_path = self.core_data.cppython_data.install_path | ||
| base_path = self._core_data.cppython_data.install_path | ||
@@ -116,0 +111,0 @@ path = base_path / name |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: cppython | ||
| Version: 0.7.1.dev31 | ||
| Version: 0.7.1.dev32 | ||
| Summary: A Python management solution for C++ dependencies | ||
@@ -5,0 +5,0 @@ License: MIT |
+1
-1
@@ -19,3 +19,3 @@ [project] | ||
| ] | ||
| version = "0.7.1.dev31" | ||
| version = "0.7.1.dev32" | ||
@@ -22,0 +22,0 @@ [project.license-files] |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
31226
3.99%687
8.02%