From 499dee6e03a025485d18cc8d0ec48cc4e57192ee Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 30 Nov 2023 06:44:22 -0600 Subject: [PATCH] move libraries_from_code_py out of backend. fix CPY_VERSION issue for web backend --- circup/__init__.py | 42 +++++++++++++++++++++++++++--------------- circup/backends.py | 45 ++++++++++++++++++++++++--------------------- circup/shared.py | 6 ++---- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/circup/__init__.py b/circup/__init__.py index 2516dba..58dd282 100644 --- a/circup/__init__.py +++ b/circup/__init__.py @@ -30,9 +30,12 @@ import update_checker from requests.auth import HTTPBasicAuth from semver import VersionInfo -from circup.shared import DATA_DIR, BAD_FILE_FORMAT, extract_metadata, CPY_VERSION, _get_modules_file +from circup.shared import DATA_DIR, BAD_FILE_FORMAT, extract_metadata, _get_modules_file from circup.backends import WebBackend, DiskBackend +#: The version of CircuitPython found on the connected device. +CPY_VERSION = "" + # Useful constants. #: Flag to indicate if the command is being run in verbose mode. VERBOSE = False @@ -410,13 +413,6 @@ class Module: ) - - - - - - - def clean_library_name(assumed_library_name): """ Most CP repos and library names are look like this: @@ -505,9 +501,6 @@ def ensure_latest_bundle(bundle): logger.info("Current bundle up to date %s.", tag) - - - def find_device(): """ Return the location on the filesystem for the connected CircuitPython device. @@ -936,6 +929,25 @@ def tags_data_save_tag(key, tag): json.dump(tags_data, data) +def libraries_from_code_py(code_py, mod_names): + """ + Parse the given code.py file and return the imported libraries + + :param str code_py: Full path of the code.py file + :return: sequence of library names + """ + # pylint: disable=broad-except + try: + found_imports = findimports.find_imports(code_py) + except Exception as ex: # broad exception because anything could go wrong + self.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] + + # ----------- CLI command definitions ----------- # # The following functions have IO side effects (for instance they emit to @@ -1014,7 +1026,6 @@ def main(ctx, verbose, path, host, password, board_id, cpy_version): # pragma: if ctx.invoked_subcommand in BOARDLESS_COMMANDS: return - ctx.obj["DEVICE_PATH"] = device_path latest_version = get_latest_release_from_url( "https://github.com/adafruit/circuitpython/releases/latest" @@ -1213,9 +1224,10 @@ def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no co if not os.path.isfile(auto_file) and not ctx.obj["using_webworkflow"]: click.secho(f"Auto file not found: {auto_file}", fg="red") sys.exit(1) - requested_installs = ctx.obj["backend"].libraries_from_imports( - auto_file, mod_names - ) + + auto_file_path = ctx.obj["backend"].get_auto_file_path(auto_file) + + requested_installs = libraries_from_code_py(auto_file_path, mod_names) else: requested_installs = modules requested_installs = sorted(set(requested_installs)) diff --git a/circup/backends.py b/circup/backends.py index 9edfc5e..96d9d90 100644 --- a/circup/backends.py +++ b/circup/backends.py @@ -9,12 +9,13 @@ import findimports import requests from requests.auth import HTTPBasicAuth -from circup.shared import DATA_DIR, BAD_FILE_FORMAT, extract_metadata, CPY_VERSION, _get_modules_file +from circup.shared import DATA_DIR, BAD_FILE_FORMAT, extract_metadata, _get_modules_file #: The location to store a local copy of code.py for use with --auto and # web workflow LOCAL_CODE_PY_COPY = os.path.join(DATA_DIR, "code.tmp.py") + class Backend: """ Backend parent class to be extended for workflow specific @@ -142,23 +143,6 @@ class Backend: """ raise NotImplementedError - def libraries_from_code_py(self, code_py, mod_names): - """ - Parse the given code.py file and return the imported libraries - - :param str code_py: Full path of the code.py file - :return: sequence of library names - """ - # pylint: disable=broad-except - try: - found_imports = findimports.find_imports(code_py) - except Exception as ex: # broad exception because anything could go wrong - self.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] class WebBackend(Backend): """ @@ -170,6 +154,7 @@ class WebBackend(Backend): self.LIB_DIR_PATH = "fs/lib/" self.host = host self.password = password + self.device_location = f"http://:{self.password}@{self.host}" def install_file_http(self, source, target): """ @@ -348,7 +333,9 @@ class WebBackend(Backend): if not module_name: # Must be a directory based module. module_name = os.path.basename(os.path.dirname(metadata["path"])) - major_version = CPY_VERSION.split(".")[0] + major_version = self.get_circuitpython_version(self.device_location)[0].split( + "." + )[0] bundle_platform = "{}mpy".format(major_version) bundle_path = os.path.join(bundle.lib_dir(bundle_platform), module_name) if os.path.isdir(bundle_path): @@ -378,6 +365,16 @@ class WebBackend(Backend): target = os.path.basename(source_path) self.install_file_http(source_path, library_path + target) + def get_auto_file_path(self, auto_file_path): + url = auto_file_path + auth = HTTPBasicAuth("", self.password) + r = requests.get(url, auth=auth) + r.raise_for_status() + with open(LOCAL_CODE_PY_COPY, "w", encoding="utf-8") as f: + f.write(r.text) + LOCAL_CODE_PY_COPY + return LOCAL_CODE_PY_COPY + def libraries_from_imports(self, code_py, mod_names): """ Parse the given code.py file and return the imported libraries @@ -428,6 +425,7 @@ class WebBackend(Backend): r.raise_for_status() self.install_dir_http(module.bundle_path, module.path) + class DiskBackend(Backend): """ Backend for interacting with a device via USB Workflow @@ -500,7 +498,9 @@ class DiskBackend(Backend): # Must be a directory based module. module_name = os.path.basename(os.path.dirname(metadata["path"])) - major_version = self.get_circuitpython_version(self.device_location)[0].split(".")[0] + major_version = self.get_circuitpython_version(self.device_location)[0].split( + "." + )[0] bundle_platform = "{}mpy".format(major_version) bundle_path = os.path.join(bundle.lib_dir(bundle_platform), module_name) if os.path.isdir(bundle_path): @@ -534,6 +534,9 @@ class DiskBackend(Backend): # Copy file. shutil.copyfile(source_path, target_path) + def get_auto_file_path(self, auto_file_path): + return auto_file_path + def libraries_from_imports(self, code_py, mod_names): """ Parse the given code.py file and return the imported libraries @@ -579,4 +582,4 @@ class DiskBackend(Backend): else: # Delete and copy file. os.remove(module.path) - shutil.copyfile(module.bundle_path, module.path) \ No newline at end of file + shutil.copyfile(module.bundle_path, module.path) diff --git a/circup/shared.py b/circup/shared.py index cc60166..5f157b1 100644 --- a/circup/shared.py +++ b/circup/shared.py @@ -10,9 +10,6 @@ BAD_FILE_FORMAT = "Invalid" #: The location of data files used by circup (following OS conventions). DATA_DIR = appdirs.user_data_dir(appname="circup", appauthor="adafruit") -#: The version of CircuitPython found on the connected device. -CPY_VERSION = "" - def _get_modules_file(path, logger): """ @@ -55,6 +52,7 @@ def _get_modules_file(path, logger): break return result + def extract_metadata(path, logger): """ Given a file path, return a dictionary containing metadata extracted from @@ -124,4 +122,4 @@ def extract_metadata(path, logger): if result: logger.info("Extracted metadata: %s", result) - return result \ No newline at end of file + return result