title: How to detect an empty paragraph in python-docx
body: <p>Given a document containing a paragraph</p>
  
  <pre><code>d = docx.Document()
  p = d.add_paragraph()
  </code></pre>
  
  <p>I expected the following technique to work every time:</p>
  
  <pre><code>if len(p._element) == 0:
      # p is empty
  </code></pre>
  
  <p>OR</p>
  
  <pre><code>if len(p._p) == 0:
      # p is empty
  </code></pre>
  
  <p>(Side question, what's the difference there? It seems that <code>p._p is p._element</code> in every case I've seen in the wild.)</p>
  
  <p>If I add a style to my paragraph, the check no longer works:</p>
  
  <pre><code>&gt;&gt;&gt; p2 = d.add_paragraph(style="Normal")
  &gt;&gt;&gt; print(len(p2._element))
  1
  </code></pre>
  
  <p>Explicitly setting <code>text=None</code> doesn't help either, not that I would expect it to.</p>
  
  <p>So how to I check if a paragraph is empty of content (specifically text and images, although more generic is better)?</p>
  
  <p><strong>Update</strong></p>
  
  <p>I messed around a little and found that setting the style apparently adds a single <code>pPr</code> element:</p>
  
  <pre><code>&gt;&gt;&gt; p2._element.getchildren()
  [&lt;CT_PPr '&lt;w:pPr&gt;' at 0x7fc9a2b64548&gt;]
  </code></pre>
  
  <p>The element itself it empty:</p>
  
  <pre><code>&gt;&gt;&gt; len(p2._element.getchildren()[0])
  0
  </code></pre>
  
  <p>But more importantly, it is not a run.</p>
  
  <p>So my test now looks like this:</p>
  
  <pre><code>def isempty(par):
      return sum(len(run) for run in par._element.xpath('w:r')) == 0
  </code></pre>
  
  <p>I don't know enough about the underlying system to have any idea if this is a reasonable solution or not, and what the caveats are.</p>
  
  <p><strong>More Update</strong></p>
  
  <p>Seems like I need to be able to handle a few different cases here:</p>
  
  <pre><code>def isempty(par):
      p = par._p
      runs = p.xpath('./w:r[./*[not(self::w:rPr)]]')
      others = p.xpath('./*[not(self::w:pPr) and not(self::w:r)] and '
                       'not(contains(local-name(), "bookmark"))')
      return len(runs) + len(others) == 0
  </code></pre>
  
  <p>This skips all <code>w:pPr</code> elements and runs with nothing but <code>w:rPr</code> elements. Any other element, except bookmarks, whether in the paragraph directly or in a run, will make the result non-empty.</p>