]> git.llucax.com Git - z.facultad/75.52/sercom.git/commitdiff
Ejecutar comandos con shell y almacenarlos como strings.
authorLeandro Lucarella <llucax@gmail.com>
Sat, 10 Mar 2007 20:00:18 +0000 (20:00 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Sat, 10 Mar 2007 20:00:18 +0000 (20:00 +0000)
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).

doc/testdata.py
sercom/model.py
sercom/subcontrollers/caso_de_prueba/__init__.py
sercom/subcontrollers/caso_de_prueba/templates/list.kid
sercom/subcontrollers/caso_de_prueba/templates/show.kid
sercom/tester.py
sercom/validators.py [deleted file]

index a29b8d693ca3eb5cb76d561b05e6e4e131420d49..a4611576a48f3e3d51cfa950b70ea7fc62bbf515 100644 (file)
@@ -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)
     '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
     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,
 
 # 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,
 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]
 
 # Ejercicios
 ej1 = c.ejercicios[0]
index a99995f6a86944afc3ca47e20a49766709b77d11..42385e7e2226e454af1cc87a481c070aef108bd3 100644 (file)
@@ -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 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")
 from formencode import Invalid
 
 hub = PackageHub("sercom")
@@ -46,43 +45,6 @@ class SOTupleCol(SOPickleCol):
 class TupleCol(PickleCol):
     baseClass = SOTupleCol
 
 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
 #}}}
 
 #{{{ Clases
@@ -349,8 +311,8 @@ class TareaPrueba(Tarea): #{{{
     # Joins
     comandos    = MultipleJoin('ComandoPrueba', joinColumn='tarea_id')
 
     # 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()
 
     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
     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
     descripcion         = UnicodeCol(length=255, default=None)
     retorno             = IntCol(default=None) # None es que no importa
     max_tiempo_cpu      = IntCol(default=None) # En segundos
index d931bf53cf0064a78e3c6e32e50b9e2d6d7a8058..8c52315c3bd1f1306cdbce0b0ae438bcd2da8ec8 100644 (file)
@@ -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 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
 #}}}
 
 #{{{ 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=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))
         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');")]
             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
 
 
 #{{{ 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')
 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)
             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):
 
     @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)
     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)
 
     @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']
             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):
 
     @expose()
     def delete(self, id):
index 356e0f58b96d9a50cc967c2ab4c789fdbe499973..c891ae8fd3c53b31c59cb07c5edb4c47404a06e2 100644 (file)
@@ -16,7 +16,7 @@
         <th>Descripción</th>
         <th>Parámetros</th>
         <th title="Código de retorno">RET</th>
         <th>Descripción</th>
         <th>Parámetros</th>
         <th title="Código de retorno">RET</th>
-        <th title="Tiempo de CPU">CPU</th>
+        <th title="Máximo tiempo de CPU">CPU</th>
         <th>Operaciones</th>
     </tr>
     <tr py:for="record in records">
         <th>Operaciones</th>
     </tr>
     <tr py:for="record in records">
@@ -25,9 +25,9 @@
                 href="${tg.url('/enunciado/show/%d' % record.enunciado.id)}"><span
                     py:replace="tg.summarize(record.enunciado.shortrepr(), 30)">enunciado</span></a></td>
         <td><span py:replace="tg.summarize(record.descripcion, 30)">descripción</span></td>
                 href="${tg.url('/enunciado/show/%d' % record.enunciado.id)}"><span
                     py:replace="tg.summarize(record.enunciado.shortrepr(), 30)">enunciado</span></a></td>
         <td><span py:replace="tg.summarize(record.descripcion, 30)">descripción</span></td>
-        <td><span py:if="record.comando" py:replace="tg.summarize(params2str(record.comando), 30)">comando --con-parámetros</span></td>
+        <td><span py:if="record.comando" py:replace="tg.summarize(record.comando, 30)">comando --con-parámetros</span></td>
         <td><span py:replace="record.retorno">retorno</span></td>
         <td><span py:replace="record.retorno">retorno</span></td>
-        <td><span py:replace="record.tiempo_cpu">tiempo de cpu</span></td>
+        <td><span py:replace="record.max_tiempo_cpu">máx tiempo de cpu</span></td>
         <td><a href="${tg.url('/caso_de_prueba/edit/%d' % record.id)}">Editar</a>
             <a href="${tg.url('/caso_de_prueba/delete/%d' % record.id)}" onclick="if (confirm('${_(u'Estás seguro? Yo creo que no...')}')) { var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit(); };return false;">Eliminar</a></td>
     </tr>
         <td><a href="${tg.url('/caso_de_prueba/edit/%d' % record.id)}">Editar</a>
             <a href="${tg.url('/caso_de_prueba/delete/%d' % record.id)}" onclick="if (confirm('${_(u'Estás seguro? Yo creo que no...')}')) { var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit(); };return false;">Eliminar</a></td>
     </tr>
index 58700e45f0f4fd8fea7d7b1cabf0d90dcc45e953..91060cde9c8a25d29d8838a0bdf374e53d94a53c 100644 (file)
@@ -25,7 +25,7 @@
     <tr>
         <th>Parámetros:</th>
        <td>
     <tr>
         <th>Parámetros:</th>
        <td>
-            <span py:if="record.comando" py:replace="params2str(record.comando)">comando --con-parámetros</span>:
+            <span py:if="record.comando" py:replace="record.comando">comando --con-parámetros</span>:
        </td>
     </tr>
     <tr>
        </td>
     </tr>
     <tr>
@@ -33,8 +33,8 @@
        <td><span py:replace="record.retorno">retorno</span></td>
     </tr>
     <tr>
        <td><span py:replace="record.retorno">retorno</span></td>
     </tr>
     <tr>
-        <th>Tiempo de CPU:</th>
-       <td><span py:replace="record.tiempo_cpu">tiempo_cpu</span></td>
+        <th>Máximo tiempo de CPU:</th>
+       <td><span py:replace="record.max_tiempo_cpu">max_tiempo_cpu</span></td>
     </tr>
 </table>
 
     </tr>
 </table>
 
index 5915182ad6114acb13e467f628e517c99faf51b2..4d6cb3f0dcfaef18f20bbb66ab803f530ef2fe82 100644 (file)
@@ -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)
     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:
     os.seteuid(0) # Dios! (para chroot)
     os.setegid(0)
     try:
diff --git a/sercom/validators.py b/sercom/validators.py
deleted file mode 100644 (file)
index 4779006..0000000
+++ /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)
-