Skip to content

Commit 448bd7a

Browse files
authored
Tighten Some Loose Ends (#102)
1 parent 39e3c5d commit 448bd7a

6 files changed

Lines changed: 180 additions & 37 deletions

File tree

cppython/console/interface.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,23 @@ def generate_project(self) -> Project:
7272

7373
@click.group()
7474
@click.option("-v", "--verbose", count=True, help="Print additional output")
75+
@click.option("--debug/--no-debug", default=False)
7576
@pass_config
76-
def cli(config: Configuration, verbose: int) -> None:
77+
def cli(config: Configuration, verbose: int, debug: bool) -> None:
7778
"""entry_point group for the CLI commands
7879
7980
Args:
8081
config: The CLI configuration object
8182
verbose: The verbosity level
83+
debug: Debug mode
8284
"""
8385
config.configuration.verbosity = verbose
86+
config.configuration.debug = debug
8487

8588

86-
@cli.command()
89+
@cli.command(name="info")
8790
@pass_config
88-
def info(config: Configuration) -> None:
91+
def info_command(config: Configuration) -> None:
8992
"""Prints project information
9093
9194
Args:
@@ -96,9 +99,22 @@ def info(config: Configuration) -> None:
9699
config.logger.info("The SCM project version is: %s", version)
97100

98101

99-
@cli.command()
102+
@cli.command(name="list")
100103
@pass_config
101-
def install(config: Configuration) -> None:
104+
def list_command(config: Configuration) -> None:
105+
"""Prints project information
106+
107+
Args:
108+
config: The CLI configuration object
109+
"""
110+
111+
version = config.query_scm()
112+
config.logger.info("The SCM project version is: %s", version)
113+
114+
115+
@cli.command(name="install")
116+
@pass_config
117+
def install_command(config: Configuration) -> None:
102118
"""Install API call
103119
104120
Args:
@@ -108,9 +124,9 @@ def install(config: Configuration) -> None:
108124
project.install()
109125

110126

111-
@cli.command()
127+
@cli.command(name="update")
112128
@pass_config
113-
def update(config: Configuration) -> None:
129+
def update_command(config: Configuration) -> None:
114130
"""Update API call
115131
116132
Args:

cppython/project.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from cppython_core.exceptions import ConfigError, PluginError
99
from cppython_core.resolution import resolve_name
1010
from cppython_core.schema import CoreData, Interface, ProjectConfiguration, PyProject
11+
from pydantic import ValidationError
1112

1213
from cppython.builder import Builder
1314
from cppython.schema import API
@@ -32,39 +33,49 @@ def __init__(
3233

3334
self.logger.info("Initializing project")
3435

35-
if (pyproject := PyProject(**pyproject_data)) is None:
36-
raise ConfigError("PyProject data is not defined")
36+
try:
37+
if (pyproject := PyProject(**pyproject_data)) is None:
38+
raise ConfigError("Table [project] is not defined")
39+
40+
if pyproject.tool is None:
41+
raise ConfigError("Table [tool] is not defined")
3742

38-
if pyproject.tool is None:
39-
raise ConfigError("Table [tool] is not defined")
43+
if pyproject.tool.cppython is None:
44+
raise ConfigError("Table [tool.cppython] is not defined")
4045

41-
if pyproject.tool.cppython is None:
42-
raise ConfigError("Table [tool.cppython] is not defined")
46+
builder = Builder(self.logger)
4347

44-
builder = Builder(self.logger)
48+
self._core_data = builder.generate_core_data(configuration, pyproject.project, pyproject.tool.cppython)
4549

46-
self._core_data = builder.generate_core_data(configuration, pyproject.project, pyproject.tool.cppython)
50+
raw_generator_plugins = builder.find_generators()
51+
generator_plugins = builder.filter_plugins(
52+
raw_generator_plugins,
53+
self.core_data.project_data.pyproject_file.parent,
54+
self.core_data.cppython_data.generator_name,
55+
"Generator",
56+
)
4757

48-
raw_generator_plugins = builder.find_generators()
49-
generator_plugins = builder.filter_plugins(
50-
raw_generator_plugins,
51-
self.core_data.project_data.pyproject_file.parent,
52-
self.core_data.cppython_data.generator_name,
53-
"Generator",
54-
)
58+
raw_provider_plugins = builder.find_providers()
59+
provider_plugins = builder.filter_plugins(
60+
raw_provider_plugins,
61+
self.core_data.project_data.pyproject_file.parent,
62+
self.core_data.cppython_data.provider_name,
63+
"Provider",
64+
)
5565

56-
raw_provider_plugins = builder.find_providers()
57-
provider_plugins = builder.filter_plugins(
58-
raw_provider_plugins,
59-
self.core_data.project_data.pyproject_file.parent,
60-
self.core_data.cppython_data.provider_name,
61-
"Provider",
62-
)
66+
generator_type, provider_type = builder.solve(generator_plugins, provider_plugins)
6367

64-
generator_type, provider_type = builder.solve(generator_plugins, provider_plugins)
68+
self._generator = builder.create_generator(
69+
self.core_data, pyproject.tool.cppython.generator, generator_type
70+
)
71+
self._provider = builder.create_provider(self.core_data, pyproject.tool.cppython.provider, provider_type)
6572

66-
self._generator = builder.create_generator(self.core_data, pyproject.tool.cppython.generator, generator_type)
67-
self._provider = builder.create_provider(self.core_data, pyproject.tool.cppython.provider, provider_type)
73+
except ConfigError:
74+
logging.exception("Unhandled configuration. CPPython will process no further")
75+
return
76+
except ValidationError as error:
77+
logging.error(error)
78+
return
6879

6980
self._enabled = True
7081

pdm.lock

Lines changed: 18 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ paths = ["LICENSE.md"]
2929
homepage = "https://github.com/Synodic-Software/CPPython"
3030
repository = "https://github.com/Synodic-Software/CPPython"
3131

32+
[project.optional-dependencies]
3233
[tool.pdm]
3334
version = {use_scm = true}
3435

@@ -42,6 +43,7 @@ lint = [
4243
test = [
4344
"pytest>=7.1.2",
4445
"pytest-cov>=3.0.0",
46+
"pytest-click>=1.1",
4547
"pytest-mock>=3.8.2",
4648
"pytest-cppython>=0.2.0.dev0",
4749
]

tests/unit/test_interface.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Tests the click interface type"""
2+
3+
from click.testing import CliRunner
4+
5+
from cppython.console.interface import cli
6+
7+
8+
class TestInterface:
9+
"""Various tests for the click interface"""
10+
11+
def test_info(self, cli_runner: CliRunner) -> None:
12+
"""Verifies that the info command functions with CPPython hooks
13+
14+
Args:
15+
cli_runner: The click runner
16+
"""
17+
18+
result = cli_runner.invoke(cli, ["info"], catch_exceptions=False)
19+
assert result.exit_code == 0
20+
21+
def test_list(self, cli_runner: CliRunner) -> None:
22+
"""Verifies that the list command functions with CPPython hooks
23+
24+
Args:
25+
cli_runner: The click runner
26+
"""
27+
28+
result = cli_runner.invoke(cli, ["list"], catch_exceptions=False)
29+
assert result.exit_code == 0
30+
31+
def test_update(self, cli_runner: CliRunner) -> None:
32+
"""Verifies that the update command functions with CPPython hooks
33+
34+
Args:
35+
cli_runner: The click runner
36+
"""
37+
38+
result = cli_runner.invoke(cli, ["update"], catch_exceptions=False)
39+
assert result.exit_code == 0
40+
41+
def test_install(self, cli_runner: CliRunner) -> None:
42+
"""Verifies that the install command functions with CPPython hooks
43+
44+
Args:
45+
cli_runner: The click runner
46+
"""
47+
48+
result = cli_runner.invoke(cli, ["install"], catch_exceptions=False)
49+
assert result.exit_code == 0

tests/unit/test_project.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Tests the Project type"""
2+
3+
4+
from pathlib import Path
5+
6+
import tomlkit
7+
from cppython_core.schema import ProjectConfiguration
8+
from pytest import FixtureRequest
9+
from pytest_cppython.mock.interface import MockInterface
10+
11+
from cppython.project import Project
12+
13+
14+
class TestProject:
15+
"""Various tests for the project object"""
16+
17+
def test_default_construction(self, request: FixtureRequest) -> None:
18+
"""The project type should be constructable without pyproject.toml support.
19+
The CPPython project uses a working pyproject.toml file, and this file is used as the test data
20+
21+
Args:
22+
request: The pytest request fixture
23+
"""
24+
25+
# Use the CPPython directory as the test data
26+
file = request.config.rootpath / "pyproject.toml"
27+
project_configuration = ProjectConfiguration(pyproject_file=file, version=None)
28+
interface = MockInterface()
29+
30+
pyproject_data = tomlkit.loads(file.read_text(encoding="utf-8"))
31+
project = Project(project_configuration, interface, pyproject_data)
32+
33+
assert project
34+
35+
def test_missing_project_table(self, tmp_path: Path) -> None:
36+
"""The project type should be constructable without the top level table
37+
38+
Args:
39+
tmp_path: Temporary directory for dummy data
40+
"""
41+
42+
file_path = tmp_path / "pyproject.toml"
43+
44+
with open(file_path, "a", encoding="utf8") as file:
45+
file.write("")
46+
47+
project_configuration = ProjectConfiguration(pyproject_file=file_path, version=None)
48+
interface = MockInterface()
49+
50+
project = Project(project_configuration, interface, {})
51+
52+
assert project

0 commit comments

Comments
 (0)