oocana-python-executor 0.16.13__tar.gz → 0.16.15__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 (20) hide show
  1. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/PKG-INFO +1 -1
  2. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/pyproject.toml +1 -1
  3. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/context.py +3 -0
  4. oocana_python_executor-0.16.15/python_executor/credential.py +48 -0
  5. oocana_python_executor-0.16.15/tests/test_credential.py +106 -0
  6. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/__init__.py +0 -0
  7. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/block.py +0 -0
  8. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/data.py +0 -0
  9. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/executor.py +0 -0
  10. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/hook.py +0 -0
  11. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/logger.py +0 -0
  12. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/matplot/matplotlib_oomol/__init__.py +0 -0
  13. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/matplot/matplotlib_oomol/oomol.py +0 -0
  14. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/matplot/oomol_matplot_helper.py +0 -0
  15. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/secret.py +0 -0
  16. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/service.py +0 -0
  17. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/topic.py +0 -0
  18. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/python_executor/utils.py +0 -0
  19. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/tests/test_cli.py +0 -0
  20. {oocana_python_executor-0.16.13 → oocana_python_executor-0.16.15}/tests/test_secret.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oocana-python-executor
3
- Version: 0.16.13
3
+ Version: 0.16.15
4
4
  Summary: a client subscribe mqtt topic to execute oocana's block
5
5
  Author-Email: l1shen <lishen1635@gmail.com>, yleaf <11785335+leavesster@users.noreply.github.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "oocana-python-executor"
3
- version = "0.16.13"
3
+ version = "0.16.15"
4
4
  authors = [
5
5
  { name = "l1shen", email = "lishen1635@gmail.com" },
6
6
  { name = "yleaf", email = "11785335+leavesster@users.noreply.github.com" },
@@ -1,4 +1,6 @@
1
1
  import logging
2
+
3
+ from .credential import replace_credential
2
4
  from oocana import Mainframe, Context, StoreKey, BlockInfo, BinValueDict, VarValueDict, InputHandleDef, is_bin_value, is_var_value
3
5
  from typing import Dict
4
6
  from .secret import replace_secret
@@ -24,6 +26,7 @@ def createContext(
24
26
  inputs_def_handles[k] = InputHandleDef(**v)
25
27
 
26
28
  inputs = replace_secret(inputs, inputs_def_handles, node_props.get("inputs_def_patch"))
29
+ inputs = replace_credential(inputs, inputs_def_handles)
27
30
 
28
31
  for k, v in inputs.items():
29
32
  input_def = inputs_def_handles.get(k)
@@ -0,0 +1,48 @@
1
+ from typing import Any, Dict
2
+ from oocana import InputHandleDef
3
+ class CredentialInput:
4
+ def __init__(self, type: str, id: str):
5
+ self.type = type
6
+ self.id = id
7
+
8
+ def generate_credential_input(credential_path: str) -> CredentialInput | None:
9
+ """Generate a CredentialInput from a credential path string.
10
+
11
+ The credential path should be in the format `${{OO_CREDENTIAL:type,name,id}}`. If the format is incorrect,
12
+ the function returns None.
13
+ """
14
+
15
+ if not (credential_path.startswith("${{OO_CREDENTIAL:") and credential_path.endswith("}}")):
16
+ # logger warning("Credential path format is incorrect. Expected to start with '${{OO_CREDENTIAL:' and end with '}}'.")
17
+ return None
18
+
19
+ if credential_path.count(",") != 2:
20
+ # logger warning("Credential path format is incorrect. Expected exactly two commas.")
21
+ return None
22
+
23
+ credential_path = credential_path.removeprefix("${{OO_CREDENTIAL:").removesuffix("}}")
24
+ if credential_path:
25
+ try:
26
+ type, _name, id = credential_path.split(",", maxsplit=2)
27
+ return CredentialInput(type, id)
28
+ except ValueError:
29
+ return None
30
+ return None
31
+
32
+ def replace_credential(
33
+ inputs: Any,
34
+ input_def: Dict[str, InputHandleDef] | None = None,
35
+ ) -> Any:
36
+ if not isinstance(inputs, dict):
37
+ return inputs
38
+
39
+ assert isinstance(inputs, dict)
40
+
41
+ for k, v in inputs.items():
42
+ current_input_def = input_def.get(k) if input_def else None
43
+ if current_input_def is None:
44
+ continue
45
+ if isinstance(v, str) and current_input_def.is_credential_handle():
46
+ cred_input = generate_credential_input(v)
47
+ inputs[k] = cred_input
48
+ return inputs
@@ -0,0 +1,106 @@
1
+ import unittest
2
+ from python_executor.credential import replace_credential, CredentialInput, generate_credential_input
3
+ from oocana import InputHandleDef
4
+ from typing import cast
5
+
6
+ ORIGIN_VALUE = "Custom,credential_name,credential_id"
7
+
8
+ class TestCredential(unittest.TestCase):
9
+
10
+ def test_credential_fallback(self):
11
+ """Test credential fallback when not a credential handle"""
12
+ v = replace_credential({
13
+ "c": "aaaa"
14
+ }, {
15
+ "c": InputHandleDef(handle="c", json_schema={
16
+ "contentMediaType": "oomol/credential"
17
+ }, value=None)
18
+ })
19
+ # Should return CredentialInput when parsed successfully
20
+ cred_input = generate_credential_input("aaaa")
21
+ self.assertIsNone(cred_input)
22
+ self.assertIsNone(v.get("c"))
23
+
24
+ def test_replace_credential(self):
25
+ """Test basic credential replacement"""
26
+ v = replace_credential({
27
+ "c": f'${{{{OO_CREDENTIAL:{ORIGIN_VALUE}}}}}'
28
+ }, {
29
+ "c": InputHandleDef(handle="c", json_schema={
30
+ "contentMediaType": "oomol/credential"
31
+ }, value=None)
32
+ })
33
+ cred_input = v.get("c")
34
+ self.assertIsInstance(cred_input, CredentialInput)
35
+ self.assertEqual(cred_input.type, "Custom")
36
+ self.assertEqual(cred_input.id, "credential_id")
37
+
38
+
39
+ def test_credential_without_content_media(self):
40
+ """Test multiple credentials replacement"""
41
+ v = replace_credential({
42
+ "c": f'${{{{OO_CREDENTIAL:{ORIGIN_VALUE}}}}}',
43
+ "a": f'${{{{OO_CREDENTIAL:{ORIGIN_VALUE}}}}}'
44
+ }, {
45
+ "c": InputHandleDef(handle="c", json_schema={
46
+ "contentMediaType": "oomol/credential"
47
+ }, value=None)
48
+ })
49
+ cred_input_c = v.get("c")
50
+ cred_input_a = v.get("a")
51
+
52
+ self.assertIsInstance(cred_input_c, CredentialInput)
53
+ self.assertEqual(cred_input_c.type, "Custom")
54
+
55
+ # 'a' should remain unchanged since no input_def for it
56
+ self.assertEqual(cred_input_a, f'${{{{OO_CREDENTIAL:{ORIGIN_VALUE}}}}}')
57
+
58
+ def test_credential_in_other_string(self):
59
+ """Test credential pattern inside other string (should not be replaced)"""
60
+ no_replace_value = f'${{{{OO_CREDENTIAL:{ORIGIN_VALUE}}}}}_bbb'
61
+ v = replace_credential({
62
+ "c": no_replace_value
63
+ }, {
64
+ "c": InputHandleDef(handle="c", json_schema={
65
+ "contentMediaType": "oomol/credential"
66
+ }, value=None)
67
+ })
68
+ # Should not be replaced because it doesn't match exact format
69
+ self.assertEqual(v.get("c"), None)
70
+
71
+ def test_generate_credential_input_valid(self):
72
+ """Test valid credential input generation"""
73
+ result = generate_credential_input("${{OO_CREDENTIAL:AWS,my_credential_name,my_credential_id}}")
74
+ self.assertIsInstance(result, CredentialInput)
75
+ result = cast(CredentialInput, result)
76
+ self.assertEqual(result.type, "AWS")
77
+ self.assertEqual(result.id, "my_credential_id")
78
+
79
+ def test_generate_credential_input_invalid_format(self):
80
+ """Test invalid credential input format"""
81
+ # Missing prefix
82
+ result = generate_credential_input("AWS,my_credential_name,my_credential_id")
83
+ self.assertIsNone(result)
84
+
85
+ # Missing suffix
86
+ result = generate_credential_input("${{OO_CREDENTIAL:AWS,my_credential_name,my_credential_id")
87
+ self.assertIsNone(result)
88
+
89
+ # Wrong prefix
90
+ result = generate_credential_input("${{OO_SECRET:AWS,my_credential_id}}")
91
+ self.assertIsNone(result)
92
+
93
+ # Empty content
94
+ result = generate_credential_input("${{OO_CREDENTIAL:}}")
95
+ self.assertIsNone(result)
96
+
97
+ # Missing comma
98
+ result = generate_credential_input("${{OO_CREDENTIAL:AWS}}")
99
+ self.assertIsNone(result)
100
+
101
+ # Only two parameters (missing third)
102
+ result = generate_credential_input("${{OO_CREDENTIAL:AWS,my_credential}}")
103
+ self.assertIsNone(result)
104
+
105
+ if __name__ == '__main__':
106
+ unittest.main()