def tryattr(obj, attrname, value):
  """Like setattr(), but validates the value without actually setting it.

  For attributes that exist but have no validator, acts as if the validator
  function allows all values.  Note: just because tryattr() returns success
  doesn't guarantee that setattr() would return success.  You need to still
  be able to handle exceptions in setattr() (due to race conditions, missing
  validators, or any other reason.)

  Args:
    obj: the object containing the attr.
    attrname: the name of the attribute to set.
    value: the value to try setting the attribute to.
  Returns:
    value, or a modified version of value as fixed up by the validator (eg.
      to coerce the data type).
  Raises:
    Any exception the validator might raise.
  """
  try:
    prop = getattr(type(obj), attrname)
  except AttributeError:
    if hasattr(obj, attrname):
      return value  # just a plain value, definitely writable
    else:
      raise  # nonexistent
  validator = getattr(prop, 'validate', None)
  if validator:
    return validator(obj, value)
  else:
    return value