Skip to content

Commit

Permalink
tests++
Browse files Browse the repository at this point in the history
  • Loading branch information
mhils committed Dec 13, 2023
1 parent 5176073 commit c6b84b8
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 32 deletions.
7 changes: 5 additions & 2 deletions pdoc/doc_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def safe_eval_type(

# Simple _eval_type has failed. We now execute all TYPE_CHECKING sections in the module and try again.
if module:
assert module.__dict__ is globalns
try:
_eval_type_checking_sections(module, set())
except Exception as e:
Expand All @@ -147,7 +148,9 @@ def safe_eval_type(
f"Error parsing type annotation {t} for {fullname}. Import of {mod} failed: {err}"
)
return t
return safe_eval_type(t, {mod: val, **globalns}, localns, module, fullname)
else:
globalns[mod] = val
return safe_eval_type(t, globalns, localns, module, fullname)


def _eval_type_checking_sections(module: types.ModuleType, seen: set) -> None:
Expand All @@ -167,7 +170,7 @@ def _eval_type_checking_sections(module: types.ModuleType, seen: set) -> None:
try:
eval(code, module.__dict__, module.__dict__)
except ImportError as e:
if mod := sys.modules.get(e.name, None):
if e.name is not None and (mod := sys.modules.get(e.name, None)):
_eval_type_checking_sections(mod, seen)
else:
raise
Expand Down
41 changes: 37 additions & 4 deletions test/test_doc_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
"typestr", ["totally_unknown_module", "!!!!", "html.unknown_attr"]
)
def test_eval_fail(typestr):
a = types.ModuleType("a")
with pytest.warns(UserWarning, match="Error parsing type annotation"):
assert safe_eval_type(typestr, {}, None, types.ModuleType("a"), "a") == typestr
assert safe_eval_type(typestr, a.__dict__, None, a, "a") == typestr


def test_eval_fail2(monkeypatch):
Expand All @@ -22,8 +23,9 @@ def test_eval_fail2(monkeypatch):
"get_source",
lambda _: "import typing\nif typing.TYPE_CHECKING:\n\traise RuntimeError()",
)
a = types.ModuleType("a")
with pytest.warns(UserWarning, match="Failed to run TYPE_CHECKING code"):
assert safe_eval_type("xyz", {}, None, types.ModuleType("a"), "a") == "xyz"
assert safe_eval_type("xyz", a.__dict__, None, a, "a") == "xyz"


def test_eval_fail3(monkeypatch):
Expand All @@ -32,24 +34,55 @@ def test_eval_fail3(monkeypatch):
"get_source",
lambda _: "import typing\nif typing.TYPE_CHECKING:\n\tFooFn = typing.Callable[[],int]",
)
a = types.ModuleType("a")
a.__dict__["typing"] = typing
with pytest.warns(
UserWarning,
match="Error parsing type annotation .+ after evaluating TYPE_CHECKING blocks",
):
assert (
safe_eval_type(
"FooFn[int]", {"typing": typing}, None, types.ModuleType("a"), "a"
"FooFn[int]", a.__dict__, None, a, "a"
)
== "FooFn[int]"
)


def test_eval_fail_import_nonexistent(monkeypatch):
monkeypatch.setattr(
doc_ast,
"get_source",
lambda _: "import typing\nif typing.TYPE_CHECKING:\n\timport nonexistent_module",
)
a = types.ModuleType("a")
with pytest.warns(UserWarning, match="No module named 'nonexistent_module'"):
assert safe_eval_type("xyz", a.__dict__, None, a, "a") == "xyz"


def test_eval_union_types_on_old_python(monkeypatch):
monkeypatch.setattr(sys, "version_info", (3, 9, 0))
with pytest.warns(
UserWarning,
match=r"You are likely attempting to use Python 3.10 syntax \(PEP 604 union types\) "
r"with an older Python release.",
r"with an older Python release.",
):
# str never implements `|`, so we can use that to trigger the error on newer versions.
safe_eval_type('"foo" | "bar"', {}, None, None, "example")


def test_recurse(monkeypatch):
def get_source(mod):
if mod == a:
return "import typing\nif typing.TYPE_CHECKING:\n\tfrom b import Foo"
else:
return "import typing\nif typing.TYPE_CHECKING:\n\tfrom a import Foo"

a = types.ModuleType("a")
b = types.ModuleType("b")

monkeypatch.setattr(doc_ast, "get_source", get_source)
monkeypatch.setitem(sys.modules, "a", a)
monkeypatch.setitem(sys.modules, "b", b)

with pytest.warns(UserWarning, match="Recursion error when importing a"):
assert safe_eval_type("xyz", a.__dict__, None, a, "a") == "xyz"
2 changes: 1 addition & 1 deletion test/test_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def outfile(self, format: str) -> Path:
),
Snapshot("pyo3_sample_library", specs=["pdoc_pyo3_sample_library"]),
Snapshot("top_level_reimports", ["top_level_reimports"]),
Snapshot("type_checking_imports"),
Snapshot("type_checking_imports", ["type_checking_imports.main"]),
Snapshot("type_stub", min_version=(3, 10)),
Snapshot(
"visibility",
Expand Down
16 changes: 8 additions & 8 deletions test/testdata/type_checking_imports.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="pdoc $VERSION"/>
<title>type_checking_imports API documentation</title>
<title>type_checking_imports.main API documentation</title>

<style>/*! * Bootstrap Reboot v5.0.0 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}</style>
<style>/*! syntax-highlighting.css */pre{line-height:125%;}span.linenos{color:inherit; background-color:transparent; padding-left:5px; padding-right:20px;}.pdoc-code .hll{background-color:#ffffcc}.pdoc-code{background:#f8f8f8;}.pdoc-code .c{color:#3D7B7B; font-style:italic}.pdoc-code .err{border:1px solid #FF0000}.pdoc-code .k{color:#008000; font-weight:bold}.pdoc-code .o{color:#666666}.pdoc-code .ch{color:#3D7B7B; font-style:italic}.pdoc-code .cm{color:#3D7B7B; font-style:italic}.pdoc-code .cp{color:#9C6500}.pdoc-code .cpf{color:#3D7B7B; font-style:italic}.pdoc-code .c1{color:#3D7B7B; font-style:italic}.pdoc-code .cs{color:#3D7B7B; font-style:italic}.pdoc-code .gd{color:#A00000}.pdoc-code .ge{font-style:italic}.pdoc-code .gr{color:#E40000}.pdoc-code .gh{color:#000080; font-weight:bold}.pdoc-code .gi{color:#008400}.pdoc-code .go{color:#717171}.pdoc-code .gp{color:#000080; font-weight:bold}.pdoc-code .gs{font-weight:bold}.pdoc-code .gu{color:#800080; font-weight:bold}.pdoc-code .gt{color:#0044DD}.pdoc-code .kc{color:#008000; font-weight:bold}.pdoc-code .kd{color:#008000; font-weight:bold}.pdoc-code .kn{color:#008000; font-weight:bold}.pdoc-code .kp{color:#008000}.pdoc-code .kr{color:#008000; font-weight:bold}.pdoc-code .kt{color:#B00040}.pdoc-code .m{color:#666666}.pdoc-code .s{color:#BA2121}.pdoc-code .na{color:#687822}.pdoc-code .nb{color:#008000}.pdoc-code .nc{color:#0000FF; font-weight:bold}.pdoc-code .no{color:#880000}.pdoc-code .nd{color:#AA22FF}.pdoc-code .ni{color:#717171; font-weight:bold}.pdoc-code .ne{color:#CB3F38; font-weight:bold}.pdoc-code .nf{color:#0000FF}.pdoc-code .nl{color:#767600}.pdoc-code .nn{color:#0000FF; font-weight:bold}.pdoc-code .nt{color:#008000; font-weight:bold}.pdoc-code .nv{color:#19177C}.pdoc-code .ow{color:#AA22FF; font-weight:bold}.pdoc-code .w{color:#bbbbbb}.pdoc-code .mb{color:#666666}.pdoc-code .mf{color:#666666}.pdoc-code .mh{color:#666666}.pdoc-code .mi{color:#666666}.pdoc-code .mo{color:#666666}.pdoc-code .sa{color:#BA2121}.pdoc-code .sb{color:#BA2121}.pdoc-code .sc{color:#BA2121}.pdoc-code .dl{color:#BA2121}.pdoc-code .sd{color:#BA2121; font-style:italic}.pdoc-code .s2{color:#BA2121}.pdoc-code .se{color:#AA5D1F; font-weight:bold}.pdoc-code .sh{color:#BA2121}.pdoc-code .si{color:#A45A77; font-weight:bold}.pdoc-code .sx{color:#008000}.pdoc-code .sr{color:#A45A77}.pdoc-code .s1{color:#BA2121}.pdoc-code .ss{color:#19177C}.pdoc-code .bp{color:#008000}.pdoc-code .fm{color:#0000FF}.pdoc-code .vc{color:#19177C}.pdoc-code .vg{color:#19177C}.pdoc-code .vi{color:#19177C}.pdoc-code .vm{color:#19177C}.pdoc-code .il{color:#666666}</style>
Expand Down Expand Up @@ -49,30 +49,30 @@ <h2>API Documentation</h2>
<main class="pdoc">
<section class="module-info">
<h1 class="modulename">
type_checking_imports </h1>
type_checking_imports<wbr>.main </h1>


<input id="mod-type_checking_imports-view-source" class="view-source-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
<input id="mod-main-view-source" class="view-source-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">

<label class="view-source-button" for="mod-type_checking_imports-view-source"><span>View Source</span></label>
<label class="view-source-button" for="mod-main-view-source"><span>View Source</span></label>

<div class="pdoc-code codehilite"><pre><span></span><span id="L-1"><a href="#L-1"><span class="linenos"> 1</span></a><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
</span><span id="L-2"><a href="#L-2"><span class="linenos"> 2</span></a>
</span><span id="L-3"><a href="#L-3"><span class="linenos"> 3</span></a><span class="kn">import</span> <span class="nn">typing</span>
</span><span id="L-4"><a href="#L-4"><span class="linenos"> 4</span></a><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TYPE_CHECKING</span>
</span><span id="L-5"><a href="#L-5"><span class="linenos"> 5</span></a>
</span><span id="L-6"><a href="#L-6"><span class="linenos"> 6</span></a><span class="kn">import</span> <span class="nn">type_checking_imports2</span>
</span><span id="L-6"><a href="#L-6"><span class="linenos"> 6</span></a><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">cached_submodule</span>
</span><span id="L-7"><a href="#L-7"><span class="linenos"> 7</span></a>
</span><span id="L-8"><a href="#L-8"><span class="linenos"> 8</span></a><span class="k">if</span> <span class="n">typing</span><span class="o">.</span><span class="n">TYPE_CHECKING</span><span class="p">:</span>
</span><span id="L-9"><a href="#L-9"><span class="linenos"> 9</span></a> <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Sequence</span>
</span><span id="L-10"><a href="#L-10"><span class="linenos">10</span></a>
</span><span id="L-11"><a href="#L-11"><span class="linenos">11</span></a><span class="k">if</span> <span class="n">TYPE_CHECKING</span><span class="p">:</span>
</span><span id="L-12"><a href="#L-12"><span class="linenos">12</span></a> <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Dict</span>
</span><span id="L-13"><a href="#L-13"><span class="linenos">13</span></a>
</span><span id="L-14"><a href="#L-14"><span class="linenos">14</span></a> <span class="kn">from</span> <span class="nn">type_checking_imports2</span> <span class="kn">import</span> <span class="n">StrOrInt</span> <span class="c1"># in module cache</span>
</span><span id="L-15"><a href="#L-15"><span class="linenos">15</span></a> <span class="kn">from</span> <span class="nn">type_checking_imports4</span> <span class="kn">import</span> <span class="n">StrOrBool</span> <span class="c1"># not in module cache</span>
</span><span id="L-14"><a href="#L-14"><span class="linenos">14</span></a> <span class="kn">from</span> <span class="nn">.cached_submodule</span> <span class="kn">import</span> <span class="n">StrOrInt</span>
</span><span id="L-15"><a href="#L-15"><span class="linenos">15</span></a> <span class="kn">from</span> <span class="nn">.uncached_submodule</span> <span class="kn">import</span> <span class="n">StrOrBool</span>
</span><span id="L-16"><a href="#L-16"><span class="linenos">16</span></a>
</span><span id="L-17"><a href="#L-17"><span class="linenos">17</span></a><span class="k">assert</span> <span class="n">type_checking_imports2</span>
</span><span id="L-17"><a href="#L-17"><span class="linenos">17</span></a><span class="k">assert</span> <span class="n">cached_submodule</span>
</span><span id="L-18"><a href="#L-18"><span class="linenos">18</span></a>
</span><span id="L-19"><a href="#L-19"><span class="linenos">19</span></a>
</span><span id="L-20"><a href="#L-20"><span class="linenos">20</span></a><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Sequence</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">b</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]):</span>
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/type_checking_imports.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<module type_checking_imports
<module type_checking_imports.main
<function def foo(a: Sequence[str], b: Dict[str, str]): ... # A method with TYPE_C…>
<var var: Sequence[int] = (1, 2, 3) # A variable with TYPE…>
<var imported_from_cached_module: str | int = 42 # A variable with a ty…>
Expand Down
Empty file.
12 changes: 12 additions & 0 deletions test/testdata/type_checking_imports/cached_submodule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import annotations

import typing

from . import cached_subsubmodule

assert cached_subsubmodule

if typing.TYPE_CHECKING:
from .cached_subsubmodule import StrOrInt

assert StrOrInt
File renamed without changes.
8 changes: 4 additions & 4 deletions test/testdata/type_checking_imports.py → test/testdata/type_checking_imports/main.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
import typing
from typing import TYPE_CHECKING

import type_checking_imports2
from . import cached_submodule

if typing.TYPE_CHECKING:
from typing import Sequence

if TYPE_CHECKING:
from typing import Dict

from type_checking_imports2 import StrOrInt # in module cache
from type_checking_imports4 import StrOrBool # not in module cache
from .cached_submodule import StrOrInt
from .uncached_submodule import StrOrBool

assert type_checking_imports2
assert cached_submodule


def foo(a: Sequence[str], b: Dict[str, str]):
Expand Down
File renamed without changes.
12 changes: 0 additions & 12 deletions test/testdata/type_checking_imports2.py

This file was deleted.

0 comments on commit c6b84b8

Please sign in to comment.