From 0306fae90cc97ebaff645664f499472c9ec920ca Mon Sep 17 00:00:00 2001 From: Neradoc Date: Wed, 1 Feb 2023 00:37:38 +0100 Subject: [PATCH 1/6] auto install improvements --- .pre-commit-config.yaml | 5 +++-- circup/__init__.py | 22 ++++++++++++++++++---- tests/bad_python.py | 6 ++++++ tests/test_circup.py | 9 +++++++++ 4 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 tests/bad_python.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e14e75..fa3d605 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,11 +15,12 @@ repos: - id: pylint name: lint (code) types: [python] - exclude: "^(docs/|examples/|setup.py$)" + exclude: "^(docs/|examples/|setup.py$|tests/bad_python.py$)" - repo: https://github.com/python/black rev: 22.3.0 hooks: - - id: black + - id: black + exclude: "^tests/bad_python.py$" - repo: https://github.com/fsfe/reuse-tool rev: v0.14.0 hooks: diff --git a/circup/__init__.py b/circup/__init__.py index 2854983..fb0d903 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -1056,7 +1056,15 @@ def libraries_from_imports(code_py, mod_names): :param str code_py: Full path of the code.py file :return: sequence of library names """ - imports = [info.name.split(".", 1)[0] for info in findimports.find_imports(code_py)] + # pylint: disable=broad-except + try: + found_imports = findimports.find_imports(code_py) + except Exception as ex: # broad exception because anything could go wrong + logger.exception(ex) + click.secho('Unable to read the auto file: "{}"'.format(str(ex)), fg="red") + sys.exit(2) + # pylint: enable=broad-except + imports = [info.name.split(".", 1)[0] for info in found_imports] return [r for r in imports if r in mod_names] @@ -1293,7 +1301,7 @@ def list_cli(ctx): # pragma: no cover @click.option("pyext", "--py", is_flag=True) @click.option("-r", "--requirement", type=click.Path(exists=True, dir_okay=False)) @click.option("--auto/--no-auto", "-a/-A") -@click.option("--auto-file", default="code.py") +@click.option("--auto-file", default=None) @click.pass_context def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no cover """ @@ -1317,8 +1325,14 @@ def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no co with open(requirement, "r", encoding="utf-8") as rfile: requirements_txt = rfile.read() requested_installs = libraries_from_requirements(requirements_txt) - elif auto: - auto_file = os.path.join(ctx.obj["DEVICE_PATH"], auto_file) + elif auto or auto_file: + if auto_file is None: + auto_file = "code.py" + if not os.path.isabs(auto_file) and not auto_file[:2] == "./": + auto_file = os.path.join(ctx.obj["DEVICE_PATH"], auto_file or "code.py") + if not os.path.isfile(auto_file): + click.secho(f"Auto file not found: {auto_file}", fg="red") + sys.exit(1) requested_installs = libraries_from_imports(auto_file, mod_names) else: requested_installs = modules diff --git a/tests/bad_python.py b/tests/bad_python.py new file mode 100644 index 0000000..a5a56e0 --- /dev/null +++ b/tests/bad_python.py @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: MIT +# pylint: disable=all + +if True: diff --git a/tests/test_circup.py b/tests/test_circup.py index 1faa69b..0f2ba51 100644 --- a/tests/test_circup.py +++ b/tests/test_circup.py @@ -1030,3 +1030,12 @@ def test_libraries_from_imports(): "adafruit_esp32spi", "adafruit_hid", ] + + +def test_libraries_from_imports_bad(): + """Ensure that we catch an import error""" + TEST_BUNDLE_MODULES = {"one.py": {}, "two.py": {}, "three.py": {}} + runner = CliRunner() + with mock.patch("circup.get_bundle_versions", return_value=TEST_BUNDLE_MODULES): + result = runner.invoke(circup.install, ["--auto-file", "./tests/bad_python.py"]) + assert result.exit_code == 2 From c46d2fdc758650dbfc5947ee797da039d5423174 Mon Sep 17 00:00:00 2001 From: Neradoc Date: Mon, 6 Feb 2023 23:36:49 +0100 Subject: [PATCH 2/6] using os.sep to find the ./ prefix --- circup/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/circup/__init__.py b/circup/__init__.py index fb0d903..3a7f1d2 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -1328,7 +1328,9 @@ def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no co elif auto or auto_file: if auto_file is None: auto_file = "code.py" - if not os.path.isabs(auto_file) and not auto_file[:2] == "./": + # pass a local file with "./" or "../" + is_relative = auto_file[:2] in ("." + os.sep, "..") + if not os.path.isabs(auto_file) and not is_relative: auto_file = os.path.join(ctx.obj["DEVICE_PATH"], auto_file or "code.py") if not os.path.isfile(auto_file): click.secho(f"Auto file not found: {auto_file}", fg="red") From f7c812261b4bd2cdb6004d9775a8eedd90309473 Mon Sep 17 00:00:00 2001 From: Neradoc Date: Mon, 6 Feb 2023 23:39:43 +0100 Subject: [PATCH 3/6] move install help to the options, remove no-auto --- circup/__init__.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/circup/__init__.py b/circup/__init__.py index 3a7f1d2..373bc2e 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -1298,23 +1298,32 @@ def list_cli(ctx): # pragma: no cover @click.argument( "modules", required=False, nargs=-1, shell_complete=completion_for_install ) -@click.option("pyext", "--py", is_flag=True) -@click.option("-r", "--requirement", type=click.Path(exists=True, dir_okay=False)) -@click.option("--auto/--no-auto", "-a/-A") -@click.option("--auto-file", default=None) +@click.option( + "--py", + is_flag=True, + help="Install the .py version of the module(s) instead of the mpy version.", +) +@click.option( + "pyext", + "-r", + "--requirement", + type=click.Path(exists=True, dir_okay=False), + help="specify a text file to install all modules listed in the text file." + " Typically requirements.txt.", +) +@click.option("--auto", "-a", help="Install the modules imported in code.py.") +@click.option( + "--auto-file", + default=None, + help="Specify the name of a file on the board to read for auto install." + " Also accepts an absolute path or a local ./ path.", +) @click.pass_context def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no cover """ Install a named module(s) onto the device. Multiple modules can be installed at once by providing more than one module name, each separated by a space. - - Option --py installs .py version of module(s). - - Option -r allows specifying a text file to install all modules listed in - the text file. - - Option -a installs based on the modules imported by code.py """ # TODO: Ensure there's enough space on the device available_modules = get_bundle_versions(get_bundles_list()) From e7853dfc3a6ed36f5b38e2091a713176f8472721 Mon Sep 17 00:00:00 2001 From: Neradoc Date: Tue, 21 Mar 2023 01:31:53 +0100 Subject: [PATCH 4/6] fix rebase --- circup/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circup/__init__.py b/circup/__init__.py index 373bc2e..4a4d049 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -1299,12 +1299,12 @@ def list_cli(ctx): # pragma: no cover "modules", required=False, nargs=-1, shell_complete=completion_for_install ) @click.option( + "pyext", "--py", is_flag=True, help="Install the .py version of the module(s) instead of the mpy version.", ) @click.option( - "pyext", "-r", "--requirement", type=click.Path(exists=True, dir_okay=False), From 62f7a657cf4d115130c692c5d0421bc09122d1fe Mon Sep 17 00:00:00 2001 From: Neradoc Date: Sat, 1 Apr 2023 17:11:23 +0200 Subject: [PATCH 5/6] Update circup/__init__.py Co-authored-by: Dan Halbert --- circup/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circup/__init__.py b/circup/__init__.py index 4a4d049..bbd0259 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -1338,7 +1338,7 @@ def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no co if auto_file is None: auto_file = "code.py" # pass a local file with "./" or "../" - is_relative = auto_file[:2] in ("." + os.sep, "..") + is_relative = auto_file.startswith("." + os.sep) or auto_file.startswith(".." + os.sep) if not os.path.isabs(auto_file) and not is_relative: auto_file = os.path.join(ctx.obj["DEVICE_PATH"], auto_file or "code.py") if not os.path.isfile(auto_file): From 82ffa73b57ad82b706a420d816bb34789eab4abd Mon Sep 17 00:00:00 2001 From: Neradoc Date: Sun, 2 Apr 2023 01:47:16 +0200 Subject: [PATCH 6/6] Other way to find local path --- circup/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circup/__init__.py b/circup/__init__.py index bbd0259..72913d4 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -1338,7 +1338,7 @@ def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no co if auto_file is None: auto_file = "code.py" # pass a local file with "./" or "../" - is_relative = auto_file.startswith("." + os.sep) or auto_file.startswith(".." + os.sep) + is_relative = auto_file.split(os.sep)[0] in [os.path.curdir, os.path.pardir] if not os.path.isabs(auto_file) and not is_relative: auto_file = os.path.join(ctx.obj["DEVICE_PATH"], auto_file or "code.py") if not os.path.isfile(auto_file):