Skip to content

Commit e69d693

Browse files
authored
Merge pull request #15 from gnir-work/feature/mask-numeric-path-parameters
Feature/mask numeric path parameters
2 parents 72a1c4e + 50ad5b3 commit e69d693

File tree

4 files changed

+40
-2
lines changed

4 files changed

+40
-2
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pip install monitored_ioloop[uvloop] # For the the additional support of the uv
1515
```
1616

1717
### Demo
18-
:pencil2: [Play with the demo in sandbox](https://codesandbox.io/p/devbox/monitored-ioloop-example-d924q4?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clt4i5fqk00063b6ii5cdc95p%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clt4i5fqk00023b6i5fk9mavr%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clt4i5fqk00043b6i2xok8884%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clt4i5fqk00053b6ijbk7icqr%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B100%252C0%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clt4i5fqk00023b6i5fk9mavr%2522%253A%257B%2522id%2522%253A%2522clt4i5fqk00023b6i5fk9mavr%2522%252C%2522tabs%2522%253A%255B%255D%257D%252C%2522clt4i5fqk00053b6ijbk7icqr%2522%253A%257B%2522id%2522%253A%2522clt4i5fqk00053b6ijbk7icqr%2522%252C%2522tabs%2522%253A%255B%255D%257D%252C%2522clt4i5fqk00043b6i2xok8884%2522%253A%257B%2522id%2522%253A%2522clt4i5fqk00043b6i2xok8884%2522%252C%2522activeTabId%2522%253A%2522clt4i5fqk00033b6i0vl1qm5r%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clt4i5fqk00033b6i0vl1qm5r%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522start%2522%257D%252C%257B%2522id%2522%253A%2522clt4i7i7h00d03b6incbja8w8%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522clt4i9ozn01d0d9hv8ftm7tt1%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Afalse%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D)
18+
📺 [Play with the demo in sandbox](https://codesandbox.io/p/devbox/monitored-ioloop-example-d924q4?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clt4i5fqk00063b6ii5cdc95p%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clt4i5fqk00023b6i5fk9mavr%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clt4i5fqk00043b6i2xok8884%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clt4i5fqk00053b6ijbk7icqr%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B100%252C0%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clt4i5fqk00023b6i5fk9mavr%2522%253A%257B%2522id%2522%253A%2522clt4i5fqk00023b6i5fk9mavr%2522%252C%2522tabs%2522%253A%255B%255D%257D%252C%2522clt4i5fqk00053b6ijbk7icqr%2522%253A%257B%2522id%2522%253A%2522clt4i5fqk00053b6ijbk7icqr%2522%252C%2522tabs%2522%253A%255B%255D%257D%252C%2522clt4i5fqk00043b6i2xok8884%2522%253A%257B%2522id%2522%253A%2522clt4i5fqk00043b6i2xok8884%2522%252C%2522activeTabId%2522%253A%2522clt4i5fqk00033b6i0vl1qm5r%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clt4i5fqk00033b6i0vl1qm5r%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522start%2522%257D%252C%257B%2522id%2522%253A%2522clt4i7i7h00d03b6incbja8w8%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522clt4i9ozn01d0d9hv8ftm7tt1%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Afalse%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D)
1919

2020

2121
### Usage

monitored_ioloop/helpers/fastapi.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,21 @@ class AsgiMiddlewareType(Protocol):
88
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: ...
99

1010

11+
def mask_numeric_segments(path: str) -> str:
12+
"""
13+
Example:
14+
mask_numeric_segments("/api/v1/users/123/name") -> "/api/v1/users/_/name"
15+
"""
16+
path_parts = path.split("/")
17+
return "/".join((part if not part.isdigit() else "_") for part in path_parts)
18+
19+
1120
def default_callback_pretty_name(scope: Scope) -> str:
1221
"""
1322
Default callback_pretty_name for FastAPI helper middleware.
1423
"""
15-
return f"[{scope['method']}] {scope['path']}"
24+
masked_path = mask_numeric_segments(scope["path"])
25+
return f"[{scope['method']}] {masked_path}"
1626

1727

1828
def get_monitor_async_io_middleware(

tests/helpers/test_fastapi.py

+9
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ def test_monitored_async_io_middleware__path_parameters_task_name(
100100
assert response.json() == "[GET] /path/parameters/test"
101101

102102

103+
@pytest.mark.usefixtures("default_monitoring_middleware")
104+
def test_monitored_async_io_middleware__path_parameters_with_numbers_are_masked_task_name(
105+
test_client: TestClient,
106+
) -> None:
107+
response = test_client.get("/path/parameters/1234")
108+
assert response.status_code == 200, response.text
109+
assert response.json() == "[GET] /path/parameters/_"
110+
111+
103112
@pytest.mark.usefixtures("default_monitoring_middleware")
104113
def test_monitored_async_io_middleware__post_method_task_name(
105114
test_client: TestClient,

tests/test_asyncio_profiler.py

+19
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,25 @@ def test_monitor_callback_error_is_handled(
5959
asyncio.run(blocking_coroutine(block_for))
6060

6161

62+
async def coroutine_with_result() -> int:
63+
await asyncio.sleep(0.1)
64+
return 10
65+
66+
67+
@pytest.mark.parametrize(
68+
"ioloop_policy_class",
69+
[MonitoredAsyncIOEventLoopPolicy, MonitoredUvloopEventLoopPolicy],
70+
)
71+
def test_callback_returns_value_even_if_monitor_callback_fails(
72+
ioloop_policy_class: typing.Type[MonitoredUvloopEventLoopPolicy],
73+
) -> None:
74+
asyncio.set_event_loop_policy(
75+
ioloop_policy_class(monitor_callback=monitor_callback_with_error)
76+
)
77+
result = asyncio.run(coroutine_with_result())
78+
assert result == 10
79+
80+
6281
async def complex_blocking_coroutine(block_for: float) -> None:
6382
busy_wait(block_for)
6483
await asyncio.sleep(1)

0 commit comments

Comments
 (0)