aiida-pythonjob 0.4.4__tar.gz → 0.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {aiida_pythonjob-0.4.4/src/aiida_pythonjob.egg-info → aiida_pythonjob-0.4.6}/PKG-INFO +2 -2
  2. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/pyproject.toml +1 -1
  3. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/__init__.py +1 -1
  4. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/launch.py +2 -2
  5. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/parsers/utils.py +6 -8
  6. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/utils.py +10 -8
  7. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6/src/aiida_pythonjob.egg-info}/PKG-INFO +2 -2
  8. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob.egg-info/requires.txt +1 -1
  9. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_parser.py +22 -1
  10. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_pyfunction.py +8 -4
  11. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/LICENSE +0 -0
  12. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/README.md +0 -0
  13. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/setup.cfg +0 -0
  14. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/calculations/__init__.py +0 -0
  15. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/calculations/common.py +0 -0
  16. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/calculations/pyfunction.py +0 -0
  17. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/calculations/pythonjob.py +0 -0
  18. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/calculations/tasks.py +0 -0
  19. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/calculations/utils.py +0 -0
  20. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/config.py +0 -0
  21. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/__init__.py +0 -0
  22. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/atoms.py +0 -0
  23. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/common_data.py +0 -0
  24. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/deserializer.py +0 -0
  25. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/jsonable_data.py +0 -0
  26. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/pickled_data.py +0 -0
  27. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/serializer.py +0 -0
  28. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/data/utils.py +0 -0
  29. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/decorator.py +0 -0
  30. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/parsers/__init__.py +0 -0
  31. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob/parsers/pythonjob.py +0 -0
  32. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob.egg-info/SOURCES.txt +0 -0
  33. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob.egg-info/dependency_links.txt +0 -0
  34. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob.egg-info/entry_points.txt +0 -0
  35. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/src/aiida_pythonjob.egg-info/top_level.txt +0 -0
  36. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_async.py +0 -0
  37. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_create_env.py +0 -0
  38. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_data.py +0 -0
  39. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_entry_points.py +0 -0
  40. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_jsonable_data.py +0 -0
  41. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_monitor.py +0 -0
  42. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_pickled_data.py +0 -0
  43. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_pythonjob.py +0 -0
  44. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_serializer.py +0 -0
  45. {aiida_pythonjob-0.4.4 → aiida_pythonjob-0.4.6}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiida-pythonjob
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: Run Python functions on a remote computer.
5
5
  Author-email: Xing Wang <xingwang1991@gmail.com>
6
6
  License: MIT License
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
38
38
  License-File: LICENSE
39
39
  Requires-Dist: aiida-core<3,>=2.7.1
40
40
  Requires-Dist: ase
41
- Requires-Dist: node-graph>=0.3.9
41
+ Requires-Dist: node-graph~=0.4.0
42
42
  Provides-Extra: test
43
43
  Requires-Dist: pgtest>=1.3.1,~=1.3; extra == "test"
44
44
  Requires-Dist: coverage~=7.0; extra == "test"
@@ -22,7 +22,7 @@ requires-python = ">=3.9"
22
22
  dependencies = [
23
23
  "aiida-core>=2.7.1,<3",
24
24
  "ase",
25
- "node-graph>=0.3.9",
25
+ "node-graph~=0.4.0",
26
26
  ]
27
27
 
28
28
  [project.optional-dependencies]
@@ -1,6 +1,6 @@
1
1
  """AiiDA plugin that run Python function on remote computers."""
2
2
 
3
- __version__ = "0.4.4"
3
+ __version__ = "0.4.6"
4
4
 
5
5
  from node_graph import socket_spec as spec
6
6
 
@@ -22,8 +22,8 @@ def _unwrap_callable(func: Any) -> Callable[..., Any] | None:
22
22
  """
23
23
  if func is None:
24
24
  return None
25
- if isinstance(func, BaseHandle) and hasattr(func, "_func"):
26
- return func._func
25
+ if isinstance(func, BaseHandle) and hasattr(func, "_callable"):
26
+ return func._callable
27
27
  if getattr(func, "is_process_function", False):
28
28
  # aiida process_function wrapper (e.g., calcfunction/workfunction)
29
29
  return func.func
@@ -44,8 +44,7 @@ def parse_outputs(
44
44
  spec = _ensure_spec(output_spec)
45
45
 
46
46
  fields = spec.fields or {}
47
- is_dyn = bool(spec.dynamic)
48
- item_spec = spec.item if is_dyn else None
47
+ is_dyn = spec.meta.dynamic
49
48
 
50
49
  if already_serialized(results):
51
50
  return {"result": results}, None
@@ -105,14 +104,13 @@ def parse_outputs(
105
104
  return None, exit_codes.ERROR_MISSING_OUTPUT
106
105
  # dynamic items
107
106
  if is_dyn:
108
- if item_spec is None:
109
- logger.warning("Outputs marked dynamic but missing 'item' schema; treating as ANY.")
110
- for name, value in remaining.items():
111
- outs[name] = serialize_ports(
112
- value,
113
- item_spec or SocketSpec(identifier="node_graph.any"),
107
+ outs.update(
108
+ serialize_ports(
109
+ remaining,
110
+ spec or SocketSpec(identifier="node_graph.any"),
114
111
  serializers=serializers,
115
112
  )
113
+ )
116
114
  return outs, None
117
115
  # not dynamic -> leftovers are unexpected (warn but continue)
118
116
  if remaining:
@@ -16,6 +16,7 @@ from typing import (
16
16
 
17
17
  from aiida.common.exceptions import NotExistent
18
18
  from aiida.orm import Computer, InstalledCode, Str, User, load_code, load_computer
19
+ from node_graph.socket_meta import SocketMeta
19
20
  from node_graph.socket_spec import SocketSpec
20
21
 
21
22
  from aiida_pythonjob.data.serializer import general_serializer
@@ -298,10 +299,6 @@ def serialize_ports(
298
299
 
299
300
  out: Dict[str, Any] = {}
300
301
  fields = spec.fields or {}
301
- is_dyn = bool(spec.dynamic)
302
- item_spec = spec.item if is_dyn else None
303
- allow_extra = _has_var_kwargs(spec)
304
- catch_schema = item_spec or SocketSpec(identifier="node_graph.any")
305
302
 
306
303
  for key, value in python_data.items():
307
304
  if key in fields:
@@ -313,10 +310,15 @@ def serialize_ports(
313
310
  out[key] = serialize_ports(value, child_spec, serializers=serializers)
314
311
  else:
315
312
  out[key] = general_serializer(value, serializers=serializers, store=False)
316
- elif (is_dyn and item_spec is not None) or allow_extra:
317
- schema = item_spec if (is_dyn and item_spec is not None) else catch_schema
318
- if schema.is_namespace():
319
- out[key] = serialize_ports(value, schema, serializers=serializers)
313
+ elif spec.meta.dynamic:
314
+ if spec.item is None:
315
+ if isinstance(value, dict):
316
+ item = SocketSpec(identifier="node_graph.namespace", meta=SocketMeta(dynamic=True))
317
+ out[key] = serialize_ports(value, item, serializers=serializers)
318
+ else:
319
+ out[key] = general_serializer(value, serializers=serializers, store=False)
320
+ elif spec.item.is_namespace():
321
+ out[key] = serialize_ports(value, spec.item, serializers=serializers)
320
322
  else:
321
323
  out[key] = general_serializer(value, serializers=serializers, store=False)
322
324
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiida-pythonjob
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: Run Python functions on a remote computer.
5
5
  Author-email: Xing Wang <xingwang1991@gmail.com>
6
6
  License: MIT License
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
38
38
  License-File: LICENSE
39
39
  Requires-Dist: aiida-core<3,>=2.7.1
40
40
  Requires-Dist: ase
41
- Requires-Dist: node-graph>=0.3.9
41
+ Requires-Dist: node-graph~=0.4.0
42
42
  Provides-Extra: test
43
43
  Requires-Dist: pgtest>=1.3.1,~=1.3; extra == "test"
44
44
  Requires-Dist: coverage~=7.0; extra == "test"
@@ -1,6 +1,6 @@
1
1
  aiida-core<3,>=2.7.1
2
2
  ase
3
- node-graph>=0.3.9
3
+ node-graph~=0.4.0
4
4
 
5
5
  [docs]
6
6
  sphinx_rtd_theme
@@ -10,7 +10,7 @@ import pytest
10
10
  from aiida import orm
11
11
  from aiida.cmdline.utils.common import get_workchain_report
12
12
  from aiida.common.links import LinkType
13
- from node_graph.socket_spec import namespace
13
+ from node_graph.socket_spec import dynamic, namespace
14
14
 
15
15
  from aiida_pythonjob.data.deserializer import all_deserializers
16
16
  from aiida_pythonjob.data.serializer import all_serializers
@@ -136,6 +136,27 @@ def test_no_output_file(fixture_localhost):
136
136
  assert exit_code == parser.exit_codes.ERROR_READING_OUTPUT_FILE
137
137
 
138
138
 
139
+ def test_dynamic_spec(fixture_localhost):
140
+ # Test with dynamic spec without item
141
+ result = {"a": 1, "b": 2, "nested": {"c": 3}}
142
+ spec_data = {"outputs_spec": dynamic().to_dict()}
143
+ parser = create_parser(result, spec_data)
144
+ exit_code = parser.parse()
145
+ assert exit_code is None
146
+ assert parser.outputs["a"] == 1
147
+ assert parser.outputs["b"] == 2
148
+ assert isinstance(parser.outputs["nested"], dict)
149
+ assert parser.outputs["nested"]["c"] == 3
150
+ # Test with dynamic spec with Any item
151
+ spec_data = {"outputs_spec": dynamic(Any).to_dict()}
152
+ parser = create_parser(result, spec_data)
153
+ exit_code = parser.parse()
154
+ assert exit_code is None
155
+ assert parser.outputs["a"] == 1
156
+ assert parser.outputs["b"] == 2
157
+ assert isinstance(parser.outputs["nested"], orm.Dict)
158
+
159
+
139
160
  @pytest.mark.parametrize(
140
161
  "error_type, status",
141
162
  [
@@ -258,7 +258,7 @@ def test_top_level_outputs_dynamic():
258
258
  def test_dynamic_rows():
259
259
  """Test function with dynamic rows."""
260
260
 
261
- row = spec.namespace(sum=any, product=any)
261
+ row = spec.namespace(sum=any, product=spec.dynamic())
262
262
 
263
263
  @pyfunction(outputs=spec.dynamic(row, sum=int))
264
264
  def test_dynamic_rows(data: spec.dynamic(row, sum=int)):
@@ -267,9 +267,9 @@ def test_dynamic_rows():
267
267
  result, node = run_get_node(
268
268
  test_dynamic_rows,
269
269
  data={
270
- "data_0": {"sum": 0, "product": 0},
271
- "data_1": {"sum": 1, "product": 2},
272
- "data_2": {"sum": 2, "product": 4},
270
+ "data_0": {"sum": 0, "product": {"a": 0}},
271
+ "data_1": {"sum": 1, "product": {"a": 2, "b": {"c": 3}}},
272
+ "data_2": {"sum": 2, "product": {"a": 4}},
273
273
  "sum": 1,
274
274
  },
275
275
  )
@@ -277,9 +277,13 @@ def test_dynamic_rows():
277
277
  # inputs should be serialized as dynamic rows
278
278
  assert node.inputs.function_inputs.data.sum.value == 1
279
279
  assert node.inputs.function_inputs.data.data_0.sum.value == 0
280
+ assert node.inputs.function_inputs.data.data_1.product.a.value == 2
281
+ assert node.inputs.function_inputs.data.data_1.product.b.c.value == 3
280
282
  # outputs should be serialized as dynamic rows
281
283
  assert node.outputs.sum.value == 1
282
284
  assert node.outputs.data_0.sum.value == 0
285
+ assert node.outputs.data_1.product.a.value == 2
286
+ assert node.outputs.data_1.product.b.c.value == 3
283
287
 
284
288
 
285
289
  def test_only_data_with_value():
File without changes