diff --git a/appdaemon/app_management.py b/appdaemon/app_management.py index 42547823b..0db27627a 100644 --- a/appdaemon/app_management.py +++ b/appdaemon/app_management.py @@ -1,15 +1,15 @@ -import sys -import traceback -import uuid -import os -import importlib -import subprocess +import asyncio +import copy import cProfile +import importlib import io -import pstats import logging -import asyncio -import copy +import os +import pstats +import subprocess +import sys +import traceback +import uuid from collections import OrderedDict import appdaemon.utils as utils @@ -884,18 +884,37 @@ async def check_app_updates(self, plugin: str = None, mode: str = "normal"): # found_files = [] modules = [] + + # One time static path additions + if mode == "init": + # Add root only if we are doing advanced importing + if self.AD.import_method != "normal": + self.logger.info("Adding %s to module import path", self.AD.app_dir) + sys.path.insert(0, self.AD.app_dir) + self.module_dirs.append(self.AD.app_dir) + + # Add any aditional import paths + for path in self.AD.import_paths: + if os.path.isdir(path): + self.logger.info("Adding %s to module import path", path) + sys.path.insert(0, path) + self.module_dirs.append(path) + else: + self.logger.warning(f"import_path {path} does not exist - not adding to path") + for root, subdirs, files in await utils.run_in_executor(self, os.walk, self.AD.app_dir, topdown=True): - # print(root, subdirs, files) + # print(root) # # Prune dir list # subdirs[:] = [d for d in subdirs if d not in self.AD.exclude_dirs and "." not in d] - if root[-11:] != "__pycache__" and root[0] != ".": - if root not in self.module_dirs: - self.logger.info("Adding %s to module import path", root) - sys.path.insert(0, root) - self.module_dirs.append(root) + if self.AD.import_method == "normal": + if root[-11:] != "__pycache__" and root[0] != ".": + if root not in self.module_dirs: + self.logger.info("Adding %s to module import path", root) + sys.path.insert(0, root) + self.module_dirs.append(root) for file in files: if file[-3:] == ".py" and file[0] != ".": diff --git a/appdaemon/appdaemon.py b/appdaemon/appdaemon.py index 07e1771d8..494755189 100755 --- a/appdaemon/appdaemon.py +++ b/appdaemon/appdaemon.py @@ -160,6 +160,12 @@ def __init__(self, logging, loop, **kwargs): self.use_stream = False utils.process_arg(self, "use_stream", kwargs) + self.import_paths = [] + utils.process_arg(self, "import_paths", kwargs) + + self.import_method = "normal" + utils.process_arg(self, "import_method", kwargs) + self.namespaces = {} utils.process_arg(self, "namespaces", kwargs) diff --git a/docs/CONFIGURE.rst b/docs/CONFIGURE.rst index efd5db0d3..045a4e9b9 100644 --- a/docs/CONFIGURE.rst +++ b/docs/CONFIGURE.rst @@ -188,6 +188,14 @@ The following options are available under the ``appdaemon`` section: This can be useful for troubleshooting thread starvation issues. - No + * - import_method + - By default, AppDaemon will add all subdirectories of appdir to the python interpreters path. This is intended to simplfy imports for simple applications. There is also an expert mode that will add just the top level appdir dircetory to the path allowing the user to use a more traditional hierarchical approach to imports. The default value for this directive is `normal`, set it to `expert` to enable the expert mode. + - No + + * - import_paths + - Use this directive to add additional arbitary directories to the python interpreter's search path. Directories must be fully qualified. + - No + .. _filters: diff --git a/docs/HISTORY.md b/docs/HISTORY.md index 599f9575f..41a76c2e0 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -7,6 +7,8 @@ - Added Pirateweather widget - contributed by [Dave Dixon](https://github.com/DaveDixon) - Updated docker image to use Alpine 3.18 and Python 3.11 - Added ability to know the topic associated with an MQTT message decode error +- Added `expert` mode for python imports via the `import_method` directive +- Added `import_path` directive to enable python imports from arbitary paths **Fixes**