¿Interpretando una cadena de Python como una statement condicional?

Estoy procesando algunos archivos de registro con formato json en python. Es muy sencillo codificar algunas consultas condicionales, por ejemplo,

line=[1,'runtime',{'elapsed':12.3,'jobname':'high38853'}] # read from json # split the record and see what jobs take over 30 seconds key,category,details=line if category == 'runtime' and details['elapsed'] > 30: print details 

¿Hay una manera de interpretar de forma segura una cadena como parte de una expresión condicional, de modo que pueda aceptar una condicional en la línea de comandos y hacerla parte de mi consulta?

 search 'details["elapsed"] > 30' 

¿Para que en código pudiera hacer algo como esto?

  if *something involving sys.argv[1]*: print line 

Esto debería hacer lo que quieras:

 from __future__ import print_function import ast import operator import sys OPERATORS = { '<': operator.lt, '<=': operator.le, '>': operator.gt, '>=': operator.ge, '==': operator.eq, '!=': operator.ne, # 'in' is using a lambda because of the opposite operator order # 'in': (lambda item, container: operator.contains(container, item), 'in': (lambda item, container: item in container), 'contains': operator.contains, } def process_conditionals(conditional_strings, variables): for conditional_string in conditional_strings: # Everything after first and op is part of second first, op, second = conditional_string.split(None, 2) resolved_operands = [] for raw_operand in (first, second): try: resolved_operand = ast.literal_eval(raw_operand) except ValueError: # If the operand is not a valid literal ve = sys.exc_info() try: # Check if the operand is a known value resolved_operand = variables[raw_operand] except KeyError: # If the operand is not a known value # Re-raise the ValueError raise ve[1], None, ve[2] resolved_operands.append(resolved_operand) yield (op, tuple(resolved_operands)) def main(lines, *conditional_strings): for line in lines: key, category, details = line variables = { 'key': key, 'category': category, 'elapsed': details['elapsed'], 'jobname': details['jobname'], } conditionals = process_conditionals(conditional_strings, variables) try: # You could check each conditional separately to determine # which ones have errors. condition = all(OPERATORS[op](*operands) for op, operands in conditionals) except TypeError: print("A literal in one of your conditionals is the wrong type. " "If you can't see it, try running each one separately.", file=sys.stderr) break except ValueError: print("An operand in one of your conditionals is neither a known " "variable nor a valid literal. If you can't see it, try " "running each one separately.", file=sys.stderr) break else: if condition: print(line) if __name__ == '__main__': lines = [ [1, 'runtime', {'elapsed': 12.3, 'jobname': 'high38853'}], [2, 'runtime', {'elapsed': 45.6, 'jobname': 'high38854'}], [3, 'runtime', {'elapsed': 78.9, 'jobname': 'high38855'}], [4, 'runtime', {'elapsed': 14.7, 'jobname': 'high38856'}], [5, 'runtime', {'elapsed': 25.8, 'jobname': 'high38857'}], [6, 'runtime', {'elapsed': 36.9, 'jobname': 'high38858'}], [7, 'runtime', {'elapsed': 75.3, 'jobname': 'high38859'}], ] conditional_strings = sys.argv[1:] main(lines, *conditional_strings) 

Ejemplos:

 $ ./SO_31999444.py 'elapsed > 30' [2, 'runtime', {'jobname': 'high38854', 'elapsed': 45.6}] [3, 'runtime', {'jobname': 'high38855', 'elapsed': 78.9}] [6, 'runtime', {'jobname': 'high38858', 'elapsed': 36.9}] [7, 'runtime', {'jobname': 'high38859', 'elapsed': 75.3}] $ ./SO_31999444.py 'elapsed > 20' 'elapsed < 50' [2, 'runtime', {'jobname': 'high38854', 'elapsed': 45.6}] [5, 'runtime', {'jobname': 'high38857', 'elapsed': 25.8}] [6, 'runtime', {'jobname': 'high38858', 'elapsed': 36.9}] $ ./SO_31999444.py 'elapsed > 20' 'elapsed < 50' 'key >= 5' [5, 'runtime', {'jobname': 'high38857', 'elapsed': 25.8}] [6, 'runtime', {'jobname': 'high38858', 'elapsed': 36.9}] $ ./SO_31999444.py "'9' in jobname" [7, 'runtime', {'jobname': 'high38859', 'elapsed': 75.3}] $ ./SO_31999444.py "jobname contains '9'" [7, 'runtime', {'jobname': 'high38859', 'elapsed': 75.3}] $ ./SO_31999444.py "jobname in ['high38857', 'high38858']" [5, 'runtime', {'jobname': 'high38857', 'elapsed': 25.8}] [6, 'runtime', {'jobname': 'high38858', 'elapsed': 36.9}] $ ./SO_31999444.py "9 in jobname" A literal in one of your conditionals is the wrong type. If you can't see it, try running each one separately. $ ./SO_31999444.py "notakey == 'something'" An operand in one of your conditionals is neither a known variable nor a valid literal. If you can't see it, try running each one separately. $ ./SO_31999444.py "2 == 2" [1, 'runtime', {'jobname': 'high38853', 'elapsed': 12.3}] [2, 'runtime', {'jobname': 'high38854', 'elapsed': 45.6}] [3, 'runtime', {'jobname': 'high38855', 'elapsed': 78.9}] [4, 'runtime', {'jobname': 'high38856', 'elapsed': 14.7}] [5, 'runtime', {'jobname': 'high38857', 'elapsed': 25.8}] [6, 'runtime', {'jobname': 'high38858', 'elapsed': 36.9}] [7, 'runtime', {'jobname': 'high38859', 'elapsed': 75.3}] $ ./SO_31999444.py [1, 'runtime', {'jobname': 'high38853', 'elapsed': 12.3}] [2, 'runtime', {'jobname': 'high38854', 'elapsed': 45.6}] [3, 'runtime', {'jobname': 'high38855', 'elapsed': 78.9}] [4, 'runtime', {'jobname': 'high38856', 'elapsed': 14.7}] [5, 'runtime', {'jobname': 'high38857', 'elapsed': 25.8}] [6, 'runtime', {'jobname': 'high38858', 'elapsed': 36.9}] [7, 'runtime', {'jobname': 'high38859', 'elapsed': 75.3}] 

Este fue un pequeño proyecto divertido :).

Realmente no hay una forma segura de hacerlo. Para los condicionales básicos, puede analizar una cadena de entrada de un formato específico. Si su entrada estaba en el formato “var> 5”, podría analizarla así:

 var, op, num = argv[1].split() var = getattr(sys.modules[__name__], var) # Get a reference to the data num = int(num) if op == ">": r = var > num elif op == "<": r = var < num ... if r:  

Para admitir sentencias más complicadas, necesitarías mejorar el analizador. Si no confía en su entrada, debe ajustar el getattr y el int en los bloques try / except. Para soportar int o float u otra var, necesitarías bastante lógica.