diff --git a/CHANGELOG.md b/CHANGELOG.md index 81a125bb..ef4f9d3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ - Imports in a TYPE_CHECKING section that reference members defined in another module's TYPE_CHECKING section now work correctly. ([#649](https://github.com/mitmproxy/pdoc/pull/649), @mhils) + - pdoc now supports Python 3.12's `type` statements and has improved `TypeAlias` rendering. + ([#651](https://github.com/mitmproxy/pdoc/pull/651), @mhils) - Add support for `code-block` ReST directives ([#624](https://github.com/mitmproxy/pdoc/pull/624), @JCGoran) - If a variable's value meets certain entropy criteria and matches an environment variable value, diff --git a/pdoc/_compat.py b/pdoc/_compat.py index 8581d78c..c1271e39 100644 --- a/pdoc/_compat.py +++ b/pdoc/_compat.py @@ -16,6 +16,24 @@ def ast_unparse(t): # type: ignore return _unparse(t).strip("\t\n \"'") +if sys.version_info >= (3, 12): + from ast import TypeAlias as ast_TypeAlias +else: # pragma: no cover + class ast_TypeAlias: + pass + +if sys.version_info >= (3, 12): + from typing import TypeAliasType +else: # pragma: no cover + class TypeAliasType: + """Placeholder class for TypeAliasType""" + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: # pragma: no cover + class TypeAlias: + pass + if sys.version_info >= (3, 9): from types import GenericAlias else: # pragma: no cover @@ -108,6 +126,9 @@ def format_usage(self): __all__ = [ "cache", "ast_unparse", + "ast_TypeAlias", + "TypeAliasType", + "TypeAlias", "GenericAlias", "UnionType", "removesuffix", diff --git a/pdoc/doc.py b/pdoc/doc.py index 139446a9..cbe2f8fe 100644 --- a/pdoc/doc.py +++ b/pdoc/doc.py @@ -44,15 +44,16 @@ from pdoc import doc_ast from pdoc import doc_pyi from pdoc import extract +from pdoc._compat import TypeAlias +from pdoc._compat import TypeAliasType +from pdoc._compat import cache +from pdoc._compat import formatannotation from pdoc.doc_types import GenericAlias from pdoc.doc_types import NonUserDefinedCallables from pdoc.doc_types import empty from pdoc.doc_types import resolve_annotations from pdoc.doc_types import safe_eval_type -from ._compat import cache -from ._compat import formatannotation - def _include_fullname_in_traceback(f): """ @@ -1089,6 +1090,11 @@ def is_typevar(self) -> bool: else: return False + @cached_property + def is_type_alias_type(self) -> bool: + """`True` if the variable is a `typing.TypeAliasType`, `False` otherwise.""" + return isinstance(self.default_value, TypeAliasType) + @cached_property def is_enum_member(self) -> bool: """`True` if the variable is an enum member, `False` otherwise.""" @@ -1102,6 +1108,10 @@ def default_value_str(self) -> str: """The variable's default value as a pretty-printed str.""" if self.default_value is empty: return "" + if isinstance(self.default_value, TypeAliasType): + return formatannotation(self.default_value.__value__) + elif self.annotation == TypeAlias: + return formatannotation(self.default_value) # This is not perfect, but a solid attempt at preventing accidental leakage of secrets. # If you have input on how to improve the heuristic, please send a pull request! diff --git a/pdoc/doc_ast.py b/pdoc/doc_ast.py index f91e1397..56c91496 100644 --- a/pdoc/doc_ast.py +++ b/pdoc/doc_ast.py @@ -21,6 +21,7 @@ import pdoc +from ._compat import ast_TypeAlias from ._compat import ast_unparse from ._compat import cache @@ -115,7 +116,11 @@ def _walk_tree( func_docstrings = {} annotations = {} for a, b in _pairwise_longest(_nodes(tree)): - if isinstance(a, ast.AnnAssign) and isinstance(a.target, ast.Name) and a.simple: + if isinstance(a, ast_TypeAlias): + name = a.name.id + elif ( + isinstance(a, ast.AnnAssign) and isinstance(a.target, ast.Name) and a.simple + ): name = a.target.id annotations[name] = unparse(a.annotation) elif ( @@ -183,6 +188,8 @@ def sort_by_source( name = a.target.id elif isinstance(a, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): name = a.name + elif isinstance(a, ast_TypeAlias): + name = a.name.id else: continue diff --git a/pdoc/templates/default/module.html.jinja2 b/pdoc/templates/default/module.html.jinja2 index 00382185..09cbbfc3 100644 --- a/pdoc/templates/default/module.html.jinja2 +++ b/pdoc/templates/default/module.html.jinja2 @@ -177,6 +177,7 @@ See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an {% endif %} {% enddefaultmacro %} {% defaultmacro variable(var) -%} + {%- if var.is_type_alias_type %}type {% endif -%} {{ var.name }}{{ annotation(var) }}{{ default_value(var) }} {% enddefaultmacro %} {% defaultmacro submodule(mod) -%} diff --git a/test/testdata/flavors_rst.html b/test/testdata/flavors_rst.html index c78b0ee1..e16e34bb 100644 --- a/test/testdata/flavors_rst.html +++ b/test/testdata/flavors_rst.html @@ -131,138 +131,141 @@

40 .. versionchanged:: 2.5 41 The *spam* parameter. 42 - 43 .. deprecated:: 3.1 - 44 Use :func:`spam` instead. + 43 .. code-block:: + 44 This is a code block. 45 46 .. deprecated:: 3.1 - 47 - 48 This text is not part of the deprecation notice. - 49 """ + 47 Use :func:`spam` instead. + 48 + 49 .. deprecated:: 3.1 50 - 51 - 52def seealso(): - 53 # this is not properly supported yet - 54 """ - 55 .. seealso:: - 56 - 57 Module :py:mod:`zipfile` - 58 Documentation of the :py:mod:`zipfile` standard module. + 51 This text is not part of the deprecation notice. + 52 """ + 53 + 54 + 55def seealso(): + 56 # this is not properly supported yet + 57 """ + 58 .. seealso:: 59 - 60 `GNU tar manual, Basic Tar Format <http://link>`_ - 61 Documentation for tar archive files, including GNU tar extensions. - 62 """ - 63 - 64 - 65def seealso_short(): - 66 # this is not properly supported yet - 67 """ - 68 .. seealso:: modules :py:mod:`zipfile`, :py:mod:`tarfile` - 69 """ - 70 - 71 - 72def tables(): - 73 """ - 74 | Header 1 | *Header* 2 | - 75 | -------- | -------- | - 76 | `Cell 1` | [Cell 2](http://example.com) link | - 77 | Cell 3 | **Cell 4** | - 78 """ - 79 - 80 - 81def footnote1(): - 82 """ - 83 Cite the relevant literature, e.g. [1]_. You may also cite these - 84 references in the notes section above. - 85 - 86 .. [1] O. McNoleg, "The integration of GIS, remote sensing, - 87 expert systems and adaptive co-kriging for environmental habitat - 88 modelling of the Highland Haggis using object-oriented, fuzzy-logic - 89 and neural-network techniques," Computers & Geosciences, vol. 22, - 90 pp. 585-588, 1996. - 91 """ - 92 - 93 - 94def footnote2(): - 95 """ - 96 Autonumbered footnotes are - 97 possible, like using [#]_ and [#]_. - 98 - 99 .. [#] This is the first one. -100 .. [#] This is the second one. + 60 Module :py:mod:`zipfile` + 61 Documentation of the :py:mod:`zipfile` standard module. + 62 + 63 `GNU tar manual, Basic Tar Format <http://link>`_ + 64 Documentation for tar archive files, including GNU tar extensions. + 65 """ + 66 + 67 + 68def seealso_short(): + 69 # this is not properly supported yet + 70 """ + 71 .. seealso:: modules :py:mod:`zipfile`, :py:mod:`tarfile` + 72 """ + 73 + 74 + 75def tables(): + 76 """ + 77 | Header 1 | *Header* 2 | + 78 | -------- | -------- | + 79 | `Cell 1` | [Cell 2](http://example.com) link | + 80 | Cell 3 | **Cell 4** | + 81 """ + 82 + 83 + 84def footnote1(): + 85 """ + 86 Cite the relevant literature, e.g. [1]_. You may also cite these + 87 references in the notes section above. + 88 + 89 .. [1] O. McNoleg, "The integration of GIS, remote sensing, + 90 expert systems and adaptive co-kriging for environmental habitat + 91 modelling of the Highland Haggis using object-oriented, fuzzy-logic + 92 and neural-network techniques," Computers & Geosciences, vol. 22, + 93 pp. 585-588, 1996. + 94 """ + 95 + 96 + 97def footnote2(): + 98 """ + 99 Autonumbered footnotes are +100 possible, like using [#]_ and [#]_. 101 -102 They may be assigned 'autonumber -103 labels' - for instance, -104 [#fourth]_ and [#third]_. -105 -106 .. [#third] a.k.a. third_ -107 -108 .. [#fourth] a.k.a. fourth_ -109 """ +102 .. [#] This is the first one. +103 .. [#] This is the second one. +104 +105 They may be assigned 'autonumber +106 labels' - for instance, +107 [#fourth]_ and [#third]_. +108 +109 .. [#third] a.k.a. third_ 110 -111 -112def footnote3(): -113 """ -114 Auto-symbol footnotes are also -115 possible, like this: [*]_ and [*]_. -116 -117 .. [*] This is the first one. -118 .. [*] This is the second one. -119 """ -120 -121 -122def footnote4(): -123 """ -124 There is no footnote for this reference [#]_. -125 """ -126 -127 -128def include(): -129 """ -130 Included from another file: -131 -132 .. include:: flavors_rst_include/include.rst -133 """ +111 .. [#fourth] a.k.a. fourth_ +112 """ +113 +114 +115def footnote3(): +116 """ +117 Auto-symbol footnotes are also +118 possible, like this: [*]_ and [*]_. +119 +120 .. [*] This is the first one. +121 .. [*] This is the second one. +122 """ +123 +124 +125def footnote4(): +126 """ +127 There is no footnote for this reference [#]_. +128 """ +129 +130 +131def include(): +132 """ +133 Included from another file: 134 -135 -136def fields(foo: str = "foo", bar: bool = True) -> str: -137 """This method has field descriptions. +135 .. include:: flavors_rst_include/include.rst +136 """ +137 138 -139 :param foo: A string, -140 defaults to None -141 :type foo: string, optional -142 :param bar: Another -143 boolean. -144 :return: Another string, -145 or maybe `None`. -146 :rtype: A string. -147 """ -148 raise NotImplementedError -149 -150 -151def fields_text_after_param(foo): -152 """This method has text after the `:param` fields. +139def fields(foo: str = "foo", bar: bool = True) -> str: +140 """This method has field descriptions. +141 +142 :param foo: A string, +143 defaults to None +144 :type foo: string, optional +145 :param bar: Another +146 boolean. +147 :return: Another string, +148 or maybe `None`. +149 :rtype: A string. +150 """ +151 raise NotImplementedError +152 153 -154 :param foo: Some text. -155 -156 Here's some more text. -157 """ +154def fields_text_after_param(foo): +155 """This method has text after the `:param` fields. +156 +157 :param foo: Some text. 158 -159 -160def fields_invalid(foo: str = "foo") -> str: -161 """This method has invalid `:param` definitions. +159 Here's some more text. +160 """ +161 162 -163 :param: What is this for? -164 -165 :unknown: This is an unknown field name. -166 """ -167 raise NotImplementedError -168 -169 -170def fields_exception(): -171 """ -172 :raises RuntimeError: Some multi-line -173 exception description. -174 """ +163def fields_invalid(foo: str = "foo") -> str: +164 """This method has invalid `:param` definitions. +165 +166 :param: What is this for? +167 +168 :unknown: This is an unknown field name. +169 """ +170 raise NotImplementedError +171 +172 +173def fields_exception(): +174 """ +175 :raises RuntimeError: Some multi-line +176 exception description. +177 """ @@ -352,13 +355,16 @@

41 .. versionchanged:: 2.5 42 The *spam* parameter. 43 -44 .. deprecated:: 3.1 -45 Use :func:`spam` instead. +44 .. code-block:: +45 This is a code block. 46 47 .. deprecated:: 3.1 -48 -49 This text is not part of the deprecation notice. -50 """ +48 Use :func:`spam` instead. +49 +50 .. deprecated:: 3.1 +51 +52 This text is not part of the deprecation notice. +53 """ @@ -400,6 +406,9 @@

This warning has a title only.

Changed in version 2.5: The spam parameter.

+
This is a code block.
+
+

Deprecated since version 3.1: Use spam() instead.

@@ -421,17 +430,17 @@
This warning has a title only.
-
53def seealso():
-54    # this is not properly supported yet
-55    """
-56    .. seealso::
-57
-58       Module :py:mod:`zipfile`
-59          Documentation of the :py:mod:`zipfile` standard module.
+            
56def seealso():
+57    # this is not properly supported yet
+58    """
+59    .. seealso::
 60
-61       `GNU tar manual, Basic Tar Format <http://link>`_
-62          Documentation for tar archive files, including GNU tar extensions.
-63    """
+61       Module :py:mod:`zipfile`
+62          Documentation of the :py:mod:`zipfile` standard module.
+63
+64       `GNU tar manual, Basic Tar Format <http://link>`_
+65          Documentation for tar archive files, including GNU tar extensions.
+66    """
 
@@ -456,11 +465,11 @@
This warning has a title only.
-
66def seealso_short():
-67    # this is not properly supported yet
-68    """
-69    .. seealso:: modules :py:mod:`zipfile`, :py:mod:`tarfile`
-70    """
+            
69def seealso_short():
+70    # this is not properly supported yet
+71    """
+72    .. seealso:: modules :py:mod:`zipfile`, :py:mod:`tarfile`
+73    """
 
@@ -480,13 +489,13 @@
This warning has a title only.
-
73def tables():
-74    """
-75    | Header 1 | *Header* 2 |
-76    | -------- | -------- |
-77    | `Cell 1` | [Cell 2](http://example.com) link |
-78    | Cell 3 | **Cell 4** |
-79    """
+            
76def tables():
+77    """
+78    | Header 1 | *Header* 2 |
+79    | -------- | -------- |
+80    | `Cell 1` | [Cell 2](http://example.com) link |
+81    | Cell 3 | **Cell 4** |
+82    """
 
@@ -523,17 +532,17 @@
This warning has a title only.
-
82def footnote1():
-83    """
-84    Cite the relevant literature, e.g. [1]_.  You may also cite these
-85    references in the notes section above.
-86
-87    .. [1] O. McNoleg, "The integration of GIS, remote sensing,
-88       expert systems and adaptive co-kriging for environmental habitat
-89       modelling of the Highland Haggis using object-oriented, fuzzy-logic
-90       and neural-network techniques," Computers & Geosciences, vol. 22,
-91       pp. 585-588, 1996.
-92    """
+            
85def footnote1():
+86    """
+87    Cite the relevant literature, e.g. [1]_.  You may also cite these
+88    references in the notes section above.
+89
+90    .. [1] O. McNoleg, "The integration of GIS, remote sensing,
+91       expert systems and adaptive co-kriging for environmental habitat
+92       modelling of the Highland Haggis using object-oriented, fuzzy-logic
+93       and neural-network techniques," Computers & Geosciences, vol. 22,
+94       pp. 585-588, 1996.
+95    """
 
@@ -567,22 +576,22 @@
This warning has a title only.
-
 95def footnote2():
- 96    """
- 97    Autonumbered footnotes are
- 98    possible, like using [#]_ and [#]_.
- 99
-100    .. [#] This is the first one.
-101    .. [#] This is the second one.
+            
 98def footnote2():
+ 99    """
+100    Autonumbered footnotes are
+101    possible, like using [#]_ and [#]_.
 102
-103    They may be assigned 'autonumber
-104    labels' - for instance,
-105    [#fourth]_ and [#third]_.
-106
-107    .. [#third] a.k.a. third_
-108
-109    .. [#fourth] a.k.a. fourth_
-110    """
+103    .. [#] This is the first one.
+104    .. [#] This is the second one.
+105
+106    They may be assigned 'autonumber
+107    labels' - for instance,
+108    [#fourth]_ and [#third]_.
+109
+110    .. [#third] a.k.a. third_
+111
+112    .. [#fourth] a.k.a. fourth_
+113    """
 
@@ -628,14 +637,14 @@
This warning has a title only.
-
113def footnote3():
-114    """
-115    Auto-symbol footnotes are also
-116    possible, like this: [*]_ and [*]_.
-117
-118    .. [*] This is the first one.
-119    .. [*] This is the second one.
-120    """
+            
116def footnote3():
+117    """
+118    Auto-symbol footnotes are also
+119    possible, like this: [*]_ and [*]_.
+120
+121    .. [*] This is the first one.
+122    .. [*] This is the second one.
+123    """
 
@@ -669,10 +678,10 @@
This warning has a title only.
-
123def footnote4():
-124    """
-125    There is no footnote for this reference [#]_.
-126    """
+            
126def footnote4():
+127    """
+128    There is no footnote for this reference [#]_.
+129    """
 
@@ -692,12 +701,12 @@
This warning has a title only.
-
129def include():
-130    """
-131    Included from another file:
-132
-133    .. include:: flavors_rst_include/include.rst
-134    """
+            
132def include():
+133    """
+134    Included from another file:
+135
+136    .. include:: flavors_rst_include/include.rst
+137    """
 
@@ -723,19 +732,19 @@
This warning has a title only.
-
137def fields(foo: str = "foo", bar: bool = True) -> str:
-138    """This method has field descriptions.
-139
-140    :param foo: A string,
-141        defaults to None
-142    :type foo: string, optional
-143    :param bar: Another
-144     boolean.
-145    :return: Another string,
-146        or maybe `None`.
-147    :rtype: A string.
-148    """
-149    raise NotImplementedError
+            
140def fields(foo: str = "foo", bar: bool = True) -> str:
+141    """This method has field descriptions.
+142
+143    :param foo: A string,
+144        defaults to None
+145    :type foo: string, optional
+146    :param bar: Another
+147     boolean.
+148    :return: Another string,
+149        or maybe `None`.
+150    :rtype: A string.
+151    """
+152    raise NotImplementedError
 
@@ -771,13 +780,13 @@
Returns
-
152def fields_text_after_param(foo):
-153    """This method has text after the `:param` fields.
-154
-155    :param foo: Some text.
-156
-157    Here's some more text.
-158    """
+            
155def fields_text_after_param(foo):
+156    """This method has text after the `:param` fields.
+157
+158    :param foo: Some text.
+159
+160    Here's some more text.
+161    """
 
@@ -805,14 +814,14 @@
Parameters
-
161def fields_invalid(foo: str = "foo") -> str:
-162    """This method has invalid `:param` definitions.
-163
-164    :param: What is this for?
-165
-166    :unknown: This is an unknown field name.
-167    """
-168    raise NotImplementedError
+            
164def fields_invalid(foo: str = "foo") -> str:
+165    """This method has invalid `:param` definitions.
+166
+167    :param: What is this for?
+168
+169    :unknown: This is an unknown field name.
+170    """
+171    raise NotImplementedError
 
@@ -840,11 +849,11 @@
Parameters
-
171def fields_exception():
-172    """
-173    :raises RuntimeError: Some multi-line
-174        exception description.
-175    """
+            
174def fields_exception():
+175    """
+176    :raises RuntimeError: Some multi-line
+177        exception description.
+178    """
 
diff --git a/test/testdata/flavors_rst.py b/test/testdata/flavors_rst.py index 3d0c1e1a..3fc9f2d5 100644 --- a/test/testdata/flavors_rst.py +++ b/test/testdata/flavors_rst.py @@ -40,6 +40,9 @@ def admonitions(): .. versionchanged:: 2.5 The *spam* parameter. + .. code-block:: + This is a code block. + .. deprecated:: 3.1 Use :func:`spam` instead. diff --git a/test/testdata/misc_py312.html b/test/testdata/misc_py312.html old mode 100644 new mode 100755 index 50acedf5..7f2bc5ff --- a/test/testdata/misc_py312.html +++ b/test/testdata/misc_py312.html @@ -23,6 +23,18 @@

API Documentation

    +
  • + MyType +
  • +
  • + foo +
  • +
  • + MyTypeWithoutDocstring +
  • +
  • + MyTypeClassic +
  • NamedTupleExample
      @@ -82,47 +94,116 @@

      -
       1"""
      - 2Testing features that either are 3.12+ only or render slightly different on 3.12.
      +                        
       1# mypy: ignore-errors
      + 2# https://github.com/python/mypy/issues/16607
        3"""
      - 4from __future__ import annotations
      - 5
      - 6from typing import NamedTuple
      - 7from typing import Optional
      - 8from typing import TypedDict
      - 9
      -10# Testing a typing.NamedTuple
      -11# we do not care very much about collections.namedtuple,
      -12# the typing version is superior for documentation and a drop-in replacement.
      -13
      -14
      -15class NamedTupleExample(NamedTuple):
      -16    """
      -17    An example for a typing.NamedTuple.
      -18    """
      + 4Testing features that either are 3.12+ only or render slightly different on 3.12.
      + 5"""
      + 6from __future__ import annotations
      + 7
      + 8import typing
      + 9from typing import NamedTuple
      +10from typing import Optional
      +11from typing import TypedDict
      +12
      +13# Testing the new Python 3.12 `type` statement.
      +14type MyType = int
      +15"""A custom Python 3.12 type."""
      +16
      +17foo: MyType
      +18"""A custom type instance."""
       19
      -20    name: str
      -21    """Name of our example tuple."""
      -22    id: int = 3
      -23
      -24
      -25# Testing some edge cases in our inlined implementation of ForwardRef._evaluate in _eval_type.
      -26class Foo(TypedDict):
      -27    a: Optional[int]
      -28    """First attribute."""
      +20
      +21type MyTypeWithoutDocstring = int
      +22
      +23MyTypeClassic: typing.TypeAlias = int
      +24"""A "classic" typing.TypeAlias."""
      +25
      +26# Testing a typing.NamedTuple
      +27# we do not care very much about collections.namedtuple,
      +28# the typing version is superior for documentation and a drop-in replacement.
       29
       30
      -31class Bar(Foo, total=False):
      -32    """A TypedDict subclass. Before 3.12, TypedDict botches __mro__."""
      -33
      -34    b: int
      -35    """Second attribute."""
      -36    c: str
      -37    # undocumented attribute
      +31class NamedTupleExample(NamedTuple):
      +32    """
      +33    An example for a typing.NamedTuple.
      +34    """
      +35
      +36    name: str
      +37    """Name of our example tuple."""
      +38    id: int = 3
      +39
      +40
      +41# Testing some edge cases in our inlined implementation of ForwardRef._evaluate in _eval_type.
      +42class Foo(TypedDict):
      +43    a: Optional[int]
      +44    """First attribute."""
      +45
      +46
      +47class Bar(Foo, total=False):
      +48    """A TypedDict subclass. Before 3.12, TypedDict botches __mro__."""
      +49
      +50    b: int
      +51    """Second attribute."""
      +52    c: str
      +53    # undocumented attribute
       
      +
      +
      + type MyType = +int + + +
      + + +

      A custom Python 3.12 type.

      +
      + + +
      +
      +
      + foo: MyType + + +
      + + +

      A custom type instance.

      +
      + + +
      +
      +
      + type MyTypeWithoutDocstring = +int + + +
      + + + + +
      +
      +
      + MyTypeClassic: TypeAlias = +int + + +
      + + +

      A "classic" typing.TypeAlias.

      +
      + + +
      @@ -134,14 +215,14 @@

      -
      16class NamedTupleExample(NamedTuple):
      -17    """
      -18    An example for a typing.NamedTuple.
      -19    """
      -20
      -21    name: str
      -22    """Name of our example tuple."""
      -23    id: int = 3
      +            
      32class NamedTupleExample(NamedTuple):
      +33    """
      +34    An example for a typing.NamedTuple.
      +35    """
      +36
      +37    name: str
      +38    """Name of our example tuple."""
      +39    id: int = 3
       
      @@ -211,9 +292,9 @@
      Inherited Members
      -
      27class Foo(TypedDict):
      -28    a: Optional[int]
      -29    """First attribute."""
      +            
      43class Foo(TypedDict):
      +44    a: Optional[int]
      +45    """First attribute."""
       
      @@ -263,13 +344,13 @@
      Inherited Members
      -
      32class Bar(Foo, total=False):
      -33    """A TypedDict subclass. Before 3.12, TypedDict botches __mro__."""
      -34
      -35    b: int
      -36    """Second attribute."""
      -37    c: str
      -38    # undocumented attribute
      +            
      48class Bar(Foo, total=False):
      +49    """A TypedDict subclass. Before 3.12, TypedDict botches __mro__."""
      +50
      +51    b: int
      +52    """Second attribute."""
      +53    c: str
      +54    # undocumented attribute
       
      diff --git a/test/testdata/misc_py312.py b/test/testdata/misc_py312.py index 5835ab23..f9d9f9fd 100755 --- a/test/testdata/misc_py312.py +++ b/test/testdata/misc_py312.py @@ -1,12 +1,28 @@ +# mypy: ignore-errors +# https://github.com/python/mypy/issues/16607 """ Testing features that either are 3.12+ only or render slightly different on 3.12. """ from __future__ import annotations +import typing from typing import NamedTuple from typing import Optional from typing import TypedDict +# Testing the new Python 3.12 `type` statement. +type MyType = int +"""A custom Python 3.12 type.""" + +foo: MyType +"""A custom type instance.""" + + +type MyTypeWithoutDocstring = int + +MyTypeClassic: typing.TypeAlias = int +"""A "classic" typing.TypeAlias.""" + # Testing a typing.NamedTuple # we do not care very much about collections.namedtuple, # the typing version is superior for documentation and a drop-in replacement. diff --git a/test/testdata/misc_py312.txt b/test/testdata/misc_py312.txt old mode 100644 new mode 100755 index 69ec2ac3..fc7d488f --- a/test/testdata/misc_py312.txt +++ b/test/testdata/misc_py312.txt @@ -1,4 +1,8 @@ + + +