title: How to get ALL undefined variables from a Jinja2 template?
body: <p>I am trying to get all undefined variables from a Jinja2 template.
  Assume that I have a template like below.</p>
  
  <pre><code>tmpstr = """
  {% for row in csv %}
  sample {{row.field1}} stuff {{row.field2}} morestuff {{row.field3}}
  {% endfor %}
  """
  </code></pre>
  
  <p>and input dictionary as below</p>
  
  <pre><code>cxt = {'csv': [
      {'field3': 1234, 'field4': 12314},
      {'field3': 2222, 'field4': 1213}
  ]}
  </code></pre>
  
  <p>Here is how I try to render it.</p>
  
  <pre><code>env = Environment(undefined=Undefined)
  tmp = env.from_string(tmpstr)
  tmpsrc = tmp.render(cxt)
  print(tmpsrc)
  </code></pre>
  
  <p>Template expect variables <code>field1</code>, <code>field2</code> and <code>field3</code> to be present. However <code>field1</code> and <code>field2</code> are not present. My aim is to find <strong>all</strong> missing variables. </p>
  
  <p>Jinja2 silently ignores missing variables. Therefore I tried to add <code>StrictUndefined</code> option:</p>
  
  <pre><code>errs = []
  try:
      env = Environment(undefined=StrictUndefined)
      tmp = env.from_string(tmpstr)
      tmpsrc = tmp.render(cxt)
  except Exception as e:
      errs.append(str(e))
  print(errs)
  </code></pre>
  
  <p>However this time jinja2 complains about <strong>only the first</strong> missing variable which is <code>field1</code>.</p>
  
  <p>Therefore I tried another option which is <code>DebugUndefined</code>.
  This option does not raise an exception and leaves missing variables placeholder in the template output untouched. Thus I can not collect missing variables. </p>
  
  <p>Can you please suggest how can I get missing variables in a jinja2 template?</p>
  
  <p>Here is runnable code if anyone wants to try it out:</p>
  
  <pre><code>from jinja2 import BaseLoader,Environment,StrictUndefined,DebugUndefined,Undefined
  tmpstr = """
  {% for row in csv %}
  sample {{row.field1}} stuff {{row.field2}} morestuff {{row.field3}}
  {% endfor %}
  """
  cxt = {'csv': [
      {'field3': 1234, 'field4': 12314},
      {'field3': 2222, 'field4': 1213}
  ]}
  env = Environment(undefined=Undefined)
  tmp = env.from_string(tmpstr)
  tmpsrc = tmp.render(cxt)
  print('CASE 1: undefined=Undefined')
  print(tmpsrc)
  
  errs = []
  try:
      env = Environment(undefined=StrictUndefined)
      tmp = env.from_string(tmpstr)
      tmpsrc = tmp.render(cxt)
  except Exception as e:
      errs.append(str(e))
  print('CASE 2: undefined=StrictUndefined')
  print(errs)
  
  errs = []
  try:
      env = Environment(undefined=DebugUndefined)
      tmp = env.from_string(tmpstr)
      tmpsrc = tmp.render(cxt)
  except Exception as e:
      errs.append(str(e))
  
  print('CASE 3: undefined=DebugUndefined')
  print(errs)
  print(tmpsrc)
  </code></pre>