Merge branch 'main' into web_workflow
# Conflicts: # circup/__init__.py
This commit is contained in:
commit
ae5cc2a9b4
5 changed files with 91 additions and 31 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ NOT_MCU_LIBRARIES = [
|
|||
#: The version of CircuitPython found on the connected device.
|
||||
CPY_VERSION = ""
|
||||
#: Module formats list (and the other form used in github files)
|
||||
PLATFORMS = {"py": "py", "7mpy": "7.x-mpy", "8mpy": "7.x-mpy"}
|
||||
PLATFORMS = {"py": "py", "8mpy": "8.x-mpy"}
|
||||
#: Commands that do not require an attached board
|
||||
BOARDLESS_COMMANDS = ["show", "bundle-add", "bundle-remove", "bundle-show"]
|
||||
#: Version identifier for a bad MPY file format
|
||||
|
|
@ -507,6 +507,7 @@ def clean_library_name(assumed_library_name):
|
|||
not_standard_names = {
|
||||
# Assumed Name : Actual Name
|
||||
"adafruit_adafruitio": "adafruit_io",
|
||||
"adafruit_asyncio": "asyncio",
|
||||
"adafruit_busdevice": "adafruit_bus_device",
|
||||
"adafruit_display_button": "adafruit_button",
|
||||
"adafruit_neopixel": "neopixel",
|
||||
|
|
@ -1193,8 +1194,8 @@ def _get_modules_file(path):
|
|||
result[os.path.basename(sfm).replace(".py", "").replace(".mpy", "")] = metadata
|
||||
for package_path in package_dir_mods:
|
||||
name = os.path.basename(os.path.dirname(package_path))
|
||||
py_files = glob.glob(os.path.join(package_path, "*.py"))
|
||||
mpy_files = glob.glob(os.path.join(package_path, "*.mpy"))
|
||||
py_files = glob.glob(os.path.join(package_path, "**/*.py"), recursive=True)
|
||||
mpy_files = glob.glob(os.path.join(package_path, "**/*.mpy"), recursive=True)
|
||||
all_files = py_files + mpy_files
|
||||
# default value
|
||||
result[name] = {"path": package_path, "mpy": bool(mpy_files)}
|
||||
|
|
@ -1329,7 +1330,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]
|
||||
|
||||
|
||||
|
|
@ -1428,12 +1437,24 @@ def tags_data_save_tag(key, tag):
|
|||
@click.option(
|
||||
"--password", help="Password to use for authentication when --host is used."
|
||||
)
|
||||
@click.option(
|
||||
"--board-id",
|
||||
default=None,
|
||||
help="Manual Board ID of the CircuitPython device. If provided in combination "
|
||||
"with --cpy-version, it overrides the detected board ID.",
|
||||
)
|
||||
@click.option(
|
||||
"--cpy-version",
|
||||
default=None,
|
||||
help="Manual CircuitPython version. If provided in combination "
|
||||
"with --board-id, it overrides the detected CPy version.",
|
||||
)
|
||||
@click.version_option(
|
||||
prog_name="CircUp",
|
||||
message="%(prog)s, A CircuitPython module updater. Version %(version)s",
|
||||
)
|
||||
@click.pass_context
|
||||
def main(ctx, verbose, path, host, password): # pragma: no cover
|
||||
def main(ctx, verbose, path, host, password, board_id, cpy_version): # pragma: no cover
|
||||
"""
|
||||
A tool to manage and update libraries on a CircuitPython device.
|
||||
"""
|
||||
|
|
@ -1469,7 +1490,11 @@ def main(ctx, verbose, path, host, password): # pragma: no cover
|
|||
click.secho("Could not find a connected CircuitPython device.", fg="red")
|
||||
sys.exit(1)
|
||||
else:
|
||||
CPY_VERSION, board_id = get_circuitpython_version(device_path)
|
||||
CPY_VERSION, board_id = (
|
||||
get_circuitpython_version(device_path)
|
||||
if board_id is None or cpy_version is None
|
||||
else (cpy_version, board_id)
|
||||
)
|
||||
click.echo(
|
||||
"Found device at {}, running CircuitPython {}.".format(
|
||||
device_path, CPY_VERSION
|
||||
|
|
@ -1599,23 +1624,34 @@ 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="code.py")
|
||||
@click.option(
|
||||
"pyext",
|
||||
"--py",
|
||||
is_flag=True,
|
||||
help="Install the .py version of the module(s) instead of the mpy version.",
|
||||
)
|
||||
@click.option(
|
||||
"-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", is_flag=True, 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())
|
||||
|
|
@ -1626,8 +1662,16 @@ 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"
|
||||
# pass a local file with "./" or "../"
|
||||
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):
|
||||
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
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ attrs==19.1.0
|
|||
Babel==2.9.1
|
||||
black==19.3b0
|
||||
bleach==3.3.0
|
||||
certifi==2022.12.7
|
||||
certifi==2023.7.22
|
||||
chardet==3.0.4
|
||||
charset-normalizer==2.0.4
|
||||
click==8.0.1
|
||||
|
|
@ -26,7 +26,7 @@ packaging==19.1
|
|||
pkginfo==1.5.0.1
|
||||
pluggy==0.13.1
|
||||
py==1.10.0
|
||||
Pygments==2.7.4
|
||||
Pygments==2.15.0
|
||||
pylint==2.9.6
|
||||
pyparsing==2.4.2
|
||||
pytest==5.1.2
|
||||
|
|
@ -35,7 +35,7 @@ pytest-faulthandler==2.0.1
|
|||
pytest-random-order==1.0.4
|
||||
pytz==2019.2
|
||||
readme-renderer==24.0
|
||||
requests==2.26.0
|
||||
requests==2.31.0
|
||||
requests-toolbelt==0.9.1
|
||||
semver==2.13.0
|
||||
six==1.12.0
|
||||
|
|
@ -51,7 +51,7 @@ toml==0.10.0
|
|||
tqdm==4.35.0
|
||||
twine==1.13.0
|
||||
update-checker==0.18.0
|
||||
urllib3==1.26.5
|
||||
urllib3==1.26.18
|
||||
wcwidth==0.1.7
|
||||
webencodings==0.5.1
|
||||
wrapt==1.12.1
|
||||
|
|
|
|||
6
tests/bad_python.py
Normal file
6
tests/bad_python.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
# pylint: disable=all
|
||||
|
||||
if True:
|
||||
|
|
@ -96,10 +96,10 @@ def test_Bundle_lib_dir():
|
|||
"adafruit/adafruit-circuitpython-bundle-py/"
|
||||
"adafruit-circuitpython-bundle-py-TESTTAG/lib"
|
||||
)
|
||||
assert bundle.lib_dir("7mpy") == (
|
||||
assert bundle.lib_dir("8mpy") == (
|
||||
"DATA_DIR/"
|
||||
"adafruit/adafruit-circuitpython-bundle-7mpy/"
|
||||
"adafruit-circuitpython-bundle-7.x-mpy-TESTTAG/lib"
|
||||
"adafruit/adafruit-circuitpython-bundle-8mpy/"
|
||||
"adafruit-circuitpython-bundle-8.x-mpy-TESTTAG/lib"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ def test_Module_mpy_mismatch():
|
|||
"""
|
||||
path = os.path.join("foo", "bar", "baz", "module.mpy")
|
||||
repo = "https://github.com/adafruit/SomeLibrary.git"
|
||||
with mock.patch("circup.CPY_VERSION", "7.0.0"):
|
||||
with mock.patch("circup.CPY_VERSION", "8.0.0"):
|
||||
bundle = circup.Bundle(TEST_BUNDLE_NAME)
|
||||
m1 = circup.Module(path, repo, "1.2.3", "1.2.3", True, bundle, (None, None))
|
||||
m2 = circup.Module(
|
||||
|
|
@ -328,7 +328,7 @@ def test_Module_mpy_mismatch():
|
|||
assert m2.outofdate is True
|
||||
assert m3.mpy_mismatch is False
|
||||
assert m3.outofdate is False
|
||||
with mock.patch("circup.CPY_VERSION", "7.0.0"):
|
||||
with mock.patch("circup.CPY_VERSION", "8.0.0"):
|
||||
assert m1.mpy_mismatch is False
|
||||
assert m1.outofdate is False
|
||||
assert m2.mpy_mismatch is False
|
||||
|
|
@ -366,7 +366,7 @@ def test_Module_row():
|
|||
path = os.path.join("foo", "bar", "baz", "module.py")
|
||||
repo = "https://github.com/adafruit/SomeLibrary.git"
|
||||
with mock.patch("circup.os.path.isfile", return_value=True), mock.patch(
|
||||
"circup.CPY_VERSION", "7.0.0"
|
||||
"circup.CPY_VERSION", "8.0.0"
|
||||
):
|
||||
m = circup.Module(path, repo, "1.2.3", None, False, bundle, (None, None))
|
||||
assert m.row == ("module", "1.2.3", "unknown", "Major Version")
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue