test_typing.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. from __future__ import annotations
  2. import logging
  3. import typing as t
  4. from abc import ABC
  5. import pytest
  6. from traitlets import (
  7. Any,
  8. Bool,
  9. CInt,
  10. Dict,
  11. Enum,
  12. HasTraits,
  13. Instance,
  14. Int,
  15. List,
  16. Set,
  17. TCPAddress,
  18. Type,
  19. Unicode,
  20. Union,
  21. default,
  22. observe,
  23. validate,
  24. )
  25. from traitlets.config import Config
  26. if not t.TYPE_CHECKING:
  27. def reveal_type(*args: t.Any, **kwargs: t.Any) -> None:
  28. pass
  29. # mypy: disallow-untyped-calls
  30. class Foo:
  31. def __init__(self, c: t.Any) -> None:
  32. self.c = c
  33. @pytest.mark.mypy_testing
  34. def mypy_decorator_typing() -> None:
  35. class T(HasTraits):
  36. foo = Unicode("").tag(config=True)
  37. @default("foo")
  38. def _default_foo(self) -> str:
  39. return "hi"
  40. @observe("foo")
  41. def _foo_observer(self, change: t.Any) -> bool:
  42. return True
  43. @validate("foo")
  44. def _foo_validate(self, commit: t.Any) -> bool:
  45. return True
  46. t = T()
  47. reveal_type(t.foo) # R: builtins.str
  48. reveal_type(t._foo_observer) # R: Any
  49. reveal_type(t._foo_validate) # R: Any
  50. @pytest.mark.mypy_testing
  51. def mypy_config_typing() -> None:
  52. c = Config(
  53. {
  54. "ExtractOutputPreprocessor": {"enabled": True},
  55. }
  56. )
  57. reveal_type(c) # R: traitlets.config.loader.Config
  58. @pytest.mark.mypy_testing
  59. def mypy_union_typing() -> None:
  60. class T(HasTraits):
  61. style = Union(
  62. [Unicode("default"), Type(klass=object)],
  63. help="Name of the pygments style to use",
  64. default_value="hi",
  65. ).tag(config=True)
  66. t = T()
  67. reveal_type(Union("foo")) # R: traitlets.traitlets.Union
  68. reveal_type(Union("").tag(sync=True)) # R: traitlets.traitlets.Union
  69. reveal_type(Union(None, allow_none=True)) # R: traitlets.traitlets.Union
  70. reveal_type(Union(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Union
  71. reveal_type(T.style) # R: traitlets.traitlets.Union
  72. reveal_type(t.style) # R: Any
  73. @pytest.mark.mypy_testing
  74. def mypy_list_typing() -> None:
  75. class T(HasTraits):
  76. latex_command = List(
  77. ["xelatex", "{filename}", "-quiet"], help="Shell command used to compile latex."
  78. ).tag(config=True)
  79. t = T()
  80. reveal_type(List(["foo"])) # R: traitlets.traitlets.List[builtins.str]
  81. reveal_type(List([""]).tag(sync=True)) # R: traitlets.traitlets.List[builtins.str]
  82. reveal_type(List(None, allow_none=True)) # R: traitlets.traitlets.List[Never]
  83. reveal_type(
  84. List(None, allow_none=True).tag(sync=True) # R: traitlets.traitlets.List[Never]
  85. )
  86. reveal_type(T.latex_command) # R: traitlets.traitlets.List[builtins.str]
  87. reveal_type(t.latex_command) # R: builtins.list[builtins.str]
  88. @pytest.mark.mypy_testing
  89. def mypy_dict_typing() -> None:
  90. class T(HasTraits):
  91. foo = Dict({}, help="Shell command used to compile latex.").tag(config=True)
  92. t = T()
  93. reveal_type(Dict(None, allow_none=True)) # R: traitlets.traitlets.Dict[builtins.str, Any]
  94. reveal_type(
  95. Dict(None, allow_none=True).tag(sync=True) # R: traitlets.traitlets.Dict[builtins.str, Any]
  96. )
  97. reveal_type(T.foo) # R: traitlets.traitlets.Dict[builtins.str, Any]
  98. reveal_type(t.foo) # R: builtins.dict[builtins.str, Any]
  99. @pytest.mark.mypy_testing
  100. def mypy_type_typing() -> None:
  101. class KernelSpec:
  102. item = Unicode("foo")
  103. class KernelSpecSubclass(KernelSpec):
  104. other = Unicode("bar")
  105. class GatewayTokenRenewerBase(ABC):
  106. item = Unicode("foo")
  107. class KernelSpecManager(HasTraits):
  108. """A manager for kernel specs."""
  109. kernel_spec_class = Type(
  110. KernelSpec,
  111. config=True,
  112. help="""The kernel spec class. This is configurable to allow
  113. subclassing of the KernelSpecManager for customized behavior.
  114. """,
  115. )
  116. other_class = Type("foo.bar.baz")
  117. other_kernel_spec_class = Type(
  118. default_value=KernelSpecSubclass,
  119. klass=KernelSpec,
  120. config=True,
  121. )
  122. gateway_token_renewer_class = Type(
  123. klass=GatewayTokenRenewerBase,
  124. config=True,
  125. help="""The class to use for Gateway token renewal. (JUPYTER_GATEWAY_TOKEN_RENEWER_CLASS env var)""",
  126. )
  127. t = KernelSpecManager()
  128. reveal_type(t.kernel_spec_class) # R: def () -> tests.test_typing.KernelSpec@129
  129. reveal_type(t.kernel_spec_class()) # R: tests.test_typing.KernelSpec@129
  130. reveal_type(t.kernel_spec_class().item) # R: builtins.str
  131. reveal_type(t.other_class) # R: builtins.type
  132. reveal_type(t.other_class()) # R: Any
  133. reveal_type(t.other_kernel_spec_class) # R: def () -> tests.test_typing.KernelSpec@129
  134. reveal_type(t.other_kernel_spec_class()) # R: tests.test_typing.KernelSpec@129
  135. reveal_type(
  136. t.gateway_token_renewer_class # R: def () -> tests.test_typing.GatewayTokenRenewerBase@135
  137. )
  138. reveal_type(t.gateway_token_renewer_class()) # R: tests.test_typing.GatewayTokenRenewerBase@135
  139. @pytest.mark.mypy_testing
  140. def mypy_unicode_typing() -> None:
  141. class T(HasTraits):
  142. export_format = Unicode(
  143. allow_none=False,
  144. help="""The export format to be used, either one of the built-in formats
  145. or a dotted object name that represents the import path for an
  146. ``Exporter`` class""",
  147. ).tag(config=True)
  148. t = T()
  149. reveal_type(
  150. Unicode( # R: traitlets.traitlets.Unicode[builtins.str, Union[builtins.str, builtins.bytes]]
  151. "foo"
  152. )
  153. )
  154. reveal_type(
  155. Unicode( # R: traitlets.traitlets.Unicode[builtins.str, Union[builtins.str, builtins.bytes]]
  156. ""
  157. ).tag(sync=True)
  158. )
  159. reveal_type(
  160. Unicode( # R: traitlets.traitlets.Unicode[Union[builtins.str, None], Union[builtins.str, builtins.bytes, None]]
  161. None, allow_none=True
  162. )
  163. )
  164. reveal_type(
  165. Unicode( # R: traitlets.traitlets.Unicode[Union[builtins.str, None], Union[builtins.str, builtins.bytes, None]]
  166. None, allow_none=True
  167. ).tag(sync=True)
  168. )
  169. reveal_type(
  170. T.export_format # R: traitlets.traitlets.Unicode[builtins.str, Union[builtins.str, builtins.bytes]]
  171. )
  172. reveal_type(t.export_format) # R: builtins.str
  173. @pytest.mark.mypy_testing
  174. def mypy_enum_typing() -> None:
  175. class T(HasTraits):
  176. log_level = Enum(
  177. (0, 10, 20, 30, 40, 50),
  178. default_value=logging.WARN,
  179. help="Set the log level by value or name.",
  180. ).tag(config=True)
  181. t = T()
  182. reveal_type(
  183. Enum( # R: traitlets.traitlets.Enum[builtins.str]
  184. ("foo",)
  185. )
  186. )
  187. reveal_type(
  188. Enum( # R: traitlets.traitlets.Enum[builtins.str]
  189. [""]
  190. ).tag(sync=True)
  191. )
  192. reveal_type(
  193. Enum( # R: traitlets.traitlets.Enum[None]
  194. None, allow_none=True
  195. )
  196. )
  197. reveal_type(
  198. Enum( # R: traitlets.traitlets.Enum[None]
  199. None, allow_none=True
  200. ).tag(sync=True)
  201. )
  202. reveal_type(
  203. T.log_level # R: traitlets.traitlets.Enum[builtins.int]
  204. )
  205. reveal_type(t.log_level) # R: builtins.int
  206. @pytest.mark.mypy_testing
  207. def mypy_set_typing() -> None:
  208. class T(HasTraits):
  209. remove_cell_tags = Set(
  210. Unicode(),
  211. default_value=[],
  212. help=(
  213. "Tags indicating which cells are to be removed,"
  214. "matches tags in ``cell.metadata.tags``."
  215. ),
  216. ).tag(config=True)
  217. safe_output_keys = Set(
  218. config=True,
  219. default_value={
  220. "metadata", # Not a mimetype per-se, but expected and safe.
  221. "text/plain",
  222. "text/latex",
  223. "application/json",
  224. "image/png",
  225. "image/jpeg",
  226. },
  227. help="Cell output mimetypes to render without modification",
  228. )
  229. t = T()
  230. reveal_type(Set("foo")) # R: traitlets.traitlets.Set
  231. reveal_type(Set("").tag(sync=True)) # R: traitlets.traitlets.Set
  232. reveal_type(Set(None, allow_none=True)) # R: traitlets.traitlets.Set
  233. reveal_type(Set(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Set
  234. reveal_type(T.remove_cell_tags) # R: traitlets.traitlets.Set
  235. reveal_type(t.remove_cell_tags) # R: builtins.set[Any]
  236. reveal_type(T.safe_output_keys) # R: traitlets.traitlets.Set
  237. reveal_type(t.safe_output_keys) # R: builtins.set[Any]
  238. @pytest.mark.mypy_testing
  239. def mypy_any_typing() -> None:
  240. class T(HasTraits):
  241. attributes = Any(
  242. config=True,
  243. default_value={
  244. "a": ["href", "title"],
  245. "abbr": ["title"],
  246. "acronym": ["title"],
  247. },
  248. help="Allowed HTML tag attributes",
  249. )
  250. t = T()
  251. reveal_type(Any("foo")) # R: traitlets.traitlets.Any
  252. reveal_type(Any("").tag(sync=True)) # R: traitlets.traitlets.Any
  253. reveal_type(Any(None, allow_none=True)) # R: traitlets.traitlets.Any
  254. reveal_type(Any(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Any
  255. reveal_type(T.attributes) # R: traitlets.traitlets.Any
  256. reveal_type(t.attributes) # R: Any
  257. @pytest.mark.mypy_testing
  258. def mypy_bool_typing() -> None:
  259. class T(HasTraits):
  260. b = Bool(True).tag(sync=True)
  261. ob = Bool(None, allow_none=True).tag(sync=True)
  262. t = T()
  263. reveal_type(
  264. Bool(True) # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
  265. )
  266. reveal_type(
  267. Bool( # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
  268. True
  269. ).tag(sync=True)
  270. )
  271. reveal_type(
  272. Bool( # R: traitlets.traitlets.Bool[Union[builtins.bool, None], Union[builtins.bool, builtins.int, None]]
  273. None, allow_none=True
  274. )
  275. )
  276. reveal_type(
  277. Bool( # R: traitlets.traitlets.Bool[Union[builtins.bool, None], Union[builtins.bool, builtins.int, None]]
  278. None, allow_none=True
  279. ).tag(sync=True)
  280. )
  281. reveal_type(
  282. T.b # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
  283. )
  284. reveal_type(t.b) # R: builtins.bool
  285. reveal_type(t.ob) # R: Union[builtins.bool, None]
  286. reveal_type(
  287. T.b # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
  288. )
  289. reveal_type(
  290. T.ob # R: traitlets.traitlets.Bool[Union[builtins.bool, None], Union[builtins.bool, builtins.int, None]]
  291. )
  292. # we would expect this to be Optional[Union[bool, int]], but...
  293. t.b = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Union[bool, int]") [assignment]
  294. t.b = None # E: Incompatible types in assignment (expression has type "None", variable has type "Union[bool, int]") [assignment]
  295. @pytest.mark.mypy_testing
  296. def mypy_int_typing() -> None:
  297. class T(HasTraits):
  298. i: Int[int, int] = Int(42).tag(sync=True)
  299. oi: Int[int | None, int | None] = Int(42, allow_none=True).tag(sync=True)
  300. t = T()
  301. reveal_type(Int(True)) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
  302. reveal_type(Int(True).tag(sync=True)) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
  303. reveal_type(
  304. Int( # R: traitlets.traitlets.Int[Union[builtins.int, None], Union[builtins.int, None]]
  305. None, allow_none=True
  306. )
  307. )
  308. reveal_type(
  309. Int( # R: traitlets.traitlets.Int[Union[builtins.int, None], Union[builtins.int, None]]
  310. None, allow_none=True
  311. ).tag(sync=True)
  312. )
  313. reveal_type(T.i) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
  314. reveal_type(t.i) # R: builtins.int
  315. reveal_type(t.oi) # R: Union[builtins.int, None]
  316. reveal_type(T.i) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
  317. reveal_type(
  318. T.oi # R: traitlets.traitlets.Int[Union[builtins.int, None], Union[builtins.int, None]]
  319. )
  320. t.i = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
  321. t.i = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") [assignment]
  322. t.i = 1.2 # E: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
  323. @pytest.mark.mypy_testing
  324. def mypy_cint_typing() -> None:
  325. class T(HasTraits):
  326. i = CInt(42).tag(sync=True)
  327. oi = CInt(42, allow_none=True).tag(sync=True)
  328. t = T()
  329. reveal_type(CInt(42)) # R: traitlets.traitlets.CInt[builtins.int, Any]
  330. reveal_type(CInt(42).tag(sync=True)) # R: traitlets.traitlets.CInt[builtins.int, Any]
  331. reveal_type(
  332. CInt(None, allow_none=True) # R: traitlets.traitlets.CInt[Union[builtins.int, None], Any]
  333. )
  334. reveal_type(
  335. CInt( # R: traitlets.traitlets.CInt[Union[builtins.int, None], Any]
  336. None, allow_none=True
  337. ).tag(sync=True)
  338. )
  339. reveal_type(T.i) # R: traitlets.traitlets.CInt[builtins.int, Any]
  340. reveal_type(t.i) # R: builtins.int
  341. reveal_type(t.oi) # R: Union[builtins.int, None]
  342. reveal_type(T.i) # R: traitlets.traitlets.CInt[builtins.int, Any]
  343. reveal_type(T.oi) # R: traitlets.traitlets.CInt[Union[builtins.int, None], Any]
  344. @pytest.mark.mypy_testing
  345. def mypy_tcp_typing() -> None:
  346. class T(HasTraits):
  347. tcp = TCPAddress()
  348. otcp = TCPAddress(None, allow_none=True)
  349. t = T()
  350. reveal_type(t.tcp) # R: Tuple[builtins.str, builtins.int]
  351. reveal_type(
  352. T.tcp # R: traitlets.traitlets.TCPAddress[Tuple[builtins.str, builtins.int], Tuple[builtins.str, builtins.int]]
  353. )
  354. reveal_type(
  355. T.tcp.tag( # R:traitlets.traitlets.TCPAddress[Tuple[builtins.str, builtins.int], Tuple[builtins.str, builtins.int]]
  356. sync=True
  357. )
  358. )
  359. reveal_type(t.otcp) # R: Union[Tuple[builtins.str, builtins.int], None]
  360. reveal_type(
  361. T.otcp # R: traitlets.traitlets.TCPAddress[Union[Tuple[builtins.str, builtins.int], None], Union[Tuple[builtins.str, builtins.int], None]]
  362. )
  363. reveal_type(
  364. T.otcp.tag( # R: traitlets.traitlets.TCPAddress[Union[Tuple[builtins.str, builtins.int], None], Union[Tuple[builtins.str, builtins.int], None]]
  365. sync=True
  366. )
  367. )
  368. t.tcp = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Tuple[str, int]") [assignment]
  369. t.otcp = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[Tuple[str, int]]") [assignment]
  370. t.tcp = None # E: Incompatible types in assignment (expression has type "None", variable has type "Tuple[str, int]") [assignment]
  371. @pytest.mark.mypy_testing
  372. def mypy_instance_typing() -> None:
  373. class T(HasTraits):
  374. inst = Instance(Foo)
  375. oinst = Instance(Foo, allow_none=True)
  376. oinst_string = Instance("Foo", allow_none=True)
  377. t = T()
  378. reveal_type(t.inst) # R: tests.test_typing.Foo
  379. reveal_type(T.inst) # R: traitlets.traitlets.Instance[tests.test_typing.Foo]
  380. reveal_type(T.inst.tag(sync=True)) # R: traitlets.traitlets.Instance[tests.test_typing.Foo]
  381. reveal_type(t.oinst) # R: Union[tests.test_typing.Foo, None]
  382. reveal_type(t.oinst_string) # R: Union[Any, None]
  383. reveal_type(T.oinst) # R: traitlets.traitlets.Instance[Union[tests.test_typing.Foo, None]]
  384. reveal_type(
  385. T.oinst.tag( # R: traitlets.traitlets.Instance[Union[tests.test_typing.Foo, None]]
  386. sync=True
  387. )
  388. )
  389. t.inst = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Foo") [assignment]
  390. t.oinst = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[Foo]") [assignment]
  391. t.inst = None # E: Incompatible types in assignment (expression has type "None", variable has type "Foo") [assignment]