2014-09-11 4 views
1

J'essaie de trouver une bonne solution à cela et rien ne me vient à l'esprit. À titre d'exercice, je suis en train de créer un gestionnaire de contexte qui gère la validation des données, quelque chose comme:Gestionnaire de contexte pour valider les données

validation = lambda x: len(x) <= 10 

with validator(validation): 
    some_data = input("Please enter a name of 10 characters or less: ") 

print(some_data) 

# OUTPUT 
>> Please enter a name of 10 characters or less: FooBarSpamEggs 
>> Please enter a name of 10 characters of less: Adam 
Adam 

Au départ, je pensais à faire cela avec unittest.mock.patch, mais je réalise que je ne peux pas appeler la fonction d'origine alors qu'il est patché, par exemple:

def patched(validation, *args): 
    while True: 
     p = __builtins__.input(args) # Doesn't work 
     if validation(p): 
      break 
    return p 

with unittest.mock.patch('builtins.input', patched): 
    input("Some prompt here: ") 
# fails on recursion error as patched calls itself 

Puis je considérais écrire un décorateur pour valider une seule ligne, mais qui est vraiment utile que si vous pouvez faire quelque chose comme:

@validate(lambda x: int(x) == 6) 
p = input("How many sides does a d6 have? ") 
# can't decorate a function call 

Je suis accroché à cette idée de gestionnaire de contexte, cependant. Malheureusement, je ne sais pas si un gestionnaire de contexte a un accès à son contenu, ou s'il est limité uniquement à ses arguments. Des pensées?

En aparté, je sais que je pourrais rendre cette fonctionnalité dans une fonction .: par exemple

def validate_input(prompt, validation, msg_if_fail=None): 
    while True: 
     p = input(prompt) 
     if validation(p): 
      break 
     if msg_if_fail is not None: 
      print(msg_if_fail) 
    return p 

Mais ce n'est pas aussi jolie. C'est, comme je l'ai dit, un exercice plus qu'un problème pratique.

Répondre

2

Vous pouvez utiliser un gestionnaire de contexte standard pour envelopper unittest.mock.patch et enregistrer une référence à la fonction input d'origine avant de patcher. Ensuite, vous pouvez passer la input originale à votre fonction patched:

import unittest.mock 
import contextlib 
from functools import partial 

def patched(validation, orig_input, *args): 
    while True: 
     p = orig_input(*args) 
     if validation(p): 
      break 
    return p 

@contextlib.contextmanager 
def validator(validate_func): 
    func = partial(patched, validate_func, input) # original input reference saved here 
    patch = unittest.mock.patch('builtins.input', func) 
    patch.start() 
    try: 
     yield 
    finally: 
     patch.stop() 

validation = lambda x: len(x) <= 10 

Ensuite, vous pouvez utiliser le ContextManager comme ceci:

with validator(validation): 
    x = input("10 or less: ") 

x = input("10 or less (unpatched): ") 
print("done") 

Exemple de sortie:

10 or less: abcdefghijklmnop 
10 or less: abcdefgdfgdgd 
10 or less: abcdef 
10 or less (unpatched): abcdefghijklmnop 
done 
+1

Je dirais que c'est un peu plus conçu, bien! (Vous semblez être au courant de cela aussi, mais je me suis dit que je le dirais de toute façon.) – dano

+0

Il n'y a pas d'ingénierie comme la sur-ingénierie. –

1

Un décorateur qui accepte les paramètres pourrait le faire assez bien (http://www.artima.com/weblogs/viewpost.jsp?thread=240845):

max10 = lambda x: len(x) <= 10 

def validator(test): 
    def wrap(func): 
     def wrapped(*args, **kwargs): 
      result = func(*args, **kwargs) 
      if test(result): 
       return result 
      return None 
     return wrapped 
    return wrap 

La façon régulière à l'utiliser:

@validator(max10) 
def valid_input(prompt="Please enter a name: "): 
    return raw_input(prompt) 

print valid_input() 

L'application du décorateur regarde manuellement plus près de ce que vous vous demandez:

valid_input = validator(max10)(raw_input) 
print valid_input("Please enter a name: ") 
Questions connexes