diff options
Diffstat (limited to 'funcweb')
-rw-r--r-- | funcweb/TODO.txt | 20 | ||||
-rw-r--r-- | funcweb/funcweb/config/app.cfg | 3 | ||||
-rw-r--r-- | funcweb/funcweb/controllers.py | 144 | ||||
-rw-r--r-- | funcweb/funcweb/static/css/style.css | 10 | ||||
-rw-r--r-- | funcweb/funcweb/static/images/loading.gif | bin | 0 -> 46766 bytes | |||
-rw-r--r-- | funcweb/funcweb/static/javascript/ajax.js | 83 | ||||
-rw-r--r-- | funcweb/funcweb/templates/master.html | 16 | ||||
-rw-r--r-- | funcweb/funcweb/templates/method.html | 9 | ||||
-rw-r--r-- | funcweb/funcweb/templates/method_args.html | 16 | ||||
-rw-r--r-- | funcweb/funcweb/templates/minion.html | 5 | ||||
-rw-r--r-- | funcweb/funcweb/templates/minions.html | 7 | ||||
-rw-r--r-- | funcweb/funcweb/templates/module.html | 2 | ||||
-rw-r--r-- | funcweb/funcweb/templates/run.html | 5 | ||||
-rw-r--r-- | funcweb/funcweb/tests/test_widget_automation.py | 130 | ||||
-rw-r--r-- | funcweb/funcweb/tests/test_widget_validation.py | 339 | ||||
-rw-r--r-- | funcweb/funcweb/widget_automation.py | 263 | ||||
-rw-r--r-- | funcweb/funcweb/widget_validation.py | 333 |
17 files changed, 1357 insertions, 28 deletions
diff --git a/funcweb/TODO.txt b/funcweb/TODO.txt new file mode 100644 index 0000000..8b76171 --- /dev/null +++ b/funcweb/TODO.txt @@ -0,0 +1,20 @@ +Things to be done for funcweb (makkalot) + + ? : not sure + - : not completed + + : completed + / : being implemented + + +Widget Automation for Minion methods : + (+) Creating InputWidgets for method arguments + (+) Creating WidgetList with created InputWidgets + (+) Creating Validators for method arguments + (+)Combining Validators and WidgetList into a RemoteForm + (/) Writing more more tests for the API + (-) Fix tha css and look and feel of the web UI it sucks! + (/) Package the funcweb (rpm thing) + (-) Test more modules export all if have time ! + +New Ideas : + -???? diff --git a/funcweb/funcweb/config/app.cfg b/funcweb/funcweb/config/app.cfg index e691bde..336dc84 100644 --- a/funcweb/funcweb/config/app.cfg +++ b/funcweb/funcweb/config/app.cfg @@ -7,7 +7,8 @@ # The commented out values below are the defaults # VIEW - +#tg.include_widgets = ['turbogears.mochikit'] +tg.mochikit_all = True # which view (template engine) to use if one is not specified in the # template name tg.defaultview = "genshi" diff --git a/funcweb/funcweb/controllers.py b/funcweb/funcweb/controllers.py index b45d81b..f2c030f 100644 --- a/funcweb/funcweb/controllers.py +++ b/funcweb/funcweb/controllers.py @@ -1,10 +1,35 @@ import logging log = logging.getLogger(__name__) -from turbogears import controllers, expose, flash, identity, redirect +from turbogears import controllers, expose, flash, identity, redirect, error_handler,validate from func.overlord.client import Overlord, Minions +from funcweb.widget_automation import WidgetListFactory,RemoteFormAutomation,RemoteFormFactory +from funcweb.widget_validation import WidgetSchemaFactory + +# it is assigned into method_display on every request +global_form = None + +def validate_decorator_updater(validator_value=None): + """ + When we pass the global_form directly to + turbogears.validate it is not updated on + every request we should pass a callable + to trigger it ;) + + @param :validator_value : Does nothing in the code + of validate it is passed but is irrelevant in our case + because we compute the global_form in the method_display + + @return : the current form and schema to validate the + current input in cherrypy's request + """ + global global_form + return global_form class Root(controllers.RootController): + + + #will be reused for widget validation @expose(template="funcweb.templates.minions") @identity.require(identity.not_anonymous()) @@ -16,7 +41,7 @@ class Root(controllers.RootController): @expose(template="funcweb.templates.minion") @identity.require(identity.not_anonymous()) - def minion(self, name, module=None, method=None): + def minion(self, name="*", module=None, method=None): """ Display module or method details for a specific minion. If only the minion name is given, it will display a list of modules @@ -26,7 +51,18 @@ class Root(controllers.RootController): """ fc = Overlord(name) if not module: # list all modules + #just list those who have get_method_args modules = fc.system.list_modules() + display_modules = [] + #FIXME slow really i know ! + for module in modules.itervalues(): + for mod in module: + #if it is not empty + if getattr(fc,mod).get_method_args()[name]: + display_modules.append(mod) + modules = {} + modules[name]=display_modules + return dict(modules=modules) else: # a module is specified if method: # minion.module.method specified; bring up execution form @@ -38,13 +74,41 @@ class Root(controllers.RootController): tg_template="funcweb.templates.module") - @expose(template="funcweb.templates.run") + @expose(template="funcweb.templates.method_args") @identity.require(identity.not_anonymous()) - def run(self, minion="*", module=None, method=None, arguments=''): + def method_display(self,minion=None,module=None,method=None): + + global global_form fc = Overlord(minion) - results = getattr(getattr(fc, module), method)(*arguments.split()) - cmd = "%s.%s.%s(%s)" % (minion, module, method, arguments) - return dict(cmd=cmd, results=results) + method_args = getattr(fc,module).get_method_args() + + if not method_args.values(): + print "Not registered method here" + return dict(minion_form = None,minion=minion,module=module,method=method) + + minion_arguments = method_args[minion][method]['args'] + if minion_arguments: + wlist_object = WidgetListFactory(minion_arguments,minion=minion,module=module,method=method) + wlist_object = wlist_object.get_widgetlist_object() + #create the validation parts for the remote form + wf = WidgetSchemaFactory(minion_arguments) + schema_man=wf.get_ready_schema() + + #create the final form + minion_form = RemoteFormAutomation(wlist_object,schema_man) + global_form = minion_form.for_widget + #print global_form + #i use that when something goes wrong to check the problem better to stay here ;) + #self.minion_form =RemoteFormFactory(wlist_object,schema_man).get_remote_form() + + del wlist_object + del minion_arguments + + return dict(minion_form =minion_form,minion=minion,module=module,method=method) + else: + return dict(minion_form = None,minion=minion,module=module,method=method) + + @expose(template="funcweb.templates.login") def login(self, forward_url=None, previous_url=None, *args, **kw): @@ -71,6 +135,72 @@ class Root(controllers.RootController): return dict(message=msg, previous_url=previous_url, logging_in=True, original_parameters=request.params, forward_url=forward_url) + + + @expose() + @identity.require(identity.not_anonymous()) + def handle_minion_error(self,tg_errors=None): + """ + The method checks the result from turbogears.validate + decorator so if it has the tg_errors we know that the + form validation is failed. That prevents the extra traffic + to be sent to the minions! + """ + if tg_errors: + #print tg_errors + return str(tg_errors) + + + @expose(allow_json=True) + @error_handler(handle_minion_error) + @validate(form=validate_decorator_updater) + @identity.require(identity.not_anonymous()) + def post_form(self,**kw): + """ + Data processing part + """ + if kw.has_key('minion') and kw.has_key('module') and kw.has_key('method'): + #assign them because we need the rest so dont control everytime + #and dont make lookup everytime ... + minion = kw['minion'] + del kw['minion'] + module = kw['module'] + del kw['module'] + method = kw['method'] + del kw['method'] + + #everytime we do that should be a clever way for that ??? + fc = Overlord(minion) + #get again the method args to get their order : + arguments=getattr(fc,module).get_method_args() + #so we know the order just allocate and put them there + cmd_args=['']*(len(kw.keys())) + + for arg in kw.keys(): + #wow what a lookup :) + index_of_arg = arguments[minion][method]['args'][arg]['order'] + cmd_args[index_of_arg]=kw[arg] + + #now execute the stuff + result = getattr(getattr(fc,module),method)(*cmd_args) + return str(result) + + else: + return "Missing arguments sorry can not proceess the form" + + @expose(template="funcweb.templates.method_args") + @identity.require(identity.not_anonymous()) + def execute_link(self,minion=None,module=None,method=None): + """ + Method is fot those minion methods that dont accept any + arguments so they provide only some information,executed + by pressing only the link ! + """ + fc = Overlord(minion) + result = getattr(getattr(fc,module),method)() + return str(result) + + @expose() def logout(self): diff --git a/funcweb/funcweb/static/css/style.css b/funcweb/funcweb/static/css/style.css index b320cb6..703e22e 100644 --- a/funcweb/funcweb/static/css/style.css +++ b/funcweb/funcweb/static/css/style.css @@ -506,7 +506,7 @@ background-position:100% -24px; /*********** My Fedora Widget stuff *************/ .col { - margin: 5px + margin: 10px padding: 0 0 2ex; } @@ -642,7 +642,7 @@ background-position:100% -24px; .col { float: left; - margin: 5px; + margin: 30px; padding: 0 0 2ex; padding-bottom: 32767px; margin-bottom: -32767px; @@ -677,6 +677,10 @@ background-position:100% -24px; } +#col5 { + width: 200px; + margin-left: 500px; +} .widget-list { /*display: table-cell;*/ } @@ -699,6 +703,8 @@ background-position:100% -24px; background-color: white; } + + .widget h2 { border-width: 1px 1px 1px 1px; font-weight: bold; diff --git a/funcweb/funcweb/static/images/loading.gif b/funcweb/funcweb/static/images/loading.gif Binary files differnew file mode 100644 index 0000000..83729ec --- /dev/null +++ b/funcweb/funcweb/static/images/loading.gif diff --git a/funcweb/funcweb/static/javascript/ajax.js b/funcweb/funcweb/static/javascript/ajax.js new file mode 100644 index 0000000..7df4749 --- /dev/null +++ b/funcweb/funcweb/static/javascript/ajax.js @@ -0,0 +1,83 @@ +function remoteFormRequest(form, target, options) { + var query = Array(); + var contents = formContents(form); + for (var j=0; j<contents[0].length; j++) + query[contents[0][j]] = contents[1][j]; + query["tg_random"] = new Date().getTime(); + //makePOSTRequest(form.action, target, queryString(query)); + remoteRequest(form, form.action, target, query, options); + return true; +} + +function remoteRequest(source, target_url, target_dom, data, options) { + //before + if (options['before']) { + eval(options['before']); + } + if ((!options['confirm']) || confirm(options['confirm'])) { + makePOSTRequest(source, target_url, getElement(target_dom), queryString(data), options); + //after + if (options['after']) { + eval(options['after']); + } + } + return true; +} + +function makePOSTRequest(source, url, target, parameters, options) { + var http_request = false; + if (window.XMLHttpRequest) { // Mozilla, Safari,... + http_request = new XMLHttpRequest(); + if (http_request.overrideMimeType) { + http_request.overrideMimeType('text/xml'); + } + } else if (window.ActiveXObject) { // IE + try { + http_request = new ActiveXObject("Msxml2.XMLHTTP"); + } catch (e) { + try { + http_request = new ActiveXObject("Microsoft.XMLHTTP"); + } catch (e) {} + } + } + if (!http_request) { + alert('Cannot create XMLHTTP instance'); + return false; + } + + var insertContents = function () { + if (http_request.readyState == 4) { + // loaded + if (options['loaded']) { + eval(options['loaded']); + } + if (http_request.status == 200) { + if(target) { + target.innerHTML = http_request.responseText; + } + //success + if (options['on_success']) { + eval(options['on_success']); + } + } else { + //failure + if (options['on_failure']) { + eval(options['on_failure']); + } else { + alert('There was a problem with the request. Status('+http_request.status+')'); + } + } + //complete + if (options['on_complete']) { + eval(options['on_complete']); + } + } + } + + http_request.onreadystatechange = insertContents; + http_request.open('POST', url, true); + http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + http_request.setRequestHeader("Content-length", parameters.length); + http_request.setRequestHeader("Connection", "close"); + http_request.send(parameters); +} diff --git a/funcweb/funcweb/templates/master.html b/funcweb/funcweb/templates/master.html index ab14ca0..073adc1 100644 --- a/funcweb/funcweb/templates/master.html +++ b/funcweb/funcweb/templates/master.html @@ -9,9 +9,9 @@ <title py:content="'Your Title Goes Here'"></title> <script type="text/javascript" src="${tg.url('/static/javascript/jquery.js')}" /> - - <link py:for="js in tg_js_head" py:strip="">${XML(js)}</link> - <link py:for="css in tg_css" py:strip="">${XML(css)}</link> + <link py:for="js in tg_js_head" py:strip="">${ET(js.display())}</link> + <link py:for="css in tg_css" py:strip="">${ET(css.display())}</link> + <script type="text/javascript" src="${tg.url('/static/javascript/ajax.js')}" /> <link href="${tg.url('/static/images/favicon.ico')}" type="image/vnd.microsoft.icon" rel="shortcut icon" /> @@ -20,10 +20,16 @@ <style type="text/css" media="screen"> @import url("/static/css/style.css"); - </style> + </style> + + <script type="text/javascript"> + jQuery._$ = MochiKit.DOM.getElement; + var myj = jQuery.noConflict(); + </script> + </head> <body py:match="body" py:attrs="select('@*')"> - <div py:for="js in tg_js_bodytop" py:replace="XML(js.display())" /> + <div py:for="js in tg_js_bodytop" py:replace="ET(js.display())" /> <div class="wrapper"> <div class="head"> <h1><a href="http://fedorahosted.org/func">Func</a></h1> diff --git a/funcweb/funcweb/templates/method.html b/funcweb/funcweb/templates/method.html index 1f67ce9..af20d86 100644 --- a/funcweb/funcweb/templates/method.html +++ b/funcweb/funcweb/templates/method.html @@ -2,18 +2,17 @@ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude"> + + <body> <div class="method"> ${minion}.${module}.${method} - - <form action="" method="GET" onsubmit="$('#results').hide().load('/run/${minion}/${module}/${method}/').show('slow'); return false;"> + + <form action="" method="GET" onsubmit="myj('#results').hide().load('/run/${minion}/${module}/${method}/').show('slow'); return false;"> <input type="text" name="arguments" id="methodargs" class="methodargs"/> </form> <div class="results" id="results" /> - <script type="text/javascript"> - $(document).ready($("#methodargs").focus()); - </script> </div> </body> </html> diff --git a/funcweb/funcweb/templates/method_args.html b/funcweb/funcweb/templates/method_args.html new file mode 100644 index 0000000..2e7d4bd --- /dev/null +++ b/funcweb/funcweb/templates/method_args.html @@ -0,0 +1,16 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:py="http://genshi.edgewall.org/" + xmlns:xi="http://www.w3.org/2001/XInclude"> + +<body> + <div py:if="minion_form" id="men"> + ${ET(minion_form.display(displays_on='genshi'))} + </div> + <div py:if="not minion_form" id="men"> + <a href="#" onclick="myj('#results').hide().load('/execute_link/${minion}/${module}/${method}/').show('slow');">Run Method</a> + <div class="results" id="results" /> + </div> +</body> +</html> + diff --git a/funcweb/funcweb/templates/minion.html b/funcweb/funcweb/templates/minion.html index f171b00..a526764 100644 --- a/funcweb/funcweb/templates/minion.html +++ b/funcweb/funcweb/templates/minion.html @@ -2,12 +2,13 @@ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude"> + <body> - <div id="modules" class="modules"> + <div id="modules" class="modules"> <ul py:for="minion, mods in modules.items()"> <h2>${minion[:13]}</h2> <li py:for="module in mods"> - <a href="#" onclick="$('#col4').hide();$('#col3').hide().load('/minion/${minion}/${module}').show('slow');">${module}</a> + <a href="#" onclick="myj('#col5').hide();myj('#col3').hide().load('/minion/${minion}/${module}').show('slow');">${module}</a> </li> </ul> </div> diff --git a/funcweb/funcweb/templates/minions.html b/funcweb/funcweb/templates/minions.html index 94ccbc1..6a39f54 100644 --- a/funcweb/funcweb/templates/minions.html +++ b/funcweb/funcweb/templates/minions.html @@ -3,20 +3,21 @@ xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="master.html"/> -<head/> + <head/> <body> - <div class="col-group"> + <div class="col-group"> <div class="col" id="col1"> <ul> <li><h2>minions</h2></li> <li py:for="minion in minions"> - <a onclick="$('#col3').hide();$('#col4').hide();$('#col2').hide().load('/minion/${minion}').show('slow');" href="#">${minion}</a> + <a onclick="jQuery('#col3').hide();myj('#col4').hide();myj('#col5').hide();getElement('col2').innerHTML=toHTML(IMG({src:'../static/images/loading.gif',width:'80',height:'80'}));myj('#col2').hide().load('/minion/${minion}').show('slow');" href="#">${minion}</a> </li> </ul> </div> <div class="col" id="col2" /> <div class="col" id="col3" /> <div class="col" id="col4" /> + <div class="col" id="col5" /> </div> </body> </html> diff --git a/funcweb/funcweb/templates/module.html b/funcweb/funcweb/templates/module.html index 2d433f6..a4794d0 100644 --- a/funcweb/funcweb/templates/module.html +++ b/funcweb/funcweb/templates/module.html @@ -7,7 +7,7 @@ <ul py:for="minion, methods in modules.items()"> <h2>${module}</h2> <li py:for="method in methods"> - <a href="#" onclick="$('#col4').hide().load('/minion/${minion}/${module}/${method}').show('slow')">${method}</a> + <a href="#" onclick="myj('#col2').hide();myj('#col5').hide().show('slow');myj('#col4').hide().load('/method_display/${minion}/${module}/${method}').show('slow')">${method}</a> </li> </ul> </div> diff --git a/funcweb/funcweb/templates/run.html b/funcweb/funcweb/templates/run.html index cb17cdd..7ae047b 100644 --- a/funcweb/funcweb/templates/run.html +++ b/funcweb/funcweb/templates/run.html @@ -2,7 +2,8 @@ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude"> -<body> - ${results.values()[0]} + <body> + + ${results.values()[0]} </body> </html> diff --git a/funcweb/funcweb/tests/test_widget_automation.py b/funcweb/funcweb/tests/test_widget_automation.py new file mode 100644 index 0000000..d02de93 --- /dev/null +++ b/funcweb/funcweb/tests/test_widget_automation.py @@ -0,0 +1,130 @@ +import unittest +import turbogears +from turbogears import testutil + +from funcweb.widget_automation import WidgetListFactory,RemoteFormAutomation,RemoteFormFactory +from funcweb.widget_validation import WidgetSchemaFactory + +class TestWidgetListFactory(unittest.TestCase): + + def setUp(self): + self.widget_factory = WidgetListFactory(self.get_test_default_args(),minion="myminion",module="mymodule",method="my_method") + + + def tearDown(self): + pass + + def test_default_args(self): + compare_with = self.get_test_default_args() + widget_list=self.widget_factory.get_widgetlist() + + #print "The widget list is like :",widget_list + + for argument_name,argument_options in compare_with.iteritems(): + assert widget_list.has_key(argument_name) == True + #print "The argument name is :",argument_name + #because some of them dont have it like boolean + if argument_options.has_key('default'): + assert argument_options['default'] == getattr(widget_list[argument_name],'default') + + if argument_options.has_key("description"): + assert argument_options['description']==getattr(widget_list[argument_name],'help_text') + + if argument_options.has_key("options"): + assert argument_options['options'] == getattr(widget_list[argument_name],"options") + + #that should be enough + def test_get_widgetlist_object(self): + compare_with = self.get_test_default_args() + widget_list_object = self.widget_factory.get_widgetlist_object() + + #print widget_list_object + + all_fields = [getattr(field,"name") for field in widget_list_object] + #print all_fields + for argument_name in compare_with.keys(): + print argument_name + assert argument_name in all_fields + #print getattr(widget_list_object,argument_name) + + + def test_remote_form(self): + schema_factory = WidgetSchemaFactory(self.get_test_default_args()) + schema_validator=schema_factory.get_ready_schema() + widget_list_object = self.widget_factory.get_widgetlist_object() + remote_form = RemoteFormAutomation(widget_list_object,schema_validator) + #print remote_form + + def test_remote_form_factory(self): + from turbogears.view import load_engines + load_engines() + + # WidgetsList object + widget_list_object = self.widget_factory.get_widgetlist_object() + #print widget_list_object + remote_form = RemoteFormFactory(widget_list_object).get_remote_form() + + #it is a key,value dict + widget_list=self.widget_factory.get_widgetlist() + #print widget_list + all_fields = [getattr(field,"name") for field in remote_form.fields] + #print all_fields + #will check if the remote form object hass all the names in it + for argument_name in widget_list.items(): + argument_name in all_fields + + + #print remote_form.render() + + def get_test_default_args(self): + return { + 'string_default':{ + 'type':'string', + 'default':'default string', + 'optional':False, + 'description':'default description' + }, + 'int_default':{ + 'type':'int', + 'default':'default int', + 'optional':False, + 'description':'default description' + }, + #no sense to have default + 'boolean_default':{ + 'type':'boolean', + 'optional':False, + 'description':'default description' + }, + 'float_default':{ + 'type':'float', + 'default':'default float', + 'optional':False, + 'description':'default description' + + }, + 'hash_default':{ + 'type':'hash', + 'default':'default hash', + 'optional':False, + 'description':'default description' + + }, + 'list_default':{ + 'type':'list', + 'default':'default list', + 'optional':False, + 'description':'default description' + + }, + #will be converted to dropdown + 'special_string':{ + 'type':'string', + 'default':'myfirst', + 'options':['myfirst','mysecond','mythird'], + 'optional':False, + 'description':'default dropdown list' + } + + } + diff --git a/funcweb/funcweb/tests/test_widget_validation.py b/funcweb/funcweb/tests/test_widget_validation.py new file mode 100644 index 0000000..c800d41 --- /dev/null +++ b/funcweb/funcweb/tests/test_widget_validation.py @@ -0,0 +1,339 @@ +import unittest +import turbogears +from turbogears import testutil +from funcweb.widget_validation import WidgetSchemaFactory,MinionIntValidator,MinionFloatValidator,MinionListValidator,MinionHashValidator +from turbogears import validators + +class TestWidgetValidator(unittest.TestCase): + + def test_string_validator(self): + wf = WidgetSchemaFactory(self.get_string_params()) + schema_man=wf.get_ready_schema() + + conversion_schema = { + 'max_length':'max', + 'min_length':'min', + 'validator':'regex' + } + + #do better test here + for argument_name,arg_options in self.get_string_params().iteritems(): + #print argument_name + assert hasattr(schema_man,argument_name)==True + #not very efficient but it si just a test :) + if argument_name != 'string_mix': + for arg,value in arg_options.iteritems(): + #print getattr(schema_man,argument_name) + if conversion_schema.has_key(arg): + if hasattr(getattr(schema_man,argument_name),conversion_schema[arg]): + #print arg,value + #couldnt find a way to test it !?? + if arg != 'validator': + assert getattr(getattr(schema_man,argument_name),conversion_schema[arg])==value + #print getattr(getattr(schema_man,argument_name),conversion_schema[arg]) + else: + #just print it to see what is inside because the test will be very hardcoded otherwise + #print getattr(schema_man,argument_name) + continue + print "Happy tests !" + + def test_int_validator(self): + wf = WidgetSchemaFactory(self.get_int_params()) + schema_man=wf.get_ready_schema() + + for argument_name,arg_options in self.get_int_params().iteritems(): + #print argument_name + assert hasattr(schema_man,argument_name)==True + #print " ",argument_name," : ",getattr(schema_man,argument_name) + + #if the argument includes some range + if arg_options.has_key('range'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert getattr(getattr(schema_man,argument_name),'max') == arg_options['range'][1] + assert getattr(getattr(schema_man,argument_name),'min') == arg_options['range'][0] + if arg_options.has_key('min'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert getattr(getattr(schema_man,argument_name),'min') == arg_options['min'] + + if arg_options.has_key('max'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert getattr(getattr(schema_man,argument_name),'max') == arg_options['max'] + + if arg_options.has_key('optional'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional'] + + + print "Happy test!" + + def test_float_validator(self): + wf = WidgetSchemaFactory(self.get_float_params()) + schema_man=wf.get_ready_schema() + + for argument_name,arg_options in self.get_float_params().iteritems(): + #print argument_name + assert hasattr(schema_man,argument_name)==True + #print " ",argument_name," : ",getattr(schema_man,argument_name) + + if arg_options.has_key('min'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert getattr(getattr(schema_man,argument_name),'min') == arg_options['min'] + + if arg_options.has_key('max'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert getattr(getattr(schema_man,argument_name),'max') == arg_options['max'] + + if arg_options.has_key('optional'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional'] + + + print "Happy test!" + + def test_bool_validator(self): + testing_data = self.get_bool_params() + wf = WidgetSchemaFactory(testing_data) + schema_man=wf.get_ready_schema() + + for argument_name,arg_options in testing_data.iteritems(): + #print argument_name + #should all the argument names really + assert hasattr(schema_man,argument_name)==True + #print " ",argument_name," : ",getattr(schema_man,argument_name) + + if arg_options.has_key('optional'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional'] + + print "Happy test!" + + + + def test_list_validator(self,the_type='list'): + if the_type == 'list': + testing_data = self.get_list_params() + else: + testing_data = self.get_hash_params() + + wf = WidgetSchemaFactory(testing_data) + schema_man=wf.get_ready_schema() + + for argument_name,arg_options in testing_data.iteritems(): + #print argument_name + #should all the argument names really + assert hasattr(schema_man,argument_name)==True + #print " ",argument_name," : ",getattr(schema_man,argument_name) + + if arg_options.has_key('validator'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert getattr(getattr(schema_man,argument_name),'regex_string') == arg_options['validator'] + + if arg_options.has_key('optional'): + #print " ",argument_name," : ",getattr(schema_man,argument_name) + assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional'] + + + print "Happy test!" + + + def test_hash_validator(self): + self.test_list_validator(the_type = 'hash') + + def test_minion_int_validator(self): + mv=MinionIntValidator(max = 44,min=2) + self.assertRaises(validators.Invalid,mv.to_python,100) + self.assertRaises(validators.Invalid,mv.to_python,1) + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + assert mv.to_python(21) == 21 + + #dont use the min + mv=MinionIntValidator(max = 44) + self.assertRaises(validators.Invalid,mv.to_python,100) + assert mv.to_python(1)==1 + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + assert mv.to_python(21) == 21 + + mv=MinionIntValidator(min=12) + self.assertRaises(validators.Invalid,mv.to_python,10) + assert mv.to_python(14)==14 + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + assert mv.to_python(21) == 21 + + mv=MinionIntValidator() + assert mv.to_python(14)==14 + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + + def test_minion_float_validator(self): + mv=MinionFloatValidator(max = 44.0,min=2.0) + self.assertRaises(validators.Invalid,mv.to_python,100.0) + self.assertRaises(validators.Invalid,mv.to_python,1.0) + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + assert mv.to_python(21.0) == 21.0 + + #dont use the min + mv=MinionFloatValidator(max = 44.0) + self.assertRaises(validators.Invalid,mv.to_python,100.0) + assert mv.to_python(1.0)==1.0 + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + assert mv.to_python(21.0) == 21.0 + + mv=MinionFloatValidator(min=12.0) + self.assertRaises(validators.Invalid,mv.to_python,10.0) + assert mv.to_python(14.0)==14.0 + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + assert mv.to_python(21.0) == 21.0 + + mv=MinionFloatValidator() + assert mv.to_python(14.0)==14.0 + self.assertRaises(validators.Invalid,mv.to_python,'some_string') + + def test_minion_list_validator(self): + + #test default + mv = MinionListValidator() + assert mv.to_python(['fedora','debian','others']) == ['fedora','debian','others'] + del mv + + #test with regex + mv = MinionListValidator(regex_string='^[A-Z]+$',not_empty=True) + assert mv.to_python(['FEDORA','MACOSX']) == ['FEDORA','MACOSX'] + self.assertRaises(validators.Invalid,mv.to_python,[]) + self.assertRaises(validators.Invalid,mv.to_python,['hey_error']) + self.assertRaises(validators.Invalid,mv.to_python,{}) + del mv + + + print "Happy testing !" + + + def test_minion_hash_validator(self): + + #test default + mv = MinionHashValidator() + assert mv.to_python({'fedora':1,'debian':2,'others':3}) == {'fedora':1,'debian':2,'others':3} + del mv + + #test with regex + mv = MinionHashValidator(regex_string='^[A-Z]+$',not_empty=True) + assert mv.to_python({'FEDORA':'FEDORA','MACOSX':'MACOSX'}) == {'FEDORA':'FEDORA','MACOSX':'MACOSX'} + self.assertRaises(validators.Invalid,mv.to_python,{}) + self.assertRaises(validators.Invalid,mv.to_python,{'hey_error':12}) + self.assertRaises(validators.Invalid,mv.to_python,"hwy") + del mv + + print "Happy testing !" + + + def get_string_params(self): + return { + 'string_default':{ + 'type':'string', + 'default':'default string', + 'description':'default description' + }, + 'string_regex':{ + 'type':'string', + 'default':'some', + 'validator':'^[a-z]*$' + } + , + 'min_max_string':{ + 'type':'string', + 'default':'myfirst', + 'optional':False, + 'description':'default dropdown list', + 'max_length':12, + 'min_length':5 + }, + 'string_mix':{ + 'type':'string', + 'optional':False, + 'max_length':123, + 'min_length':12, + 'validator':'^[A-Z]+$' + } + } + + def get_int_params(self): + return { + 'int_default':{ + 'type':'int', + 'default':2, + 'description':'default integer' + }, + 'min_max':{ + 'type':'int', + 'default':12, + 'optional':False, + 'description':'default dropdown list', + 'max':12, + 'min':5 + }, + 'range_int':{ + 'type':'int', + 'optional':False, + 'range':[1,55] + } + } + + def get_float_params(self): + return { + 'float_default':{ + 'type':'float', + 'default':2.0, + 'description':'default float' + }, + 'min_max':{ + 'type':'float', + 'default':11.0, + 'optional':False, + 'description':'default dropdown list', + 'max':12.0, + 'min':5.0 + }, + } + + def get_list_params(self): + return { + 'list_default':{ + 'type':'list', + 'default':'cooler', + 'description':'default list' + }, + 'list_regex':{ + 'type':'list', + 'default':'hey', + 'optional':False, + 'description':'default regex list', + 'validator':'^[A-Z]$' + }, + } + + def get_hash_params(self): + return { + 'hash_default':{ + 'type':'hash', + 'default':{'hey':12}, + 'description':'default hash' + }, + 'hash_regex':{ + 'type':'hash', + 'default':{'hey':12}, + 'optional':False, + 'description':'default regex hash', + 'validator':'^[A-Z]$' + }, + } + + def get_bool_params(self): + return { + 'bool_default':{ + 'type':'boolean', + 'default':{'hey':12}, + 'description':'default hash', + 'optional':False, + }, + } + + + diff --git a/funcweb/funcweb/widget_automation.py b/funcweb/funcweb/widget_automation.py new file mode 100644 index 0000000..5d299bd --- /dev/null +++ b/funcweb/funcweb/widget_automation.py @@ -0,0 +1,263 @@ +#the purpose of that module is to make widget automation +#for registered minion modules so we dont hace to write +#that same boring stuff for every added module ! + +from turbogears.widgets.base import Widget,WidgetsList +from turbogears import widgets + +class WidgetListFactory(object): + """ + The class is responsible for taking the method arguments + and converting them to the appropriate widget equivalents + + Examples : + """ + #which type matches for which InputWidget + __convert_table={ + 'int':{ + 'default_value':"TextField", + }, + 'string':{ + 'default_value':"TextField", + 'options':"SingleSelectField"}, + 'boolean':{ + 'default_value':"CheckBox"}, + 'float':{ + 'default_value':"TextField", + }, + 'hash':{ + 'default_value':"TextArea"}, + 'list':{ + 'default_value':"TextArea"} + } + #will contain the input widget created in that class + + def __init__(self,argument_dict,minion=None,module=None,method=None): + """ + Initiated with argument_dict of a method to return back + a WidgetsList object to be placed into a form object + + @param:argument_dict : The structure we got here is like + {'arg1':{'type':'int'..},'arg2':{'min':111} ...} + """ + + self.__argument_dict = argument_dict + self.__widget_list={} + + #these fields will be passed ass hidden so we need them + self.minion = minion + self.module = module + self.method = method + + def __add_general_widget(self): + + #key is the argument_name and the argument are options + for key,argument in self.__argument_dict.iteritems(): + #get the type of the argument + current_type = argument['type'] + + act_special = False #if it has some special parameters + #it should be passed to its specialized method,if it has + #for example options in its key it should be shown as a + #SingleSelectField not a TextFiled + + for type_match in self.__convert_table[current_type].keys(): + if type_match!='default_value' and argument.has_key(type_match): + act_special = True + + # print key,argument + + if act_special: + #calling for example __add_specialized_string(..) + getattr(self,"_%s__add_specialized_%s"%(self.__class__.__name__,current_type))(argument,key) + else: + temp_object = getattr(widgets,self.__convert_table[current_type]['default_value'])() + #add common options to it + self.__add_commons_to_object(temp_object,argument,key) + #add a new entry to final list + self.__widget_list[key]=temp_object + #print "That have the object :",getattr(self.__widget_list[key],"default") + del temp_object + + #print "That have the object :",getattr(self.__widget_list["list_default"],"default") + + #adding the hidden fields (that part wass adde later can be made more generic) + if self.minion: + self.__widget_list['minion']= getattr(widgets,'HiddenField')(name="minion",value=self.minion,default=self.minion) + if self.module: + self.__widget_list['module']= getattr(widgets,'HiddenField')(name="module",value=self.module,default=self.module) + if self.method: + self.__widget_list['method']= getattr(widgets,'HiddenField')(name="method",value=self.method,default=self.method) + + + + def __add_specialized_string(self,argument,argument_name): + """ + Specialized option adder, called when the type:string is used + with option 'options' so we should create a new SingleSelectField + that one canno be created like others because user should supply options + in the constructor of the SingleSelectField so it becomes a special one + + @param : argument : the argument options, + @param : argument_name : the name of the argument also the name of the widget + @return : Nothing + """ + + #allittle bit difficult to follow but that structure does + #temp_object = SingleSelectField() for example + + temp_object = getattr(widgets,self.__convert_table[argument['type']]['options'])(options = argument['options']) + self.__add_commons_to_object(temp_object,argument,argument_name) + #add a new entry to final list + self.__widget_list[argument_name]=temp_object + del temp_object + + def __add_commons_to_object(self,object,argument,argument_name): + """ + As it was thought all input widgets have the same + common parameters they take so that method will add + them to instantiated object for ex (TextField) if they + occur into the argument ... + + @param object : instantiated inputwidget object + @param method argument to lookup {type:'int','max':12 ...} + @return :None + """ + #firstly set the name of the argument + setattr(object,"name",argument_name) + + #print "The argument name is :",argument_name + #print "The argument options are :",argument + + if argument.has_key('default'): + setattr(object,"default",argument["default"]) + if argument.has_key('description'): + setattr(object,'help_text',argument['description']) + + def get_widgetlist(self): + """ + Return back a dictionay with argument_name : input_widget + pairs. That method may not be called directly,get_widgetlist_object + is better for using in web interface + """ + #compute the list + self.__add_general_widget() + return self.__widget_list + + def get_widgetlist_object(self): + """ + Method return back the final widgetlist object + which is turbogears.widgets.WidgetsList + """ + + #print self.__widget_list + if len(self.__widget_list.keys())==0: + self.__add_general_widget() #not very efficient + + widget_list_object = widgets.WidgetsList() + for name,input_widget in self.__widget_list.iteritems(): + #it is a list indeed + widget_list_object.append(input_widget) + + #get the object back + #print widget_list_object + return widget_list_object + +#################################################################################################################### +from turbogears.widgets.base import CoreWD +from turbogears.widgets import RemoteForm +from turbogears import validators, expose + +class RemoteFormAutomation(CoreWD): + """ + Base class for ajaxian Form creation + """ + + name = "Ajaxian Minion Submit Form" + + template = """ + <div> + ${for_widget.display(action='post_form')} + <div id="loading"></div> + <div id="post_data"></div> + </div> + """ + + full_class_name = "funcweb.controllers.Root" + + def __init__(self,generated_fields,validator_schema,*args,**kwarg): + """ + The constructor part same as normal one except + it takes a WidgetsList object into generated_fields + which is generated by WidgetListFactory + """ + #call the master :) + super(RemoteFormAutomation,self).__init__(*args,**kwarg) + self.for_widget = RemoteForm( + fields = generated_fields, + validator = validator_schema, + name = "minion_form", + update = "col5", + before='getElement(\'loading\').innerHTML=toHTML(IMG({src:\'../static/images/loading.gif\',width:\'80\',height:\'80\'}));', + on_complete='getElement(\'loading\' ).innerHTML=\'Done!\';', + ) + +#################################################################################################### +class RemoteFormFactory(object): + """ + Gets the WidgetListFactory object + and return back a RemoteForm the same as above + just for testing + """ + #some values that may want to change later + name = "minion_form", + update = "col5", + before='getElement(\'loading\').innerHTML=toHTML(IMG({src:\'../static/images/loading.gif\',width:\'80\',height:\'80\'}));', + on_complete='getElement(\'loading\' ).innerHTML=\'Done!\';', + submit_text = "Send Minion Form" + action = "/post_form" + + def __init__(self,wlist_object,validator_schema): + self.wlist_object = wlist_object + self.validator_schema = validator_schema + + def get_remote_form(self): + + #print self.wlist_object + + return RemoteForm( + name = self.name, + fields = self.wlist_object, + before = self.before, + on_complete = self.on_complete, + update = self.update, + submit_text = self.submit_text, + action = self.action, + validator = self.validator_schema + ) + +class RemoteLinkFactory(CoreWD): + """ + A rpc executer for the methods without arguments + """ + + name = "Minion Remote Link Executer" + + template =""" + <div> + ${for_widget.display()} + <div id="items">Minion Result</div> + </div> + """ + + def __init__(self,*args,**kwargs): + """The init part here""" + super(SomeRemoteLink,self).__init__(*args,**kwargs) + self.for_widget = LinkRemoteFunction( + name = "catexecuter", + action = "/catlister", + data = dict(give_all="all"), + update = "items" + ) + + diff --git a/funcweb/funcweb/widget_validation.py b/funcweb/funcweb/widget_validation.py new file mode 100644 index 0000000..0347fa6 --- /dev/null +++ b/funcweb/funcweb/widget_validation.py @@ -0,0 +1,333 @@ +from turbogears import validators #the shiny validator part + +class WidgetSchemaFactory(object): + """ + The purpose of the class is to produce a + validators.Schema object according to method + arguments that are retrieved from minions + """ + + def __init__(self,method_argument_dict): + """ + @param method_argument_dict : The dict that is + from minion in format of {'arg':{'type':'string','options':[...]}} + the format is defined in func/minion/func_arg.py + """ + + self.method_argument_dict = method_argument_dict + self.validator_list = {} #the validator that will create final schema + + def _add_validators(self): + """ + Method is an entry point of factory iters over the all arguments + and according to their types it sends the process to more specialized + validator adders + """ + + for argument_name,argument_values in self.method_argument_dict.iteritems(): + #some lazy stuff :) + #for ex : _add_int_validator(some_arg) + getattr(self,"_add_%s_validator"%(argument_values['type']))(argument_name) + + def _add_boolean_validator(self,argument_name): + bool_data_set = {} + + #the optional keyword + if self.method_argument_dict[argument_name].has_key('optional'): + if self.method_argument_dict[argument_name]['optional']: + bool_data_set['not_empty']=False + else: + bool_data_set['not_empty']=True + + + if bool_data_set: + self.validator_list[argument_name]=validators.Bool(**bool_data_set) + else: + self.validator_list[argument_name]=validators.Bool() + + + + + def _add_int_validator(self,argument_name): + """ + Gets the options of the int type and adds a + new validator to validator_list + """ + #the initializer for the int_validator + int_data_set = {} + + #the optional keyword + if self.method_argument_dict[argument_name].has_key('optional'): + if self.method_argument_dict[argument_name]['optional']: + int_data_set['not_empty']=False + else: + int_data_set['not_empty']=True + + if self.method_argument_dict[argument_name].has_key('range'): + #because the range is [min,max] list the 0 is min 1 is the max + int_data_set['min']=self.method_argument_dict[argument_name]['range'][0] + int_data_set['max']=self.method_argument_dict[argument_name]['range'][1] + if self.method_argument_dict[argument_name].has_key('min'): + int_data_set['min']=self.method_argument_dict[argument_name]['min'] + if self.method_argument_dict[argument_name].has_key('max'): + int_data_set['max']=self.method_argument_dict[argument_name]['max'] + + #add the validator to the list + if int_data_set: + self.validator_list[argument_name]=MinionIntValidator(**int_data_set) + else: + self.validator_list[argument_name]=MinionIntValidator() + + + + + def _add_string_validator(self,argument_name): + """ + Gets the options of the string type and adds a + new validator to validator_list + """ + + string_data_set={} + str_validator_list =[] + + if self.method_argument_dict[argument_name].has_key('optional'): + if self.method_argument_dict[argument_name]['optional']: + string_data_set['not_empty']=False + else: + string_data_set['not_empty']=True + + if self.method_argument_dict[argument_name].has_key('min_length'): + string_data_set['min']=self.method_argument_dict[argument_name]['min_length'] + if self.method_argument_dict[argument_name].has_key('max_length'): + string_data_set['max']=self.method_argument_dict[argument_name]['max_length'] + if self.method_argument_dict[argument_name].has_key('validator'): + str_validator_list.append(getattr(validators,'Regex')(self.method_argument_dict[argument_name]['validator'])) + + #if we have set a string_data_set + if string_data_set: + str_validator_list.append(getattr(validators,'String')(**string_data_set)) + + #if true it should be a validator.All thing + if len(str_validator_list)>1: + self.validator_list[argument_name]=getattr(validators,'All')(*str_validator_list) + elif str_validator_list: + self.validator_list[argument_name]=str_validator_list[0] + else: #if there is no option + self.validator_list[argument_name]=getattr(validators,'String')() + + + + + def _add_float_validator(self,argument_name): + """ + Gets the options of the float type and adds a + new validator to validator_list + """ + + #the initializer for the float_validator + float_data_set = {} + + #is it optional + if self.method_argument_dict[argument_name].has_key('optional'): + if self.method_argument_dict[argument_name]['optional']: + float_data_set['not_empty']=False + else: + float_data_set['not_empty']=True + + + if self.method_argument_dict[argument_name].has_key('min'): + float_data_set['min']=self.method_argument_dict[argument_name]['min'] + if self.method_argument_dict[argument_name].has_key('max'): + float_data_set['max']=self.method_argument_dict[argument_name]['max'] + + #add the validator to the list + if float_data_set: + self.validator_list[argument_name]=MinionFloatValidator(**float_data_set) + else: + self.validator_list[argument_name]=MinionFloatValidator() + + + + def _add_list_validator(self,argument_name,the_type='list'): + """ + Gets the options of the list type and adds a + new validator to validator_list + """ + list_data_set = {} + + #is it optional + if self.method_argument_dict[argument_name].has_key('optional'): + if self.method_argument_dict[argument_name]['optional']: + list_data_set['not_empty']=False + else: + list_data_set['not_empty']=True + + if self.method_argument_dict[argument_name].has_key('validator'): + list_data_set['regex_string'] = self.method_argument_dict[argument_name]['validator'] + + if list_data_set: + if the_type == 'list': + self.validator_list[argument_name]=MinionListValidator(**list_data_set) + else: + self.validator_list[argument_name]=MinionHashValidator(**list_data_set) + + else: + if the_type == 'list': + self.validator_list[argument_name]=MinionListValidator() + else: + self.validator_list[argument_name]=MinionHashValidator() + + + + def _add_hash_validator(self,argument_name): + """ + Gets the options of the hash type and adds a + new validator to validator_list + """ + self._add_list_validator(argument_name,the_type = 'hash') + + + def get_ready_schema(self): + """ + Get the final validator schema + """ + final_schema = validators.Schema() + if not self.validator_list: + self._add_validators() + + for vd_name,vd in self.validator_list.iteritems(): + #setattr(final_schema,vd_name,vd) + getattr(final_schema,'fields')[vd_name]= vd + + return final_schema + +######################################################################## +class MinionIntValidator(validators.FancyValidator): + + """ + Confirms that the input/output is of the proper type of int. + + """ + #automatically will be assigned + min = None + max = None + + def _to_python(self,value,state): + """ + Will check just the type here and return + value to be validated in validate_python + """ + try: + value = int(value) + except (ValueError, TypeError): + raise validators.Invalid('The field should be integer',value,state) + + return int(value) + + + def validate_python(self,value,state): + """ + The actual validator + """ + #firstly run the supers one + if self.min and self.min: + if value < self.min: + raise validators.Invalid('The number you entered should be bigger that %d'%(self.min),value,state) + + if self.max and self.max: + if value > self.max: + raise validators.Invalid('The number you entered exceeds the %d'%(self.max),value,state) + + +################################################################## +class MinionFloatValidator(MinionIntValidator): + + def _to_python(self,value,state): + """ + Will check just the type here and return + value to be validated in validate_python + """ + try: + value = float(value) + except (ValueError, TypeError): + raise validators.Invalid('The field should be a float',value,state) + + return float(value) + +################################################################# +class MinionListValidator(validators.FancyValidator): + + regex_string = None + + def _to_python(self,value,state): + """ + Will check just the type here and return + value to be validated in validate_python + """ + #will add more beautiful validation here after + #integrate the complex widgets for lists and dicts + if self.not_empty: + if len(value)==0: + raise validators.Invalid('Empty list passed when not_empty is set',value,state) + + + tmp = [] + if type(tmp) != type(value): + value = list(value) + value = [list_value.strip() for list_value in value] + + return value + + def validate_python(self,value,state): + import re + if self.regex_string: + try: + compiled_regex = re.compile(self.regex_string) + except Exception,e: + raise validators.Invalid('The passed regex_string is not a valid expression'%self.regex_string,value,state) + + for list_value in value: + if not re.match(compiled_regex,str(list_value)): + raise validators.Invalid('The %s doesnt match to the regex expression that was supplied'%list_value,value,state) + + #there is no else for now :) + +class MinionHashValidator(validators.FancyValidator): + + regex_string = None + + def _to_python(self,value,state): + """ + Will check just the type here and return + value to be validated in validate_python + """ + #will add more beautiful validation here after + #integrate the complex widgets for lists and dicts + if self.not_empty: + if len(value)==0: + raise validators.Invalid('Empty hash passed when not_empty is set',value,state) + + #check the type firstly + tmp = {} + if type(tmp) != type(value): + raise validators.Invalid('The value passed to MinionHashValidator should be a dict object',value,state) + + #print value + return value + + def validate_python(self,value,state): + #print value + import re + if self.regex_string: + try: + compiled_regex = re.compile(self.regex_string) + except Exception,e: + raise validators.Invalid('The passed regex_string is not a valid expression'%self.regex_string,value,state) + for dict_value in value.itervalues(): + if not re.match(compiled_regex,str(dict_value)): + raise validators.Invalid('The %s doesnt match to the regex expression that was supplied'%dict_value,value,state) + + + +if __name__ == "__main__": + pass |