expr-codegen 0.8.2__tar.gz → 0.8.4__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 (28) hide show
  1. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/PKG-INFO +4 -9
  2. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/README.md +3 -8
  3. expr_codegen-0.8.4/expr_codegen/_version.py +1 -0
  4. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/codes.py +13 -18
  5. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/expr.py +4 -29
  6. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/printer.py +3 -3
  7. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/printer.py +3 -3
  8. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/tool.py +22 -1
  9. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/PKG-INFO +4 -9
  10. expr_codegen-0.8.2/expr_codegen/_version.py +0 -1
  11. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/LICENSE +0 -0
  12. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/__init__.py +0 -0
  13. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/dag.py +0 -0
  14. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/latex/__init__.py +0 -0
  15. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/latex/printer.py +0 -0
  16. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/model.py +0 -0
  17. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/__init__.py +0 -0
  18. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/code.py +0 -0
  19. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/template.py.j2 +0 -0
  20. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/__init__.py +0 -0
  21. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/code.py +0 -0
  22. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/template.py.j2 +0 -0
  23. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/SOURCES.txt +0 -0
  24. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/dependency_links.txt +0 -0
  25. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/requires.txt +0 -0
  26. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/top_level.txt +0 -0
  27. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/pyproject.toml +0 -0
  28. {expr_codegen-0.8.2 → expr_codegen-0.8.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: expr_codegen
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: symbol expression to polars expression tool
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: BSD 3-Clause License
@@ -197,17 +197,12 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
197
197
  4. 可以将`删除行`与`重采样`做为分割线,一大块代码分成多个`DAG`串联。复杂不易理解,所以最终没有实现
198
198
 
199
199
  ## 小技巧
200
-
201
- 1. `sympy`不支持`==`,而是当成两个对象比较。例如:
202
- 1. `if_else(OPEN==CLOSE, HIGH, LOW)`, 一开始就变成了`if_else(False, HIGH, LOW)`
203
- 2. 可以用`Eq`来代替,`if_else(Eq(OPEN, CLOSE), HIGH, LOW)`。具体示例请参考`Alpha101`中的`alpha_021`
204
-
205
- 2. `sympy`不支持`bool`转`int`。例如:
200
+ 1. `sympy`不支持`bool`转`int`。例如:
206
201
  1. `(OPEN < CLOSE) * -1`报错 `TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'`
207
202
  2. 可以用`if_else`代替。`if_else(OPEN<CLOSE, 1, 0)*-1`。具体示例请参考`Alpha101`中的`alpha_064`
208
- 3. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
203
+ 2. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
209
204
 
210
- 以上三种问题本项目都使用`ast`进行了处理,可以简化使用
205
+ 以上两种问题本项目都使用`ast`进行了处理,可以简化使用
211
206
 
212
207
  ## 下划线开头的变量
213
208
  1. 输出的数据,所有以`_`开头的列,最后会被自动删除。所以需要保留的变量一定不要以`_`开头
@@ -147,17 +147,12 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
147
147
  4. 可以将`删除行`与`重采样`做为分割线,一大块代码分成多个`DAG`串联。复杂不易理解,所以最终没有实现
148
148
 
149
149
  ## 小技巧
150
-
151
- 1. `sympy`不支持`==`,而是当成两个对象比较。例如:
152
- 1. `if_else(OPEN==CLOSE, HIGH, LOW)`, 一开始就变成了`if_else(False, HIGH, LOW)`
153
- 2. 可以用`Eq`来代替,`if_else(Eq(OPEN, CLOSE), HIGH, LOW)`。具体示例请参考`Alpha101`中的`alpha_021`
154
-
155
- 2. `sympy`不支持`bool`转`int`。例如:
150
+ 1. `sympy`不支持`bool`转`int`。例如:
156
151
  1. `(OPEN < CLOSE) * -1`报错 `TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'`
157
152
  2. 可以用`if_else`代替。`if_else(OPEN<CLOSE, 1, 0)*-1`。具体示例请参考`Alpha101`中的`alpha_064`
158
- 3. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
153
+ 2. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
159
154
 
160
- 以上三种问题本项目都使用`ast`进行了处理,可以简化使用
155
+ 以上两种问题本项目都使用`ast`进行了处理,可以简化使用
161
156
 
162
157
  ## 下划线开头的变量
163
158
  1. 输出的数据,所有以`_`开头的列,最后会被自动删除。所以需要保留的变量一定不要以`_`开头
@@ -0,0 +1 @@
1
+ __version__ = "0.8.4"
@@ -126,14 +126,7 @@ class SympyTransformer(ast.NodeTransformer):
126
126
  node.comparators[i] = ast.Name(new_com_value, ctx=ast.Load())
127
127
  self.args_new.add(new_com_value)
128
128
 
129
- # OPEN==CLOSE,要转成Eq
130
- if isinstance(node.ops[0], ast.Eq):
131
- # 等号会直接比较变成False
132
- node = ast.Call(
133
- func=ast.Name(id='Eq', ctx=ast.Load()),
134
- args=[node.left, node.comparators[0]],
135
- keywords=[],
136
- )
129
+ assert len(node.comparators) == 1, f"不支持连续等号,请手工添加括号, {ast.unparse(node)}"
137
130
 
138
131
  self.generic_visit(node)
139
132
  return node
@@ -153,22 +146,22 @@ class SympyTransformer(ast.NodeTransformer):
153
146
 
154
147
  def visit_BinOp(self, node):
155
148
  # TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'
156
- if isinstance(node.op, ast.Mult):
149
+ if isinstance(node.op, (ast.Mult, ast.Add, ast.Div, ast.Sub)):
157
150
  # (OPEN < CLOSE) * -1
158
151
  if isinstance(node.left, ast.Compare):
159
152
  node.left = ast.Call(
160
- func=ast.Name(id='if_else', ctx=ast.Load()),
161
- args=[node.left, ast.Constant(value=1), ast.Constant(value=0)],
153
+ func=ast.Name(id='int_', ctx=ast.Load()),
154
+ args=[node.left],
162
155
  keywords=[],
163
156
  )
164
157
  # -1*(OPEN < CLOSE)
165
158
  if isinstance(node.right, ast.Compare):
166
159
  node.right = ast.Call(
167
- func=ast.Name(id='if_else', ctx=ast.Load()),
168
- args=[node.right, ast.Constant(value=1), ast.Constant(value=0)],
160
+ func=ast.Name(id='int_', ctx=ast.Load()),
161
+ args=[node.right],
169
162
  keywords=[],
170
163
  )
171
- # 这种情况要处理吗?
164
+ # 这种情况,已经包含
172
165
  # (OPEN < CLOSE)*(OPEN < CLOSE)
173
166
 
174
167
  if isinstance(node.left, ast.Name):
@@ -238,8 +231,10 @@ def source_replace(source):
238
231
  # 其实会导致?与:错配,但无所谓,只要多执行几次即可
239
232
  source, num = re.subn(r'\?(.+?):(.+?)', r' if( \1 )else \2', source, flags=re.S)
240
233
  # break
241
- # 异或转成乘方,或、与
242
- source = source.replace('^', '**').replace('||', '|').replace('&&', '&')
234
+ # 或、与
235
+ source = source.replace('||', '|').replace('&&', '&')
236
+ # 异或转成乘方
237
+ source = source.replace('^', '**')
243
238
  return source
244
239
 
245
240
 
@@ -288,7 +283,7 @@ def _add_default_type(globals_):
288
283
  return globals_
289
284
 
290
285
 
291
- def sources_to_exprs(globals_, *sources, safe: bool = True):
286
+ def sources_to_exprs(globals_, *sources):
292
287
  """将源代码转换成表达式"""
293
288
 
294
289
  globals_ = _add_default_type(globals_)
@@ -297,5 +292,5 @@ def sources_to_exprs(globals_, *sources, safe: bool = True):
297
292
  register_symbols(funcs_new, globals_, is_function=True)
298
293
  register_symbols(args_new, globals_, is_function=False)
299
294
  register_symbols(targets_new, globals_, is_function=False)
300
- exprs_dict = dict_to_exprs(assigns, globals_, safe)
295
+ exprs_dict = dict_to_exprs(assigns, globals_)
301
296
  return raw, exprs_dict
@@ -1,7 +1,6 @@
1
- import re
2
1
  from functools import reduce
3
2
 
4
- from sympy import Mul, preorder_traversal, symbols, Function, simplify, Add, Basic, Symbol
3
+ from sympy import Mul, preorder_traversal, symbols, Function, simplify, Add, Basic, Symbol, sympify
5
4
 
6
5
  # 预定义前缀,算子用前缀进行区分更方便。
7
6
  # 当然也可以用是否在某容器中进行分类
@@ -44,35 +43,11 @@ def register_symbols(syms, globals_, is_function: bool):
44
43
  return globals_
45
44
 
46
45
 
47
- def dict_to_exprs(exprs_src, globals_, safe: bool = True):
48
- exprs_src = {k: safe_eval(v, globals_, k, safe) for k, v in exprs_src.items()}
46
+ def dict_to_exprs(exprs_src, globals_):
47
+ exprs_src = {k: sympify(v, globals_, evaluate=False) for k, v in exprs_src.items()}
49
48
  return exprs_src
50
49
 
51
50
 
52
- def safe_eval(string, globals_, k=None, safe: bool = True):
53
- # print(string)
54
- code = compile(string, '<user input>', 'eval')
55
- if safe:
56
- reason = None
57
- banned = ('eval', 'compile', 'exec', 'getattr', 'hasattr', 'setattr', 'delattr',
58
- 'classmethod', 'globals', 'help', 'input', 'isinstance', 'issubclass', 'locals',
59
- 'open', 'print', 'property', 'staticmethod', 'vars')
60
- for name in code.co_names:
61
- if re.search(r'^__\S*__$', name):
62
- reason = 'attributes not allowed'
63
- elif name in banned:
64
- reason = 'code execution not allowed'
65
- if reason:
66
- raise NameError(f'{name} not allowed : {reason}')
67
-
68
- try:
69
- return eval(code, globals_)
70
- except:
71
- print(k)
72
- print(string)
73
- raise
74
-
75
-
76
51
  def append_node(node, output_exprs):
77
52
  """添加到队列。其中,-x将转为x
78
53
 
@@ -361,7 +336,7 @@ def _replace__repeat(e):
361
336
  node_name = get_node_name(node)
362
337
  node_args0_name = get_node_name(node.args[0])
363
338
  if node_name == node_args0_name:
364
- if node.name in ('cs_rank', 'sign', 'Abs', 'abs_'):
339
+ if node_name in ('cs_rank', 'sign', 'Abs', 'abs_'):
365
340
  replacements.append((node, node.args[0]))
366
341
  for node, replacement in replacements:
367
342
  print(node, ' -> ', replacement)
@@ -59,15 +59,15 @@ class PandasStrPrinter(StrPrinter):
59
59
 
60
60
  def _print_Or(self, expr):
61
61
  PREC = PRECEDENCE["Mul"]
62
- return "%s | %s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
62
+ return " | ".join(self.parenthesize(arg, PREC) for arg in expr.args)
63
63
 
64
64
  def _print_Xor(self, expr):
65
65
  PREC = PRECEDENCE["Mul"]
66
- return "%s ^ %s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
66
+ return " ^ ".join(self.parenthesize(arg, PREC) for arg in expr.args)
67
67
 
68
68
  def _print_And(self, expr):
69
69
  PREC = PRECEDENCE["Mul"]
70
- return "%s & %s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
70
+ return " & ".join(self.parenthesize(arg, PREC) for arg in expr.args)
71
71
 
72
72
  def _print_Not(self, expr):
73
73
  PREC = PRECEDENCE["Mul"]
@@ -57,15 +57,15 @@ class PolarsStrPrinter(StrPrinter):
57
57
 
58
58
  def _print_Or(self, expr):
59
59
  PREC = PRECEDENCE["Mul"]
60
- return "%s | %s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
60
+ return " | ".join(self.parenthesize(arg, PREC) for arg in expr.args)
61
61
 
62
62
  def _print_Xor(self, expr):
63
63
  PREC = PRECEDENCE["Mul"]
64
- return "%s ^ %s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
64
+ return " ^ ".join(self.parenthesize(arg, PREC) for arg in expr.args)
65
65
 
66
66
  def _print_And(self, expr):
67
67
  PREC = PRECEDENCE["Mul"]
68
- return "%s & %s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
68
+ return " & ".join(self.parenthesize(arg, PREC) for arg in expr.args)
69
69
 
70
70
  def _print_Not(self, expr):
71
71
  PREC = PRECEDENCE["Mul"]
@@ -5,13 +5,34 @@ from typing import Sequence, Dict, Optional
5
5
 
6
6
  from black import Mode, format_str
7
7
  from sympy import simplify, cse, symbols, numbered_symbols
8
+ from sympy.core.expr import Expr
9
+ from sympy.logic import boolalg
8
10
 
9
11
  from expr_codegen.codes import sources_to_exprs
10
12
  from expr_codegen.expr import get_current_by_prefix, get_children, replace_exprs
11
13
  from expr_codegen.model import dag_start, dag_end, dag_middle
12
14
 
15
+ # ===============================
16
+ # TypeError: expecting bool or Boolean, not `ts_delay(X, 3)`.
17
+ # ts_delay(X, 3) & ts_delay(Y, 3)
18
+ boolalg.as_Boolean = lambda x: x
19
+
20
+
21
+ # AttributeError: 'StrictGreaterThan' object has no attribute 'diff'
22
+ # ts_count(open > 1, 2) == 2
23
+ def _diff(self, *symbols, **assumptions):
24
+ assumptions.setdefault("evaluate", False)
25
+ from sympy.core.function import _derivative_dispatch
26
+ return _derivative_dispatch(self, *symbols, **assumptions)
27
+
28
+
29
+ Expr.diff = _diff
30
+
31
+
32
+ # ===============================
13
33
 
14
34
  def simplify2(expr):
35
+ # return simplify(expr)
15
36
  try:
16
37
  expr = simplify(expr)
17
38
  except AttributeError as e:
@@ -238,7 +259,7 @@ class ExprTool:
238
259
  style='polars', template_file='template.py.j2',
239
260
  date='date', asset='asset') -> str:
240
261
  """通过字符串生成代码, 加了缓存,多次调用不重复生成"""
241
- raw, exprs_dict = sources_to_exprs(self.globals_, source, *more_sources, safe=False)
262
+ raw, exprs_dict = sources_to_exprs(self.globals_, source, *more_sources)
242
263
 
243
264
  # 生成代码
244
265
  code, G = _TOOL_.all(exprs_dict, style=style, template_file=template_file,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: expr_codegen
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: symbol expression to polars expression tool
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: BSD 3-Clause License
@@ -197,17 +197,12 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
197
197
  4. 可以将`删除行`与`重采样`做为分割线,一大块代码分成多个`DAG`串联。复杂不易理解,所以最终没有实现
198
198
 
199
199
  ## 小技巧
200
-
201
- 1. `sympy`不支持`==`,而是当成两个对象比较。例如:
202
- 1. `if_else(OPEN==CLOSE, HIGH, LOW)`, 一开始就变成了`if_else(False, HIGH, LOW)`
203
- 2. 可以用`Eq`来代替,`if_else(Eq(OPEN, CLOSE), HIGH, LOW)`。具体示例请参考`Alpha101`中的`alpha_021`
204
-
205
- 2. `sympy`不支持`bool`转`int`。例如:
200
+ 1. `sympy`不支持`bool`转`int`。例如:
206
201
  1. `(OPEN < CLOSE) * -1`报错 `TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'`
207
202
  2. 可以用`if_else`代替。`if_else(OPEN<CLOSE, 1, 0)*-1`。具体示例请参考`Alpha101`中的`alpha_064`
208
- 3. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
203
+ 2. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
209
204
 
210
- 以上三种问题本项目都使用`ast`进行了处理,可以简化使用
205
+ 以上两种问题本项目都使用`ast`进行了处理,可以简化使用
211
206
 
212
207
  ## 下划线开头的变量
213
208
  1. 输出的数据,所有以`_`开头的列,最后会被自动删除。所以需要保留的变量一定不要以`_`开头
@@ -1 +0,0 @@
1
- __version__ = "0.8.2"
File without changes
File without changes