diff --git a/platformio/home/rpc/handlers/base.py b/platformio/home/rpc/handlers/base.py index 7b3f5d8f46..97a1ec606a 100644 --- a/platformio/home/rpc/handlers/base.py +++ b/platformio/home/rpc/handlers/base.py @@ -13,5 +13,58 @@ # limitations under the License. +import inspect +import os + + class BaseRPCHandler: + PATH_ARGUMENT_MARKERS = ( + "path", + "paths", + "dir", + "dirs", + "directory", + "directories", + "file", + "files", + "folder", + "folders", + ) + factory = None + + def __getattribute__(self, name): + attr = super().__getattribute__(name) + if name.startswith("_") or not callable(attr): + return attr + + def wrapped(*args, **kwargs): + args, kwargs = self._normalize_path_arguments(attr, args, kwargs) + return attr(*args, **kwargs) + + return wrapped + + def _normalize_path_arguments(self, method, args, kwargs): + bound_args = inspect.signature(method).bind_partial(*args, **kwargs) + for param_name, value in bound_args.arguments.items(): + if not self._is_path_argument(param_name): + continue + bound_args.arguments[param_name] = self._normalize_path_value(value) + return bound_args.args, bound_args.kwargs + + def _is_path_argument(self, param_name): + parts = param_name.lower().split("_") + return any(marker in parts for marker in self.PATH_ARGUMENT_MARKERS) + + def _normalize_path_value(self, value): + if not value: + return value + if isinstance(value, str): + return os.path.normpath(value) + if isinstance(value, list): + return [self._normalize_path_value(item) for item in value] + if isinstance(value, tuple): + return tuple(self._normalize_path_value(item) for item in value) + if isinstance(value, set): + return {self._normalize_path_value(item) for item in value} + return value