diff --git a/robocorp-code/src/robocorp_code/plugins/resolve_interpreter.py b/robocorp-code/src/robocorp_code/plugins/resolve_interpreter.py index fe4541dbb3..09ed02f640 100644 --- a/robocorp-code/src/robocorp_code/plugins/resolve_interpreter.py +++ b/robocorp-code/src/robocorp_code/plugins/resolve_interpreter.py @@ -16,7 +16,7 @@ try: from robocorp_code.rcc import Rcc # noqa -except: +except ImportError: # Automatically add it to the path if executing as a plugin the first time. sys.path.append( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -48,11 +48,7 @@ _CachedFileMTimeInfo = namedtuple("_CachedFileMTimeInfo", "st_mtime, st_size, path") -_CachedInterpreterMTime = Tuple[ - Optional[_CachedFileMTimeInfo], - Optional[_CachedFileMTimeInfo], - Optional[_CachedFileMTimeInfo], -] +_CachedInterpreterMTime = Tuple[Optional[_CachedFileMTimeInfo], ...] def _get_mtime_cache_info(file_path: Path) -> Optional[_CachedFileMTimeInfo]: @@ -62,7 +58,7 @@ def _get_mtime_cache_info(file_path: Path) -> Optional[_CachedFileMTimeInfo]: try: stat = file_path.stat() return _CachedFileMTimeInfo(stat.st_mtime, stat.st_size, str(file_path)) - except: + except Exception: # It could be removed in the meanwhile. log.exception(f"Unable to get mtime info for: {file_path}") return None @@ -260,6 +256,12 @@ def _obtain_mtime( env_json_path_file_info.mtime_info if env_json_path_file_info else None, ) + def _obtain_package_yaml_mtime( + self, + package_yaml_file_info: _CachedFileInfo, + ) -> _CachedInterpreterMTime: + return (package_yaml_file_info.mtime_info,) + def is_cache_valid( self, robot_yaml_file_info: _CachedFileInfo, @@ -270,6 +272,12 @@ def is_cache_valid( robot_yaml_file_info, conda_config_file_info, env_json_path_file_info ) + def is_package_yaml_cache_valid( + self, + package_yaml_file_info: _CachedFileInfo, + ) -> bool: + return self._mtime == self._obtain_package_yaml_mtime(package_yaml_file_info) + class _CacheInfo(object): """ @@ -375,13 +383,13 @@ def touch(self, info: IInterpreterInfo, force: bool = False): temp_dir_path = Path(temp_dir) try: temp_dir_path.mkdir(exist_ok=True) - except: + except Exception: log.exception(f"Error making dir: {temp_dir_path}") try: recycle_path: Path = temp_dir_path / "recycle.now" recycle_path.touch() - except: + except Exception: log.exception(f"Error touching: {recycle_path}") @@ -499,19 +507,34 @@ def _compute_base_interpreter_info_for_doc_uri( fs_path = Path(uris.to_fs_path(doc_uri)) # Note: there's a use-case where a directory may be passed to # compute as the doc_uri, so, handle that too. + found_robot_yaml = False + found_package_yaml = False + for path in itertools.chain(iter([fs_path]), fs_path.parents): robot_yaml: Path = path / "robot.yaml" if robot_yaml.exists(): + found_robot_yaml = True + break + + package_yaml: Path = path / "package.yaml" + found_package_yaml = True + if package_yaml.exists(): + found_package_yaml = True break else: # i.e.: Could not find any robot.yaml in the structure. log.debug("Could not find robot.yaml for: %s", fs_path) return None + if found_package_yaml: + # RCC does not have a way to consume the package.yaml directly, + # so, what we do at this point is generate the ` + robot_yaml_file_info = _CacheInfo.get_file_info(package_yaml) + # Ok, we have the robot_yaml, so, we should be able to run RCC with it. try: robot_yaml_file_info = _CacheInfo.get_file_info(robot_yaml) - except: + except Exception: log.exception("Error collecting info from: %s", robot_yaml) return None @@ -527,7 +550,7 @@ def _compute_base_interpreter_info_for_doc_uri( try: conda_config_file_info = _CacheInfo.get_file_info(conda_config_path) - except: + except Exception: log.exception("Error collecting info from: %s", conda_config_path) return None @@ -536,7 +559,7 @@ def _compute_base_interpreter_info_for_doc_uri( if env_json_path.exists(): try: env_json_path_file_info = _CacheInfo.get_file_info(env_json_path) - except: + except Exception: log.exception("Error collecting info from: %s", env_json_path) return None @@ -547,7 +570,7 @@ def _compute_base_interpreter_info_for_doc_uri( pm, ) - except: + except Exception: log.exception(f"Error getting interpreter info for: {doc_uri}") return None diff --git a/robocorp-code/tests/robocorp_code_tests/_resources/action_package/my_action.py b/robocorp-code/tests/robocorp_code_tests/_resources/action_package/my_action.py new file mode 100644 index 0000000000..c310cc95ed --- /dev/null +++ b/robocorp-code/tests/robocorp_code_tests/_resources/action_package/my_action.py @@ -0,0 +1,6 @@ +from robocorp.actions import action + + +@action +def my_action() -> str: + return "result" diff --git a/robocorp-code/tests/robocorp_code_tests/_resources/action_package/package.yaml b/robocorp-code/tests/robocorp_code_tests/_resources/action_package/package.yaml new file mode 100644 index 0000000000..bf6382c033 --- /dev/null +++ b/robocorp-code/tests/robocorp_code_tests/_resources/action_package/package.yaml @@ -0,0 +1,59 @@ +# Required: Defines the name of the action package. +name: My awesome cookie maker + +# Required: A description of what's in the action package. +description: This does cookies + +# Required: The current version of this action package. +version: 0.2.3 + +# Required: A link to where the documentation on the package lives. +documentation: https://github.com/robocorp/actions-cookbook/blob/master/database-postgres/README.md + +# Required: +# Defines the Python dependencies which should be used to launch the +# actions. +# The action server will automatically create a new python environment +# based on this specification. +# Note that at this point the only operator supported is `=`. +dependencies: + conda-forge: + # This section is required: at least the python version must be specified. + - python=3.10.12 + - pip=23.2.1 + - robocorp-truststore=0.8.0 + + pip: + # This section is required: at least `robocorp-actions` must + # be specified. + # Note: robocorp-actions is special case because the action server + # has coupling with the library. This means that if the version of + # robocorp-actions is not pinned to a value the action server will + # select a version based on a version that's known to work with the + # current version of the action server. + # If the version is pinned, then the action server will validate + # if the given version can be used with it. + - robocorp-actions + - robocorp=1.4.3 + - pytz=2023.3 + + local-wheels: + # This section is optional. + # It's possible to add references to a wheel/.zip relative + # to the folder containing this package.yaml. + - ./wheels/my-wheel.whl + +post-install: + # This can be used to run custom commands which will still affect the + # environment after it is created (the changes to the environment will + # be cached). + - python -m robocorp.browser install chrome --isolated + +packaging: + # This section is optional. + # By default all files and folders in this directory are packaged when uploaded. + # Add exclusion rules below (expects glob format: https://docs.python.org/3/library/glob.html) + exclude: + - '*.temp' + - '.vscode/**' + \ No newline at end of file diff --git a/robocorp-code/tests/robocorp_code_tests/test_resolve_interpreter.py b/robocorp-code/tests/robocorp_code_tests/test_resolve_interpreter.py index 2e15b52b34..20b83f5d02 100644 --- a/robocorp-code/tests/robocorp_code_tests/test_resolve_interpreter.py +++ b/robocorp-code/tests/robocorp_code_tests/test_resolve_interpreter.py @@ -161,6 +161,40 @@ def test_fix_entry(): ) +def test_resolve_interpreter_action_package( + cases: CasesFixture, + config_provider: IConfigProvider, + rcc_conda_installed, + rcc_patch, +) -> None: + from robocorp_ls_core import uris + from robocorp_ls_core.constants import NULL + from robocorp_ls_core.ep_providers import ( + EPConfigurationProvider, + EPEndPointProvider, + ) + from robocorp_ls_core.pluginmanager import PluginManager + + from robocorp_code.plugins.resolve_interpreter import ( + RobocorpResolveInterpreter, + _CacheInfo, + ) + + _CacheInfo._cache_hit_files = 0 + + pm = PluginManager() + pm.set_instance(EPConfigurationProvider, config_provider) + pm.set_instance(EPEndPointProvider, NULL) + + resolve_interpreter = RobocorpResolveInterpreter(weak_pm=weakref.ref(pm)) + path = cases.get_path("action_package") + rcc_patch.apply() + interpreter_info = resolve_interpreter.get_interpreter_info_for_doc_uri( + uris.from_fs_path(path) + ) + print(interpreter_info) + + def test_resolve_interpreter( cases: CasesFixture, config_provider: IConfigProvider,