mypy spurious error: "module" has no attribute "XPath" with etree
<p>I'm trying to use <code>mypy</code> to do type checking in some code that uses the <a href="http://lxml.de/" rel="nofollow">LXML</a> library to parse XML.</p>

<p>On each line where I use <code>etree.XPath</code>, I get a spurious error from <code>mypy</code>.  For example, the following trivial script</p>

<pre><code>from lxml import etree    
NameXPath = etree.XPath("Name/text()")
</code></pre>

<p>generates the error</p>

<pre><code>test.py:3: error: "module" has no attribute "XPath"
</code></pre>

<p>But the script runs fine and my <code>XPath</code>'s work correctly at runtime.</p>

<p>I also tried <code>#type:ignore</code> on the import, which I thought might tell <code>mypy</code> not to type-check that library, but that did not suppress the errors.</p>

<pre><code>from lxml import etree # type:ignore    
NameXPath = etree.XPath("Name/text()")
</code></pre>

<p>I did have some success at suppressing some of the errors by moving the calls to <code>etree.XPath</code> into a separate function which doesn't have any type annotations, but that seemed like a hack and forced me to arrange my code in awkward ways.  </p>

<p>I would like to know if there's a way to completely suppress these spurious errors, or to possibly hint that the <code>etree.XPath</code> function does exist since it doesn't seem to be able to figure that out on its own.</p>

<p>To be clear, I don't actually care that <code>mypy</code> knows the correct types for structures coming out of the <code>lxml</code> library.  I'm more concerned about putting the type info on my own classes that I'm shoving the parsed information into, so I want to have type-checked functions which use <code>etree.XPath</code> to do queries, find the data, and then shove them into type-annotated classes that are defined within my script.</p>

<p><code>mypy</code> doesn't seem to have difficulty with other functions in <code>etree</code>, for example it's fine with my calls to <code>etree.parse</code></p>

<p>I'm currently using <code>mypy</code> 0.4.4</p>

<p>It looks like this is a bug in <a href="https://github.com/python/typeshed" rel="nofollow">typeshed</a>, the community-contributed collection of type annotations for the stdlib and various third party libraries.</p>

<p>In particular, it looks as if the <a href="https://github.com/python/typeshed/tree/master/third_party/3/lxml" rel="nofollow">stubs for lxml</a> are missing definitions for XPath entirely. This is probably an oversight -- I would try filing a bug on the issue tracker or try submitting a pull request containing a fix.</p>

<p>Once this is fixed, and mypy resyncs itself with the latest version of typeshed, you'll need to install mypy from the <a href="https://github.com/python/mypy" rel="nofollow">git repo</a> for the time being (at least, until mypy 0.4.5 comes out sometime in October).</p>

<p>In the meantime, you can work around this by doing this:</p>

<pre><code>from lxml.etree import XPath  # type: ignore
NameXPath = XPath("Name/text()")
# mypy considers NameXPath to have a type of Any
</code></pre>

<p>...or, if you'd prefer having a more specific definition of XPath, do this:</p>

<pre><code>import typing

if typing.TYPE_CHECKING:
    # typing.TYPE_CHECKING is always False at runtime, so this
    # branch is parsed by typecheckers only
    class XPath:
        # Provide a method header stubs with signatures to fool
        # mypy into understanding what the interface for XPath is
else:
    # Actually executed at runtime
    from lxml.etree import XPath  # type: ignore

NameXPath = XPath("Name/text()")
</code></pre>

python lxml mypy