[snip]

>     tostr = lambda self: "{code: %s, message: %s}" % code, message

I don't know Python's scoping rules, but to get the same effect in
Ruby you also need to use a block to capture the scope at the time of
definition - so use +define_method+ instead of +def+:

ERROR_CODES = {
  :MyEx1 => {'code' => 1, 'message' => "Oops!"},
  :MyEx2 => {'code' => 2, 'message' => "Whoops!"},
  }

for k, v in ERROR_CODES
  Object.const_set(k, Class.new(Exception) {
    define_method :to_s do
      "{code: #{v['code']}, message: #{v['message']}}"
    end
  })
end

o = MyEx1.new
p o

#=> #<MyEx1: {code: 2, message: Whoops!}>

HTH,

Sean