programgarden-core 1.14.3__tar.gz → 1.14.5__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 (82) hide show
  1. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/PKG-INFO +1 -1
  2. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/expression/evaluator.py +72 -10
  3. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/validation.py +5 -0
  4. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/pyproject.toml +1 -1
  5. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/README.md +0 -0
  6. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/__init__.py +0 -0
  7. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/__init__.py +0 -0
  8. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/client.py +0 -0
  9. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/components.py +0 -0
  10. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/listener.py +0 -0
  11. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/mixins.py +0 -0
  12. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/products.py +0 -0
  13. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/sql.py +0 -0
  14. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/bases/storage.py +0 -0
  15. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/exceptions.py +0 -0
  16. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/expression/__init__.py +0 -0
  17. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/i18n/__init__.py +0 -0
  18. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/i18n/locales/en.json +0 -0
  19. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/i18n/locales/ko.json +0 -0
  20. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/i18n/translator.py +0 -0
  21. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/korea_alias.py +0 -0
  22. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/__init__.py +0 -0
  23. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/connection_rule.py +0 -0
  24. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/credential.py +0 -0
  25. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/edge.py +0 -0
  26. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/event.py +0 -0
  27. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/exchange.py +0 -0
  28. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/field_binding.py +0 -0
  29. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/job.py +0 -0
  30. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/plugin_resource.py +0 -0
  31. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/resilience.py +0 -0
  32. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/resource.py +0 -0
  33. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/models/workflow.py +0 -0
  34. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/__init__.py +0 -0
  35. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/account_futures.py +0 -0
  36. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/account_korea_stock.py +0 -0
  37. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/account_stock.py +0 -0
  38. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/ai.py +0 -0
  39. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/backtest.py +0 -0
  40. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/backtest_futures.py +0 -0
  41. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/backtest_korea_stock.py +0 -0
  42. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/backtest_stock.py +0 -0
  43. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/base.py +0 -0
  44. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/broker.py +0 -0
  45. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/calculation.py +0 -0
  46. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/condition.py +0 -0
  47. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/data.py +0 -0
  48. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/data_futures.py +0 -0
  49. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/data_korea_stock.py +0 -0
  50. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/data_stock.py +0 -0
  51. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/display.py +0 -0
  52. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/event.py +0 -0
  53. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/fundamental_korea_stock.py +0 -0
  54. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/fundamental_stock.py +0 -0
  55. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/infra.py +0 -0
  56. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/market_external.py +0 -0
  57. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/market_status.py +0 -0
  58. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/open_orders_futures.py +0 -0
  59. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/open_orders_korea_stock.py +0 -0
  60. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/open_orders_stock.py +0 -0
  61. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/order.py +0 -0
  62. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/portfolio.py +0 -0
  63. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/realtime_futures.py +0 -0
  64. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/realtime_korea_stock.py +0 -0
  65. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/realtime_stock.py +0 -0
  66. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/risk.py +0 -0
  67. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/symbol.py +0 -0
  68. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/symbol_futures.py +0 -0
  69. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/symbol_korea_stock.py +0 -0
  70. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/symbol_stock.py +0 -0
  71. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/nodes/trigger.py +0 -0
  72. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/presets/__init__.py +0 -0
  73. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/presets/news_analyst.json +0 -0
  74. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/presets/risk_manager.json +0 -0
  75. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/presets/strategist.json +0 -0
  76. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/presets/technical_analyst.json +0 -0
  77. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/registry/__init__.py +0 -0
  78. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/registry/credential_registry.py +0 -0
  79. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/registry/dynamic_node_registry.py +0 -0
  80. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/registry/node_registry.py +0 -0
  81. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/registry/plugin_registry.py +0 -0
  82. {programgarden_core-1.14.3 → programgarden_core-1.14.5}/programgarden_core/retry_executor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden-core
3
- Version: 1.14.3
3
+ Version: 1.14.5
4
4
  Summary: ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의
5
5
  Author: 프로그램동산
6
6
  Author-email: coding@programgarden.com
@@ -24,7 +24,7 @@ Jinja2 스타일 {{ }} 표현식 평가기
24
24
  - 파일/네트워크 접근 차단
25
25
  """
26
26
 
27
- from typing import Any, Dict, Optional, List, Set
27
+ from typing import Any, Callable, Dict, Optional, List, Set
28
28
  import re
29
29
  import ast
30
30
  import operator
@@ -787,6 +787,14 @@ class SafeEvaluator:
787
787
  "True": True,
788
788
  "False": False,
789
789
  "None": None,
790
+ # JSON-style literal aliases. Workflow definitions are authored as JSON
791
+ # (and frequently AI-generated), where the null/boolean literals are
792
+ # `null`/`true`/`false`, not Python's `None`/`True`/`False`. Accepting
793
+ # both spellings avoids an "undefined variable: null" failure on an
794
+ # otherwise valid `{{ x != null }}` expression.
795
+ "null": None,
796
+ "true": True,
797
+ "false": False,
790
798
  }
791
799
 
792
800
  def __init__(self, context: Dict[str, Any]):
@@ -996,28 +1004,82 @@ class ExpressionEvaluator:
996
1004
 
997
1005
  return self.EXPRESSION_PATTERN.sub(replace_expr, value)
998
1006
 
999
- def evaluate_fields(self, fields: Dict[str, Any]) -> Dict[str, Any]:
1007
+ def evaluate_fields(
1008
+ self,
1009
+ fields: Dict[str, Any],
1010
+ on_error: Optional[Callable[[str, Exception], None]] = None,
1011
+ ) -> Dict[str, Any]:
1000
1012
  """
1001
1013
  필드 딕셔너리의 모든 값 평가
1002
1014
 
1003
1015
  Args:
1004
1016
  fields: 노드의 fields 딕셔너리
1017
+ on_error: (선택) leaf 표현식 평가 실패 시 호출되는 콜백
1018
+ ``(expression_text, exception)``. 기본값 None.
1019
+
1020
+ - None 이면 leaf 실패 시 **기존대로 예외를 raise** 한다
1021
+ (정상 runtime/dry_run 동작 불변 — 이 메서드의 default 시맨틱).
1022
+ - 콜백이 주어지면 raise 하지 않고 ``on_error(value, exc)`` 를
1023
+ 호출한 뒤 **원본 leaf 값을 그대로 유지**한다. 이는
1024
+ ``evaluate_all_bindings`` 와 동일한 leaf 단위(per-leaf)
1025
+ try/except 시맨틱으로, deep_validate 가 미해결 binding 을
1026
+ leaf 단위로 수집할 수 있게 한다.
1027
+
1028
+ dict/list 재귀 시 콜백은 그대로 전파된다.
1005
1029
 
1006
1030
  Returns:
1007
1031
  평가된 필드 딕셔너리
1008
1032
  """
1009
1033
  result = {}
1010
1034
  for key, value in fields.items():
1011
- if isinstance(value, dict):
1012
- # 중첩 딕셔너리 재귀 처리
1013
- result[key] = self.evaluate_fields(value)
1014
- elif isinstance(value, list):
1015
- # 리스트 내 표현식 처리
1016
- result[key] = [self.evaluate(item) for item in value]
1017
- else:
1018
- result[key] = self.evaluate(value)
1035
+ result[key] = self._evaluate_field_value(value, on_error)
1019
1036
  return result
1020
1037
 
1038
+ def _evaluate_field_value(
1039
+ self,
1040
+ value: Any,
1041
+ on_error: Optional[Callable[[str, Exception], None]],
1042
+ ) -> Any:
1043
+ """evaluate_fields 의 단일 값 평가.
1044
+
1045
+ 재귀 깊이는 정상 모드(``on_error is None``)와 deep 모드(콜백 주어짐)에서
1046
+ **동일**하다 — 기존 ``evaluate_fields`` 시맨틱과 100% 일치시킨다:
1047
+
1048
+ - dict 값 → ``evaluate_fields`` 재귀 (콜백 전파)
1049
+ - list 값 → ``[self.evaluate(item) for item in value]`` (각 item 을
1050
+ ``self.evaluate`` 로만 평가; **list 내부 dict/nested list 는 재귀하지
1051
+ 않는다** — 기존 동작). deep 모드면 item 단위 on_error try/except 적용.
1052
+ - 그 외(leaf) → ``self.evaluate(value)`` (deep 모드면 leaf 단위 on_error)
1053
+
1054
+ ``on_error`` 가 주어진 경우(deep 모드)에만 평가 실패를 raise 하지 않고
1055
+ 콜백을 호출한 뒤 원본 값을 유지한다.
1056
+ """
1057
+ if isinstance(value, dict):
1058
+ # 중첩 딕셔너리 재귀 처리 — 콜백 전파 (기존 동작)
1059
+ return self.evaluate_fields(value, on_error)
1060
+ if isinstance(value, list):
1061
+ # 리스트는 각 item 을 self.evaluate 로만 평가 (기존 동작 — dict 재귀 안 함)
1062
+ return [self._evaluate_leaf(item, on_error) for item in value]
1063
+ # leaf 값
1064
+ return self._evaluate_leaf(value, on_error)
1065
+
1066
+ def _evaluate_leaf(
1067
+ self,
1068
+ value: Any,
1069
+ on_error: Optional[Callable[[str, Exception], None]],
1070
+ ) -> Any:
1071
+ """단일 leaf 값 평가 (dict/list 재귀 없음). deep 모드면 leaf 단위 on_error."""
1072
+ if on_error is None or not self.is_expression(value):
1073
+ # 기존 경로: on_error 없으면 실패 시 raise (동작 불변),
1074
+ # 표현식이 아니면 그대로 반환.
1075
+ return self.evaluate(value)
1076
+ # on_error 모드 + 표현식 leaf: leaf 단위 try/except (evaluate_all_bindings 시맨틱)
1077
+ try:
1078
+ return self.evaluate(value)
1079
+ except Exception as exc: # noqa: BLE001 - leaf 단위로 사유를 콜백에 전달
1080
+ on_error(value, exc)
1081
+ return value
1082
+
1021
1083
  def _eval_expression(self, expression: str) -> Any:
1022
1084
  """단일 표현식 평가"""
1023
1085
  evaluator = SafeEvaluator(self._context_dict)
@@ -64,6 +64,11 @@ class ErrorCode(str, Enum):
64
64
  DRY_RUN_CREDENTIAL_MISSING = "DRY_RUN_CREDENTIAL_MISSING"
65
65
  DRY_RUN_DEPENDENCY_FAILURE = "DRY_RUN_DEPENDENCY_FAILURE"
66
66
 
67
+ # Deep validation (virtual full-execution / deep_validate)
68
+ DEEP_VALIDATION_NODE_ERROR = "DEEP_VALIDATION_NODE_ERROR"
69
+ DEEP_VALIDATION_FLOW_BROKEN = "DEEP_VALIDATION_FLOW_BROKEN"
70
+ DEEP_VALIDATION_BINDING_UNRESOLVED = "DEEP_VALIDATION_BINDING_UNRESOLVED"
71
+
67
72
 
68
73
  _DEFAULT_SEVERITY: Dict[ErrorCode, ErrorSeverity] = {
69
74
  ErrorCode.UNKNOWN_PLUGIN: ErrorSeverity.WARNING,
@@ -5,7 +5,7 @@ authors = [
5
5
  homepage = "https://programgarden.com"
6
6
  requires-python = ">=3.12"
7
7
  name = "programgarden-core"
8
- version = "1.14.3"
8
+ version = "1.14.5"
9
9
  description = "ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의"
10
10
  readme = "README.md"
11
11