Skip to content

i2mint/verb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

verb

Easy make mini-languages to do python things.

To install: pip install verb

Do things like this:

from verb import mk_executer

func_of_key = {
    'plus': lambda x, y: x + y,
    'minus': lambda x, y: x - y,
}
execute = mk_executer(func_of_key)
execute('3 minus 2 plus 1')
## 2
execute('9 minus 6')
## 3

A quick intro to Command

Uses cases: In situations where you want to get some input from a user (from the web, in a command line, etc.) that specifies a computation to be carried out, you know (right) that you definitely shouldn't resort to using eval or exec. Because it's dangerous for everyone involved -- let's just not go there.

verb offers an alternative: Easily building minilanguages that will allow the user to only execute the functions you choose, through a vocabulary you choose, and everyone can go home (as) safe (as you allow).

In a nutshell, you make a key-to-func mapping (or use the default). This func_of_key mapping is what specifies your interpreter:

from verb import *
import operator as o

func_of_key = {  # Note: Order represents precedence!
    '-': o.sub,
    '+': o.add,
    '*': o.mul,
    '/': o.truediv,
}

Now you have a minilanguage! Out-of-the-box it will allow you to "speak it in string" or "speak it in json/dict", but you can extend to enable the language to be written in any container you want.

If you give it a "command string":

command_str = '1 + 2 - 3 * 4 / 8'
command = Command(command_str, func_of_key)

command_str = '1 + 2 - 3 * 4 / 8' command = Command(command_str, func_of_key)

It will use func_of_key to both parse it and replace the keys with an indication that the corresponding function should be called. command is a callable object, and when you call it, it executes it's instructions:

command()
1.5

It may be useful to see what the operation structure looks like

d = command.to_dict()
d
{'-': ({'+': (1, 2)}, {'*': (3, {'/': (4, 8)})})}
# Or if you read better with indents

from functools import partial
import json
from lined import Pipe

print_jdict = Pipe(partial(json.dumps, indent=2), print)  # Note: Only works if your dict is JSON-izable. 

print_jdict(d)
{
  "-": [
    {
      "+": [
        1,
        2
      ]
    },
    {
      "*": [
        3,
        {
          "/": [
            4,
            8
          ]
        }
      ]
    }
  ]
}

That same dict can be used as a parameter to make the same command

command = Command(d, func_of_key)
command()
1.5

Example: Table Selector Mini Language

import operator as o
from typing import Callable, Mapping
from functools import partial

import pandas as pd
from lined import Pipe
from verb import str_to_basic_pyobj, Command


dflt_func_of_key_for_table_selection = {  # Note: Order represents precedence!
    '&': o.__and__,
    '==': o.__eq__,
    '<=': o.__le__,
    '>=': o.__ge__,
    '<': o.__lt__,
    '>': o.__gt__,
}


def mk_table_selector(
    table: pd.DataFrame,
    func_of_key: Mapping[str, Callable] = dflt_func_of_key_for_table_selection
):

    def leaf_processor(x):
        x = str_to_basic_pyobj(x)
        if x in table:
            return table[x]
        return x

    run_command = Pipe(
        partial(
            Command.from_string,
            func_of_key=func_of_key,
            leaf_processor=leaf_processor
        ),
        lambda f: f(),
        lambda idx: table[idx],
    )

    return run_command
import pandas as pd

df = pd.DataFrame(
    [{'source': 'audio', 'bt': 5, 'tt': 7, 'annot': 'cat'},
     {'source': 'audio',
        'bt': 6,
        'tt': 9,
        'annot': 'dog',
        'comments': 'barks and chases cat away'},
        {'source': 'visual', 'bt': 5, 'tt': 8, 'annot': 'cat'},
        {'source': 'visual',
         'bt': 6,
         'tt': 15,
         'annot': 'dog',
         'comments': 'dog remains in view after bark ceases'}]
)
df
source bt tt annot comments
0 audio 5 7 cat NaN
1 audio 6 9 dog barks and chases cat away
2 visual 5 8 cat NaN
3 visual 6 15 dog dog remains in view after bark ceases
run_command = mk_table_selector(df)
run_command('source == audio')
source bt tt annot comments
0 audio 5 7 cat NaN
1 audio 6 9 dog barks and chases cat away
run_command('tt <= 8')
source bt tt annot comments
0 audio 5 7 cat NaN
2 visual 5 8 cat NaN
run_command('source == audio & tt <= 8')
source bt tt annot comments
0 audio 5 7 cat NaN