From 40a4418260c7a2356aed6f37d3347c755f455ad8 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 12:12:25 -0400 Subject: [PATCH 1/9] Readd Project Tests --- tests/unit/test_project.py | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/unit/test_project.py diff --git a/tests/unit/test_project.py b/tests/unit/test_project.py new file mode 100644 index 0000000..95648e3 --- /dev/null +++ b/tests/unit/test_project.py @@ -0,0 +1,48 @@ +"""Tests the Project type""" + + +from pathlib import Path + +import tomlkit +from cppython_core.schema import ProjectConfiguration +from pytest import FixtureRequest +from pytest_cppython.mock.interface import MockInterface + +from cppython.project import Project + + +class TestProject: + """Various tests for the project object""" + + def test_default_construction(self, request: FixtureRequest) -> None: + """The project type should be constructable without pyproject.toml support. + The CPPython project uses a working pyproject.toml file, and this file is used as the test data + + Args: + request: The pytest request fixture + """ + + # Use the CPPython directory as the test data + file = request.config.rootpath / "pyproject.toml" + project_configuration = ProjectConfiguration(pyproject_file=file, version=None) + interface = MockInterface() + + pyproject_data = tomlkit.loads(file.read_text(encoding="utf-8")) + project = Project(project_configuration, interface, pyproject_data) + + assert project + + def test_missing_project_table(self, tmp_path: Path) -> None: + """The project type should be constructable without the top level table + + Args: + tmp_path: Temporary directory for dummy data + """ + + file = tmp_path / "pyproject.toml" + project_configuration = ProjectConfiguration(pyproject_file=file, version=None) + interface = MockInterface() + + project = Project(project_configuration, interface, {}) + + assert project From a6560ce9b59ba93ec73a6bd2fb02f82fe425840e Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 12:59:47 -0400 Subject: [PATCH 2/9] Add Interface Tests --- cppython/console/interface.py | 25 +++++++++++++++++++------ cppython/project.py | 2 +- pdm.lock | 15 ++++++++++++++- pyproject.toml | 2 ++ tests/unit/test_interface.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 tests/unit/test_interface.py diff --git a/cppython/console/interface.py b/cppython/console/interface.py index 1af28b1..244488e 100644 --- a/cppython/console/interface.py +++ b/cppython/console/interface.py @@ -83,9 +83,9 @@ def cli(config: Configuration, verbose: int) -> None: config.configuration.verbosity = verbose -@cli.command() +@cli.command(name="info") @pass_config -def info(config: Configuration) -> None: +def info_command(config: Configuration) -> None: """Prints project information Args: @@ -96,9 +96,22 @@ def info(config: Configuration) -> None: config.logger.info("The SCM project version is: %s", version) -@cli.command() +@cli.command(name="list") @pass_config -def install(config: Configuration) -> None: +def list_command(config: Configuration) -> None: + """Prints project information + + Args: + config: The CLI configuration object + """ + + version = config.query_scm() + config.logger.info("The SCM project version is: %s", version) + + +@cli.command(name="install") +@pass_config +def install_command(config: Configuration) -> None: """Install API call Args: @@ -108,9 +121,9 @@ def install(config: Configuration) -> None: project.install() -@cli.command() +@cli.command(name="update") @pass_config -def update(config: Configuration) -> None: +def update_command(config: Configuration) -> None: """Update API call Args: diff --git a/cppython/project.py b/cppython/project.py index 95c801e..8c209f3 100644 --- a/cppython/project.py +++ b/cppython/project.py @@ -33,7 +33,7 @@ def __init__( self.logger.info("Initializing project") if (pyproject := PyProject(**pyproject_data)) is None: - raise ConfigError("PyProject data is not defined") + raise ConfigError("Table [project] is not defined") if pyproject.tool is None: raise ConfigError("Table [tool] is not defined") diff --git a/pdm.lock b/pdm.lock index e7d751a..c0656e3 100644 --- a/pdm.lock +++ b/pdm.lock @@ -186,6 +186,15 @@ dependencies = [ "pluggy<2.0,>=0.12", ] +[[package]] +name = "pytest-click" +version = "1.1.0" +summary = "Pytest plugin for Click" +dependencies = [ + "click>=6.0", + "pytest>=5.0", +] + [[package]] name = "pytest-cov" version = "4.0.0" @@ -241,7 +250,7 @@ summary = "Module for decorators, wrappers and monkey patching." [metadata] lock_version = "4.1" -content_hash = "sha256:6c4208d24350a6d735fc4f9ef0ddec2c92294b8f164526f2f2727573ee2bb61f" +content_hash = "sha256:5a4fbce743e784ef715a342da7048d20117766aa05d1e4e8761f6954d664e260" [metadata.files] "astroid 2.15.0" = [ @@ -550,6 +559,10 @@ content_hash = "sha256:6c4208d24350a6d735fc4f9ef0ddec2c92294b8f164526f2f2727573e {url = "https://files.pythonhosted.org/packages/b2/68/5321b5793bd506961bd40bdbdd0674e7de4fb873ee7cab33dd27283ad513/pytest-7.2.2-py3-none-any.whl", hash = "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e"}, {url = "https://files.pythonhosted.org/packages/b9/29/311895d9cd3f003dd58e8fdea36dd895ba2da5c0c90601836f7de79f76fe/pytest-7.2.2.tar.gz", hash = "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"}, ] +"pytest-click 1.1.0" = [ + {url = "https://files.pythonhosted.org/packages/72/1a/eb53371999b94b3c995c00117f3a232dbf6f56c7152a52cf3e3777e7d49d/pytest_click-1.1.0-py3-none-any.whl", hash = "sha256:eade4742c2f02c345e78a32534a43e8db04acf98d415090539dacc880b7cd0e9"}, + {url = "https://files.pythonhosted.org/packages/ec/ec/bca3cd29ba2b025ae41666b851f6ff05fb77cb4c13719baaeda6a757772a/pytest_click-1.1.0.tar.gz", hash = "sha256:fdd9f6721f877dda021e7c5dc73e70aecd37e5ed23ec6820f8a7b3fd7b4f8d30"}, +] "pytest-cov 4.0.0" = [ {url = "https://files.pythonhosted.org/packages/ea/70/da97fd5f6270c7d2ce07559a19e5bf36a76f0af21500256f005a69d9beba/pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, {url = "https://files.pythonhosted.org/packages/fe/1f/9ec0ddd33bd2b37d6ec50bb39155bca4fe7085fa78b3b434c05459a860e3/pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, diff --git a/pyproject.toml b/pyproject.toml index b800b8a..60a02bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ paths = ["LICENSE.md"] homepage = "https://github.com/Synodic-Software/CPPython" repository = "https://github.com/Synodic-Software/CPPython" +[project.optional-dependencies] [tool.pdm] version = {use_scm = true} @@ -42,6 +43,7 @@ lint = [ test = [ "pytest>=7.1.2", "pytest-cov>=3.0.0", + "pytest-click>=1.1", "pytest-mock>=3.8.2", "pytest-cppython>=0.2.0.dev0", ] diff --git a/tests/unit/test_interface.py b/tests/unit/test_interface.py new file mode 100644 index 0000000..7b0474a --- /dev/null +++ b/tests/unit/test_interface.py @@ -0,0 +1,29 @@ +"""Tests the click interface type""" + +from click.testing import CliRunner + +from cppython.console.interface import cli + + +class TestInterface: + """Various tests for the click interface""" + + def test_info(self, cli_runner: CliRunner) -> None: + """Verifies that the info command functions with CPPython hooks + + Args: + cli_runner: The click runner + """ + + result = cli_runner.invoke(cli, ["info"]) + assert result.exit_code == 0 + + def test_list(self, cli_runner: CliRunner) -> None: + """Verifies that the list command functions with CPPython hooks + + Args: + cli_runner: The click runner + """ + + result = cli_runner.invoke(cli, ["list"]) + assert result.exit_code == 0 From 0d4fc6928a8898bc8781965c486684eff48ca685 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 13:18:09 -0400 Subject: [PATCH 3/9] Add debug option --- cppython/console/interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cppython/console/interface.py b/cppython/console/interface.py index 244488e..0a32df1 100644 --- a/cppython/console/interface.py +++ b/cppython/console/interface.py @@ -72,6 +72,7 @@ def generate_project(self) -> Project: @click.group() @click.option("-v", "--verbose", count=True, help="Print additional output") +@click.option("--debug/--no-debug", default=False) @pass_config def cli(config: Configuration, verbose: int) -> None: """entry_point group for the CLI commands From f2a41d6793738cecc1b135236a73b23c9ba75938 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 13:19:22 -0400 Subject: [PATCH 4/9] Add Update/Install Commands --- tests/unit/test_interface.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/unit/test_interface.py b/tests/unit/test_interface.py index 7b0474a..01a96fe 100644 --- a/tests/unit/test_interface.py +++ b/tests/unit/test_interface.py @@ -27,3 +27,23 @@ def test_list(self, cli_runner: CliRunner) -> None: result = cli_runner.invoke(cli, ["list"]) assert result.exit_code == 0 + + def test_update(self, cli_runner: CliRunner) -> None: + """Verifies that the update command functions with CPPython hooks + + Args: + cli_runner: The click runner + """ + + result = cli_runner.invoke(cli, ["update"]) + assert result.exit_code == 0 + + def test_install(self, cli_runner: CliRunner) -> None: + """Verifies that the install command functions with CPPython hooks + + Args: + cli_runner: The click runner + """ + + result = cli_runner.invoke(cli, ["install"]) + assert result.exit_code == 0 From 00da8d2e54e71f04e749dba1870e19bd068f2b3a Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 13:24:47 -0400 Subject: [PATCH 5/9] Propagate Exceptions --- tests/unit/test_interface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_interface.py b/tests/unit/test_interface.py index 01a96fe..396ed28 100644 --- a/tests/unit/test_interface.py +++ b/tests/unit/test_interface.py @@ -15,7 +15,7 @@ def test_info(self, cli_runner: CliRunner) -> None: cli_runner: The click runner """ - result = cli_runner.invoke(cli, ["info"]) + result = cli_runner.invoke(cli, ["info"], catch_exceptions=False) assert result.exit_code == 0 def test_list(self, cli_runner: CliRunner) -> None: @@ -25,7 +25,7 @@ def test_list(self, cli_runner: CliRunner) -> None: cli_runner: The click runner """ - result = cli_runner.invoke(cli, ["list"]) + result = cli_runner.invoke(cli, ["list"], catch_exceptions=False) assert result.exit_code == 0 def test_update(self, cli_runner: CliRunner) -> None: @@ -35,7 +35,7 @@ def test_update(self, cli_runner: CliRunner) -> None: cli_runner: The click runner """ - result = cli_runner.invoke(cli, ["update"]) + result = cli_runner.invoke(cli, ["update"], catch_exceptions=False) assert result.exit_code == 0 def test_install(self, cli_runner: CliRunner) -> None: @@ -45,5 +45,5 @@ def test_install(self, cli_runner: CliRunner) -> None: cli_runner: The click runner """ - result = cli_runner.invoke(cli, ["install"]) + result = cli_runner.invoke(cli, ["install"], catch_exceptions=False) assert result.exit_code == 0 From cfe4abb7f3aa687c74005007af2d14b095249351 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 13:25:48 -0400 Subject: [PATCH 6/9] Ingest Debug Option --- cppython/console/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cppython/console/interface.py b/cppython/console/interface.py index 0a32df1..38357dc 100644 --- a/cppython/console/interface.py +++ b/cppython/console/interface.py @@ -74,7 +74,7 @@ def generate_project(self) -> Project: @click.option("-v", "--verbose", count=True, help="Print additional output") @click.option("--debug/--no-debug", default=False) @pass_config -def cli(config: Configuration, verbose: int) -> None: +def cli(config: Configuration, verbose: int, debug: bool) -> None: """entry_point group for the CLI commands Args: From c46bad1b3f12f17f9a1b4a34335fb32b1306cfdc Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 13:26:03 -0400 Subject: [PATCH 7/9] Ignore Use --- cppython/console/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cppython/console/interface.py b/cppython/console/interface.py index 38357dc..327b2f6 100644 --- a/cppython/console/interface.py +++ b/cppython/console/interface.py @@ -74,7 +74,7 @@ def generate_project(self) -> Project: @click.option("-v", "--verbose", count=True, help="Print additional output") @click.option("--debug/--no-debug", default=False) @pass_config -def cli(config: Configuration, verbose: int, debug: bool) -> None: +def cli(config: Configuration, verbose: int, _debug: bool) -> None: """entry_point group for the CLI commands Args: From 3a162fb8ead89be556ec0f1e0208dbfe256999f8 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 13 Mar 2023 23:55:47 -0400 Subject: [PATCH 8/9] Add Debug Mode --- cppython/console/interface.py | 4 +++- pdm.lock | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cppython/console/interface.py b/cppython/console/interface.py index 327b2f6..53f5b03 100644 --- a/cppython/console/interface.py +++ b/cppython/console/interface.py @@ -74,14 +74,16 @@ def generate_project(self) -> Project: @click.option("-v", "--verbose", count=True, help="Print additional output") @click.option("--debug/--no-debug", default=False) @pass_config -def cli(config: Configuration, verbose: int, _debug: bool) -> None: +def cli(config: Configuration, verbose: int, debug: bool) -> None: """entry_point group for the CLI commands Args: config: The CLI configuration object verbose: The verbosity level + debug: Debug mode """ config.configuration.verbosity = verbose + config.configuration.debug = debug @cli.command(name="info") diff --git a/pdm.lock b/pdm.lock index c0656e3..c3e1b38 100644 --- a/pdm.lock +++ b/pdm.lock @@ -63,7 +63,7 @@ dependencies = [ [[package]] name = "cppython-core" -version = "0.6.1.dev54" +version = "0.6.1.dev55" requires_python = ">=3.11" summary = "Data definitions for CPPython" dependencies = [ @@ -349,9 +349,9 @@ content_hash = "sha256:5a4fbce743e784ef715a342da7048d20117766aa05d1e4e8761f6954d {url = "https://files.pythonhosted.org/packages/f9/06/5f6555205d13f8811558b73fa37596519272fb077ad7f9faa4e4162a23a4/coverage-7.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2ef6cae70168815ed91388948b5f4fcc69681480a0061114db737f957719f03"}, {url = "https://files.pythonhosted.org/packages/fe/0c/9c463e24ac89408c13084ddaefdb7c2d55ed2da7c556214159b261df4737/coverage-7.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd5a12239c0006252244f94863f1c518ac256160cd316ea5c47fb1a11b25889a"}, ] -"cppython-core 0.6.1.dev54" = [ - {url = "https://files.pythonhosted.org/packages/8c/63/1bc02ac40ff4943e483394f2b61d56aa57eb923dc8f192ec65d0ab92799a/cppython-core-0.6.1.dev54.tar.gz", hash = "sha256:65980526a5b800e7685832c4e0e9b735d807ae21187a6dbe5d4fba8af136c378"}, - {url = "https://files.pythonhosted.org/packages/c8/9d/5b0f9b2f8b3baf08a69f34546b5c7f7910941f115a67aee67e7201cb0718/cppython_core-0.6.1.dev54-py3-none-any.whl", hash = "sha256:fb373745a4eb41856a088b316fcbac0abf2eaab42662309177a78b7335ede71d"}, +"cppython-core 0.6.1.dev55" = [ + {url = "https://files.pythonhosted.org/packages/39/57/f1f6c1fe5788418688f548a060ee85dbc759a80021b8cff2dc570b375f5c/cppython-core-0.6.1.dev55.tar.gz", hash = "sha256:dacadaba350ecd0664e26e9839ba77f203e183a07d4cec5d0a8cf9315885eba0"}, + {url = "https://files.pythonhosted.org/packages/6c/32/8a84b3e5e52896825b17c08f1af51df1d7eec7d71c9da33a79b01d7663ac/cppython_core-0.6.1.dev55-py3-none-any.whl", hash = "sha256:0057b6adfb8500e05f79e43106f6b7876782254cece8efbc255c8401206c4f5e"}, ] "dill 0.3.6" = [ {url = "https://files.pythonhosted.org/packages/7c/e7/364a09134e1062d4d5ff69b853a56cf61c223e0afcc6906b6832bcd51ea8/dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, From 1cc9c80b8eac1b6f2b6398ec8f36e595b5ba3e7b Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Wed, 15 Mar 2023 00:01:18 -0400 Subject: [PATCH 9/9] Handle Exceptions --- cppython/project.py | 61 ++++++++++++++++++++++---------------- tests/unit/test_project.py | 8 +++-- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/cppython/project.py b/cppython/project.py index 8c209f3..c295a11 100644 --- a/cppython/project.py +++ b/cppython/project.py @@ -8,6 +8,7 @@ from cppython_core.exceptions import ConfigError, PluginError from cppython_core.resolution import resolve_name from cppython_core.schema import CoreData, Interface, ProjectConfiguration, PyProject +from pydantic import ValidationError from cppython.builder import Builder from cppython.schema import API @@ -32,39 +33,49 @@ def __init__( self.logger.info("Initializing project") - if (pyproject := PyProject(**pyproject_data)) is None: - raise ConfigError("Table [project] is not defined") + try: + if (pyproject := PyProject(**pyproject_data)) is None: + raise ConfigError("Table [project] is not defined") + + if pyproject.tool is None: + raise ConfigError("Table [tool] is not defined") - if pyproject.tool is None: - raise ConfigError("Table [tool] is not defined") + if pyproject.tool.cppython is None: + raise ConfigError("Table [tool.cppython] is not defined") - if pyproject.tool.cppython is None: - raise ConfigError("Table [tool.cppython] is not defined") + builder = Builder(self.logger) - builder = Builder(self.logger) + self._core_data = builder.generate_core_data(configuration, pyproject.project, pyproject.tool.cppython) - self._core_data = builder.generate_core_data(configuration, pyproject.project, pyproject.tool.cppython) + raw_generator_plugins = builder.find_generators() + generator_plugins = builder.filter_plugins( + raw_generator_plugins, + self.core_data.project_data.pyproject_file.parent, + self.core_data.cppython_data.generator_name, + "Generator", + ) - raw_generator_plugins = builder.find_generators() - generator_plugins = builder.filter_plugins( - raw_generator_plugins, - self.core_data.project_data.pyproject_file.parent, - self.core_data.cppython_data.generator_name, - "Generator", - ) + raw_provider_plugins = builder.find_providers() + provider_plugins = builder.filter_plugins( + raw_provider_plugins, + self.core_data.project_data.pyproject_file.parent, + self.core_data.cppython_data.provider_name, + "Provider", + ) - raw_provider_plugins = builder.find_providers() - provider_plugins = builder.filter_plugins( - raw_provider_plugins, - self.core_data.project_data.pyproject_file.parent, - self.core_data.cppython_data.provider_name, - "Provider", - ) + generator_type, provider_type = builder.solve(generator_plugins, provider_plugins) - generator_type, provider_type = builder.solve(generator_plugins, provider_plugins) + self._generator = builder.create_generator( + self.core_data, pyproject.tool.cppython.generator, generator_type + ) + self._provider = builder.create_provider(self.core_data, pyproject.tool.cppython.provider, provider_type) - self._generator = builder.create_generator(self.core_data, pyproject.tool.cppython.generator, generator_type) - self._provider = builder.create_provider(self.core_data, pyproject.tool.cppython.provider, provider_type) + except ConfigError: + logging.exception("Unhandled configuration. CPPython will process no further") + return + except ValidationError as error: + logging.error(error) + return self._enabled = True diff --git a/tests/unit/test_project.py b/tests/unit/test_project.py index 95648e3..e505f18 100644 --- a/tests/unit/test_project.py +++ b/tests/unit/test_project.py @@ -39,8 +39,12 @@ def test_missing_project_table(self, tmp_path: Path) -> None: tmp_path: Temporary directory for dummy data """ - file = tmp_path / "pyproject.toml" - project_configuration = ProjectConfiguration(pyproject_file=file, version=None) + file_path = tmp_path / "pyproject.toml" + + with open(file_path, "a", encoding="utf8") as file: + file.write("") + + project_configuration = ProjectConfiguration(pyproject_file=file_path, version=None) interface = MockInterface() project = Project(project_configuration, interface, {})