misc

  1import abc
  2from ctypes import Structure
  3from dataclasses import dataclass
  4import functools
  5from functools import cached_property
  6from functools import lru_cache
  7import sched
  8from typing import Generic
  9from typing import TypeVar
 10from typing import Union
 11
 12# https://github.com/mitmproxy/pdoc/issues/226
 13
 14
 15class Descriptor:
 16    def __init__(self, func):
 17        self.__doc__ = func.__doc__
 18
 19    def __get__(self, instance, owner):
 20        return self if instance is None else getattr(instance, "_x", 0)
 21
 22    def __set__(self, instance, value):
 23        instance._x = value
 24
 25
 26class Issue226:
 27    @Descriptor
 28    def size(self):
 29        """This is the size"""
 30
 31
 32# Testing function and object default values
 33
 34
 35def default_func():
 36    pass
 37
 38
 39default_obj = object()
 40
 41var_with_default_obj = default_obj
 42"""this shouldn't render the object address"""
 43var_with_default_func = default_func
 44"""this just renders like a normal function"""
 45
 46
 47def func_with_defaults(a=default_obj, b=default_func):
 48    """this shouldn't render object or function addresses"""
 49    pass
 50
 51
 52# Testing classmethod links in code
 53class ClassmethodLink:
 54    """
 55    You can either do
 56
 57    >>> ClassmethodLink.bar()
 58    42
 59
 60    or
 61
 62    ```python
 63    ClassmethodLink.bar()
 64    ```
 65
 66    neither will be linked.
 67    """
 68
 69    @classmethod
 70    def bar(cls):
 71        return 42
 72
 73
 74# Testing generic bases
 75
 76T = TypeVar("T")
 77
 78
 79class GenericParent(Generic[T]):
 80    """GenericParent"""
 81
 82
 83class NonGenericChild(GenericParent[str]):
 84    """NonGenericChild"""
 85
 86
 87# Testing docstring inheritance
 88
 89
 90class Base:
 91    def __init__(self):
 92        """init"""
 93        super().__init__()
 94
 95    def foo(self):
 96        """foo"""
 97        pass
 98
 99    @classmethod
100    def bar(cls):
101        """bar"""
102        pass
103
104    @staticmethod
105    def baz():
106        """baz"""
107        pass
108
109    @property
110    def qux(self):
111        """qux"""
112        return
113
114    @cached_property
115    def quux(self):
116        """quux"""
117        return
118
119    quuux: int = 42
120    """quuux"""
121
122
123class Child(Base):
124    def __init__(self):
125        super().__init__()
126
127    def foo(self):
128        pass
129
130    @classmethod
131    def bar(cls):
132        pass
133
134    @staticmethod
135    def baz():
136        pass
137
138    @property
139    def qux(self):
140        return
141
142    @cached_property
143    def quux(self):
144        return
145
146    quuux: int = 42
147
148
149# Testing that an attribute that is only annotated does not trigger a "submodule not found" warning.
150
151only_annotated: int
152
153
154# Testing that a private class in __all__ is displayed
155
156
157class _Private:
158    """private class"""
159
160    pass
161
162    def _do(self):
163        """private method"""
164
165
166# Testing a class attribute that is a lambda (which generates quirky sources)
167
168
169class LambdaAttr:
170    # not really supported, but also shouldn't crash.
171    attr = lambda x: 42  # noqa
172
173
174# Testing different docstring annotations
175# fmt: off
176
177
178def foo():
179    """no indents"""
180
181
182def bar():
183    """no
184indents"""
185
186
187def baz():
188    """one
189    indent"""
190
191
192def qux():
193    """
194    two
195    indents
196    """
197
198
199class Indented:
200    def foo(self):
201        """no indents"""
202
203    def bar(self):
204        """no
205indents"""
206
207    def baz(self):
208        """one
209        indent"""
210
211    def qux(self):
212        """
213        two
214        indents
215        """
216
217    @lru_cache()
218    def foo_decorated(self):
219        """no indents"""
220
221    @lru_cache()
222    # comment
223    def foo_commented(self):
224        """no indents"""
225
226    @lru_cache()
227    def bar_decorated(self):
228        """no
229indents"""
230
231    @lru_cache()
232    def baz_decorated(self):
233        """one
234        indent"""
235
236    @lru_cache()
237    def qux_decorated(self):
238        """
239        two
240        indents
241        """
242
243    @lru_cache(
244        maxsize=42
245    )
246    def quux_decorated(self):
247        """multi-line decorator, https://github.com/mitmproxy/pdoc/issues/246"""
248
249
250def _protected_decorator(f):
251    return f
252
253
254@_protected_decorator
255def fun_with_protected_decorator():
256    """This function has a protected decorator (name starting with a single `_`)."""
257
258
259class UnhashableDataDescriptor:
260    def __get__(self):
261        pass
262    __hash__ = None  # type: ignore
263
264
265unhashable = UnhashableDataDescriptor()
266
267
268class AbstractClass(metaclass=abc.ABCMeta):
269    """This class shouldn't show a constructor as it's abstract."""
270    @abc.abstractmethod
271    def foo(self):
272        pass
273
274
275# Adapted from https://github.com/mitmproxy/pdoc/issues/320
276
277def make_adder(a: int):
278    def add_func(b: int) -> int:
279        """This function adds two numbers."""
280        return a + b
281    return add_func
282
283
284add_four = make_adder(4)
285add_five = make_adder(5)
286"""This function adds five."""
287add_six = make_adder(6)
288add_six.__doc__ = "This function adds six."
289
290
291# Adapted from https://github.com/mitmproxy/pdoc/issues/335
292def linkify_links():
293    """
294    This docstring contains links that are also identifiers:
295
296    - [`linkify_links`](https://example.com/)
297    - [misc.linkify_links](https://example.com/)
298    - [`linkify_links()`](https://example.com/)
299    - [misc.linkify_links()](https://example.com/)
300    - [link in target](https://example.com/misc.linkify_links)
301    - [explicit linking](#AbstractClass.foo)
302    """
303
304
305class Issue352aMeta(type):
306    def __call__(cls, *args, **kwargs):
307        """Meta.__call__"""
308
309
310class Issue352a(metaclass=Issue352aMeta):
311    def __init__(self):
312        """Issue352.__init__ should be preferred over Meta.__call__."""
313
314
315class Issue352bMeta(type):
316    def __call__(cls, *args, **kwargs):
317        pass
318
319
320class Issue352b(metaclass=Issue352bMeta):
321    """No docstrings for the constructor here."""
322
323
324class CustomCallMeta(type):
325    def __call__(cls, *args, **kwargs):
326        """Custom docstring in metaclass.`__call__`"""
327
328
329class CustomCall(metaclass=CustomCallMeta):
330    """A class where the constructor is defined by its metaclass."""
331
332
333class Headings:
334    """
335    # Heading 1
336
337    Here is some text.
338
339    ## Heading 2
340
341    Here is some text.
342
343    ### Heading 3
344
345    Here is some text.
346
347    #### Heading 4
348
349    Here is some text.
350
351    ##### Heading 5
352
353    Here is some text.
354
355    ###### Heading 6
356
357    Here is some text.
358
359    """
360
361
362class CustomRepr:
363    def __repr__(self):
364        return "°<script>alert(1)</script>"
365
366
367def repr_not_syntax_highlightable(x=CustomRepr()):
368    """The default value for x fails to highlight with pygments."""
369
370
371class ClassDecorator:
372    """This is a class that wraps a function. It will be documented correctly."""
373    def __init__(self, func):
374        self._func = func
375
376
377@ClassDecorator
378def another_decorated_function(arg: str) -> str:
379    """This is another decorated function. It will not be documented correctly."""
380    raise NotImplementedError
381
382
383class SubclassRef:
384    class SubClass:
385        pass
386
387    def __init__(self, x: "SubClass"):
388        print(x)
389
390
391class ClassAsAttribute:
392    static_attr_to_class = ClassDecorator
393    """this is a static attribute that point to a Class (not an instance)"""
394
395    static_attr_to_instance = ClassDecorator(None)
396    """this is a static attribute that point to an instance"""
397
398
399class scheduler(sched.scheduler):
400    """Test for broken links for inherited methods, https://github.com/mitmproxy/pdoc/issues/490"""
401
402
403class __init__:
404    """https://github.com/mitmproxy/pdoc/issues/519"""
405
406
407def dynamically_modify_docstring1():
408    """this should **not** be the docstring."""
409
410
411def dynamically_modify_docstring2():
412    pass
413
414
415dynamically_modify_docstring1.__doc__ = "https://github.com/mitmproxy/pdoc/issues/536"
416dynamically_modify_docstring2.__doc__ = "https://github.com/mitmproxy/pdoc/issues/536"
417
418
419def _docstring_modifier(fn):
420    fn.__doc__ = "https://github.com/mitmproxy/pdoc/issues/536"
421    return fn
422
423
424@_docstring_modifier
425def dynamically_modify_docstring3():
426    """This should **not** be the docstring."""
427
428
429@_docstring_modifier
430def dynamically_modify_docstring4():
431    pass
432
433
434class DocstringFromNew:
435    def __new__(cls, *args, **kwargs):
436        """This is a class with a docstring inferred from `__new__`."""
437
438
439
440
441
442
443class SingleDispatchMethodExample:
444    @functools.singledispatchmethod
445    def fancymethod(self, str_or_int: Union[str, int]):
446        """A fancy method which is capable of handling either `str` or `int`.
447
448        :param str_or_int: string or integer to handle
449        """
450        raise NotImplementedError(f"{type(str_or_int)=} not implemented!")
451
452    @fancymethod.register
453    def fancymethod_handle_str(self, str_to_handle: str):
454        """Fancy method handles a string.
455
456        :param str_to_handle: string which will be handled
457        """
458        print(f"{type(str_to_handle)} = '{str_to_handle}")
459
460    @fancymethod.register
461    def _fancymethod_handle_int(self, int_to_handle: int):
462        """Fancy method handles int (not shown in doc).
463
464        :param int_to_handle: int which will be handled
465        """
466        print(f"{type(int_to_handle)} = '{int_to_handle:x}'")
467
468
469@dataclass(init=False)
470class DataclassStructure(Structure):
471    """DataclassStructure raises for `inspect.signature`."""
472
473
474__all__ = [
475    "Issue226",
476    "var_with_default_obj",
477    "var_with_default_func",
478    "func_with_defaults",
479    "ClassmethodLink",
480    "GenericParent",
481    "NonGenericChild",
482    "Child",
483    "only_annotated",
484    "_Private",
485    "LambdaAttr",
486    "foo",
487    "bar",
488    "baz",
489    "qux",
490    "Indented",
491    "fun_with_protected_decorator",
492    "unhashable",
493    "AbstractClass",
494    "add_four",
495    "add_five",
496    "add_six",
497    "linkify_links",
498    "Issue352a",
499    "Issue352b",
500    "CustomCall",
501    "Headings",
502    "repr_not_syntax_highlightable",
503    "ClassDecorator",
504    "another_decorated_function",
505    "SubclassRef",
506    "ClassAsAttribute",
507    "scheduler",
508    "__init__",
509    "dynamically_modify_docstring1",
510    "dynamically_modify_docstring2",
511    "dynamically_modify_docstring3",
512    "dynamically_modify_docstring4",
513    "DocstringFromNew",
514    "SingleDispatchMethodExample",
515    "DataclassStructure",
516]
class Issue226:
27class Issue226:
28    @Descriptor
29    def size(self):
30        """This is the size"""
size

This is the size

var_with_default_obj = <object object>

this shouldn't render the object address

def var_with_default_func():
36def default_func():
37    pass

this just renders like a normal function

def func_with_defaults(a=<object object>, b=<function default_func>):
48def func_with_defaults(a=default_obj, b=default_func):
49    """this shouldn't render object or function addresses"""
50    pass

this shouldn't render object or function addresses

class GenericParent(typing.Generic[~T]):
80class GenericParent(Generic[T]):
81    """GenericParent"""

GenericParent

class NonGenericChild(misc.GenericParent[str]):
84class NonGenericChild(GenericParent[str]):
85    """NonGenericChild"""

NonGenericChild

class Child(Base):
124class Child(Base):
125    def __init__(self):
126        super().__init__()
127
128    def foo(self):
129        pass
130
131    @classmethod
132    def bar(cls):
133        pass
134
135    @staticmethod
136    def baz():
137        pass
138
139    @property
140    def qux(self):
141        return
142
143    @cached_property
144    def quux(self):
145        return
146
147    quuux: int = 42
Child()
125    def __init__(self):
126        super().__init__()

init

def foo(self):
128    def foo(self):
129        pass

foo

@classmethod
def bar(cls):
131    @classmethod
132    def bar(cls):
133        pass

bar

@staticmethod
def baz():
135    @staticmethod
136    def baz():
137        pass

baz

qux
139    @property
140    def qux(self):
141        return

qux

quux
143    @cached_property
144    def quux(self):
145        return

quux

quuux: int = 42

quuux

only_annotated: int
class _Private:
158class _Private:
159    """private class"""
160
161    pass
162
163    def _do(self):
164        """private method"""

private class

class LambdaAttr:
170class LambdaAttr:
171    # not really supported, but also shouldn't crash.
172    attr = lambda x: 42  # noqa
def attr(x):
172    attr = lambda x: 42  # noqa
def foo():
179def foo():
180    """no indents"""

no indents

def bar():
183def bar():
184    """no
185indents"""

no indents

def baz():
188def baz():
189    """one
190    indent"""

one indent

def qux():
193def qux():
194    """
195    two
196    indents
197    """

two indents

class Indented:
200class Indented:
201    def foo(self):
202        """no indents"""
203
204    def bar(self):
205        """no
206indents"""
207
208    def baz(self):
209        """one
210        indent"""
211
212    def qux(self):
213        """
214        two
215        indents
216        """
217
218    @lru_cache()
219    def foo_decorated(self):
220        """no indents"""
221
222    @lru_cache()
223    # comment
224    def foo_commented(self):
225        """no indents"""
226
227    @lru_cache()
228    def bar_decorated(self):
229        """no
230indents"""
231
232    @lru_cache()
233    def baz_decorated(self):
234        """one
235        indent"""
236
237    @lru_cache()
238    def qux_decorated(self):
239        """
240        two
241        indents
242        """
243
244    @lru_cache(
245        maxsize=42
246    )
247    def quux_decorated(self):
248        """multi-line decorator, https://github.com/mitmproxy/pdoc/issues/246"""
def foo(self):
201    def foo(self):
202        """no indents"""

no indents

def bar(self):
204    def bar(self):
205        """no
206indents"""

no indents

def baz(self):
208    def baz(self):
209        """one
210        indent"""

one indent

def qux(self):
212    def qux(self):
213        """
214        two
215        indents
216        """

two indents

@lru_cache()
def foo_decorated(self):
218    @lru_cache()
219    def foo_decorated(self):
220        """no indents"""

no indents

@lru_cache()
def foo_commented(self):
222    @lru_cache()
223    # comment
224    def foo_commented(self):
225        """no indents"""

no indents

@lru_cache()
def bar_decorated(self):
227    @lru_cache()
228    def bar_decorated(self):
229        """no
230indents"""

no indents

@lru_cache()
def baz_decorated(self):
232    @lru_cache()
233    def baz_decorated(self):
234        """one
235        indent"""

one indent

@lru_cache()
def qux_decorated(self):
237    @lru_cache()
238    def qux_decorated(self):
239        """
240        two
241        indents
242        """

two indents

@lru_cache(maxsize=42)
def quux_decorated(self):
244    @lru_cache(
245        maxsize=42
246    )
247    def quux_decorated(self):
248        """multi-line decorator, https://github.com/mitmproxy/pdoc/issues/246"""
def fun_with_protected_decorator():
255@_protected_decorator
256def fun_with_protected_decorator():
257    """This function has a protected decorator (name starting with a single `_`)."""

This function has a protected decorator (name starting with a single _).

def unhashable(unknown):
class AbstractClass:
269class AbstractClass(metaclass=abc.ABCMeta):
270    """This class shouldn't show a constructor as it's abstract."""
271    @abc.abstractmethod
272    def foo(self):
273        pass

This class shouldn't show a constructor as it's abstract.

@abc.abstractmethod
def foo(self):
271    @abc.abstractmethod
272    def foo(self):
273        pass
def add_four(b: int) -> int:
279    def add_func(b: int) -> int:
280        """This function adds two numbers."""
281        return a + b

This function adds two numbers.

def add_five(b: int) -> int:
279    def add_func(b: int) -> int:
280        """This function adds two numbers."""
281        return a + b

This function adds five.

def add_six(b: int) -> int:
279    def add_func(b: int) -> int:
280        """This function adds two numbers."""
281        return a + b

This function adds six.

class Issue352a:
311class Issue352a(metaclass=Issue352aMeta):
312    def __init__(self):
313        """Issue352.__init__ should be preferred over Meta.__call__."""
Issue352a()
312    def __init__(self):
313        """Issue352.__init__ should be preferred over Meta.__call__."""

Issue352.__init__ should be preferred over Meta.__call__.

class Issue352b:
321class Issue352b(metaclass=Issue352bMeta):
322    """No docstrings for the constructor here."""

No docstrings for the constructor here.

class CustomCall:
330class CustomCall(metaclass=CustomCallMeta):
331    """A class where the constructor is defined by its metaclass."""

A class where the constructor is defined by its metaclass.

CustomCall(*args, **kwargs)
326    def __call__(cls, *args, **kwargs):
327        """Custom docstring in metaclass.`__call__`"""

Custom docstring in metaclass.__call__

class Headings:
334class Headings:
335    """
336    # Heading 1
337
338    Here is some text.
339
340    ## Heading 2
341
342    Here is some text.
343
344    ### Heading 3
345
346    Here is some text.
347
348    #### Heading 4
349
350    Here is some text.
351
352    ##### Heading 5
353
354    Here is some text.
355
356    ###### Heading 6
357
358    Here is some text.
359
360    """

Heading 1

Here is some text.

Heading 2

Here is some text.

Heading 3

Here is some text.

Heading 4

Here is some text.

Heading 5

Here is some text.

Heading 6

Here is some text.

def repr_not_syntax_highlightable(x=°<script>alert(1)</script>):
368def repr_not_syntax_highlightable(x=CustomRepr()):
369    """The default value for x fails to highlight with pygments."""

The default value for x fails to highlight with pygments.

class ClassDecorator:
372class ClassDecorator:
373    """This is a class that wraps a function. It will be documented correctly."""
374    def __init__(self, func):
375        self._func = func

This is a class that wraps a function. It will be documented correctly.

ClassDecorator(func)
374    def __init__(self, func):
375        self._func = func
another_decorated_function = <ClassDecorator object>

This is another decorated function. It will not be documented correctly.

class SubclassRef:
384class SubclassRef:
385    class SubClass:
386        pass
387
388    def __init__(self, x: "SubClass"):
389        print(x)
SubclassRef(x: SubclassRef.SubClass)
388    def __init__(self, x: "SubClass"):
389        print(x)
class SubclassRef.SubClass:
385    class SubClass:
386        pass
class ClassAsAttribute:
392class ClassAsAttribute:
393    static_attr_to_class = ClassDecorator
394    """this is a static attribute that point to a Class (not an instance)"""
395
396    static_attr_to_instance = ClassDecorator(None)
397    """this is a static attribute that point to an instance"""
static_attr_to_class = <class 'ClassDecorator'>

this is a static attribute that point to a Class (not an instance)

static_attr_to_instance = <ClassDecorator object>

this is a static attribute that point to an instance

class scheduler(sched.scheduler):
400class scheduler(sched.scheduler):
401    """Test for broken links for inherited methods, https://github.com/mitmproxy/pdoc/issues/490"""

Test for broken links for inherited methods, https://github.com/mitmproxy/pdoc/issues/490

class __init__:
404class __init__:
405    """https://github.com/mitmproxy/pdoc/issues/519"""
def dynamically_modify_docstring1():
408def dynamically_modify_docstring1():
409    """this should **not** be the docstring."""
def dynamically_modify_docstring2():
412def dynamically_modify_docstring2():
413    pass
def dynamically_modify_docstring3():
425@_docstring_modifier
426def dynamically_modify_docstring3():
427    """This should **not** be the docstring."""
def dynamically_modify_docstring4():
430@_docstring_modifier
431def dynamically_modify_docstring4():
432    pass
class DocstringFromNew:
435class DocstringFromNew:
436    def __new__(cls, *args, **kwargs):
437        """This is a class with a docstring inferred from `__new__`."""
DocstringFromNew(*args, **kwargs)
436    def __new__(cls, *args, **kwargs):
437        """This is a class with a docstring inferred from `__new__`."""

This is a class with a docstring inferred from __new__.

class SingleDispatchMethodExample:
444class SingleDispatchMethodExample:
445    @functools.singledispatchmethod
446    def fancymethod(self, str_or_int: Union[str, int]):
447        """A fancy method which is capable of handling either `str` or `int`.
448
449        :param str_or_int: string or integer to handle
450        """
451        raise NotImplementedError(f"{type(str_or_int)=} not implemented!")
452
453    @fancymethod.register
454    def fancymethod_handle_str(self, str_to_handle: str):
455        """Fancy method handles a string.
456
457        :param str_to_handle: string which will be handled
458        """
459        print(f"{type(str_to_handle)} = '{str_to_handle}")
460
461    @fancymethod.register
462    def _fancymethod_handle_int(self, int_to_handle: int):
463        """Fancy method handles int (not shown in doc).
464
465        :param int_to_handle: int which will be handled
466        """
467        print(f"{type(int_to_handle)} = '{int_to_handle:x}'")
@functools.singledispatchmethod
def fancymethod(self, str_or_int: Union[str, int]):
445    @functools.singledispatchmethod
446    def fancymethod(self, str_or_int: Union[str, int]):
447        """A fancy method which is capable of handling either `str` or `int`.
448
449        :param str_or_int: string or integer to handle
450        """
451        raise NotImplementedError(f"{type(str_or_int)=} not implemented!")

A fancy method which is capable of handling either str or int.

Parameters
  • str_or_int: string or integer to handle
@fancymethod.register
def fancymethod_handle_str(self, str_to_handle: str):
453    @fancymethod.register
454    def fancymethod_handle_str(self, str_to_handle: str):
455        """Fancy method handles a string.
456
457        :param str_to_handle: string which will be handled
458        """
459        print(f"{type(str_to_handle)} = '{str_to_handle}")

Fancy method handles a string.

Parameters
  • str_to_handle: string which will be handled
@dataclass(init=False)
class DataclassStructure(_ctypes.Structure):
470@dataclass(init=False)
471class DataclassStructure(Structure):
472    """DataclassStructure raises for `inspect.signature`."""

DataclassStructure raises for inspect.signature.