[dialogwizard] ajout de la lib dialogwizard
Ignore-this: c07ebd286368479de93370a7dbfa765e darcs-hash:20090723203207-bd074-66c4713209814daab11becece91749a4e906dd64.gz
This commit is contained in:
parent
143558cb0e
commit
63bdff02a0
4 changed files with 1838 additions and 0 deletions
0
lib/dialogwizard/__init__.py
Normal file
0
lib/dialogwizard/__init__.py
Normal file
1628
lib/dialogwizard/dialog.py
Normal file
1628
lib/dialogwizard/dialog.py
Normal file
File diff suppressed because it is too large
Load diff
42
lib/dialogwizard/dialogwizard.py
Executable file
42
lib/dialogwizard/dialogwizard.py
Executable file
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# DIALOGWIZARD.PY--
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 Antoine Durand-Gasselin
|
||||||
|
# Author: Antoine Durand-Gasselin <adg@crans.org>
|
||||||
|
#
|
||||||
|
|
||||||
|
from wizard import Step, PreviousStep, EndScenario
|
||||||
|
from itertools import izip
|
||||||
|
import dialog, time
|
||||||
|
|
||||||
|
def really_quit(dico):
|
||||||
|
raise EndScenario("game over!", data = dico)
|
||||||
|
|
||||||
|
class DialogStepGenerator():
|
||||||
|
u"""This class defines a step, that will prompt the user for various
|
||||||
|
fields."""
|
||||||
|
|
||||||
|
def __init__(self, backtitle):
|
||||||
|
self.d = dialog.Dialog()
|
||||||
|
self.d.add_persistent_args(["--backtitle", backtitle])
|
||||||
|
|
||||||
|
def form_step(self, title, enonce, form):
|
||||||
|
def fn(dico, default):
|
||||||
|
fields = [ ( field[1], default.get(field[0], dico.get(field[0], ''))) + field[2:] for field in form ]
|
||||||
|
rc, res = self.d.form(enonce, fields = fields, title=title)
|
||||||
|
if rc == 2: really_quit(dico)
|
||||||
|
if rc == 1: raise PreviousStep
|
||||||
|
for field, val in izip (form, res):
|
||||||
|
dico[field[0]] = val
|
||||||
|
return dico
|
||||||
|
return Step(fn)
|
||||||
|
|
||||||
|
def select_step(self, title, enonce, var, choix):
|
||||||
|
def fn(dico, default):
|
||||||
|
rc, res = self.d.menu(enonce, choices = choix, title = title)
|
||||||
|
if rc == 2: really_quit(dico)
|
||||||
|
if rc == 1: raise PreviousStep
|
||||||
|
dico[var] = res
|
||||||
|
return dico
|
||||||
|
return Step(fn)
|
168
lib/dialogwizard/wizard.py
Executable file
168
lib/dialogwizard/wizard.py
Executable file
|
@ -0,0 +1,168 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 Antoine Durand-Gasselin
|
||||||
|
# Author: Antoine Durand-Gasselin <adg@crans.org>
|
||||||
|
#
|
||||||
|
|
||||||
|
class TryAgain(Exception):
|
||||||
|
"""Exception raised when the step should be taken again."""
|
||||||
|
|
||||||
|
class PreviousStep(Exception):
|
||||||
|
"""Exception raised when should be backtracked to previous step."""
|
||||||
|
|
||||||
|
class EndScenario(Exception):
|
||||||
|
"""Exception raised when the scenario should be halted.
|
||||||
|
'msg' is the error message
|
||||||
|
'data' is the updated state object"""
|
||||||
|
def __init__(self, msg, data=None):
|
||||||
|
self.msg = msg
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
class Step:
|
||||||
|
u"""This class defines a step. A step is defined by providing a
|
||||||
|
function that, given an environment and an expected answer will
|
||||||
|
return an update of the environment (as dictionnary)."""
|
||||||
|
def __init__(self, update_fn):
|
||||||
|
self.update = update_fn
|
||||||
|
|
||||||
|
def run(self, env, default):
|
||||||
|
"""This function makes the call"""
|
||||||
|
return self.update(env, default)
|
||||||
|
|
||||||
|
class Scenario:
|
||||||
|
u"""This class allows us to define scenarios."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
u"""empty scenario"""
|
||||||
|
self.steps = None
|
||||||
|
|
||||||
|
def nest(self, step):
|
||||||
|
u"""Adds a step to a scenario"""
|
||||||
|
if not isinstance(step, Step):
|
||||||
|
raise TypeError("Can only bind steps")
|
||||||
|
self.steps = ('NEST', step, self.steps)
|
||||||
|
|
||||||
|
def branch(self, cond, plan_A, plan_B):
|
||||||
|
u"""Makes a test (will call it passing the environnement), and
|
||||||
|
depending on the result, will process one of the two scenarios"""
|
||||||
|
if not callable(cond):
|
||||||
|
raise TypeError("cond must be callable")
|
||||||
|
if not isinstance(plan_A, Scenario) or not isinstance(plan_B, Scenario):
|
||||||
|
raise TypeError("Can only branch on scenarios")
|
||||||
|
self.steps = ('BRANCH', (cond, plan_A, plan_B) , self.steps)
|
||||||
|
|
||||||
|
def quote(self, scenario):
|
||||||
|
u"""Runs a scenario as a single scenario step"""
|
||||||
|
|
||||||
|
if not isinstance(scenario, Scenario):
|
||||||
|
raise TypeError("scenario must be a scenario")
|
||||||
|
|
||||||
|
def quote_scenar (dict1, dict2):
|
||||||
|
try:
|
||||||
|
return Running(scenario).run()
|
||||||
|
except EndScenario:
|
||||||
|
raise PreviousStep
|
||||||
|
|
||||||
|
self.nest(Step(quote_scenar))
|
||||||
|
|
||||||
|
class Running:
|
||||||
|
u"""To run scenarios"""
|
||||||
|
|
||||||
|
env = {}
|
||||||
|
defaults = {}
|
||||||
|
steps = None
|
||||||
|
stack = None
|
||||||
|
|
||||||
|
def __init__(self, scenario):
|
||||||
|
if not isinstance(scenario, Scenario):
|
||||||
|
raise TypeError("Can only run Scenarios")
|
||||||
|
accu = scenario.steps
|
||||||
|
|
||||||
|
# To avoid brain spots on the walls, we shall reverse the list
|
||||||
|
# of steps.
|
||||||
|
while accu:
|
||||||
|
self.steps = accu[0], accu[1], self.steps
|
||||||
|
accu = accu[2]
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
if self.steps:
|
||||||
|
# Case of a Branching
|
||||||
|
if self.steps[0] == 'BRANCH' :
|
||||||
|
# As it is (should be) an epsilon-test we won't
|
||||||
|
# backtrack it.
|
||||||
|
cond, plan_A, plan_B = self.steps[1]
|
||||||
|
self.steps = self.steps[2]
|
||||||
|
if cond(self.env):
|
||||||
|
plan_steps = plan_A.steps
|
||||||
|
else:
|
||||||
|
plan_steps = plan_B.steps
|
||||||
|
# Let's not forget we need to reverse the steps lists.
|
||||||
|
while plan_steps:
|
||||||
|
self.steps = plan_steps[0], plan_steps[1], self.steps
|
||||||
|
plan_steps = plan_steps[2]
|
||||||
|
|
||||||
|
# Case of nesting
|
||||||
|
elif self.steps[0] == 'NEST':
|
||||||
|
try:
|
||||||
|
this_step = self.steps[1]
|
||||||
|
new_env = this_step.run(self.env, self.defaults)
|
||||||
|
# Should we perform sanity checks on new_env ? and raise
|
||||||
|
# TryAgain if it fails ? After updating defaults ?
|
||||||
|
|
||||||
|
self.stack = (self.env.copy(), new_env, self.steps, self.stack)
|
||||||
|
self.env.update(new_env)
|
||||||
|
self.defaults = {}
|
||||||
|
self.steps = self.steps[2]
|
||||||
|
|
||||||
|
except PreviousStep:
|
||||||
|
if self.stack:
|
||||||
|
self.env, self.defaults, self.steps, self.stack = self.stack
|
||||||
|
else:
|
||||||
|
raise EndScenario("No previous step", self.env)
|
||||||
|
|
||||||
|
except TryAgain:
|
||||||
|
# We can update defaults
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Should not be called
|
||||||
|
raise "invalid step"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self.steps:
|
||||||
|
self.step()
|
||||||
|
return (self.env)
|
||||||
|
|
||||||
|
|
||||||
|
# For testing issues
|
||||||
|
def prompt(var):
|
||||||
|
def fn(dict, default):
|
||||||
|
a = raw_input(u"%s (%s):= " % (var, default.get(var, '<no default>')))
|
||||||
|
if a == 'n':
|
||||||
|
raise TryAgain
|
||||||
|
elif a == 'b':
|
||||||
|
raise PreviousStep
|
||||||
|
else:
|
||||||
|
return { var : a }
|
||||||
|
return Step(fn)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
s = Scenario()
|
||||||
|
t = Scenario()
|
||||||
|
u = Scenario()
|
||||||
|
|
||||||
|
for i in ['toto', 'tata', 'titi', 'tutu']:
|
||||||
|
s.nest(prompt(i))
|
||||||
|
t.nest(prompt(i[1]))
|
||||||
|
|
||||||
|
for i in range(6,9):
|
||||||
|
u.nest(prompt(str(i)))
|
||||||
|
|
||||||
|
u.quote(t)
|
||||||
|
|
||||||
|
for i in range(12,15):
|
||||||
|
u.nest(prompt(str(i)))
|
||||||
|
|
||||||
|
|
||||||
|
print (Running(u).run())
|
Loading…
Add table
Add a link
Reference in a new issue