Repository: QuantStack/py2vega

Issue:
Built-in functions
The Python built-in `len` function could be mapped to the Vega `length` function
The Python built-in `slice` function could be mapped to the Vega `slice` function
The Python built-in `str` function could be mapped to the Vega `toString` function
...

Discussion:

Patch:
diff --git a/py2vega/main.py b/py2vega/main.py
index d00a21a..f153309 100644
--- a/py2vega/main.py
+++ b/py2vega/main.py
@@ -20,6 +20,16 @@ operator_mapping = {
     ast.Mod: '%'
 }
 
+# Note that built-in functions like `abs`, `min`, `max` which already have an equivalent in
+# Vega expressions are already supported automatically
+builtin_function_mapping = {
+    'bool': '(isValid({args}) ? toBoolean({args}) : false)',
+    'float': 'toNumber({args})',
+    'int': 'floor(toNumber({args}))',
+    'len': 'length({args})',
+    'str': 'toString({args})'
+}
+
 
 class Py2VegaSyntaxError(SyntaxError):
     pass
@@ -220,11 +230,13 @@ class VegaExpressionVisitor(ast.NodeVisitor):
         if isinstance(node.func, ast.Attribute):
             func_name = node.func.attr
 
+        args = ', '.join([self.visit(arg) for arg in node.args])
+
+        if func_name in builtin_function_mapping:
+            return builtin_function_mapping[func_name].format(args=args)
+
         if func_name in vega_functions:
-            return '{}({})'.format(
-                func_name,
-                ', '.join([self.visit(arg) for arg in node.args])
-            )
+            return '{func_name}({args})'.format(func_name=func_name, args=args)
 
         raise NameError('name \'{}\' is not defined, only a subset of Python is supported'.format(func_name))
 


Test patch:
diff --git a/test/test_py2vega.py b/test/test_py2vega.py
index ea54940..4fbbb57 100644
--- a/test/test_py2vega.py
+++ b/test/test_py2vega.py
@@ -93,6 +93,35 @@ def test_compare():
     assert py2vega(code, whitelist) == '(indexof(value, \'chevrolet\') == -1)'
 
 
+def test_call():
+    code = 'toBoolean(3)'
+    assert py2vega(code, whitelist) == 'toBoolean(3)'
+
+    code = 'bool(3)'
+    assert py2vega(code, whitelist) == '(isValid(3) ? toBoolean(3) : false)'
+
+    code = 'toString(3)'
+    assert py2vega(code, whitelist) == 'toString(3)'
+
+    code = 'str(3)'
+    assert py2vega(code, whitelist) == 'toString(3)'
+
+    code = 'toNumber("3")'
+    assert py2vega(code, whitelist) == 'toNumber(\'3\')'
+
+    code = 'float("3")'
+    assert py2vega(code, whitelist) == 'toNumber(\'3\')'
+
+    code = 'int("3")'
+    assert py2vega(code, whitelist) == 'floor(toNumber(\'3\'))'
+
+    code = 'length(value)'
+    assert py2vega(code, whitelist) == 'length(value)'
+
+    code = 'len(value)'
+    assert py2vega(code, whitelist) == 'length(value)'
+
+
 def foo(value):
     return 'red' if value < 150 else 'green'