💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
This report summarizes scanner evidence for vitalik/django-ninja, a Python project with 9,207 GitHub stars. SynthScan examined 40,697 lines of code across 255 source files, recording 482 pattern matches distributed across 15 syntactic categories. The legacy adjusted pattern signal is 13.1 (Moderate pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 381 finding(s).
Are imports declared and package/API names verified? 58 finding(s).
Are wrappers, guards, and repeated structures necessary? 387 finding(s).
Which changes need focused human review? 482 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 482 distinct pattern matches across 15 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_csrf.py | 71 | def obtain_csrf_token_get_no_auth_route(request): | CODE |
| LOW | tests/test_csrf.py | 78 | def obtain_csrf_token_post_no_auth_route(request): | CODE |
| LOW | tests/test_csrf.py | 145 | def test_csrf_cookies_can_be_obtained(): | CODE |
| LOW | tests/test_filter_schema.py | 33 | def test_annotated_without_filter_lookup(): | CODE |
| LOW | tests/test_filter_schema.py | 44 | def test_improperly_configured_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 55 | def test_improperly_configured_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 66 | def test_empty_q_when_none_ignored_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 78 | def test_empty_q_when_none_ignored_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 149 | def test_q_is_a_list_deprecated(implicit_field_name): | CODE |
| LOW | tests/test_filter_schema.py | 168 | def test_q_is_a_list_annotated(implicit_field_name): | CODE |
| LOW | tests/test_filter_schema.py | 188 | def test_field_level_expression_connector_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 205 | def test_field_level_expression_connector_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 225 | def test_class_level_expression_connector_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 239 | def test_class_level_expression_connector_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 253 | def test_class_level_and_field_level_expression_connector_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 272 | def test_class_level_and_field_level_expression_connector_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 294 | def test_ignore_none_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 305 | def test_ignore_none_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 316 | def test_ignore_none_class_level_deprecated(): | CODE |
| LOW | tests/test_filter_schema.py | 330 | def test_ignore_none_class_level_annotated(): | CODE |
| LOW | tests/test_filter_schema.py | 344 | def test_field_level_custom_expression(): | CODE |
| LOW | tests/test_filter_schema.py | 367 | def test_class_level_custom_expression(): | CODE |
| LOW | tests/test_filter_schema.py | 393 | def test_multiple_filter_lookup_instances_error(): | CODE |
| LOW | tests/test_filter_schema.py | 406 | def test_pydantic_field_with_extra_warns(): | CODE |
| LOW | tests/test_export_openapi_schema.py | 50 | def test_export_default_without_api_endpoint(mock): | CODE |
| LOW | tests/test_inheritance_routers.py | 86 | def test_inheritance_responses(path, expected_status, expected_response): | CODE |
| LOW | tests/test_utils.py | 18 | def test_replace_path_param_notation(input, expected_output): | CODE |
| LOW | tests/test_utils.py | 22 | def test_contribute_operation_args(): | CODE |
| LOW | tests/test_pagination.py | 620 | def test_limit_offset_per_endpoint_max_limit(): | CODE |
| LOW | tests/test_pagination.py | 667 | def test_pagination_works_with_unnamed_classes(): | CODE |
| LOW | tests/test_pagination.py | 680 | def test_no_pagination_without_query_params(): | CODE |
| LOW | tests/test_pagination.py | 707 | def test_find_collection_response_skips_none_and_not_set(): | CODE |
| LOW | tests/test_pagination.py | 730 | def test_find_collection_response_raises_when_no_collection(): | CODE |
| LOW | tests/test_body.py | 59 | def test_body_validation_error(): | CODE |
| LOW | tests/test_body.py | 75 | def test_incorrect_annotation(): | CODE |
| LOW | tests/test_body.py | 90 | def validation_error_from_error_contexts( | CODE |
| LOW | tests/test_body.py | 118 | def test_body_custom_validation_error(): | CODE |
| LOW | tests/test_response.py | 202 | def test_pydantic_httpurl_schema(): | CODE |
| LOW | tests/test_annotations_py314.py | 38 | def test_paginated_route_with_deferred_annotations(): | CODE |
| LOW | tests/test_annotations_py314.py | 56 | def test_schema_with_self_referencing_resolver_annotation(): | CODE |
| LOW | tests/test_compatibility.py | 14 | def test_modelschema_custom_annotation(): | CODE |
| LOW | tests/test_compatibility.py | 34 | def test_modelschema_fk_schema_annotation(): | CODE |
| LOW | tests/test_openapi_schema.py | 165 | def method_union_payload_and_simple(request, data: Union[int, TypeB]): | CODE |
| LOW | tests/test_openapi_schema.py | 183 | def method_test_title_description( | CODE |
| LOW | tests/test_openapi_schema.py | 193 | def method_test_deprecated_example_examples( | CODE |
| LOW | tests/test_openapi_schema.py | 698 | def test_schema_title_description(schema): | CODE |
| LOW | tests/test_openapi_schema.py | 758 | def test_schema_deprecated_example_examples(schema): | CODE |
| LOW | tests/test_openapi_schema.py | 836 | def test_union_payload_simple(schema): | CODE |
| LOW | tests/test_openapi_schema.py | 860 | def test_new_union_payload_type(schema): | CODE |
| LOW | tests/test_openapi_schema.py | 895 | def test_unique_operation_ids(capsys): | CODE |
| LOW | tests/test_openapi_schema.py | 982 | def test_all_paths_typed_params_rendered(): | CODE |
| LOW | tests/test_openapi_schema.py | 1012 | def test_no_default_for_custom_items_attribute(): | CODE |
| LOW | tests/test_openapi_schema.py | 1047 | def test_by_alias_uses_serialization_alias_simple(): | CODE |
| LOW | tests/test_openapi_schema.py | 1072 | def test_by_alias_uses_validation_alias_simple(): | CODE |
| LOW | tests/test_openapi_schema.py | 1098 | def test_by_alias_uses_serialization_alias_model(): | CODE |
| LOW | tests/test_forms_and_files.py | 71 | def test_empty_multipart_body_field(): | CODE |
| LOW | tests/test_pydantic_migrate.py | 23 | def test_optional_pydantic_model(): | CODE |
| LOW | tests/test_orm_schemas.py | 376 | def test_textfield_max_length_validates_input_only(): | CODE |
| LOW | tests/test_orm_schemas.py | 405 | def test_textfield_max_length_api_flow(): | CODE |
| LOW | tests/test_orm_schemas.py | 603 | def test_duplicate_schema_names(): | CODE |
| 254 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/__init__.py | 5 | CODE | |
| LOW | ninja/__init__.py | 7 | CODE | |
| LOW | ninja/__init__.py | 8 | CODE | |
| LOW | ninja/__init__.py | 8 | CODE | |
| LOW | ninja/__init__.py | 8 | CODE | |
| LOW | ninja/__init__.py | 9 | CODE | |
| LOW | ninja/__init__.py | 10 | CODE | |
| LOW | ninja/__init__.py | 10 | CODE | |
| LOW | ninja/__init__.py | 11 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 12 | CODE | |
| LOW | ninja/__init__.py | 29 | CODE | |
| LOW | ninja/__init__.py | 30 | CODE | |
| LOW | ninja/__init__.py | 31 | CODE | |
| LOW | ninja/__init__.py | 32 | CODE | |
| LOW | ninja/__init__.py | 33 | CODE | |
| LOW | ninja/__init__.py | 33 | CODE | |
| LOW | ninja/errors.py | 13 | CODE | |
| LOW | ninja/errors.py | 14 | CODE | |
| LOW | ninja/router.py | 30 | CODE | |
| LOW | ninja/webhooks.py | 9 | CODE | |
| LOW | ninja/main.py | 39 | CODE | |
| LOW | ninja/signature/__init__.py | 1 | CODE | |
| LOW | ninja/signature/__init__.py | 2 | CODE | |
| LOW | ninja/security/__init__.py | 1 | CODE | |
| LOW | ninja/security/__init__.py | 1 | CODE | |
| LOW | ninja/security/__init__.py | 1 | CODE | |
| LOW | ninja/security/__init__.py | 2 | CODE | |
| LOW | ninja/security/__init__.py | 2 | CODE | |
| LOW | ninja/openapi/__init__.py | 1 | CODE | |
| LOW | ninja/openapi/docs.py | 16 | CODE | |
| LOW | ninja/openapi/urls.py | 9 | CODE | |
| LOW | ninja/openapi/views.py | 10 | CODE | |
| LOW | ninja/openapi/schema.py | 29 | CODE | |
| LOW | ninja/testing/__init__.py | 1 | CODE | |
| LOW | ninja/testing/__init__.py | 1 | CODE | |
| LOW | ninja/params/models.py | 24 | CODE | |
| LOW | ninja/orm/__init__.py | 1 | CODE | |
| LOW | ninja/orm/__init__.py | 2 | CODE | |
| LOW | ninja/orm/__init__.py | 3 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/files.py | 6 | __all__ = ["UploadedFile"] | CODE |
| LOW | ninja/responses.py | 10 | __all__ = [ | CODE |
| LOW | ninja/constants.py | 3 | __all__ = ["NOT_SET"] | CODE |
| LOW | ninja/__init__.py | 35 | __all__ = [ | CODE |
| LOW | ninja/renderers.py | 8 | __all__ = ["BaseRenderer", "JSONRenderer"] | CODE |
| LOW | ninja/types.py | 5 | __all__ = ["DictStrAny", "TCallable", "TSchemaClass"] | CODE |
| LOW | ninja/operation.py | 51 | __all__ = ["Operation", "PathView", "ResponseObject"] | CODE |
| LOW | ninja/parser.py | 9 | __all__ = ["Parser"] | CODE |
| LOW | ninja/utils.py | 7 | __all__ = [ | CODE |
| LOW | ninja/errors.py | 16 | __all__ = [ | CODE |
| LOW | ninja/errors.py | 87 | def set_default_exc_handlers(api: "NinjaAPI") -> None: | CODE |
| LOW | ninja/router.py | 33 | __all__ = ["Router", "RouterMount", "BoundRouter"] | CODE |
| LOW | ninja/webhooks.py | 11 | __all__ = ["Webhook", "get_webhooks"] | CODE |
| LOW | ninja/streaming.py | 8 | __all__ = ["StreamFormat", "SSE", "JSONL"] | CODE |
| LOW | ninja/main.py | 41 | __all__ = ["NinjaAPI"] | CODE |
| LOW⚡ | ninja/main.py | 664 | def set_default_exception_handlers(self) -> None: | CODE |
| LOW | ninja/schema.py | 48 | __all__ = ["BaseModel", "Field", "DjangoGetter", "Schema"] | CODE |
| LOW | ninja/compatibility/util.py | 4 | __all__ = ["UNION_TYPES", "get_annotations_from_namespace"] | CODE |
| LOW | ninja/signature/__init__.py | 4 | __all__ = ["ViewSignature", "is_async"] | CODE |
| LOW | ninja/signature/details.py | 30 | __all__ = [ | CODE |
| LOW | ninja/signature/utils.py | 13 | __all__ = [ | CODE |
| LOW | ninja/security/session.py | 8 | __all__ = ["SessionAuth", "SessionAuthSuperUser", "SessionAuthIsStaff"] | CODE |
| LOW | ninja/security/__init__.py | 5 | __all__ = [ | CODE |
| LOW | ninja/security/http.py | 11 | __all__ = ["HttpAuthBase", "HttpBearer", "DecodeError", "HttpBasicAuth"] | CODE |
| LOW | ninja/security/apikey.py | 10 | __all__ = ["APIKeyBase", "APIKeyQuery", "APIKeyCookie", "APIKeyHeader"] | CODE |
| LOW | ninja/security/base.py | 9 | __all__ = ["SecuritySchema", "AuthBase"] | CODE |
| LOW | ninja/openapi/__init__.py | 3 | __all__ = ["get_schema"] | CODE |
| LOW | ninja/openapi/urls.py | 11 | __all__ = ["get_openapi_urls", "get_root_url"] | CODE |
| LOW | ninja/testing/__init__.py | 3 | __all__ = ["TestClient", "TestAsyncClient"] | CODE |
| LOW | ninja/params/models.py | 26 | __all__ = [ | CODE |
| LOW | ninja/params/__init__.py | 7 | __all__ = [ | CODE |
| LOW | ninja/orm/fields.py | 17 | __all__ = ["create_m2m_link_type", "get_schema_field", "get_related_field_schema"] | CODE |
| LOW | ninja/orm/shortcuts.py | 6 | __all__ = ["S", "L"] | CODE |
| LOW | ninja/orm/__init__.py | 5 | __all__ = ["create_schema", "register_field", "ModelSchema"] | CODE |
| LOW | ninja/orm/factory.py | 25 | __all__ = ["SchemaFactory", "factory", "create_schema"] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | docs/docs/tutorial/part-4.md | 192 | from django.core.exceptions import ValidationError | CODE |
| CRITICAL | docs/docs/tutorial/part-8.md | 288 | from django.core.exceptions import ValidationError | CODE |
| CRITICAL | ninja/static/ninja/redoc.standalone.js | 41 | `;class As extends n.PureComponent{render(){return n.createElement("div",{style:{textAlign:"center"}},n.createElement(_s | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/operation.py | 67 | CODE | |
| LOW | ninja/operation.py | 586 | CODE | |
| LOW | ninja/router.py | 234 | CODE | |
| LOW | ninja/router.py | 274 | CODE | |
| LOW | ninja/router.py | 314 | CODE | |
| LOW | ninja/router.py | 354 | CODE | |
| LOW | ninja/router.py | 394 | CODE | |
| LOW | ninja/router.py | 434 | CODE | |
| LOW | ninja/router.py | 480 | CODE | |
| LOW | ninja/router.py | 583 | CODE | |
| LOW | ninja/router.py | 597 | CODE | |
| LOW | ninja/webhooks.py | 22 | CODE | |
| LOW | ninja/main.py | 53 | CODE | |
| LOW | ninja/main.py | 124 | CODE | |
| LOW | ninja/main.py | 167 | CODE | |
| LOW | ninja/main.py | 210 | CODE | |
| LOW | ninja/main.py | 253 | CODE | |
| LOW | ninja/main.py | 296 | CODE | |
| LOW | ninja/main.py | 339 | CODE | |
| LOW | ninja/main.py | 384 | CODE | |
| LOW | ninja/main.py | 398 | CODE | |
| LOW | ninja/params/functions.py | 11 | CODE | |
| LOW | ninja/params/functions.py | 50 | CODE | |
| LOW | ninja/params/functions.py | 89 | CODE | |
| LOW | ninja/params/functions.py | 128 | CODE | |
| LOW | ninja/params/functions.py | 167 | CODE | |
| LOW | ninja/params/functions.py | 206 | CODE | |
| LOW | ninja/params/functions.py | 245 | CODE | |
| LOW | ninja/params/models.py | 198 | CODE | |
| LOW | ninja/params/__init__.py | 77 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/filter_schema.py | 155 | CODE | |
| LOW | ninja/operation.py | 324 | CODE | |
| LOW | ninja/operation.py | 560 | CODE | |
| LOW | ninja/router.py | 118 | CODE | |
| LOW | ninja/router.py | 172 | CODE | |
| LOW | ninja/router.py | 754 | CODE | |
| LOW | ninja/webhooks.py | 57 | CODE | |
| LOW | ninja/main.py | 522 | CODE | |
| LOW | ninja/schema.py | 61 | CODE | |
| LOW | ninja/signature/details.py | 356 | CODE | |
| LOW | ninja/signature/details.py | 126 | CODE | |
| LOW | ninja/signature/details.py | 187 | CODE | |
| LOW | ninja/signature/details.py | 221 | CODE | |
| LOW | ninja/management/utils.py | 7 | CODE | |
| LOW | ninja/openapi/schema.py | 408 | CODE | |
| LOW | ninja/openapi/schema.py | 78 | CODE | |
| LOW | ninja/openapi/schema.py | 221 | CODE | |
| LOW | ninja/testing/client.py | 144 | CODE | |
| LOW | ninja/orm/metaclass.py | 68 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_csrf_async.py | 75 | # Define the async function and manually set csrf_exempt attribute | COMMENT |
| MEDIUM | ninja/operation.py | 659 | # Create a unique view function for this PathView | COMMENT |
| MEDIUM | ninja/operation.py | 662 | # Create a wrapper for async view | COMMENT |
| MEDIUM | ninja/operation.py | 674 | # Create a wrapper for sync view | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/signature/details.py | 275 | # 1) if type of the param is defined as one of the Param's subclasses - we just use that definition | COMMENT |
| MEDIUM⚡ | ninja/static/ninja/redoc.standalone.js | 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):"function"==typeof de | CODE |
| MEDIUM⚡ | ninja/static/ninja/redoc.standalone.js | 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):"function"==typeof de | CODE |
| MEDIUM⚡ | ninja/static/ninja/redoc.standalone.js | 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):"function"==typeof de | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_forms.py | 81 | # username: str | COMMENT |
| LOW | tests/test_union.py | 1 | # This is no longer the case in pydantic 2 | COMMENT |
| LOW | tests/test_union.py | 21 | # assert client.get("/test?value=today").json() == ["today", "str"] | COMMENT |
| LOW | docs/docs/guide/model-schema.md | 61 | class Meta: | COMMENT |
| LOW | ninja/schema.py | 141 | COMMENT | |
| LOW | ninja/static/ninja/swagger-ui-init.js | 21 | // configObject.requestInterceptor = (req) => { | COMMENT |
| LOW | ninja/static/ninja/swagger-ui-init.js | 41 | // req.headers['X-CSRFToken'] = swaggerUi.dataset.csrfToken | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_app.py | 98 | except Exception: | CODE |
| LOW | ninja/operation.py | 233 | except Exception as e: | CODE |
| LOW | ninja/operation.py | 333 | except Exception as exc: | CODE |
| LOW | ninja/operation.py | 512 | except Exception as e: | CODE |
| LOW | ninja/operation.py | 571 | except Exception as exc: | CODE |
| LOW | ninja/security/http.py | 84 | except Exception as e: # dear contributors please do not change to valueerror - here can be multiple exceptions | CODE |
| LOW | ninja/params/models.py | 137 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_schema.py | 211 | except ValidationError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/static/ninja/redoc.standalone.js | 41 | `;class As extends n.PureComponent{render(){return n.createElement("div",{style:{textAlign:"center"}},n.createElement(_s | CODE |
| LOW | ninja/static/ninja/redoc.standalone.js | 752 | `;var Bd=Object.defineProperty,Ud=Object.defineProperties,qd=Object.getOwnPropertyDescriptors,Vd=Object.getOwnPropertySy | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_app.py | 16 | # make sure to check how this will work with versioning | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ninja/pagination.py | 619 | # Check if Input schema has any fields | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_auth.py | 363 | async def handle_request(request): | CODE |