|
8 | 8 | import pstats |
9 | 9 | import subprocess |
10 | 10 | import sys |
| 11 | +import threading |
11 | 12 | import traceback |
12 | 13 | from collections import OrderedDict |
13 | 14 | from collections.abc import AsyncGenerator, Iterable |
@@ -558,10 +559,10 @@ async def terminate_sequence(self, name: str) -> bool: |
558 | 559 |
|
559 | 560 | return True |
560 | 561 |
|
561 | | - async def read_all(self, config_files: Iterable[Path] = None) -> AllAppConfig: |
| 562 | + async def read_all(self, config_files: Iterable[Path]) -> AllAppConfig: |
562 | 563 | config_files = config_files or self.dependency_manager.config_files |
563 | 564 |
|
564 | | - async def config_model_factory() -> AsyncGenerator[AllAppConfig, None, None]: |
| 565 | + async def config_model_factory() -> AsyncGenerator[AllAppConfig, None]: |
565 | 566 | """Creates a generator that sets the config_path of app configs""" |
566 | 567 | for path in config_files: |
567 | 568 | @ade.wrap_async(self.error, self.AD.app_dir, "Reading user apps") |
@@ -608,7 +609,8 @@ def update(d1: dict, d2: dict) -> dict: |
608 | 609 |
|
609 | 610 | async def check_app_config_files(self, update_actions: UpdateActions): |
610 | 611 | """Updates self.mtimes_config and self.app_config""" |
611 | | - files = await self.get_app_config_files() |
| 612 | + # get_files_in_other_thread = utils.executor_decorator(self.get_app_config_files) |
| 613 | + files = await self.get_app_config_files_async() |
612 | 614 | self.dependency_manager.app_deps.update(files) |
613 | 615 |
|
614 | 616 | # If there were config file changes |
@@ -686,6 +688,7 @@ def read_config_file(self, file: Path) -> AllAppConfig: |
686 | 688 |
|
687 | 689 | This function is primarily used by the create/edit/remove app methods that write yaml files. |
688 | 690 | """ |
| 691 | + assert threading.current_thread().name.startswith("ThreadPool") |
689 | 692 | raw_cfg = utils.read_config_file(file, app_config=True) |
690 | 693 | if not bool(raw_cfg): |
691 | 694 | self.logger.warning( |
@@ -849,7 +852,7 @@ def _process_import_paths(self): |
849 | 852 | case 'default' | 'expert' | None: |
850 | 853 | # Get unique set of the absolute paths of all the subdirectories containing python files |
851 | 854 | python_file_parents = set( |
852 | | - f.parent.resolve() for f in Path(self.AD.app_dir).rglob("*.py") |
| 855 | + f.parent.resolve() for f in self.get_python_files() |
853 | 856 | ) |
854 | 857 |
|
855 | 858 | # Filter out any that have __init__.py files in them |
@@ -907,43 +910,65 @@ async def _init_dep_manager(self): |
907 | 910 | async def safe_dep_create(self: "AppManagement"): |
908 | 911 | try: |
909 | 912 | self.dependency_manager = DependencyManager( |
910 | | - python_files=await self.get_python_files(), |
911 | | - config_files=await self.get_app_config_files() |
| 913 | + python_files=await self.get_python_files_async(), |
| 914 | + config_files=await self.get_app_config_files_async() |
912 | 915 | ) |
913 | 916 | self.config_filecheck.mtimes = {} |
914 | 917 | self.python_filecheck.mtimes = {} |
915 | 918 | except ValidationError as e: |
916 | | - raise ade.BadAppConfigFile("Error creating dependency manager") from e |
| 919 | + raise ade.DependencyManagerError("Failed to create dependency manager") from e |
917 | 920 | except ade.AppDaemonException as e: |
918 | 921 | raise e |
919 | 922 |
|
920 | 923 | await safe_dep_create(self) |
921 | 924 |
|
922 | | - @utils.executor_decorator |
923 | | - def get_python_files(self) -> Iterable[Path]: |
924 | | - """Iterates through ``*.py`` in the app directory. Excludes directory names defined in exclude_dirs and with a "." character. Also excludes files that aren't readable.""" |
| 925 | + def get_python_files(self) -> set[Path]: |
| 926 | + """Get a set of valid Python files in the app directory. |
| 927 | +
|
| 928 | + Valid files are ones that are readable, not inside an excluded directory, and not starting with a "." character. |
| 929 | + """ |
| 930 | + assert threading.current_thread().name.startswith("ThreadPool") |
925 | 931 | return set( |
926 | | - f |
927 | | - for f in self.AD.app_dir.resolve().rglob("*.py") |
928 | | - if f.parent.name not in self.AD.exclude_dirs # apply exclude_dirs |
929 | | - and "." not in f.parent.name # also excludes *.egg-info folders |
930 | | - and os.access(f, os.R_OK) # skip unreadable files |
| 932 | + utils.recursive_get_files( |
| 933 | + base=self.AD.app_dir.resolve(), |
| 934 | + suffix=".py", |
| 935 | + exclude=set(self.AD.exclude_dirs), |
| 936 | + ) |
931 | 937 | ) |
932 | 938 |
|
933 | 939 | @utils.executor_decorator |
934 | | - def get_app_config_files(self) -> Iterable[Path]: |
935 | | - """Iterates through config files in the config directory. Excludes directory names defined in exclude_dirs and files with a "." character. Also excludes files that aren't readable.""" |
| 940 | + def get_python_files_async(self) -> set[Path]: |
| 941 | + """Get a set of valid app config files in the app directory. |
| 942 | +
|
| 943 | + Valid files are ones that are readable, not inside an excluded directory, and not starting with a "." character. |
| 944 | + """ |
| 945 | + return self.get_python_files() |
| 946 | + |
| 947 | + def get_app_config_files(self) -> set[Path]: |
| 948 | + """Get a set of valid app fonfig files in the app directory. |
| 949 | +
|
| 950 | + Valid files are ones that are readable, not inside an excluded directory, and not starting with a "." character. |
| 951 | + """ |
| 952 | + assert threading.current_thread().name.startswith("ThreadPool") |
936 | 953 | return set( |
937 | | - f |
938 | | - for f in self.AD.app_dir.resolve().rglob(f"*{self.ext}") |
939 | | - if f.parent.name not in self.AD.exclude_dirs # apply exclude_dirs |
940 | | - and "." not in f.stem |
941 | | - and os.access(f, os.R_OK) # skip unreadable files |
| 954 | + utils.recursive_get_files( |
| 955 | + base=self.AD.app_dir.resolve(), |
| 956 | + suffix=self.ext, |
| 957 | + exclude=set(self.AD.exclude_dirs), |
| 958 | + ) |
942 | 959 | ) |
943 | 960 |
|
| 961 | + @utils.executor_decorator |
| 962 | + def get_app_config_files_async(self) -> set[Path]: |
| 963 | + """Get a set of valid app config files in the app directory. |
| 964 | +
|
| 965 | + Valid files are ones that are readable, not inside an excluded directory, and not starting with a "." character. |
| 966 | + """ |
| 967 | + return self.get_app_config_files() |
| 968 | + |
944 | 969 | async def check_app_python_files(self, update_actions: UpdateActions): |
945 | 970 | """Checks the python files in the app directory. Part of self.check_app_updates sequence""" |
946 | | - files = await self.get_python_files() |
| 971 | + files = await self.get_python_files_async() |
947 | 972 | self.dependency_manager.update_python_files(files) |
948 | 973 |
|
949 | 974 | # We only need to init the modules necessary for the new apps, not reloaded ones |
|
0 commit comments