Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ninja/signature/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,21 @@ def __init__(self, path: str, view_func: Callable[..., Any]) -> None:
self.has_kwargs = False

self.params = []

# Get list of arguments to ignore if _ninja_ignore_args is set
ignore_args = getattr(view_func, "_ninja_ignore_args", [])

for name, arg in self.signature.parameters.items():
if name == "request":
# TODO: maybe better assert that 1st param is request or check by type?
# maybe even have attribute like `has_request`
# so that users can ignore passing request if not needed
continue

if name in ignore_args:
# Skip arguments that should be ignored
continue

if arg.kind == arg.VAR_KEYWORD:
# Skipping **kwargs
self.has_kwargs = True
Expand Down
7 changes: 7 additions & 0 deletions ninja/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"is_debug_server",
"normalize_path",
"contribute_operation_callback",
"ignore_args",
]


Expand Down Expand Up @@ -71,3 +72,9 @@ def contribute_operation_args(
if not hasattr(func, "_ninja_contribute_args"):
func._ninja_contribute_args = [] # type: ignore
func._ninja_contribute_args.append((arg_name, arg_type, arg_source)) # type: ignore


def ignore_args(func: Callable[..., Any], *arg_names: str) -> None:
if not hasattr(func, "_ninja_ignore_args"):
func._ninja_ignore_args = [] # type: ignore
func._ninja_ignore_args.extend(arg_names) # type: ignore
48 changes: 48 additions & 0 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,51 @@ def dec_multi(request):
assert response.status_code == 200
assert response.json() == {"count": 4, "items": [1, 2, 3, 4]}
assert response["X-Decorator"] == "some_decorator"


class DependencyService:
def get_data(self):
return "injected_data"


def inject_dependency(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
kwargs["injected_service"] = DependencyService()
return view_func(request, *args, **kwargs)

wrapper._ninja_ignore_args = ["injected_service"]
return wrapper


def test_ninja_ignore_args_integration():
"""Integration test for _ninja_ignore_args functionality."""
api = NinjaAPI()

@api.get("/test-ignore/{item_id}")
@inject_dependency
def test_view(
request, item_id: int, query_param: str, injected_service: DependencyService
):
data = injected_service.get_data()
return {"item_id": item_id, "query_param": query_param, "injected_data": data}

# Test that the endpoint works correctly
client = TestClient(api)
response = client.get("/test-ignore/123?query_param=test")
assert response.status_code == 200
assert response.json() == {
"item_id": 123,
"query_param": "test",
"injected_data": "injected_data",
}

# Test that injected_service is not in the OpenAPI schema
schema = api.get_openapi_schema()
parameters = schema["paths"]["/api/test-ignore/{item_id}"]["get"]["parameters"]

# Should have item_id (path) and query_param (query), but not injected_service
param_names = [p["name"] for p in parameters]
assert "item_id" in param_names
assert "query_param" in param_names
assert "injected_service" not in param_names