text: import re
  from validate_email import validate_email
  import ipaddress
  try:
      import urlparse
  except ImportError:
      import urllib.parse as urlparse
  import uuid
  import struct
  from jinja2 import Template
  import time
  import sys
  
  printer = ""
  
  def validate(proto_message):
      func = file_template(proto_message)
      global printer
      printer += func + "\n"
      exec(func)
      try:
          return generate_validate
      except NameError:
          return locals()['generate_validate']
  
  def print_validate(proto_message):
      return "".join([s for s in printer.splitlines(True) if s.strip()])
  
  def has_validate(field):
      if field.GetOptions() is None:
          return False
      for option_descriptor, option_value in field.GetOptions().ListFields():
          if option_descriptor.full_name == "validate.rules":
              return True
      return False
  
  def byte_len(s):
      try:
          return len(s.encode('utf-8'))
      except:
          return len(s)
  
  def _validateHostName(host):
      if len(host) > 253:
          return False
  
      s = host.rsplit(".",1)[0].lower()
  
      for part in s.split("."):
          if len(part) == 0 or len(part) > 63:
              return False
  
          # Host names cannot begin or end with hyphens
          if s[0] == "-" or s[len(s)-1] == '-':
              return False
          for r in part:
              if (r < 'A' or r > 'Z') and (r < 'a' or r > 'z') and (r < '0' or r > '9') and r != '-':
                  return False
      return True
  
  def _validateEmail(addr):
      if '<' in addr and '>' in addr: addr = addr.split("<")[1].split(">")[0]
  
      if not validate_email(addr):
          return False
  
      if len(addr) > 254:
          return False
  
      parts = addr.split("@")
      if len(parts[0]) > 64:
          return False
      return _validateHostName(parts[1])
  
  def _has_field(message_pb, property_name):
      # NOTE: As of proto3, HasField() only works for message fields, not for
      #       singular (non-message) fields. First try to use HasField and
      #       if it fails (with a ValueError) we manually consult the fields.
      try:
          return message_pb.HasField(property_name)
      except:
          all_fields = set([field.name for field in message_pb.DESCRIPTOR.fields])
          return property_name in all_fields
  
  def const_template(option_value, name):
      const_tmpl = """{%- if str(o.string) and o.string.HasField('const') -%}
      if {{ name }} != \"{{ o.string['const'] }}\":
          raise ValidationFailed(\"{{ name }} not equal to {{ o.string['const'] }}\")
      {%- elif str(o.bool) and o.bool['const'] != "" -%}
      if {{ name }} != {{ o.bool['const'] }}:
          raise ValidationFailed(\"{{ name }} not equal to {{ o.bool['const'] }}\")
      {%- elif str(o.enum) and o.enum['const'] -%}
      if {{ name }} != {{ o.enum['const'] }}:
          raise ValidationFailed(\"{{ name }} not equal to {{ o.enum['const'] }}\")
      {%- elif str(o.bytes) and o.bytes.HasField('const') -%}
          {% if sys.version_info[0] >= 3 %}
      if {{ name }} != {{ o.bytes['const'] }}:
          raise ValidationFailed(\"{{ name }} not equal to {{ o.bytes['const'] }}\")
          {% else %}
      if {{ name }} != b\"{{ o.bytes['const'].encode('string_escape') }}\":
          raise ValidationFailed(\"{{ name }} not equal to {{ o.bytes['const'].encode('string_escape') }}\")
          {% endif %}
      {%- endif -%}
      """
      return Template(const_tmpl).render(sys = sys, o = option_value, name = name, str = str)
  
  def in_template(value, name):
      in_tmpl = """
      {%- if value['in'] %}
      if {{ name }} not in {{ value['in'] }}:
          raise ValidationFailed(\"{{ name }} not in {{ value['in'] }}\")
      {%- endif -%}
      {%- if value['not_in'] %}
      if {{ name }} in {{ value['not_in'] }}:
          raise ValidationFailed(\"{{ name }} in {{ value['not_in'] }}\")
      {%- endif -%}
      """
      return Template(in_tmpl).render(value = value, name = name)
  
  def string_template(option_value, name):
      str_templ = """
      {{ const_template(o, name) -}}
      {{ in_template(o.string, name) -}}
      {%- set s = o.string -%}
      {%- if s['len'] %}
      if len({{ name }}) != {{ s['len'] }}:
          raise ValidationFailed(\"{{ name }} length does not equal {{ s['len'] }}\")
      {%- endif -%}
      {%- if s['min_len'] %}
      if len({{ name }}) < {{ s['min_len'] }}:
          raise ValidationFailed(\"{{ name }} length is less than {{ s['min_len'] }}\")
      {%- endif -%}
      {%- if s['max_len'] %}
      if len({{ name }}) > {{ s['max_len'] }}:
          raise ValidationFailed(\"{{ name }} length is more than {{ s['max_len'] }}\")
      {%- endif -%}
      {%- if s['len_bytes'] %}
      if byte_len({{ name }}) != {{ s['len_bytes'] }}:
          raise ValidationFailed(\"{{ name }} length does not equal {{ s['len_bytes'] }}\")
      {%- endif -%}
      {%- if s['min_bytes'] %}
      if byte_len({{ name }}) < {{ s['min_bytes'] }}:
          raise ValidationFailed(\"{{ name }} length is less than {{ s['min_bytes'] }}\")
      {%- endif -%}
      {%- if s['max_bytes'] %}
      if byte_len({{ name }}) > {{ s['max_bytes'] }}:
          raise ValidationFailed(\"{{ name }} length is greater than {{ s['max_bytes'] }}\")
      {%- endif -%}
      {%- if s['pattern'] %}
      if re.search(r\'{{ s['pattern'] }}\', {{ name }}) is None:
          raise ValidationFailed(\"{{ name }} pattern does not match {{ s['pattern'] }}\")
      {%- endif -%}
      {%- if s['prefix'] %}
      if not {{ name }}.startswith(\"{{ s['prefix'] }}\"):
          raise ValidationFailed(\"{{ name }} does not start with prefix {{ s['prefix'] }}\")
      {%- endif -%}
      {%- if s['suffix'] %}
      if not {{ name }}.endswith(\"{{ s['suffix'] }}\"):
          raise ValidationFailed(\"{{ name }} does not end with suffix {{ s['suffix'] }}\")
      {%- endif -%}
      {%- if s['contains'] %}
      if not \"{{ s['contains'] }}\" in {{ name }}:
          raise ValidationFailed(\"{{ name }} does not contain {{ s['contains'] }}\")
      {%- endif -%}
      {%- if s['not_contains'] %}
      if \"{{ s['not_contains'] }}\" in {{ name }}:
          raise ValidationFailed(\"{{ name }} contains {{ s['not_contains'] }}\")
      {%- endif -%}
      {%- if s['email'] %}
      if not _validateEmail({{ name }}):
          raise ValidationFailed(\"{{ name }} is not a valid email\")
      {%- endif -%}
      {%- if s['hostname'] %}
      if not _validateHostName({{ name }}):
          raise ValidationFailed(\"{{ name }} is not a valid email\")
      {%- endif -%}
      {%- if s['address'] %}
      try:
          ipaddress.ip_address({{ name }})
      except ValueError:
          if not _validateHostName({{ name }}):
              raise ValidationFailed(\"{{ name }} is not a valid address\")
      {%- endif -%}
      {%- if s['ip'] %}
      try:
          ipaddress.ip_address({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid ip\")
      {%- endif -%}
      {%- if s['ipv4'] %}
      try:
          ipaddress.IPv4Address({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid ipv4\")
      {%- endif -%}
      {%- if s['ipv6'] %}
      try:
          ipaddress.IPv6Address({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid ipv6\")
      {%- endif %}
      {%- if s['uri'] %}
      url = urlparse.urlparse({{ name }})
      if not all([url.scheme, url.netloc, url.path]):
          raise ValidationFailed(\"{{ name }} is not a valid uri\")
      {%- endif %}
      {%- if s['uri_ref'] %}
      url = urlparse.urlparse({{ name }})
      if not all([url.scheme, url.path]) and url.fragment:
          raise ValidationFailed(\"{{ name }} is not a valid uri ref\")
      {%- endif -%}
      {%- if s['uuid'] %}
      try:
          uuid.UUID({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid UUID\")     
      {%- endif -%}
      """
      return Template(str_templ).render(o = option_value, name = name, const_template = const_template, in_template = in_template)
  
  def required_template(value, name):
      req_tmpl = """{%- if value['required'] -%}
      if not _has_field(p, \"{{ name.split('.')[-1] }}\"):
          raise ValidationFailed(\"{{ name }} is required.\")
      {%- endif -%}
      """
      return Template(req_tmpl).render(value = value, name = name)
  
  def message_template(option_value, name, repeated = False):
      message_tmpl = """{%- if m.message %}
      {{- required_template(m.message, name) }}
      {%- endif -%}
      {%- if m.message and m.message['skip'] %}
      # Skipping validation for {{ name }}
      {%- else %}
      {% if repeated %}
      if {{ name }}:
      {% else %}
      if _has_field(p, \"{{ name.split('.')[-1] }}\"):
      {% endif %}
          embedded = validate(p.{{ name }})(p.{{ name }})
          if embedded is not None:
              return embedded
      {%- endif -%}
      """
      return Template(message_tmpl).render(m = option_value, name = name, required_template = required_template, repeated = repeated)
  
  def bool_template(option_value, name):
      bool_tmpl = """
      {{ const_template(o, name) -}}
      """
      return Template(bool_tmpl).render(o = option_value, name = name, const_template = const_template)
  
  def num_template(option_value, name, num):
      num_tmpl = """{%- if num.HasField('const') and str(o.float) == "" -%}
      if {{ name }} != {{ num['const'] }}:
          raise ValidationFailed(\"{{ name }} not equal to {{ num['const'] }}\")
      {%- endif -%}
      {%- if num.HasField('const') and str(o.float) != "" %}
      if {{ name }} != struct.unpack(\"f\", struct.pack(\"f\", ({{ num['const'] }})))[0]:
          raise ValidationFailed(\"{{ name }} not equal to {{ num['const'] }}\")
      {%- endif -%}
      {{ in_template(num, name) }}
      {%- if num.HasField('lt') %}
          {%- if num.HasField('gt') %}
              {%- if num['lt'] > num['gt'] %}
      if {{ name }} <= {{ num['gt'] }} or {{ name }} >= {{ num ['lt'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['lt'], num['gt'] }}\")
              {%- else %}
      if {{ name }} >= {{ num['lt'] }} and {{ name }} <= {{ num['gt'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['gt'], num['lt'] }}\")
              {%- endif -%}
          {%- elif num.HasField('gte') %}
              {%- if num['lt'] > num['gte'] %}
      if {{ name }} < {{ num['gte'] }} or {{ name }} >= {{ num ['lt'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['lt'], num['gte'] }}\")
              {%- else %}
      if {{ name }} >= {{ num['lt'] }} and {{ name }} < {{ num['gte'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['gte'], num['lt'] }}\")
              {%- endif -%}
          {%- else %}
      if {{ name }} >= {{ num['lt'] }}:
          raise ValidationFailed(\"{{ name }} is not lesser than {{ num['lt'] }}\")
          {%- endif -%}
      {%- elif num.HasField('lte') %}
          {%- if num.HasField('gt') %}
              {%- if num['lte'] > num['gt'] %}
      if {{ name }} <= {{ num['gt'] }} or {{ name }} > {{ num ['lte'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['lte'], num['gt'] }}\")
              {%- else %}
      if {{ name }} > {{ num['lte'] }} and {{ name }} <= {{ num['gt'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['gt'], num['lte'] }}\")
              {%- endif -%}
          {%- elif num.HasField('gte') %}
              {%- if num['lte'] > num['gte'] %}
      if {{ name }} < {{ num['gte'] }} or {{ name }} > {{ num ['lte'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['lte'], num['gte'] }}\")
              {%- else %}
      if {{ name }} > {{ num['lte'] }} and {{ name }} < {{ num['gte'] }}:
          raise ValidationFailed(\"{{ name }} is not in range {{ num['gte'], num['lte'] }}\")
              {%- endif -%}
          {%- else %}
      if {{ name }} > {{ num['lte'] }}:
          raise ValidationFailed(\"{{ name }} is not lesser than or equal to {{ num['lte'] }}\")
          {%- endif -%}
      {%- elif num.HasField('gt') %}
      if {{ name }} <= {{ num['gt'] }}:
          raise ValidationFailed(\"{{ name }} is not greater than {{ num['gt'] }}\")
      {%- elif num.HasField('gte') %}
      if {{ name }} < {{ num['gte'] }}:
          raise ValidationFailed(\"{{ name }} is not greater than or equal to {{ num['gte'] }}\")    
      {%- endif -%}
      """
      return Template(num_tmpl).render(o = option_value, name = name, num = num, in_template = in_template, str = str)
  
  def dur_arr(dur):
      value = 0
      arr = []
      for val in dur:
          value += val.seconds
          value += (10**-9 * val.nanos)
          arr.append(value)
          value = 0
      return arr
  
  def dur_lit(dur):
      value = dur.seconds + (10**-9 * dur.nanos)
      return value
  
  def duration_template(option_value, name, repeated = False):
      dur_tmpl = """
      {{- required_template(o.duration, name) }}
      {% if repeated %}
      if {{ name }}:
      {% else %}
      if _has_field(p, \"{{ name.split('.')[-1] }}\"):
      {% endif %}
          dur = {{ name }}.seconds + round((10**-9 * {{ name }}.nanos), 9)
          {%- set dur = o.duration -%}
          {%- if dur.HasField('lt') %} 
          lt = {{ dur_lit(dur['lt']) }} 
          {% endif %}
          {%- if dur.HasField('lte') %} 
          lte = {{ dur_lit(dur['lte']) }} 
          {% endif %}
          {%- if dur.HasField('gt') %} 
          gt = {{ dur_lit(dur['gt']) }} 
          {% endif %}
          {%- if dur.HasField('gte') %} 
          gte = {{ dur_lit(dur['gte']) }} 
          {% endif %}
          {%- if dur.HasField('const') %}
          if dur != {{ dur_lit(dur['const']) }}:
              raise ValidationFailed(\"{{ name }} is not equal to {{ dur_lit(dur['const']) }}\")
          {%- endif -%}
          {%- if dur['in'] %}
          if dur not in {{ dur_arr(dur['in']) }}:
              raise ValidationFailed(\"{{ name }} is not in {{ dur_arr(dur['in']) }}\") 
          {%- endif -%}
          {%- if dur['not_in'] %}
          if dur in {{ dur_arr(dur['not_in']) }}:
              raise ValidationFailed(\"{{ name }} is not in {{ dur_arr(dur['not_in']) }}\") 
          {%- endif -%}
          {%- if dur.HasField('lt') %}
              {%- if dur.HasField('gt') %}
                  {%- if dur_lit(dur['lt']) > dur_lit(dur['gt']) %}
          if dur <= gt or dur >= lt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['lt']), dur_lit(dur['gt']) }}\")
                  {%- else -%}
          if dur >= lt and dur <= gt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['gt']), dur_lit(dur['lt']) }}\")
                  {%- endif -%}
              {%- elif dur.HasField('gte') %}
                  {%- if dur_lit(dur['lt']) > dur_lit(dur['gte']) %}
          if dur < gte or dur >= lt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['lt']), dur_lit(dur['gte']) }}\")
                  {%- else -%}
          if dur >= lt and dur < gte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['gte']), dur_lit(dur['lt']) }}\")
                  {%- endif -%}
              {%- else -%}
          if dur >= lt:
              raise ValidationFailed(\"{{ name }} is not lesser than {{ dur_lit(dur['lt']) }}\")    
              {%- endif -%}
          {%- elif dur.HasField('lte') %}
              {%- if dur.HasField('gt') %}
                  {%- if dur_lit(dur['lte']) > dur_lit(dur['gt']) %}
          if dur <= gt or dur > lte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['lte']), dur_lit(dur['gt']) }}\")
                  {%- else -%}
          if dur > lte and dur <= gt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['gt']), dur_lit(dur['lte']) }}\")
                  {%- endif -%}
              {%- elif dur.HasField('gte') %}
                  {%- if dur_lit(dur['lte']) > dur_lit(dur['gte']) %}
          if dur < gte or dur > lte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['lte']), dur_lit(dur['gte']) }}\")
                  {%- else -%}
          if dur > lte and dur < gte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(dur['gte']), dur_lit(dur['lte']) }}\")
                  {%- endif -%}
              {%- else -%}
          if dur > lte:
              raise ValidationFailed(\"{{ name }} is not lesser than or equal to {{ dur_lit(dur['lte']) }}\")                   
              {%- endif -%}
          {%- elif dur.HasField('gt') %}
          if dur <= gt:
              raise ValidationFailed(\"{{ name }} is not greater than {{ dur_lit(dur['gt']) }}\")
          {%- elif dur.HasField('gte') %}
          if dur < gte:
              raise ValidationFailed(\"{{ name }} is not greater than or equal to {{ dur_lit(dur['gte']) }}\")
          {%- endif -%}
      """
      return Template(dur_tmpl).render(o = option_value, name = name, required_template = required_template, dur_lit = dur_lit, dur_arr = dur_arr, repeated = repeated)
  
  def timestamp_template(option_value, name, repeated = False):
      timestamp_tmpl = """
      {{- required_template(o.timestamp, name) }}
      {% if repeated %}
      if {{ name }}:
      {% else %}
      if _has_field(p, \"{{ name.split('.')[-1] }}\"):
      {% endif %}
          ts = {{ name }}.seconds + round((10**-9 * {{ name }}.nanos), 9)
          {%- set ts = o.timestamp -%}
          {%- if ts.HasField('lt') %} 
          lt = {{ dur_lit(ts['lt']) }} 
          {% endif -%}
          {%- if ts.HasField('lte') %} 
          lte = {{ dur_lit(ts['lte']) }} 
          {% endif -%}
          {%- if ts.HasField('gt') %} 
          gt = {{ dur_lit(ts['gt']) }} 
          {% endif -%}
          {%- if ts.HasField('gte') %} 
          gte = {{ dur_lit(ts['gte']) }} 
          {% endif -%}
          {%- if ts.HasField('const') %}
          if ts != {{ dur_lit(ts['const']) }}:
              raise ValidationFailed(\"{{ name }} is not equal to {{ dur_lit(ts['const']) }}\")
          {% endif %}
          {%- if ts['in'] %}
          if ts not in {{ dur_arr(ts['in']) }}:
              raise ValidationFailed(\"{{ name }} is not in {{ dur_arr(ts['in']) }}\") 
          {%- endif %}
          {%- if ts['not_in'] %}
          if ts in {{ dur_arr(ts['not_in']) }}:
              raise ValidationFailed(\"{{ name }} is not in {{ dur_arr(ts['not_in']) }}\") 
          {%- endif %}
          {%- if ts.HasField('lt') %}
              {%- if ts.HasField('gt') %}
                  {%- if dur_lit(ts['lt']) > dur_lit(ts['gt']) %}
          if ts <= gt or ts >= lt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['lt']), dur_lit(ts['gt']) }}\")
                  {%- else -%}
          if ts >= lt and ts <= gt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['gt']), dur_lit(ts['lt']) }}\")
                  {%- endif -%}
              {%- elif ts.HasField('gte') %}
                  {%- if dur_lit(ts['lt']) > dur_lit(ts['gte']) %}
          if ts < gte or ts >= lt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['lt']), dur_lit(ts['gte']) }}\")
                  {%- else -%}
          if ts >= lt and ts < gte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['gte']), dur_lit(ts['lt']) }}\")
                  {%- endif -%}
              {%- else -%}
          if ts >= lt:
              raise ValidationFailed(\"{{ name }} is not lesser than {{ dur_lit(ts['lt']) }}\")    
              {%- endif -%}
          {%- elif ts.HasField('lte') %}
              {%- if ts.HasField('gt') %}
                  {%- if dur_lit(ts['lte']) > dur_lit(ts['gt']) %}
          if ts <= gt or ts > lte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['lte']), dur_lit(ts['gt']) }}\")
                  {%- else -%}
          if ts > lte and ts <= gt:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['gt']), dur_lit(ts['lte']) }}\")
                  {%- endif -%}
              {%- elif ts.HasField('gte') %}
                  {%- if dur_lit(ts['lte']) > dur_lit(ts['gte']) %}
          if ts < gte or ts > lte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['lte']), dur_lit(ts['gte']) }}\")
                  {%- else -%}
          if ts > lte and ts < gte:
              raise ValidationFailed(\"{{ name }} is not in range {{ dur_lit(ts['gte']), dur_lit(ts['lte']) }}\")
                  {%- endif -%}
              {%- else -%}
          if ts > lte:
              raise ValidationFailed(\"{{ name }} is not lesser than or equal to {{ dur_lit(ts['lte']) }}\")                   
              {%- endif -%}
          {%- elif ts.HasField('gt') %}
          if ts <= gt:
              raise ValidationFailed(\"{{ name }} is not greater than {{ dur_lit(ts['gt']) }}\")
          {%- elif ts.HasField('gte') %}
          if ts < gte:
              raise ValidationFailed(\"{{ name }} is not greater than or equal to {{ dur_lit(ts['gte']) }}\")
          {%- elif ts.HasField('lt_now') %}
          now = time.time()
              {%- if ts.HasField('within') %}
          within = {{ dur_lit(ts['within']) }}
          if ts >= now or ts >= now - within:
              raise ValidationFailed(\"{{ name }} is not within range {{ dur_lit(ts['within']) }}\")
              {%- else %}
          if ts >= now:
              raise ValidationFailed(\"{{ name }} is not lesser than now\")
              {%- endif -%} 
          {%- elif ts.HasField('gt_now') %}
          now = time.time()
              {%- if ts.HasField('within') %}
          within = {{ dur_lit(ts['within']) }}
          if ts <= now or ts <= now + within:
              raise ValidationFailed(\"{{ name }} is not within range {{ dur_lit(ts['within']) }}\")
              {%- else %}
          if ts <= now:
              raise ValidationFailed(\"{{ name }} is not greater than now\")    
              {%- endif -%}
          {%- elif ts.HasField('within') %}
          now = time.time()
          within = {{ dur_lit(ts['within']) }}
          if ts >= now + within or ts <= now - within:
               raise ValidationFailed(\"{{ name }} is not within range {{ dur_lit(ts['within']) }}\")
          {%- endif -%}
      """
      return Template(timestamp_tmpl).render(o = option_value, name = name, required_template = required_template, dur_lit = dur_lit, dur_arr = dur_arr, repeated = repeated)
  
  def wrapper_template(option_value, name, repeated = False):
      wrapper_tmpl = """
      {% if repeated %}
      if {{ name }}:
      {% else %}
      if p.HasField(\"{{ name[2:] }}\"):
      {% endif %}
          {%- if str(option_value.float) %}
          {{- num_template(option_value, name + ".value", option_value.float)|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.double) %}
          {{- num_template(option_value, name + ".value", option_value.double)|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.int32) %}
          {{- num_template(option_value, name + ".value", option_value.int32)|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.int64) %}
          {{- num_template(option_value, name + ".value", option_value.int64)|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.uint32) %}
          {{- num_template(option_value, name + ".value", option_value.uint32)|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.uint64) %}
          {{- num_template(option_value, name + ".value", option_value.uint64)|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.bool) %}
          {{- bool_template(option_value, name + ".value")|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.string) %}
          {{- string_template(option_value, name + ".value")|indent(4,True) -}}
          {% endif -%}
          {%- if str(option_value.bytes) %}
          {{- bytes_template(option_value, name + ".value")|indent(4,True) -}}
          {% endif -%}
      {%- if str(option_value.message) and option_value.message['required'] %}
      else:
          raise ValidationFailed(\"{{ name }} is required.\")
      {%- endif %}
      """
      return Template(wrapper_tmpl).render(option_value = option_value, name = name, str = str, num_template = num_template, bool_template = bool_template, string_template = string_template, bytes_template = bytes_template, repeated = repeated)
  
  def enum_values(field):
      return [x.number for x in field.enum_type.values]
  
  def enum_template(option_value, name, field):
      enum_tmpl = """
      {{ const_template(option_value, name) -}}
      {{ in_template(option_value.enum, name) -}}
      {% if option_value.enum['defined_only'] %}
      if {{ name }} not in {{ enum_values(field) }}:
          raise ValidationFailed(\"{{ name }} is not defined\")
      {% endif %}
      """
      return Template(enum_tmpl).render(option_value = option_value, name = name, const_template = const_template, in_template = in_template, field = field, enum_values = enum_values)
  
  def any_template(option_value, name, repeated = False):
      any_tmpl = """
      {{- required_template(o, name) }}
      {%- if o['in'] %}
      {% if repeated %}
      if {{ name }}:
      {% else %}
      if _has_field(p, \"{{ name.split('.')[-1] }}\"):
      {% endif %}
          if {{ name }}.type_url not in {{ o['in'] }}:
              raise ValidationFailed(\"{{ name }} not in {{ o['in'] }}\")
      {%- endif %}
      {%- if o['not_in'] %}
      {% if repeated %}
      if {{ name }}:
      {% else %}
      if _has_field(p, \"{{ name.split('.')[-1] }}\"):
      {% endif %}
          if {{ name }}.type_url in {{ o['not_in'] }}:
              raise ValidationFailed(\"{{ name }} in {{ o['not_in'] }}\")
      {%- endif %}
      """
      return Template(any_tmpl).render(o = option_value.any, name = name, required_template = required_template, repeated = repeated)
  
  def bytes_template(option_value, name):
      bytes_tmpl = """
      {{ const_template(o, name) -}}
      {{ in_template(o.bytes, name) -}}
      {%- if b['len'] %}
      if len({{ name }}) != {{ b['len'] }}:
          raise ValidationFailed(\"{{ name }} length does not equal {{ b['len'] }}\")
      {%- endif -%}
      {%- if b['min_len'] %}
      if len({{ name }}) < {{ b['min_len'] }}:
          raise ValidationFailed(\"{{ name }} length is less than {{ b['min_len'] }}\")
      {%- endif -%}
      {%- if b['max_len'] %}
      if len({{ name }}) > {{ b['max_len'] }}:
          raise ValidationFailed(\"{{ name }} length is more than {{ b['max_len'] }}\")
      {%- endif -%}
      {%- if b['ip'] %}
      try:
          ipaddress.ip_address({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid ip\")
      {%- endif -%}
      {%- if b['ipv4'] %}
      try:
          ipaddress.IPv4Address({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid ipv4\")
      {%- endif -%}
      {%- if b['ipv6'] %}
      try:
          ipaddress.IPv6Address({{ name }})
      except ValueError:
          raise ValidationFailed(\"{{ name }} is not a valid ipv6\")
      {%- endif -%}
      {% if b['pattern'] %}
          {% if sys.version_info[0] >= 3%}
      if re.search({{ b['pattern'].encode('unicode-escape') }}, {{ name }}) is None:
          raise ValidationFailed(\"{{ name }} pattern does not match b['pattern'].encode('unicode-escape')\")
          {% else %}
      if re.search(b\"{{ b['pattern'].encode('unicode-escape') }}\", {{ name }}) is None:
          raise ValidationFailed(\"{{ name }} pattern does not match \")
          {% endif %}
      {% endif %}
      {% if b['contains'] %}
          {% if sys.version_info[0] >= 3 %}
      if not {{ b['contains'] }} in {{ name }}:
          raise ValidationFailed(\"{{ name }} does not contain {{ b['contains'] }}\")
          {% else %}
      if not b\"{{ b['contains'].encode('string_escape') }}\" in {{ name }}:
          raise ValidationFailed(\"{{ name }} does not contain \")
          {% endif %}
      {% endif %}
      {% if b['prefix'] %}
          {% if sys.version_info[0] >= 3 %}
      if not {{ name }}.startswith({{ b['prefix'] }}):
          raise ValidationFailed(\"{{ name }} does not start with prefix {{ b['prefix'] }}\")
          {% else %}
      if not {{name}}.startswith(b\"{{ b['prefix'].encode('string_escape') }}\"):
          raise ValidationFailed(\"{{ name }} does not start with prefix {{ b['prefix'].encode('string_escape') }}\")
          {% endif %}
      {% endif %}
      {% if b['suffix'] %}
          {% if sys.version_info[0] >= 3 %}
      if not {{ name }}.endswith({{ b['suffix'] }}):
          raise ValidationFailed(\"{{ name }} does not end with suffix {{ b['suffix'] }}\")
          {% else %}
      if not {{name}}.endswith(b\"{{ b['suffix'].encode('string_escape') }}\"):
          raise ValidationFailed(\"{{ name }} does not end with suffix {{ b['suffix'] }}\")
          {% endif %}
      {% endif %}
      """
      return Template(bytes_tmpl).render(sys=sys,o = option_value, name = name, const_template = const_template, in_template = in_template, b = option_value.bytes)
  
  def switcher_template(accessor, name, field, map = False):
      switcher_tmpl = """
      {%- if str(accessor.float) %}
      {{- num_template(accessor, name, accessor.float)|indent(4,True) -}}
      {%- elif str(accessor.double) %}
      {{- num_template(accessor, name, accessor.double)|indent(4,True) -}}
      {%- elif str(accessor.int32) %}
      {{- num_template(accessor, name, accessor.int32)|indent(4,True) -}}
      {%- elif str(accessor.int64) %}
      {{- num_template(accessor, name, accessor.int64)|indent(4,True) -}}
      {%- elif str(accessor.uint32) %}
      {{- num_template(accessor, name, accessor.uint32)|indent(4,True) -}}
      {%- elif str(accessor.uint64) %}
      {{- num_template(accessor, name, accessor.uint64)|indent(4,True) -}}
      {%- elif str(accessor.sint32) %}
      {{- num_template(accessor, name, accessor.sint32)|indent(4,True) -}}
      {%- elif str(accessor.sint64) %}
      {{- num_template(accessor, name, accessor.sint64)|indent(4,True) -}}
      {%- elif str(accessor.fixed32) %}
      {{- num_template(accessor, name, accessor.fixed32)|indent(4,True) -}}
      {%- elif str(accessor.fixed64) %}
      {{- num_template(accessor, name, accessor.fixed64)|indent(4,True) -}}
      {%- elif str(accessor.sfixed32) %}
      {{- num_template(accessor, name, accessor.sfixed32)|indent(4,True) -}}
      {%- elif str(accessor.sfixed64) %}
      {{- num_template(accessor, name, accessor.sfixed64)|indent(4,True) -}}
      {%- elif str(accessor.bool) %}
      {{- bool_template(accessor, name)|indent(4,True) -}}
      {%- elif str(accessor.string) %}
      {{- string_template(accessor, name)|indent(4,True) -}}
      {%- elif str(accessor.enum) and map %}
      {{- enum_template(accessor, name, field.message_type.fields[1])|indent(4,True) -}}
      {%- elif str(accessor.enum) and not map %}
      {{- enum_template(accessor, name, field)|indent(4,True) -}}
      {%- elif str(accessor.duration) %}
      {{- duration_template(accessor, name, True)|indent(4,True) -}}
      {%- elif str(accessor.timestamp) %}
      {{- timestamp_template(accessor, name, True)|indent(4,True) -}}
      {%- elif str(accessor.message) %}
      {{- message_template(accessor, name, True)|indent(4,True) -}}
      {%- elif str(accessor.any) %}
      {{- any_template(accessor, name, True)|indent(4,True) -}}
      {%- elif str(accessor.message) %}
      {{- message_template(accessor, name, True)|indent(4,True) -}}
      {%- endif %}
      """
      return Template(switcher_tmpl).render(accessor = accessor, name = name, str = str, num_template = num_template, bool_template = bool_template, string_template = string_template, enum_template = enum_template, duration_template = duration_template, timestamp_template = timestamp_template, any_template = any_template, message_template = message_template, field = field, map = map)
  
  def repeated_template(option_value, name, field):
      rep_tmpl = """
      {%- if o and o.repeated['min_items'] %}
      if len({{ name }}) < {{ o.repeated['min_items'] }}:
          raise ValidationFailed(\"{{ name }} needs to contain at least {{ o.repeated['min_items'] }} items\")
      {%- endif %}
      {%- if o and o.repeated['max_items'] %}
      if len({{ name }}) > {{ o.repeated['max_items'] }}:
          raise ValidationFailed(\"{{ name }} needs to contain at most {{ o.repeated['max_items'] }} items\")
      {%- endif %}
      {%- if o and o.repeated['unique'] %}
      seen = set()
      for item in {{ name }}:
          if item in seen:
              raise ValidationFailed(\"{{ name }} must contain unique items. %s has been repeated.\" %item)
          else:
              seen.add(item)
      {%- endif %}
      {%- if message_type %}
      for item in {{ name }}:
          {%- if o and o.repeated and o.repeated.items.message.skip %}
          pass
          {% else %}
          validate(item)(item)
          {% endif %}
      {%- endif %}
      {%- if o and str(o.repeated['items']) %}
      for item in {{ name }}:
          {%- set accessor = o.repeated['items'] -%}
          {{ switcher_template(accessor, 'item', field) }}
          pass
      {%- endif %}
      """
      return Template(rep_tmpl).render(o = option_value, name = name, message_type = field.message_type, str = str, field = field, switcher_template = switcher_template)
  
  def is_map(field):
      return field.label == 3 and field.message_type and len(field.message_type.fields) == 2 and \
             field.message_type.fields[0].name == "key" and field.message_type.fields[1].name == "value"
  
  def map_template(option_value, name, field):
      map_tmpl = """
      {%- if o and o.map['min_pairs'] %}
      if len({{ name }}) < {{ o.map['min_pairs'] }}:
          raise ValidationFailed(\"{{ name }} needs to contain at least {{ o.map['min_pairs'] }} items\")
      {%- endif %}
      {%- if o and o.map['max_pairs'] %}
      if len({{ name }}) > {{ o.map['max_pairs'] }}:
          raise ValidationFailed(\"{{ name }} can contain at most {{ o.map['max_pairs'] }} items\")
      {%- endif %}
      {%- if o and o.map['no_sparse'] -%}
      raise UnimplementedException(\"no_sparse validation is not implemented because protobuf maps cannot be sparse in Python\")
      {%- endif %}
      {%- if o and (str(o.map['keys']) or str(o.map['values']))%}
      for key in {{ name }}:
          {%- set keys = o.map['keys'] -%}
          {%- set values = o.map['values'] -%}
          {%- if str(keys.double) %}
          {{- num_template(keys, 'key', keys.double)|indent(4,True) -}}
          {%- elif str(keys.int32) %}
          {{- num_template(keys, 'key', keys.int32)|indent(4,True) -}}
          {%- elif str(keys.int64) %}
          {{- num_template(keys, 'key', keys.int64)|indent(4,True) -}}
          {%- elif str(keys.uint32) %}
          {{- num_template(keys, 'key', keys.uint32)|indent(4,True) -}}
          {%- elif str(keys.uint64) %}
          {{- num_template(keys, 'key', keys.uint64)|indent(4,True) -}}
          {%- elif str(keys.sint32) %}
          {{- num_template(keys, 'key', keys.sint32)|indent(4,True) -}}
          {%- elif str(keys.sint64) %}
          {{- num_template(keys, 'key', keys.sint64)|indent(4,True) -}}
          {%- elif str(keys.fixed32) %}
          {{- num_template(keys, 'key', keys.fixed32)|indent(4,True) -}}
          {%- elif str(keys.fixed64) %}
          {{- num_template(keys, 'key', keys.fixed64)|indent(4,True) -}}
          {%- elif str(keys.sfixed32) %}
          {{- num_template(keys, 'key', keys.sfixed32)|indent(4,True) -}}
          {%- elif str(keys.sfixed64) %}
          {{- num_template(keys, 'key', keys.sfixed64)|indent(4,True) -}}
          {%- elif str(keys.bool) %}
          {{- bool_template(keys, 'key')|indent(4,True) -}}
          {%- elif str(keys.string) %}
          {{- string_template(keys, 'key')|indent(4,True) -}}
          {%- endif %}
          {%- set values = o.map['values'] -%}
          {{ switcher_template(values, name +'[key]', field, True) }}
          pass
      {%- endif %}
      """
      return Template(map_tmpl).render(o = option_value, name = name, message_type = field.message_type, str = str, field = field, switcher_template = switcher_template, num_template = num_template, string_template = string_template, bool_template = bool_template)
  
  def rule_type(field):
      name = "p."+ field.name
      if has_validate(field) and field.message_type is None:
          for option_descriptor, option_value in field.GetOptions().ListFields():
              if option_descriptor.full_name == "validate.rules":
                  if str(option_value.string):
                      return string_template(option_value, name )
                  elif str(option_value.message):
                      return message_template(option_value, field.name)
                  elif str(option_value.bool):
                      return bool_template(option_value, name)
                  elif str(option_value.float):
                      return num_template(option_value, name, option_value.float)
                  elif str(option_value.double):
                      return num_template(option_value, name, option_value.double)
                  elif str(option_value.int32):
                      return num_template(option_value, name, option_value.int32)
                  elif str(option_value.int64):
                      return num_template(option_value, name, option_value.int64)
                  elif str(option_value.uint32):
                      return num_template(option_value, name, option_value.uint32)
                  elif str(option_value.uint64):
                      return num_template(option_value, name, option_value.uint64)
                  elif str(option_value.sint32):
                      return num_template(option_value, name, option_value.sint32)
                  elif str(option_value.sint64):
                      return num_template(option_value, name, option_value.sint64)
                  elif str(option_value.fixed32):
                      return num_template(option_value, name, option_value.fixed32)
                  elif str(option_value.fixed64):
                      return num_template(option_value, name, option_value.fixed64)
                  elif str(option_value.sfixed32):
                      return num_template(option_value, name, option_value.sfixed32)
                  elif str(option_value.sfixed64):
                      return num_template(option_value, name, option_value.sfixed64)
                  elif str(option_value.enum):
                      return enum_template(option_value, name, field)
                  elif str(option_value.bytes):
                      return bytes_template(option_value, name)
                  elif str(option_value.repeated):
                      return repeated_template(option_value, name, field)
                  elif str(option_value.map):
                      return map_template(option_value, name, field)
                  elif str(option_value.required):
                      return required_template(option_value, name)
      if field.message_type:
          for option_descriptor, option_value in field.GetOptions().ListFields():
              if option_descriptor.full_name == "validate.rules":
                  if str(option_value.duration):
                      return duration_template(option_value, name)
                  elif str(option_value.timestamp):
                      return timestamp_template(option_value, name)
                  elif str(option_value.float) or str(option_value.int32) or str(option_value.int64) or \
                          str(option_value.double) or str(option_value.uint32) or str(option_value.uint64) or \
                          str(option_value.bool) or str(option_value.string) or str(option_value.bytes):
                      return wrapper_template(option_value, name)
                  elif str(option_value.message) is not "":
                      return message_template(option_value, field.name)
                  elif str(option_value.any):
                      return any_template(option_value, name)
                  elif str(option_value.repeated):
                      return repeated_template(option_value, name, field)
                  elif str(option_value.map):
                      return map_template(option_value, name, field)
                  elif str(option_value.required):
                      return required_template(option_value, name)
          if field.message_type.full_name.startswith("google.protobuf"):
              return ""
          elif is_map(field):
              return map_template(None, name, field)
          elif field.label == 3:
              return repeated_template(None, name, field)
          else:
              return message_template(None, field.name)
      return ""
  
  def file_template(proto_message):
      file_tmp = """
  # Validates {{ p.DESCRIPTOR.name }}
  def generate_validate(p):
      {%- for option_descriptor, option_value in p.DESCRIPTOR.GetOptions().ListFields() %}
          {%- if option_descriptor.full_name == "validate.disabled" and option_value %}
      return None
          {%- endif -%}
      {%- endfor -%}
      {%- for oneof in p.DESCRIPTOR.oneofs %}
      present = False
          {%- for field in oneof.fields %}
      if _has_field(p, \"{{ field.name }}\"):
          present = True
          {{ rule_type(field)|indent(4,True) }}
          {%- endfor %}
          {% for option in oneof.GetOptions().ListFields() %}
          {% if option[0].name == 'required' and option[1] %}
      if not present:
          raise ValidationFailed(\"Oneof {{ oneof.name }} is required\")
          {% endif %}
          {% endfor %}
      {%- endfor %}
      {%- for field in p.DESCRIPTOR.fields -%}
          {%- if not field.containing_oneof %}
      {{ rule_type(field) -}}
          {%- endif %}
      {%- endfor %}
      return None"""
      return Template(file_tmp).render(rule_type = rule_type, p = proto_message)
  
  class UnimplementedException(Exception):
      pass
  
  class ValidationFailed(Exception):
      pass
  