k1lib.kast module

Small module with helper functions to analyze and manipulate Python abstract syntax tree

class k1lib.kast.walk[source]

Bases: BaseCli

__init__()[source]

Walks the AST tree, returning (elem, parent, parent accessor, depth) tuple. Example:

"abc.py" | cat() | join("\n") | kast.walk() # returns generator of tuples
__ror__(m: str)[source]
class k1lib.kast.pretty[source]

Bases: BaseCli

__init__()[source]

Returns list[str] containing the prettified tree structure of the AST. Mainly for asthetic, not for parsing by other tools downstream. Example:

"x+3" | kast.pretty()
"lambda x: x+3" | kast.pretty()
__ror__(it: str)[source]
k1lib.kast.py2js(m)[source]

Converts simple python expression into js code. Example:

kast.py2js("x+3")                 # returns ('(x+3)', {'x'})
kast.py2js("lambda x: x+3")       # returns ('(x+3)', {'x'})
kast.py2js("lambda y: y+3")       # returns ('(y+3)', {'y'})
kast.py2js("lambda y: y+3*x")     # returns ('(y+(3*x))', {'x', 'y'})
kast.py2js("x<3 and 45>10")       # returns ('((x<3)&&(45>10))', {'x'})
kast.py2js("abs((x//5.1) * 2.3)") # returns ('Math.abs((Math.floor(x/5.1)*2.3))', {'x'})
kast.py2js("not x > 3")           # returns ('!(x>3)', {'x'})

This will return a tuple of 2 objects. First is the JS code, and second is the set of unknown variables in the JS code. If the code is a lambda function, then it only considers what’s inside the function. More complex examples:

# returns ('[...Array(10).keys()].map((x, i) => [x, i]).filter(([x, y]) => ((x%3)===1)).map(([x, y]) => (Math.pow(x, y)+z))', {'z'})
kast.py2js("[x**y + z for x, y in enumerate(range(10)) if x % 3 == 1]")

Both JS and Python code when run should return [1, 256, 823543] for z = 0.

A lot of builtin Python functions are transpiled automatically, including:

abs, all, any, divmod, enumerate, float, getattr, hasattr, int, iter
len, list, max, min, pow, print, range, reversed, round, set, setattr
str, sum, tuple, type, zip

Other builtin operations don’t quite make sense though. But the neat thing is, any functions beyond these builtin functions are not transpiled, and instead just passes along to the JS code. This allows you to kinda mix and match code styles from both Python and JS. For example:

# returns ("(document.querySelector(('#' + someIdx + '_tail')).style['color'] = (((Math.round(Math.pow(x, 2))===Math.abs(-12))) ? ('red') : ('green')))", {'someIdx', 'x'})
kast.py2js("setattr(document.querySelector(f'#{someIdx}_tail').style, 'color', 'red' if round(x**2) == Math.abs(-12) else 'green')")

See how we’re both using the Python transpiled ** (to Math.pow(...)), yet uses Math.abs(...) directly, which is not defined in Python, but is defined in JS, without any trouble. Also notice that document.querySelector and f-strings work too. So in short, overall syntax is Python’s, but you can call random JS functions!

This can do lots of transformations, but don’t expect it to be airtight. I’ve done all I can to make the thing airtight, but I’m sure you can come up with some complex scenario where it would fail. This is meant to be applied on short pieces of code only.

k1lib.kast.asyncGuard(data: (header, fIdx)) -> (header, fIdx, async?)[source]

Converts ._jsF() function signatures from (header, fIdx) into (header, fIdx, is fIdx async?). Example:

# returns tuple (header, fIdx, async?)
kast.asyncGuard(apply("x**2")._jsF(None))