kopia lustrzana https://github.com/simonw/datasette
Include asyncio task information in /-/threads debug page
rodzic
2039e78e58
commit
d6b6c9171f
|
|
@ -2,6 +2,7 @@ import asyncio
|
|||
import collections
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
|
|
@ -477,12 +478,19 @@ class Datasette:
|
|||
|
||||
def threads(self):
|
||||
threads = list(threading.enumerate())
|
||||
return {
|
||||
d = {
|
||||
"num_threads": len(threads),
|
||||
"threads": [
|
||||
{"name": t.name, "ident": t.ident, "daemon": t.daemon} for t in threads
|
||||
],
|
||||
}
|
||||
# Only available in Python 3.7+
|
||||
if hasattr(asyncio, "all_tasks"):
|
||||
tasks = asyncio.all_tasks()
|
||||
d.update(
|
||||
{"num_tasks": len(tasks), "tasks": [_cleaner_task_str(t) for t in tasks]}
|
||||
)
|
||||
return d
|
||||
|
||||
def table_metadata(self, database, table):
|
||||
"Fetch table-specific metadata."
|
||||
|
|
@ -684,3 +692,14 @@ class DatasetteRouter(AsgiRouter):
|
|||
await asgi_send_html(
|
||||
send, await template.render_async(info), status=status, headers=headers
|
||||
)
|
||||
|
||||
|
||||
_cleaner_task_str_re = re.compile(r"\S*site-packages/")
|
||||
|
||||
|
||||
def _cleaner_task_str(task):
|
||||
s = str(task)
|
||||
# This has something like the following in it:
|
||||
# running at /Users/simonw/Dropbox/Development/datasette/venv-3.7.5/lib/python3.7/site-packages/uvicorn/main.py:361>
|
||||
# Clean up everything up to and including site-packages
|
||||
return _cleaner_task_str_re.sub("", s)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@
|
|||
{% block content %}
|
||||
<h1>{{ filename }}</h1>
|
||||
|
||||
<pre>{{ data|tojson(indent=4) }}</pre>
|
||||
<pre>{{ data_json }}</pre>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -27,5 +27,8 @@ class JsonDataView(BaseView):
|
|||
return await self.render(
|
||||
["show_json.html"],
|
||||
request=request,
|
||||
context={"filename": self.filename, "data": data},
|
||||
context={
|
||||
"filename": self.filename,
|
||||
"data_json": json.dumps(data, indent=4),
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ Shows currently attached databases. `Databases example <https://latest.datasette
|
|||
/-/threads
|
||||
----------
|
||||
|
||||
Shows details of threads. `Threads example <https://latest.datasette.io/-/threads>`_::
|
||||
Shows details of threads and ``asyncio`` tasks. `Threads example <https://latest.datasette.io/-/threads>`_::
|
||||
|
||||
{
|
||||
"num_threads": 2,
|
||||
|
|
@ -128,5 +128,11 @@ Shows details of threads. `Threads example <https://latest.datasette.io/-/thread
|
|||
"ident": 123145319682048,
|
||||
"name": "Thread-1"
|
||||
},
|
||||
],
|
||||
"num_tasks": 3,
|
||||
"tasks": [
|
||||
"<Task pending coro=<RequestResponseCycle.run_asgi() running at uvicorn/protocols/http/httptools_impl.py:385> cb=[set.discard()]>",
|
||||
"<Task pending coro=<Server.serve() running at uvicorn/main.py:361> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10365c3d0>()]> cb=[run_until_complete.<locals>.<lambda>()]>",
|
||||
"<Task pending coro=<LifespanOn.main() running at uvicorn/lifespan/on.py:48> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10364f050>()]>>"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from .fixtures import ( # noqa
|
|||
)
|
||||
import json
|
||||
import pytest
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
|
||||
|
|
@ -1229,6 +1230,14 @@ def test_metadata_json(app_client):
|
|||
assert METADATA == response.json
|
||||
|
||||
|
||||
def test_threads_json(app_client):
|
||||
response = app_client.get("/-/threads.json")
|
||||
expected_keys = {"threads", "num_threads"}
|
||||
if sys.version_info >= (3, 7, 0):
|
||||
expected_keys.update({"tasks", "num_tasks"})
|
||||
assert expected_keys == set(response.json.keys())
|
||||
|
||||
|
||||
def test_plugins_json(app_client):
|
||||
response = app_client.get("/-/plugins.json")
|
||||
assert [
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue