diff --git a/docs/build.py b/docs/build.py new file mode 100644 index 000000000..479841e80 --- /dev/null +++ b/docs/build.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +import shutil +import subprocess +from pathlib import Path + + +here = Path(__file__).parent + +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) + +if (here / "public").exists(): + shutil.rmtree(here / "public") +subprocess.run(["hugo"], cwd=here / "src") diff --git a/docs/build.sh b/docs/build.sh deleted file mode 100755 index eaeb87907..000000000 --- a/docs/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit -set -o pipefail -set -o nounset -# set -o xtrace - -SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -pushd ${SCRIPTPATH} - -for script in scripts/*.py ; do - output="${script##*/}" - output="src/generated/${output%.*}.html" - echo "Generating output for ${script} into ${output} ..." - "${script}" > "${output}" -done - -rm -rf ./public -cd src -hugo diff --git a/docs/modd.conf b/docs/modd.conf index 3f16cdb59..f3fd350bc 100644 --- a/docs/modd.conf +++ b/docs/modd.conf @@ -1,5 +1,5 @@ -scripts/*.py { - prep: ./build.sh +scripts/** { + prep: python3 build.py } { diff --git a/docs/scripts/api-events.py b/docs/scripts/api-events.py new file mode 100644 index 000000000..31b0f3301 --- /dev/null +++ b/docs/scripts/api-events.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +import contextlib +import inspect +import textwrap +from pathlib import Path +from typing import List, Type + +import mitmproxy.addons.next_layer # noqa +from mitmproxy import hooks, log, addonmanager +from mitmproxy.proxy import server_hooks, layer +from mitmproxy.proxy.layers import http, tcp, tls, websocket + +known = set() + + +def category(name: str, hooks: List[Type[hooks.Hook]]) -> None: + all_params = [ + list(inspect.signature(hook.__init__).parameters.values())[1:] + for hook in hooks + ] + + # slightly overengineered, but this was fun to write. ¯\_(ツ)_/¯ + imports = set() + types = set() + for params in all_params: + for param in params: + try: + mod = inspect.getmodule(param.annotation).__name__ + if mod == "typing": + # this is ugly, but can be removed once we are on Python 3.9+ only + imports.add(inspect.getmodule(param.annotation.__args__[0]).__name__) + types.add(param.annotation._name) + else: + imports.add(mod) + except AttributeError: + raise ValueError(f"Missing type annotation: {params}") + imports.discard("builtins") + if types: + print(f"from typing import {', '.join(sorted(types))}") + print("from mitmproxy import ctx") + for imp in sorted(imports): + print(f"import {imp}") + print() + + print(f"class {name}_Events:") + + first = True + for hook, params in zip(hooks, all_params): + if first: + first = False + else: + print() + if hook.name in known: + 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(textwrap.indent(f'"""\n{doc}\n"""', " ")) + if params: + print(f' ctx.log(f"{hook.name}: {" ".join("{" + p.name + "=}" for p in params)}")') + else: + print(f' ctx.log("{hook.name}")') + print("") + + +outfile = Path(__file__).parent.parent / "src" / "generated" / "events.py" + +with outfile.open("w") as f, contextlib.redirect_stdout(f): + + category( + "Lifecycle", + [ + addonmanager.LoadHook, + hooks.RunningHook, + hooks.ConfigureHook, + hooks.DoneHook, + ] + ) + + category( + "Connection", + [ + server_hooks.ClientConnectedHook, + server_hooks.ClientDisconnectedHook, + server_hooks.ServerConnectHook, + server_hooks.ServerConnectedHook, + server_hooks.ServerDisconnectedHook, + ] + ) + + category( + "HTTP", + [ + http.HttpRequestHeadersHook, + http.HttpRequestHook, + http.HttpResponseHeadersHook, + http.HttpResponseHook, + http.HttpErrorHook, + http.HttpConnectHook, + ] + ) + + category( + "TCP", + [ + tcp.TcpStartHook, + tcp.TcpMessageHook, + tcp.TcpEndHook, + tcp.TcpErrorHook, + ] + ) + + category( + "TLS", + [ + tls.TlsClienthelloHook, + tls.TlsStartHook, + ] + ) + + category( + "WebSocket", + [ + websocket.WebsocketStartHook, + websocket.WebsocketMessageHook, + websocket.WebsocketEndHook, + websocket.WebsocketErrorHook, + ] + ) + + category( + "Advanced_Lifecycle", + [ + layer.NextLayerHook, + hooks.UpdateHook, + log.AddLogHook, + ] + ) + +not_documented = set(hooks.all_hooks.keys()) - known +if not_documented: + raise RuntimeError(f"Not documented: {not_documented}") diff --git a/docs/scripts/api.py b/docs/scripts/api-render.py old mode 100755 new mode 100644 similarity index 84% rename from docs/scripts/api.py rename to docs/scripts/api-render.py index e2af27450..11f5714cc --- a/docs/scripts/api.py +++ b/docs/scripts/api-render.py @@ -20,6 +20,7 @@ pdoc.render.configure( ) modules = [ + here / ".." / "src" / "generated" / "events.py", "mitmproxy.proxy.context", "mitmproxy.http", "mitmproxy.flow", @@ -39,6 +40,8 @@ if api_content.exists(): api_content.mkdir() for module in modules: + if isinstance(module, Path): + continue filename = f"api/{ module.replace('.','/') }.html" (api_content / f"{module}.md").write_text(f""" --- @@ -47,20 +50,8 @@ url: "{filename}" menu: addons: - parent: 'API Reference' + parent: 'API' --- {{{{< readfile file="/generated/{filename}" >}}}} """) - -(api_content / f"_index.md").write_text(f""" ---- -title: "API Reference" -layout: single -menu: - addons: - weight: 5 ---- - -# API Reference -""") diff --git a/docs/scripts/events.py b/docs/scripts/events.py deleted file mode 100755 index 6462c6341..000000000 --- a/docs/scripts/events.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python3 -import inspect -import textwrap -from typing import List, Type - -import mitmproxy.addons.next_layer # noqa -from mitmproxy import hooks, log, addonmanager -from mitmproxy.proxy import server_hooks, layer -from mitmproxy.proxy.layers import http, tcp, tls, websocket - -known = set() - - -def category(name: str, hooks: List[Type[hooks.Hook]]) -> None: - print(f"### {name} Events") - print("```python") - - all_params = [ - list(inspect.signature(hook.__init__).parameters.values())[1:] - for hook in hooks - ] - - # slightly overengineered, but this was fun to write. ¯\_(ツ)_/¯ - imports = set() - types = set() - for params in all_params: - for param in params: - try: - mod = inspect.getmodule(param.annotation).__name__ - if mod == "typing": - # this is ugly, but can be removed once we are on Python 3.9+ only - imports.add(inspect.getmodule(param.annotation.__args__[0]).__name__) - types.add(param.annotation._name) - else: - imports.add(mod) - except AttributeError: - raise ValueError(f"Missing type annotation: {params}") - imports.discard("builtins") - if types: - print(f"from typing import {', '.join(sorted(types))}") - print("from mitmproxy import ctx") - for imp in sorted(imports): - print(f"import {imp}") - print() - - first = True - for hook, params in zip(hooks, all_params): - if first: - first = False - else: - print() - if hook.name in known: - 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(textwrap.indent(f'"""\n{doc}\n"""', " ")) - if params: - print(f' ctx.log(f"{hook.name}: {" ".join("{" + p.name + "=}" for p in params)}")') - else: - print(f' ctx.log("{hook.name}")') - print("```") - - -category( - "Lifecycle", - [ - addonmanager.LoadHook, - hooks.RunningHook, - hooks.ConfigureHook, - hooks.DoneHook, - ] -) - -category( - "Connection", - [ - server_hooks.ClientConnectedHook, - server_hooks.ClientDisconnectedHook, - server_hooks.ServerConnectHook, - server_hooks.ServerConnectedHook, - server_hooks.ServerDisconnectedHook, - ] -) - -category( - "HTTP", - [ - http.HttpRequestHeadersHook, - http.HttpRequestHook, - http.HttpResponseHeadersHook, - http.HttpResponseHook, - http.HttpErrorHook, - http.HttpConnectHook, - ] -) - -category( - "TCP", - [ - tcp.TcpStartHook, - tcp.TcpMessageHook, - tcp.TcpEndHook, - tcp.TcpErrorHook, - ] -) - -category( - "TLS", - [ - tls.TlsClienthelloHook, - tls.TlsStartHook, - ] -) - -category( - "WebSocket", - [ - websocket.WebsocketStartHook, - websocket.WebsocketMessageHook, - websocket.WebsocketEndHook, - websocket.WebsocketErrorHook, - ] -) - -category( - "Advanced Lifecycle", - [ - layer.NextLayerHook, - hooks.UpdateHook, - log.AddLogHook, - ] -) - -not_documented = set(hooks.all_hooks.keys()) - known -if not_documented: - raise RuntimeError(f"Not documented: {not_documented}") - -# print("
%s | %s |
---|