api docs++

This commit is contained in:
Maximilian Hils 2021-02-04 22:52:28 +01:00
parent a7d1f32c89
commit 51193f1d20
15 changed files with 188 additions and 113 deletions

View File

@ -10,7 +10,7 @@ for script in (here / "scripts").glob("*.py"):
print(f"Generating output for {script.name}...")
out = subprocess.check_output(["python3", script.absolute()], text=True)
if out:
(here / "src" / "generated" / f"{script.stem}.html").write_text(out)
(here / "src" / "generated" / f"{script.stem}.html").write_text(out, encoding="utf8")
if (here / "public").exists():
shutil.rmtree(here / "public")

View File

@ -13,7 +13,7 @@ from mitmproxy.proxy.layers import http, tcp, tls, websocket
known = set()
def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
def category(name: str, desc: str, hooks: List[Type[hooks.Hook]]) -> None:
all_params = [
list(inspect.signature(hook.__init__).parameters.values())[1:]
for hook in hooks
@ -42,7 +42,8 @@ def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
print(f"import {imp}")
print()
print(f"class {name}_Events:")
print(f"class {name}Events:")
print(f' """{desc}"""')
first = True
for hook, params in zip(hooks, all_params):
@ -54,7 +55,7 @@ def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
raise RuntimeError(f"Already documented: {hook}")
known.add(hook.name)
doc = inspect.getdoc(hook)
print(f" def {hook.name}({', '.join(str(p) for p in params)}):")
print(f" def {hook.name}({', '.join(str(p) for p in ['self'] + params)}):")
print(textwrap.indent(f'"""\n{doc}\n"""', " "))
if params:
print(f' ctx.log(f"{hook.name}: {" ".join("{" + p.name + "=}" for p in params)}")')
@ -66,9 +67,11 @@ def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
outfile = Path(__file__).parent.parent / "src" / "generated" / "events.py"
with outfile.open("w") as f, contextlib.redirect_stdout(f):
print("# This file is autogenerated, do not edit manually.")
category(
"Lifecycle",
"",
[
addonmanager.LoadHook,
hooks.RunningHook,
@ -79,6 +82,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"Connection",
"",
[
server_hooks.ClientConnectedHook,
server_hooks.ClientDisconnectedHook,
@ -90,6 +94,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"HTTP",
"",
[
http.HttpRequestHeadersHook,
http.HttpRequestHook,
@ -102,6 +107,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"TCP",
"",
[
tcp.TcpStartHook,
tcp.TcpMessageHook,
@ -112,6 +118,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"TLS",
"",
[
tls.TlsClienthelloHook,
tls.TlsStartHook,
@ -120,6 +127,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"WebSocket",
"",
[
websocket.WebsocketStartHook,
websocket.WebsocketMessageHook,
@ -129,7 +137,8 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
)
category(
"Advanced_Lifecycle",
"AdvancedLifecycle",
"",
[
layer.NextLayerHook,
hooks.UpdateHook,

View File

@ -3,7 +3,7 @@ import os
import shutil
from pathlib import Path
import pdoc.render
import pdoc.render_helpers
here = Path(__file__).parent
@ -18,14 +18,19 @@ pdoc.render.configure(
template_directory=here / "pdoc-template",
edit_url_map=edit_url_map,
)
# We can't configure Hugo, but we can configure pdoc.
pdoc.render_helpers.formatter.cssclass = "chroma"
modules = [
here / ".." / "src" / "generated" / "events.py",
"mitmproxy.proxy.context",
"mitmproxy.http",
"mitmproxy.addonmanager",
"mitmproxy.certs",
#"mitmproxy.connection",
"mitmproxy.flow",
"mitmproxy.http",
"mitmproxy.proxy.server_hooks",
"mitmproxy.tcp",
"mitmproxy.websocket",
here / ".." / "src" / "generated" / "events.py",
]
pdoc.pdoc(
@ -42,7 +47,7 @@ api_content.mkdir()
for module in modules:
if isinstance(module, Path):
continue
filename = f"api/{ module.replace('.','/') }.html"
filename = f"api/{module.replace('.', '/')}.html"
(api_content / f"{module}.md").write_text(f"""
---
title: "{module}"
@ -55,3 +60,5 @@ menu:
{{{{< readfile file="/generated/{filename}" >}}}}
""")
(here / ".." / "src" / "content" / "addons-api.md").touch()

View File

@ -31,7 +31,7 @@ for example in examples:
f" * [{example.name}](#{slug}){comment}"
)
listings.append(f"""
<h2 id="{slug}">Example: {example.name}</h2>
<h3 id="{slug}">Example: {example.name}</h3>
```python
{code}

View File

@ -1,25 +1,20 @@
{% extends "default/module.html.jinja2" %}
{% block nav %}{% endblock %}
{% block style_layout %}{% endblock %}
{% block style_pygments %}{% endblock %}
{#
We do a bit of hackery here: generated/events.py is automatically created by scripts/api-events.py,
and then documented using a heavily customized style.
To document all event hooks, we do a bit of hackery:
1. scripts/api-events.py auto-generates generated/events.py.
2. scripts/api-render.py renders generated/events.py together with the remaining API docs.
3. This templates hides some elements of the default pdoc template.
#}
{% if module.name == "events" %}
{% macro module_name() %}
<style type="text/css">
.pdoc .classattr {
margin-left: 0 !important;
}
</style>
{% endmacro %}
{% macro view_source(doc) %}
{% if doc.type == "function" %}
<details>
<summary>View Source</summary>
{{ doc.source | dedent | highlight }}
</details>
{% if doc.type != "module" %}
{{ default_view_source(doc) }}
{% endif %}
{% endmacro %}
{% macro is_public(doc) %}
@ -27,10 +22,18 @@ and then documented using a heavily customized style.
{{ default_is_public(doc) }}
{% endif %}
{% endmacro %}
{% macro class(cls) %}
<h3>
{{ headerlink(cls) }}
<strong>{{ cls.name.replace("_", " ") }}</strong>
</h3>
{% else %}
{% macro is_public(doc) %}
{% if doc.modulename == "mitmproxy.addonmanager" %}
{% if doc.qualname.startswith("Loader") and not doc.name.startswith("_") %}
true
{% endif %}
{% elif doc.modulename == "mitmproxy.certs" %}
{% if doc.qualname == "Cert" or doc.qualname.startswith("Cert.") %}
{{ default_is_public(doc) }}
{% endif %}
{% elif doc.name is not in(["from_state", "get_state", "set_state"]) %}
{{ default_is_public(doc) }}
{% endif %}
{% endmacro %}
{% endif %}

View File

@ -1,4 +1,12 @@
@import "./syntax";
/* background for both hugo *and* pdoc. */
.chroma pre, pre.chroma {
background-color: #f7f7f7;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
padding: .5rem 0 .5rem .5rem;
}
@import "./badge";
$primary: #C93312;
@ -20,6 +28,10 @@ bulma.io v0.8.0 | MIT License | github.com/jgthms/bulma */
@import "./bulma/components/_all";
@import "./bulma/layout/_all";
html {
scroll-behavior: smooth;
}
html, body {
height: 100%;
}

View File

@ -1,59 +1,82 @@
/* Background */ .chroma { color: #f8f8f2; background-color: #272822 }
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
/* Background */ .chroma { background-color: #ffffff }
/* Other */ .chroma .x { }
/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: 100%; overflow: auto; display: block; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; display: block; }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; }
/* Keyword */ .chroma .k { color: #66d9ef }
/* KeywordConstant */ .chroma .kc { color: #66d9ef }
/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
/* KeywordReserved */ .chroma .kr { color: #66d9ef }
/* KeywordType */ .chroma .kt { color: #66d9ef }
/* NameAttribute */ .chroma .na { color: #a6e22e }
/* NameClass */ .chroma .nc { color: #a6e22e }
/* NameConstant */ .chroma .no { color: #66d9ef }
/* NameDecorator */ .chroma .nd { color: #a6e22e }
/* NameException */ .chroma .ne { color: #a6e22e }
/* NameFunction */ .chroma .nf { color: #a6e22e }
/* NameOther */ .chroma .nx { color: #a6e22e }
/* NameTag */ .chroma .nt { color: #f92672 }
/* Literal */ .chroma .l { color: #ae81ff }
/* LiteralDate */ .chroma .ld { color: #e6db74 }
/* LiteralString */ .chroma .s { color: #e6db74 }
/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
/* LiteralNumber */ .chroma .m { color: #ae81ff }
/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
/* Operator */ .chroma .o { color: #f92672 }
/* OperatorWord */ .chroma .ow { color: #f92672 }
/* Comment */ .chroma .c { color: #75715e }
/* CommentHashbang */ .chroma .ch { color: #75715e }
/* CommentMultiline */ .chroma .cm { color: #75715e }
/* CommentSingle */ .chroma .c1 { color: #75715e }
/* CommentSpecial */ .chroma .cs { color: #75715e }
/* CommentPreproc */ .chroma .cp { color: #75715e }
/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
/* GenericDeleted */ .chroma .gd { color: #f92672 }
/* GenericEmph */ .chroma .ge { font-style: italic }
/* GenericInserted */ .chroma .gi { color: #a6e22e }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Keyword */ .chroma .k { color: #000000; font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: #000000; font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: #000000; font-weight: bold }
/* KeywordNamespace */ .chroma .kn { color: #000000; font-weight: bold }
/* KeywordPseudo */ .chroma .kp { color: #000000; font-weight: bold }
/* KeywordReserved */ .chroma .kr { color: #000000; font-weight: bold }
/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold }
/* Name */ .chroma .n { }
/* NameAttribute */ .chroma .na { color: #008080 }
/* NameBuiltin */ .chroma .nb { color: #0086b3 }
/* NameBuiltinPseudo */ .chroma .bp { color: #999999 }
/* NameClass */ .chroma .nc { color: #445588; font-weight: bold }
/* NameConstant */ .chroma .no { color: #008080 }
/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold }
/* NameEntity */ .chroma .ni { color: #800080 }
/* NameException */ .chroma .ne { color: #990000; font-weight: bold }
/* NameFunction */ .chroma .nf { color: #990000; font-weight: bold }
/* NameFunctionMagic */ .chroma .fm { }
/* NameLabel */ .chroma .nl { color: #990000; font-weight: bold }
/* NameNamespace */ .chroma .nn { color: #555555 }
/* NameOther */ .chroma .nx { }
/* NameProperty */ .chroma .py { }
/* NameTag */ .chroma .nt { color: #000080 }
/* NameVariable */ .chroma .nv { color: #008080 }
/* NameVariableClass */ .chroma .vc { color: #008080 }
/* NameVariableGlobal */ .chroma .vg { color: #008080 }
/* NameVariableInstance */ .chroma .vi { color: #008080 }
/* NameVariableMagic */ .chroma .vm { }
/* Literal */ .chroma .l { }
/* LiteralDate */ .chroma .ld { }
/* LiteralString */ .chroma .s { color: #dd1144 }
/* LiteralStringAffix */ .chroma .sa { color: #dd1144 }
/* LiteralStringBacktick */ .chroma .sb { color: #dd1144 }
/* LiteralStringChar */ .chroma .sc { color: #dd1144 }
/* LiteralStringDelimiter */ .chroma .dl { color: #dd1144 }
/* LiteralStringDoc */ .chroma .sd { color: #dd1144 }
/* LiteralStringDouble */ .chroma .s2 { color: #dd1144 }
/* LiteralStringEscape */ .chroma .se { color: #dd1144 }
/* LiteralStringHeredoc */ .chroma .sh { color: #dd1144 }
/* LiteralStringInterpol */ .chroma .si { color: #dd1144 }
/* LiteralStringOther */ .chroma .sx { color: #dd1144 }
/* LiteralStringRegex */ .chroma .sr { color: #009926 }
/* LiteralStringSingle */ .chroma .s1 { color: #dd1144 }
/* LiteralStringSymbol */ .chroma .ss { color: #990073 }
/* LiteralNumber */ .chroma .m { color: #009999 }
/* LiteralNumberBin */ .chroma .mb { color: #009999 }
/* LiteralNumberFloat */ .chroma .mf { color: #009999 }
/* LiteralNumberHex */ .chroma .mh { color: #009999 }
/* LiteralNumberInteger */ .chroma .mi { color: #009999 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #009999 }
/* LiteralNumberOct */ .chroma .mo { color: #009999 }
/* Operator */ .chroma .o { color: #000000; font-weight: bold }
/* OperatorWord */ .chroma .ow { color: #000000; font-weight: bold }
/* Punctuation */ .chroma .p { }
/* Comment */ .chroma .c { color: #999988; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic }
/* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold; font-style: italic }
/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold; font-style: italic }
/* Generic */ .chroma .g { }
/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd }
/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic }
/* GenericError */ .chroma .gr { color: #aa0000 }
/* GenericHeading */ .chroma .gh { color: #999999 }
/* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd }
/* GenericOutput */ .chroma .go { color: #888888 }
/* GenericPrompt */ .chroma .gp { color: #555555 }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #75715e }
/* GenericSubheading */ .chroma .gu { color: #aaaaaa }
/* GenericTraceback */ .chroma .gt { color: #aa0000 }
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
/* TextWhitespace */ .chroma .w { color: #bbbbbb }

View File

@ -5,6 +5,7 @@ theme = "mitmproxydocs"
publishDir = "../public"
RelativeURLs = true
pygmentsCodefences = true
pygmentsUseClasses = true
[indexes]
tag = "tags"

View File

@ -25,7 +25,8 @@ header with a count of the number of responses seen:
{{< example src="examples/addons/http-add-header.py" lang="py" >}}
## Addon Events
## Example Addons
The following addons showcase all available event hooks.
{{< readfile file="/generated/api/events.html" >}}

View File

@ -1,6 +1,6 @@
---
title: "Example Addons"
title: "Examples"
menu:
addons:
weight: 6

View File

@ -7,13 +7,11 @@ menu:
# Addons
Mitmproxy's addon mechanism consists of a set of APIs that support components of
any complexity. Addons interact with mitmproxy by responding to **events**,
which allow them to hook into and change mitmproxy's behaviour. They are
configured through **[options]({{< relref concepts-options >}})**, which can be
set in mitmproxy's config file, changed interactively by users, or passed on the
command-line. Finally, they can expose **commands**, which allows users to
invoke their actions either directly or by binding them to keys in the
Mitmproxy's addon mechanism consists of a set of APIs that support components of any complexity. Addons interact with
mitmproxy by responding to [events]({{< relref addons-api >}}), which allow them to hook into and change mitmproxy's
behaviour. They are configured through [options]({{< relref addons-options >}}), which can be set in mitmproxy's config
file, changed interactively by users, or passed on the command-line. Finally, they can expose [commands]({{< relref
addons-commands >}}), which allows users to invoke their actions either directly or by binding them to keys in the
interactive tools.
Addons are an exceptionally powerful part of mitmproxy. In fact, much of
@ -30,16 +28,16 @@ functionality to third-party scripters and extenders.
This document will show you how to build addons using **events**, **options**
and **commands**. However, this is not an API manual, and the mitmproxy source
code remains the canonical reference. One easy way to explore the API from the
command-line is to use [pydoc](https://docs.python.org/3/library/pydoc.html).
command-line is to use [pdoc](https://pdoc.dev/).
Here, for example, is a command that shows the API documentation for the
mitmproxy's HTTP flow classes:
```bash
pydoc mitmproxy.http
pdoc mitmproxy.http
```
You will be referring to the mitmproxy API documentation frequently, so keep
**pydoc** or an equivalent handy.
**pdoc** or an equivalent handy.
# Anatomy of an addon
@ -55,7 +53,7 @@ it into your mitmproxy tool of choice. We'll use mitmpdump in these examples,
but the flag is identical for all tools:
```bash
> mitmdump -s ./anatomy.py
mitmdump -s ./anatomy.py
```
Here are a few things to note about the code above:
@ -63,10 +61,9 @@ Here are a few things to note about the code above:
- Mitmproxy picks up the contents of the `addons` global list and loads what it
finds into the addons mechanism.
- Addons are just objects - in this case our addon is an instance of `Counter`.
- The `request` method is an example of an **event**. Addons simply implement a
method for each event they want to handle. Each event has a signature
consisting of arguments that are passed to the method. For `request`, this is
an instance of `mitmproxy.http.HTTPFlow`.
- The `request` method is an example of an *event*. Addons simply implement a
method for each event they want to handle. Each event and its signature are documented
in the [API documentation]({{< relref "addons-api" >}}).
- Finally, the `ctx` module is a holdall module that exposes a set of standard
objects that are commonly used in addons. We could pass a `ctx` object as the
first parameter to every event, but we've found it neater to just expose it as

View File

@ -0,0 +1,11 @@
---
title: "mitmproxy.addonmanager"
url: "api/mitmproxy/addonmanager.html"
menu:
addons:
parent: 'API'
---
{{< readfile file="/generated/api/mitmproxy/addonmanager.html" >}}

View File

@ -0,0 +1,11 @@
---
title: "mitmproxy.certs"
url: "api/mitmproxy/certs.html"
menu:
addons:
parent: 'API'
---
{{< readfile file="/generated/api/mitmproxy/certs.html" >}}

View File

@ -1,11 +0,0 @@
---
title: "mitmproxy.proxy.context"
url: "api/mitmproxy/proxy/context.html"
menu:
addons:
parent: 'API'
---
{{< readfile file="/generated/api/mitmproxy/proxy/context.html" >}}

View File

@ -0,0 +1,11 @@
---
title: "mitmproxy.proxy.server_hooks"
url: "api/mitmproxy/proxy/server_hooks.html"
menu:
addons:
parent: 'API'
---
{{< readfile file="/generated/api/mitmproxy/proxy/server_hooks.html" >}}