Skip to content

Commit

Permalink
Add support for Python 3.13 (#730)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhils authored Sep 11, 2024
1 parent 1f7b17e commit 8597593
Show file tree
Hide file tree
Showing 27 changed files with 1,710 additions and 1,604 deletions.
2 changes: 1 addition & 1 deletion .github/python-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.12.0
3.13-dev
8 changes: 5 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ jobs:
matrix:
include:
- os: windows-latest
py: 3.12.0
py: 3.13-dev
args: --cov-fail-under=100
- os: macos-latest
py: 3.12.0
py: 3.13-dev
args: --cov-fail-under=100
- os: ubuntu-latest
py: 3.12.0
py: 3.13-dev
args: --cov-fail-under=100
- os: ubuntu-latest
py: 3.12.4
- os: ubuntu-latest
py: 3.11.5
- os: ubuntu-latest
Expand Down
16 changes: 8 additions & 8 deletions pdoc/doc_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,11 @@ def _parse_class(source: str) -> ast.ClassDef:
Returns an empty ast.ClassDef if source is empty.
"""
tree = _parse(source)
assert len(tree.body) <= 1
if tree.body:
if tree.body and len(tree.body) == 1:
t = tree.body[0]
assert isinstance(t, ast.ClassDef)
return t
return ast.ClassDef(body=[], decorator_list=[]) # type: ignore
if isinstance(t, ast.ClassDef):
return t
return ast.ClassDef(name="PdocStub", body=[], decorator_list=[]) # type: ignore


@cache
Expand All @@ -256,16 +255,17 @@ def _parse_function(source: str) -> ast.FunctionDef | ast.AsyncFunctionDef:
Returns an empty ast.FunctionDef if source is empty.
"""
tree = _parse(source)
assert len(tree.body) <= 1
if tree.body:
if tree.body and len(tree.body) == 1:
t = tree.body[0]
if isinstance(t, (ast.FunctionDef, ast.AsyncFunctionDef)):
return t
else:
# we have a lambda function,
# to simplify the API return the ast.FunctionDef stub.
pass
return ast.FunctionDef(body=[], decorator_list=[]) # type: ignore
return ast.FunctionDef(
name="pdoc_stub", args=ast.arguments(), body=[], decorator_list=[]
) # type: ignore


def _parse(
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Typing :: Typed",
]

Expand Down
15 changes: 8 additions & 7 deletions test/test_doc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import builtins
import dataclasses
from pathlib import Path
import sys
import types
from unittest.mock import patch

Expand Down Expand Up @@ -120,13 +119,15 @@ def test_builtin_source():
assert m.source_lines is None


@pytest.mark.skipif(sys.version_info < (3, 9), reason="3.9+ only")
def test_raising_getdoc():
class Foo:
@classmethod
@property
def __doc__(self):
raise RuntimeError
class FooMeta(type):
def __getattribute__(cls, name):
if name == "__doc__":
raise RuntimeError
return super().__getattribute__(name)

class Foo(metaclass=FooMeta):
pass

x = Class(Foo.__module__, Foo.__qualname__, Foo, (Foo.__module__, Foo.__qualname__))
with pytest.warns(UserWarning, match="inspect.getdoc(.+) raised an exception"):
Expand Down
4 changes: 3 additions & 1 deletion test/test_doc_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ def test_eval_fail_import_nonexistent(monkeypatch):
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'"):
with pytest.warns(
UserWarning, match="No module named 'nonexistent_module'|Import of xyz failed"
):
assert safe_eval_type("xyz", a.__dict__, None, a, "a") == "xyz"


Expand Down
15 changes: 5 additions & 10 deletions test/test_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def outfile(self, format: str) -> Path:
Snapshot("ast_parsing"),
Snapshot("collections_abc", min_version=(3, 9)),
Snapshot("demo", min_version=(3, 9)),
Snapshot("enums", min_version=(3, 12)),
Snapshot("enums", min_version=(3, 13)),
Snapshot("flavors_google"),
Snapshot("flavors_numpy"),
Snapshot("flavors_rst"),
Expand Down Expand Up @@ -136,16 +136,10 @@ def outfile(self, format: str) -> Path:
with_output_directory=True,
),
Snapshot("misc"),
Snapshot(
"misc_py39",
min_version=(3, 9),
),
Snapshot("misc_py39", min_version=(3, 9)),
Snapshot("misc_py310", min_version=(3, 10)),
Snapshot("misc_py311", min_version=(3, 11)),
Snapshot(
"misc_py312",
min_version=(3, 12),
),
Snapshot("misc_py312", min_version=(3, 12)),
Snapshot("misc_py313", min_version=(3, 13)),
Snapshot("math_demo", render_options={"math": True}),
Snapshot("math_misc", render_options={"math": True}),
Snapshot("mermaid_demo", render_options={"mermaid": True}, min_version=(3, 9)),
Expand All @@ -165,6 +159,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", ["type_checking_imports.main"]),
Snapshot("typed_dict", min_version=(3, 13)),
Snapshot("type_stubs", ["type_stubs"], min_version=(3, 10)),
Snapshot(
"visibility",
Expand Down
22 changes: 11 additions & 11 deletions test/testdata/enums.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<var BAR = <IntEnum.BAR: 2>>
<var name # inherited from enum.Enum.name, The name of the Enum…>
<var value # inherited from enum.Enum.value, The value of the Enu…>
<method def conjugate(unknown): ... # inherited from builtins.int.conjugate, Returns self, the co…>
<method def conjugate(self, /): ... # inherited from builtins.int.conjugate, Returns self, the co…>
<method def bit_length(self, /): ... # inherited from builtins.int.bit_length, Number of bits neces…>
<method def bit_count(self, /): ... # inherited from builtins.int.bit_count, Number of ones in th…>
<method def to_bytes(self, /, length=1, byteorder='big', *, signed=False): ... # inherited from builtins.int.to_bytes, Return an array of b…>
Expand All @@ -35,24 +35,24 @@
<var name # inherited from enum.Enum.name, The name of the Enum…>
<var value # inherited from enum.Enum.value, The value of the Enu…>
<method def encode(self, /, encoding='utf-8', errors='strict'): ... # inherited from builtins.str.encode, Encode the string us…>
<method def replace(self, old, new, count=-1, /): ... # inherited from builtins.str.replace, Return a copy with a…>
<method def replace(self, old, new, /, count=-1): ... # inherited from builtins.str.replace, Return a copy with a…>
<method def split(self, /, sep=None, maxsplit=-1): ... # inherited from builtins.str.split, Return a list of the…>
<method def rsplit(self, /, sep=None, maxsplit=-1): ... # inherited from builtins.str.rsplit, Return a list of the…>
<method def join(self, iterable, /): ... # inherited from builtins.str.join, Concatenate any numb…>
<method def capitalize(self, /): ... # inherited from builtins.str.capitalize, Return a capitalized…>
<method def casefold(self, /): ... # inherited from builtins.str.casefold, Return a version of …>
<method def title(self, /): ... # inherited from builtins.str.title, Return a version of …>
<method def center(self, width, fillchar=' ', /): ... # inherited from builtins.str.center, Return a centered st…>
<method def count(unknown): ... # inherited from builtins.str.count, S.count(sub[, start[…>
<method def count(unknown): ... # inherited from builtins.str.count, Return the number of…>
<method def expandtabs(self, /, tabsize=8): ... # inherited from builtins.str.expandtabs, Return a copy where …>
<method def find(unknown): ... # inherited from builtins.str.find, S.find(sub[, start[,…>
<method def find(unknown): ... # inherited from builtins.str.find, Return the lowest in…>
<method def partition(self, sep, /): ... # inherited from builtins.str.partition, Partition the string…>
<method def index(unknown): ... # inherited from builtins.str.index, S.index(sub[, start[…>
<method def index(unknown): ... # inherited from builtins.str.index, Return the lowest in…>
<method def ljust(self, width, fillchar=' ', /): ... # inherited from builtins.str.ljust, Return a left-justif…>
<method def lower(self, /): ... # inherited from builtins.str.lower, Return a copy of the…>
<method def lstrip(self, chars=None, /): ... # inherited from builtins.str.lstrip, Return a copy of the…>
<method def rfind(unknown): ... # inherited from builtins.str.rfind, S.rfind(sub[, start[…>
<method def rindex(unknown): ... # inherited from builtins.str.rindex, S.rindex(sub[, start…>
<method def rfind(unknown): ... # inherited from builtins.str.rfind, Return the highest i…>
<method def rindex(unknown): ... # inherited from builtins.str.rindex, Return the highest i…>
<method def rjust(self, width, fillchar=' ', /): ... # inherited from builtins.str.rjust, Return a right-justi…>
<method def rstrip(self, chars=None, /): ... # inherited from builtins.str.rstrip, Return a copy of the…>
<method def rpartition(self, sep, /): ... # inherited from builtins.str.rpartition, Partition the string…>
Expand All @@ -61,8 +61,8 @@
<method def swapcase(self, /): ... # inherited from builtins.str.swapcase, Convert uppercase ch…>
<method def translate(self, table, /): ... # inherited from builtins.str.translate, Replace each charact…>
<method def upper(self, /): ... # inherited from builtins.str.upper, Return a copy of the…>
<method def startswith(unknown): ... # inherited from builtins.str.startswith, S.startswith(prefix[…>
<method def endswith(unknown): ... # inherited from builtins.str.endswith, S.endswith(suffix[, …>
<method def startswith(unknown): ... # inherited from builtins.str.startswith, Return True if the s…>
<method def endswith(unknown): ... # inherited from builtins.str.endswith, Return True if the s…>
<method def removeprefix(self, prefix, /): ... # inherited from builtins.str.removeprefix, Return a str with th…>
<method def removesuffix(self, suffix, /): ... # inherited from builtins.str.removesuffix, Return a str with th…>
<method def isascii(self, /): ... # inherited from builtins.str.isascii, Return True if all c…>
Expand All @@ -78,8 +78,8 @@
<method def isidentifier(self, /): ... # inherited from builtins.str.isidentifier, Return True if the s…>
<method def isprintable(self, /): ... # inherited from builtins.str.isprintable, Return True if the s…>
<method def zfill(self, width, /): ... # inherited from builtins.str.zfill, Pad a numeric string…>
<method def format(unknown): ... # inherited from builtins.str.format, S.format(*args, **kw…>
<method def format_map(unknown): ... # inherited from builtins.str.format_map, S.format_map(mapping…>
<method def format(self, /, *args, **kwargs): ... # inherited from builtins.str.format, Return a formatted v…>
<method def format_map(self, mapping, /): ... # inherited from builtins.str.format_map, Return a formatted v…>
<static def maketrans(unknown): ... # inherited from builtins.str.maketrans, Return a translation…>
>
>
Loading

0 comments on commit 8597593

Please sign in to comment.