diff --git a/Makefile b/Makefile index 7277801..aa1afbd 100644 --- a/Makefile +++ b/Makefile @@ -67,8 +67,11 @@ generate-test-outputs: @rm -rf tests/outputs/with_hooks_dir @uv run efemel process "**/*.py" --cwd tests/inputs/basic --out tests/outputs/with_hooks_dir --hooks tests/hooks/multiple - @rm -rf tests/outputs/with_hooks_dir - @uv run efemel process "**/*.py" --cwd tests/inputs/process_data --out tests/outputs/process_data --pick user_data + @rm -rf tests/outputs/process_data_pick + @uv run efemel process "**/*.py" --cwd tests/inputs/process_data --out tests/outputs/process_data_pick --pick user_data + + @rm -rf tests/outputs/process_data_unwrap + @uv run efemel process "**/*.py" --cwd tests/inputs/process_data --out tests/outputs/process_data_unwrap --unwrap user_data # Clean build artifacts and cache files diff --git a/efemel/cli.py b/efemel/cli.py index 5e6b847..4c00fe8 100644 --- a/efemel/cli.py +++ b/efemel/cli.py @@ -56,7 +56,13 @@ def info(): multiple=True, help="Pick specific dictionary keys to extract (can be used multiple times)", ) -def process(file_pattern, out, flatten, cwd, env, workers, hooks, pick): +@click.option( + "--unwrap", + "-u", + multiple=True, + help="Extract specific values from the processed data dictionary, merging them (can be used multiple times)", +) +def process(file_pattern, out, flatten, cwd, env, workers, hooks, pick, unwrap): """Process Python files and extract public dictionary variables to JSON. FILE_PATTERN: Glob pattern to match Python files (e.g., "**/*.py") @@ -75,6 +81,9 @@ def process(file_pattern, out, flatten, cwd, env, workers, hooks, pick): if pick: hooks_manager.add("process_data", process_data_hooks.pick_data(pick)) + if unwrap: + hooks_manager.add("process_data", process_data_hooks.unwrap_data(unwrap)) + # Load user-defined hooks if a path is specified if hooks: if os.path.isfile(hooks): diff --git a/efemel/hooks/process_data.py b/efemel/hooks/process_data.py index 01dd226..133474c 100644 --- a/efemel/hooks/process_data.py +++ b/efemel/hooks/process_data.py @@ -7,3 +7,26 @@ def _pick(context): context["data"] = {key: value for key, value in context["data"].items() if key in keys} return _pick + + +def unwrap_data(keys): + """Extracts specific values from the processed data dictionary, merges them.""" + + def _unwrap(context): + if not keys: + return + + merged = {} + + for key in keys: + if key not in context["data"]: + continue + + value = context["data"][key] + + if isinstance(value, dict): + merged.update(value) + + context["data"] = merged + + return _unwrap diff --git a/tests/outputs/process_data/data.json b/tests/outputs/process_data_pick/data.json similarity index 100% rename from tests/outputs/process_data/data.json rename to tests/outputs/process_data_pick/data.json diff --git a/tests/outputs/process_data_unwrap/data.json b/tests/outputs/process_data_unwrap/data.json new file mode 100644 index 0000000..161840b --- /dev/null +++ b/tests/outputs/process_data_unwrap/data.json @@ -0,0 +1,10 @@ +{ + "admin": { + "name": "Admin User", + "role": "admin" + }, + "guest": { + "name": "Guest User", + "role": "guest" + } +} \ No newline at end of file diff --git a/tests/outputs/with_hooks_dir/simple_test_1_2_3.json b/tests/outputs/with_hooks_dir/simple_test_1_2_3.json new file mode 100644 index 0000000..5b621d9 --- /dev/null +++ b/tests/outputs/with_hooks_dir/simple_test_1_2_3.json @@ -0,0 +1,10 @@ +{ + "test_dict": { + "key": "value", + "number": 42 + }, + "config": { + "app": "test", + "version": "1.0" + } +} \ No newline at end of file diff --git a/tests/outputs/with_hooks_dir/test_data_1_2_3.json b/tests/outputs/with_hooks_dir/test_data_1_2_3.json new file mode 100644 index 0000000..185be7f --- /dev/null +++ b/tests/outputs/with_hooks_dir/test_data_1_2_3.json @@ -0,0 +1,33 @@ +{ + "config": { + "name": "test_app", + "version": "1.0.0", + "debug": true, + "features": [ + "auth", + "api", + "ui" + ] + }, + "settings": { + "database": { + "host": "localhost", + "port": 5432, + "name": "app_db" + }, + "cache": { + "type": "redis", + "ttl": 3600 + } + }, + "user_data": { + "admin": { + "name": "Admin User", + "role": "admin" + }, + "guest": { + "name": "Guest User", + "role": "guest" + } + } +} \ No newline at end of file diff --git a/tests/outputs/with_hooks_dir/test_dir/config_1_2_3.json b/tests/outputs/with_hooks_dir/test_dir/config_1_2_3.json new file mode 100644 index 0000000..aac2fc9 --- /dev/null +++ b/tests/outputs/with_hooks_dir/test_dir/config_1_2_3.json @@ -0,0 +1,10 @@ +{ + "app_config": { + "app_name": "my_app", + "port": 8080 + }, + "database_config": { + "url": "postgresql://localhost/mydb", + "pool_size": 10 + } +} \ No newline at end of file diff --git a/tests/outputs/with_hooks_dir/test_dir/subdir/nested_1_2_3.json b/tests/outputs/with_hooks_dir/test_dir/subdir/nested_1_2_3.json new file mode 100644 index 0000000..3c40433 --- /dev/null +++ b/tests/outputs/with_hooks_dir/test_dir/subdir/nested_1_2_3.json @@ -0,0 +1,14 @@ +{ + "nested_data": { + "level": "deep", + "items": [ + 1, + 2, + 3 + ] + }, + "metadata": { + "created_by": "test", + "timestamp": "2025-01-01" + } +} \ No newline at end of file diff --git a/tests/outputs/with_hooks_dir/test_dir/utils_1_2_3.json b/tests/outputs/with_hooks_dir/test_dir/utils_1_2_3.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/tests/outputs/with_hooks_dir/test_dir/utils_1_2_3.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py index 0f1ff40..a1e3276 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -51,9 +51,15 @@ def get_test_scenarios(): { "name": "process data - pick", "inputs_dir": test_dir / "inputs/process_data", - "outputs_dir": test_dir / "outputs/process_data", + "outputs_dir": test_dir / "outputs/process_data_pick", "process_args": ["--pick", "user_data"], }, + { + "name": "process data - unwrap", + "inputs_dir": test_dir / "inputs/process_data", + "outputs_dir": test_dir / "outputs/process_data_unwrap", + "process_args": ["--unwrap", "user_data"], + }, ]