Funktionsparameter näher beschreiben in 2.3?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
henning
User
Beiträge: 274
Registriert: Dienstag 26. Juli 2005, 18:37

Also, das ganze arbeitet noch mit Cheetah zusammen, das sollte man aber sehr leicht ändern können bzw. auf eine andere Template-Engine übertragbar sein.
Ausserdem hat mich mein Gedächtnis ausgetrickst, uch hatte die Klasse PDecimal genannt und nicht PNumeric.

Zunächst mal die Basisklasse Param:

Code: Alles auswählen

import exceptions
from Cheetah.Template import Template

# What to do when validate fails
RAISE = 0        # Raise an exception
USE_DEFAULT = 1  # Use default value
ROBUST = 2       # Try to read what you can,
                 # even if some of the data

class ParamError(exceptions.TypeError):
  def __init__(self, p, s=None):
    exceptions.TypeError.__init__(self)
    self.p = p
    self.s = s
  def __str__(self):
    return "Parameter '%s' ist nicht vom Typ '%s'%s." % (
        self.arg.display, self.arg.typeinfo,
        self.s and (": " + s) or "")

class UncleanError(exceptions.TypeError):
  def __init__(self):
    exceptions.TypeError.__init__(self)
  def __str__(self):
    return "Parameter unvalidiert benutzt!"
    
class Param(Template):
  """
    Encapsulates all functions and data related
    to a parameter.
    A parameter is something that is entered by a user
    in a form and then used by a pice of code.
  """
  def __init__(self, display, internal, value="", default=None, invalid=RAISE, *args, **kwargs):
    Template.__init__(self, *args, **kwargs)
    self.display = display
    self.internal = internal
    self.value = value
    self.invalid = invalid
    self.default = default
    if self.default == None:
      self.default = self.value
    self.typeinfo = "Parameter"

    self.is_clean = False
    
  def validate(self):
    """Validate self.value, react as wanted on failure.
    Override _clean() to validate your way."""
    if not self._clean():
      if self.invalid == RAISE:
        raise ParamError(self)
      else: # self.invalid in (ROBUST, USE_DEFAULT)
        self.value = self.default
    self.is_clean = True

  def __str__(self):
    """Display the value in a nice form.
    (Override _output() to make you own form)"""
    if not self.is_clean:
      #raise UncleanError(self)
      # Be kind, validate now instead of complaining
      self.validate()
    return self._output()
    
  # TODO: Move this to _input,
  # And build a wrapper around it with a nice name
  def input(self):
    """
      Thats how the frontend may ask for self.value.
      Note that its perfectly possible to choose
      special in-/output-Methods for specific params
      or classes at the frontend side.
    """
    return """<input type="text" name="%s" value="%s" />""" % (self.internal, self.value)
    
  def acquire(self, field):
    """
      Read a "dirty" value from Field f.
    """
    self.is_clean = False
    _getfromfield(self, field)

  def acquire_direct(self, v):
    """
      Read a "dirty" value through argument v.
    """
    self.is_clean = False
    self._getdirect(v)
  
  #
  #
  #
    
  def _clean(self):
    """
      If needed transform self.value to a type
      pleasent for your Param-Class,
      Return True if that went okay and self.value afterwards contains
      some useable Data of your favourite type,
      else return False.
    """
    self.value = str(self.value)
    return True

  def _output(self):
    """Thats how the frontend may display self.value.
      This function is meant to be overridden in subclasses."""
    return str(self.value)

  def _getfromfield(self, f):
    """
      Override this method if you have
      a different way of reading data.

      Note that the type of value may change later in
      clean(), just read here as far as you can get
      WITHOUT error checking.
      (i.e. dont raise an error here if the user
       supplied something wrong or nothing or to much
       or what ever can go wrong with the data.)
    """
    self._getdirect(f.getfirst(self.internal))
    
  def _getdirect(self, v):
    """
      Override this method if you have
      a different way of reading data directly.
      You may call this from getfromfield.

      Note that the type of value may change later in
      clean(), just read here as far as you can get
      WITHOUT error checking.
      (i.e. dont raise an error here if the user
       supplied something wrong or nothing or to much
       or what ever can go wrong with the data.)
    """
    self.value = v
Davon abgeleitet ist z.B. PText.
Die heißt jetzt bei mir PTextBase weil ich von jeder Param-Klasse nochmal eine Cheetah-Klasse ableite, die hier z.B. PText heissen würde, wie gesagt, den cheetah-kram kann man vermeiden wenn man will.

Code: Alles auswählen

from Param import Param
from mod_python import apache
    
class PTextBase(Param):
  """
    A parameter class for simple and short text.
  """
  def __init__(self, display, internal, value="", maxlen=0, *args, **kwargs):

    Param.__init__(self, display, internal, value, *args, **kwargs)
    self.maxlen = maxlen
    self.typeinfo = "Text" + (maxlen and (" der Länge %d" % maxlen) or "")

  def _clean(self):
    return (not self.maxlen) or len(self.value) <= self.maxlen
Hier die Cheetah-Ableitung.
Ist zwar ein bisschen overhead, aber ich finds schöner als das in strings zu quoten.
(Ausserdem ist es damit als teil des Frontends austauschbar). Ich denke, der cheetah-code erklärt sich von selbst, wenn man python kann.

Code: Alles auswählen

#extends PTextBase

#def input
  #set $sz = $maxlen and (" size=%d" % $maxlen) or ""
  <input type="text" name="$internal" value="$value"$sz/>
#end def
PDecimal:

Code: Alles auswählen

from Param import Param

class PDecimalBase(PText):
  def __init__(self, display, internal, value="", digits=0, *args, **kwargs):
    PTextBase.__init__(self, display, internal, value, *args, **kwargs)
    self.typeinfo = "Dezimalzahl" + digits and (" mit %d Stellen" % digits) or "")
    self.digits = digits
    
  def _clean(self):
    if (not self.digits) or len(self.value) == self.digits:
      try:
        self.value = int(self.value)
        return True
      except ValueError:
        pass
    return False
Hier nochmal was interessanteres, PList für Combo-Boxen:

Code: Alles auswählen

from Param import Param, ParamError

class PListBase(Param):
  """
    Encapsulates a List.
    This means: The user can select one or more
    items from a given list (choices).
    The selection is the value.
  """
  def __init__(self, display, internal, value="",
      choices=(), multiple=False, *args, **kwargs):
    Param.__init__(self, display, internal, value,
        *args, **kwargs)
    self.choices = choices
    self.multiple = multiple
    self.typeinfo = "Auswahl aus den Werten %s%s" % (
        str(self.choices), (self.multiple and " (mehrere erlaubt)" or ""))

  def _clean(self):
    l = len(self.choices)
    v = {}
    if self.multiple:
      v = {}
      # self.value still is a list of
      # (hopefully) indexes to choices
      for x in self.value:
        if type(x) == int and x in range(l):
          v[x] = True # This is slightly ROBUST, but wont harm too much
        else:
          if not self.invalid == ROBUST:
            return False
      # Now self.values is something like
      # {1: True, 4: True, 5: True}
      # with all indices in range(l)
      return True
      
    else: # value should be an index for choices
      try: self.value = int(self.value)
      except: return False
      return self.value in range(l)

  def _output(self):
    s = comma = ""
    if self.multiple:
      # Return a simple, comma-separated list. (Thats read- and parseable)
      for i in range(len(self.choices)):
        if i in self.value:
          s += comma + self.choices[i]
          comma = ", "
      return s

    else:
      # Just return our value
      return Param._output(self)
  
  def _getfromfield(self, f):
    if self.multiple: # We should receive a list
      self.value = f.getlist(self.internal, [])

    else: # Just get the value
      super(PListBase, self).getfromfield(self, f)

Code: Alles auswählen

#extends PListBase

#def input
  #set $ml = $multiple and ' multiple="multiple"' or ""
  <select$ml>
  #for $i in $range($len($choices))
    #if $multiple
      #set $sel = $value.has_key($i) and ' selected="selected"' or ""
    #else
      #set $sel = ($i == $value) and ' selected="selected"' or ""
    #end if
    <option value="$i"$sel>$choices[$i]</option>
  #end for
  </select>
#end def
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Und hier wieder die obligatorische WSGI Lösung :-)
http://trac.pocoo.org/browser/wsgi/trun ... alidate.py
TUFKAB – the user formerly known as blackbird
henning
User
Beiträge: 274
Registriert: Dienstag 26. Juli 2005, 18:37

n1.
Wenn ich meinen Kram mal komplett nach WSGI transformiere, werd ich das sicher noch um einiges erweitern (+mir vielleicht erlauben, da eine Objektorientierung nach dem Vorbild von meiner jetzigen Lösung reinzusetzen)
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

So. Hab jetzt meine WSGI Lösung so abgeändert, dass sie den Application Output Parst und in die Inputfelder die Werte wieder einsetzt. Somit übernimmt die jetzt sämtliche Aufgaben. Wenn man nichts zurückschreiben lassen will reicht ein 'environ['wsgi.middleware.validate.autoinsert'] = False' und er wird damit aufhören :-)
TUFKAB – the user formerly known as blackbird
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Argh. Weil mich das jetzt etwas aufgehalten hat, hab ich ich jetzt trotzdem eine Beispielanwendung für die ValidationMiddleware geschrieben.

Den Code sieht man hier, den aktuellen Snapshot Code herunterladen kann man hier.
TUFKAB – the user formerly known as blackbird
henning
User
Beiträge: 274
Registriert: Dienstag 26. Juli 2005, 18:37

Wie ist denn jetzt eigentlich der Status?
Hat dieser Mensch zurückgeschrieben?
Ist WSGI deiner Meinung nach immernoch "considered harmful" oder hattest du nur was übersehen?
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

henning hat geschrieben:Wie ist denn jetzt eigentlich der Status?
Hat dieser Mensch zurückgeschrieben?
Ist WSGI deiner Meinung nach immernoch "considered harmful" oder hattest du nur was übersehen?
Noch hab ich keine Antwort von ihm, aber WSGI ist nach wie vor zu empfehlen.
TUFKAB – the user formerly known as blackbird
Antworten