From: Leandro Lucarella Date: Sat, 10 Mar 2007 20:00:18 +0000 (+0000) Subject: Ejecutar comandos con shell y almacenarlos como strings. X-Git-Tag: 0_9~87 X-Git-Url: https://git.llucax.com/software/sercom.git/commitdiff_plain/245f7025181c555c285d8b598caa180a69c19812?ds=inline Ejecutar comandos con shell y almacenarlos como strings. Se eliminar todo lo relacionado con Parametros (ParamValidator, modulo sercom.validators -que tenía sólo eso-, ParamCol, etc.) y se almacenan los comandos como simples string porque ahora se ejecutan los comandos usando el shell. La razón para no hacer esto en el sistema viejo era la carencia de un chroot completo, cosa que ahora sí tiene el sistema. Esto además de simplificar el modelo de datos, hace mucho más flexible la especificación de comandos/casos de prueba, ya que se pueden utilizar cosas propias del shell (como el glob). --- diff --git a/doc/testdata.py b/doc/testdata.py index a29b8d6..a461157 100644 --- a/doc/testdata.py +++ b/doc/testdata.py @@ -20,7 +20,7 @@ cf = tf.add_comando(1, 'make tito', retorno=0, max_cant_archivos=15, 'sin usar un Makefile (debe ser un solo archivo que se llame tito.c)') tp = TareaPrueba(nombre='Probar', terminar_si_falla=True, rechazar_si_falla=True) -cp = tp.add_comando(1, [], retorno=0, terminar_si_falla=True, +cp = tp.add_comando(1, retorno=0, terminar_si_falla=True, rechazar_si_falla=True, descripcion='Prueba normalmente, sin filtros') # Enunciados @@ -37,9 +37,9 @@ c = Curso(anio=2007, cuatrimestre=1, numero=1, descripcion=u'Martes', # Casos de prueba cp1 = e1.add_caso_de_prueba(nombre=u'Sin parámetros', retorno=0, - descripcion=u'Un caso', comando=[]) + descripcion=u'Un caso', comando='./tito') cp2 = e1.add_caso_de_prueba(nombre=u'2 parámetross', retorno=0, - comando='--test -c "con espacios"') + comando='./tito --test -c "con espacios"') # Ejercicios ej1 = c.ejercicios[0] diff --git a/sercom/model.py b/sercom/model.py index a99995f..42385e7 100644 --- a/sercom/model.py +++ b/sercom/model.py @@ -8,7 +8,6 @@ from sqlobject.inheritance import InheritableSQLObject from sqlobject.col import PickleValidator, UnicodeStringValidator from turbogears import identity from turbogears.identity import encrypt_password as encryptpw -from sercom.validators import params_to_list, ParseError from formencode import Invalid hub = PackageHub("sercom") @@ -46,43 +45,6 @@ class SOTupleCol(SOPickleCol): class TupleCol(PickleCol): baseClass = SOTupleCol -class ParamsValidator(UnicodeStringValidator): - def to_python(self, value, state): - if isinstance(value, basestring) or value is None: - value = super(ParamsValidator, self).to_python(value, state) - try: - value = params_to_list(value) - except ParseError, e: - raise Invalid("invalid parameters in the ParamsCol '%s', parse " - "error: %s" % (self.name, e), value, state) - elif not isinstance(value, (list, tuple)): - raise Invalid("expected a tuple, list or valid string in the " - "ParamsCol '%s', got %s %r instead" - % (self.name, type(value), value), value, state) - return value - def from_python(self, value, state): - if isinstance(value, (list, tuple)): - value = ' '.join([repr(p) for p in value]) - elif isinstance(value, basestring) or value is None: - value = super(ParamsValidator, self).to_python(value, state) - try: - params_to_list(value) - except ParseError, e: - raise Invalid("invalid parameters in the ParamsCol '%s', parse " - "error: %s" % (self.name, e), value, state) - else: - raise Invalid("expected a tuple, list or valid string in the " - "ParamsCol '%s', got %s %r instead" - % (self.name, type(value), value), value, state) - return value - -class SOParamsCol(SOUnicodeCol): - def createValidators(self): - return [ParamsValidator(db_encoding=self.dbEncoding, name=self.name)] - -class ParamsCol(UnicodeCol): - baseClass = SOParamsCol - #}}} #{{{ Clases @@ -349,8 +311,8 @@ class TareaPrueba(Tarea): #{{{ # Joins comandos = MultipleJoin('ComandoPrueba', joinColumn='tarea_id') - def add_comando(self, orden, comando, **kw): - return ComandoPrueba(tarea=self, orden=orden, comando=comando, **kw) + def add_comando(self, orden, **kw): + return ComandoPrueba(tarea=self, orden=orden, comando='', **kw) def remove_comando(self, orden): ComandoPrueba.pk.get(self.id, orden).destroySelf() @@ -364,7 +326,7 @@ class Comando(InheritableSQLObject): #{{{ RET_ANY = None RET_FAIL = -1 # Campos - comando = ParamsCol(length=255, notNone=True) + comando = UnicodeCol(length=255, notNone=True) descripcion = UnicodeCol(length=255, default=None) retorno = IntCol(default=None) # None es que no importa max_tiempo_cpu = IntCol(default=None) # En segundos diff --git a/sercom/subcontrollers/caso_de_prueba/__init__.py b/sercom/subcontrollers/caso_de_prueba/__init__.py index d931bf5..8c52315 100644 --- a/sercom/subcontrollers/caso_de_prueba/__init__.py +++ b/sercom/subcontrollers/caso_de_prueba/__init__.py @@ -10,7 +10,6 @@ from turbogears import paginate from docutils.core import publish_parts from sercom.subcontrollers import validate as val from sercom.model import CasoDePrueba, Enunciado -from sercom.validators import ParamsValidator #}}} #{{{ Configuración @@ -69,10 +68,10 @@ class CasoDePruebaForm(W.TableForm): validator=V.UnicodeString(not_empty=False, max=255, strip=True)) comando = W.TextField(label=_(u'Comando'), - validator=ParamsValidator(not_empty=False, strip=True)) + validator=V.UnicodeString(not_empty=False, strip=True)) retorno = W.TextField(label=_(u'Código de retorno'), validator=V.Int(not_empty=False, strip=True)) - tiempo_cpu = W.TextField(label=_(u'Tiempo de CPU'), + max_tiempo_cpu = W.TextField(label=_(u'Máximo tiempo de CPU'), validator=V.Number(not_empty=False, strip=True)) fields = Fields() javascript = [W.JSSource("MochiKit.DOM.focusOnLoad('form_nombre');")] @@ -82,9 +81,6 @@ form = CasoDePruebaForm() #{{{ Controlador -def params2str(params): - return ' '.join([repr(p)[1:] for p in params]) - class CasoDePruebaController(controllers.Controller, identity.SecureResource): """Basic model admin interface""" require = identity.has_permission('admin') @@ -107,8 +103,7 @@ class CasoDePruebaController(controllers.Controller, identity.SecureResource): r = cls.select() else: r = cls.selectBy(enunciadoID=enunciado) - return dict(records=r, name=name, namepl=namepl, parcial=enunciado, - params2str=params2str) + return dict(records=r, name=name, namepl=namepl, parcial=enunciado) @expose(template='kid:%s.templates.new' % __name__) def new(self, **kw): @@ -128,8 +123,7 @@ class CasoDePruebaController(controllers.Controller, identity.SecureResource): def edit(self, id, **kw): """Edit record in model""" r = validate_get(id) - return dict(name=name, namepl=namepl, record=r, form=form, - params2str=params2str) + return dict(name=name, namepl=namepl, record=r, form=form) @validate(form=form) @error_handler(edit) @@ -148,7 +142,7 @@ class CasoDePruebaController(controllers.Controller, identity.SecureResource): r.desc = '' else: r.desc = publish_parts(r.descripcion, writer_name='html')['html_body'] - return dict(name=name, namepl=namepl, record=r, params2str=params2str) + return dict(name=name, namepl=namepl, record=r) @expose() def delete(self, id): diff --git a/sercom/subcontrollers/caso_de_prueba/templates/list.kid b/sercom/subcontrollers/caso_de_prueba/templates/list.kid index 356e0f5..c891ae8 100644 --- a/sercom/subcontrollers/caso_de_prueba/templates/list.kid +++ b/sercom/subcontrollers/caso_de_prueba/templates/list.kid @@ -16,7 +16,7 @@ Descripción Parámetros RET - CPU + CPU Operaciones @@ -25,9 +25,9 @@ href="${tg.url('/enunciado/show/%d' % record.enunciado.id)}">enunciado descripción - comando --con-parámetros + comando --con-parámetros retorno - tiempo de cpu + máx tiempo de cpu Editar Eliminar diff --git a/sercom/subcontrollers/caso_de_prueba/templates/show.kid b/sercom/subcontrollers/caso_de_prueba/templates/show.kid index 58700e4..91060cd 100644 --- a/sercom/subcontrollers/caso_de_prueba/templates/show.kid +++ b/sercom/subcontrollers/caso_de_prueba/templates/show.kid @@ -25,7 +25,7 @@ Parámetros: - comando --con-parámetros: + comando --con-parámetros: @@ -33,8 +33,8 @@ retorno - Tiempo de CPU: - tiempo_cpu + Máximo tiempo de CPU: + max_tiempo_cpu diff --git a/sercom/tester.py b/sercom/tester.py index 5915182..4d6cb3f 100644 --- a/sercom/tester.py +++ b/sercom/tester.py @@ -298,9 +298,15 @@ def ejecutar_comando_fuente(self, path, entrega): #{{{ unzip(self.archivos_entrada, path) # TODO try/except comando_ejecutado = entrega.add_comando_ejecutado(self) # Abro archivos para fds básicos (FIXME) - options = dict(close_fds=True, stdin=None, stdout=None, stderr=None, - preexec_fn=SecureProcess(self, 'var/chroot_pepe', '/home/sercom/build')) - log.debug(_(u'Ejecutando como root: %s'), ' '.join(self.comando)) + options = dict( + close_fds=True, + stdin=None, + stdout=None, + stderr=None, + shell=True, + preexec_fn=SecureProcess(self, 'var/chroot_pepe', '/home/sercom/build') + ) + log.debug(_(u'Ejecutando como root: %s'), self.comando) os.seteuid(0) # Dios! (para chroot) os.setegid(0) try: diff --git a/sercom/validators.py b/sercom/validators.py deleted file mode 100644 index 4779006..0000000 --- a/sercom/validators.py +++ /dev/null @@ -1,88 +0,0 @@ -# vim: set et sw=4 sts=4 encoding=utf-8 : - -from turbogears.validators import * - -class ParseError(ValueError): pass - -def params_to_list(params): - r"""Parsea un string de forma similar al bash, separando por espacios y - teniendo en cuenta comillas simples y dobles para agrupar. Para poner - comillas se puede usar el \ como caracter de escape (\' y \") y también - interpreta \n y \t. Devuelve una lista con los parámetros encontrados. - >>> param2seq('--prueba') - ['--prueba'] - >>> params_to_list('--prueba larga "con espacios"') - ['--prueba', 'larga', 'con espacios'] - >>> params_to_list(u'''"con enter\\nentre 'comillas \\"dobles\\"'" --unicode''') - [u'con enter\nentre \'comillas "dobles"\'', u'--unicode'] - >>> params_to_list('"archivo\\tseparado\\tpor\\ttabs" -h') - ['archivo\tseparado\tpor\ttabs', '-h'] - """ - # Constantes - SEP, TOKEN, DQUOTE, SQUOTE = ' ', None, '"', "'" - seq = [] - buff = '' - escape = False - state = SEP - if not params: return seq - for c in params: - # Es un caracter escapado - if escape: - if c == 'n': - buff += '\n' - elif c == 't': - buff += '\t' - else: - buff += c - escape = False - continue - # Es una secuencia de escape - if c == '\\': - escape = True - continue - # Si está buscando espacios - if state == SEP: - if c == SEP: - continue - else: - state = TOKEN # Encontró - if state == TOKEN: - if c == DQUOTE: - state = DQUOTE - continue - if c == SQUOTE: - state = SQUOTE - continue - if c == SEP: - state = SEP - seq.append(buff) - buff = '' - continue - buff += c - continue - if state == DQUOTE: - if c == DQUOTE: - state = TOKEN - continue - buff += c - continue - if state == SQUOTE: - if c == SQUOTE: - state = TOKEN - continue - buff += c - continue - raise ParseError, 'Invalid syntax' - if state == DQUOTE or state == SQUOTE: - raise ParseError, 'Missing closing quote %s' % state - if buff: - seq.append(buff) - return seq - -class ParamsValidator(UnicodeString): - def validate_python(self, value, state): - try: - params_to_list(value) - except ParseError, e: - raise Invalid(str(e), value, state) -