nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: goreleaser/nfpm
Pull request #123 (2020-03-13 15:12:36)

Fix glob patterns for files and config_files
We use nfpm for creating Debian packages. In our nfpm.yaml file we make use of glob patterns for `files`. In the following example we copy all artifacts from `a` to `/tmp/a` and from `b` to `/tmp/b`:
```
files:
  "./a/**/*": "/tmp/a/"
  "./b/**/*": "/tmp/b/"
```

nfpm does not work as expected when the directories `a` or `b` only contain a single file (e.g. ./a/test.txt). In this case nfpm creates a deb package that contains the content of ./a/test.txt as a file at the path /tmp/a. Instead we expect ./a/test.txt to be packaged as file at the path /tmp/a/test.txt.

For our use cases this fix is kind of important and the only work-around right now is to make sure that there are always at least two files below a and b.

matlec: It would be nice if you could push a new version that contains this fix :-)

Patch:
diff --git a/glob/glob.go b/glob/glob.go
index f60fba5..d499684 100644
--- a/glob/glob.go
+++ b/glob/glob.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"github.com/mattn/go-zglob"
 	"github.com/pkg/errors"
@@ -51,8 +52,8 @@ func Glob(glob, dst string) (map[string]string, error) {
 	}
 	files := make(map[string]string)
 	prefix := longestCommonPrefix(matches)
-	// the prefix may not be a complete path, in that case use the parent directory
-	if _, err := os.Stat(prefix); os.IsNotExist(err) {
+	// the prefix may not be a complete path or may use glob patterns, in that case use the parent directory
+	if _, err := os.Stat(prefix); os.IsNotExist(err) || strings.ContainsAny(glob, "*") {
 		prefix = filepath.Dir(prefix)
 	}
 	for _, src := range matches {


Test patch:
diff --git a/glob/glob_test.go b/glob/glob_test.go
index ce6ad9a..eb53d1f 100644
--- a/glob/glob_test.go
+++ b/glob/glob_test.go
@@ -44,6 +44,11 @@ func TestGlob(t *testing.T) {
 	assert.Equal(t, "/foo/bar/dir_b/test_b.txt", files["testdata/dir_a/dir_b/test_b.txt"])
 	assert.Equal(t, "/foo/bar/dir_c/test_c.txt", files["testdata/dir_a/dir_c/test_c.txt"])
 
+	singleFile, err := Glob("testdata/dir_a/dir_b/*", "/foo/bar")
+	assert.NoError(t, err)
+	assert.Len(t, singleFile, 1)
+	assert.Equal(t, "/foo/bar/test_b.txt", singleFile["testdata/dir_a/dir_b/test_b.txt"])
+
 	nilvalue, err := Glob("does/not/exist", "/foo/bar")
 	assert.EqualError(t, err, "does/not/exist: file does not exist")
 	assert.Nil(t, nilvalue)
Repository: vitalik/django-ninja
Pull request #158 (2021-06-21 09:58:05)

Grab http status code phrases from http library
This is related to Issue #147.

This PR replaces the status code description with the description in the `http.client` library. This is the same way Django finds HTTP status code descriptions ([Django source](https://github.com/django/django/blob/225d96533a8e05debd402a2bfe566487cc27d95f/django/http/response.py#L133)).

Looking into allowing custom status descriptions, there seems to be no way to override the description Django uses, making the ability to change the description in the OpenAPI schema useless and could encourage incorrect OpenAPI specifications. Feel free to correct me if I'm wrong though!


Patch:
diff --git a/ninja/openapi/schema.py b/ninja/openapi/schema.py
index 7d26070..5a4ebbb 100644
--- a/ninja/openapi/schema.py
+++ b/ninja/openapi/schema.py
@@ -1,4 +1,5 @@
 import warnings
+from http.client import responses
 from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Set, Tuple, Type
 
 from pydantic import BaseModel
@@ -179,7 +180,7 @@ class OpenAPISchema(dict):
             if status == Ellipsis:
                 continue  # it's not yet clear what it means if user want's to output any other code
 
-            description = status < 300 and "OK" or "Error"
+            description = responses.get(status, "Unknown Status Code")
             details: Dict[int, Any] = {status: {"description": description}}
             if model not in [None, NOT_SET]:
                 schema, _ = self._create_schema_from_model(model, by_alias=False)


Test patch:
diff --git a/tests/test_response_multiple.py b/tests/test_response_multiple.py
index 93fb6cc..ca2a45c 100644
--- a/tests/test_response_multiple.py
+++ b/tests/test_response_multiple.py
@@ -147,7 +147,7 @@ def test_schema():
                     "schema": {"$ref": "#/components/schemas/UserModel"}
                 }
             },
-            "description": "OK",
+            "description": "Accepted",
         },
     }
 
@@ -163,7 +163,7 @@ def test_no_content():
 
     schema = api.get_openapi_schema()
     details = schema["paths"]["/api/check_no_content"]["get"]["responses"]
-    assert details == {204: {"description": "OK"}}
+    assert details == {204: {"description": "No Content"}}
 
 
 def test_validates():
Repository: vitalik/django-ninja
Pull request #209 (2021-08-27 17:41:10)

Fix for #208 - Missing schema in requestBody


Patch:
diff --git a/ninja/openapi/schema.py b/ninja/openapi/schema.py
index 5a4ebbb..2f326c3 100644
--- a/ninja/openapi/schema.py
+++ b/ninja/openapi/schema.py
@@ -135,11 +135,15 @@ class OpenAPISchema(dict):
         schema = model_schema(model, ref_prefix=REF_PREFIX, by_alias=by_alias)
         if schema.get("definitions"):
             self.add_schema_definitions(schema["definitions"])
-        name, details = list(schema["properties"].items())[0]
 
-        # ref = details["$ref"]
-        required = name in schema.get("required", {})
-        return details, required
+        if len(schema["properties"]) == 1:
+            name, details = list(schema["properties"].items())[0]
+
+            # ref = details["$ref"]
+            required = name in schema.get("required", {})
+            return details, required
+        else:
+            return schema, True
 
     def request_body(self, operation: Operation) -> DictStrAny:
         # TODO: refactor


Test patch:
diff --git a/tests/test_openapi_schema.py b/tests/test_openapi_schema.py
index 12ade9b..2bac2d6 100644
--- a/tests/test_openapi_schema.py
+++ b/tests/test_openapi_schema.py
@@ -1,6 +1,6 @@
 import pytest
 from typing import List
-from ninja import NinjaAPI, Schema
+from ninja import Body, NinjaAPI, Schema
 from django.test import Client, override_settings
 
 
@@ -23,10 +23,15 @@ def method(request, data: Payload):
 
 
 @api.post("/test_list", response=List[Response])
-def method_list(request, data: List[Payload]):
+def method_list_response(request, data: List[Payload]):
     return []
 
 
+@api.post("/test-body", response=List[Response])
+def method_body(request, i: int = Body(...), f: float = Body(...)):
+    return [i, f]
+
+
 def test_schema_views(client: Client):
     assert client.get("/api/").status_code == 404
     assert client.get("/api/docs").status_code == 200
@@ -122,6 +127,40 @@ def test_schema():
         },
     }
 
+    # --------------------------------------------------------------
+    method_list = schema["paths"]["/api/test-body"]["post"]
+
+    assert method_list["requestBody"] == {
+        'content': {
+            'application/json': {
+                'schema': {
+                    "properties": {
+                        "f": {"title": "F", "type": "number"},
+                        "i": {"title": "I", "type": "integer"},
+                    },
+                    "required": ["i", "f"],
+                    "title": "BodyParams",
+                    "type": "object",
+                }
+            }
+        },
+        'required': True
+    }
+    assert method_list["responses"] == {
+        200: {
+            "content": {
+                "application/json": {
+                    "schema": {
+                        "items": {"$ref": "#/components/schemas/Response"},
+                        "title": "Response",
+                        "type": "array",
+                    }
+                }
+            },
+            "description": "OK",
+        }
+    }
+
 
 def test_unique_operation_ids():
Repository: vitalik/django-ninja
Pull request #231 (2021-09-21 06:45:27)

Fix typo
fix typo


Patch:
diff --git a/ninja/orm/factory.py b/ninja/orm/factory.py
index f297ce5..ffdf4fa 100644
--- a/ninja/orm/factory.py
+++ b/ninja/orm/factory.py
@@ -43,7 +43,7 @@ class SchemaFactory:
         name = name or model.__name__
 
         if fields and exclude:
-            raise ConfigError("Only one of 'include' or 'exclude' should be set.")
+            raise ConfigError("Only one of 'fields' or 'exclude' should be set.")
 
         key = self.get_key(model, name, depth, fields, exclude, custom_fields)
         if key in self.schemas:


Test patch:
diff --git a/tests/test_orm_schemas.py b/tests/test_orm_schemas.py
index c72b96b..0cfbe9e 100644
--- a/tests/test_orm_schemas.py
+++ b/tests/test_orm_schemas.py
@@ -369,7 +369,9 @@ def test_exceptions():
         class Meta:
             app_label = "tests"
 
-    with pytest.raises(ConfigError):
+    with pytest.raises(
+        ConfigError, match="Only one of 'fields' or 'exclude' should be set."
+    ):
         create_schema(MyModel2, fields=["f1"], exclude=["f2"])
 
     with pytest.raises(ConfigError):
Repository: vitalik/django-ninja
Pull request #232 (2021-09-25 18:14:59)

Add warning for path params not in view signature


Patch:
diff --git a/ninja/signature/details.py b/ninja/signature/details.py
index 4a77152..294c15e 100644
--- a/ninja/signature/details.py
+++ b/ninja/signature/details.py
@@ -1,4 +1,5 @@
 import inspect
+import warnings
 from collections import defaultdict, namedtuple
 from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional, Tuple
 
@@ -67,6 +68,32 @@ class ViewSignature:
 
         self.models: TModels = self._create_models()
 
+        self._validate_view_path_params()
+
+    def _validate_view_path_params(self) -> None:
+        """verify all path params are present in the path model fields"""
+        if self.path_params_names:
+            path_model = next(
+                (m for m in self.models if m._param_source == "path"), None
+            )
+            missing = tuple(
+                sorted(
+                    name
+                    for name in self.path_params_names
+                    if not (path_model and name in path_model._flatten_map)
+                )
+            )
+            if missing:
+                warnings.warn_explicit(
+                    UserWarning(
+                        f"Field(s) {missing} are in the view path, but were not found in the view signature."
+                    ),
+                    category=None,
+                    filename=inspect.getfile(self.view_func),
+                    lineno=inspect.getsourcelines(self.view_func)[1],
+                    source=None,
+                )
+
     def _create_models(self) -> TModels:
         params_by_source_cls: Dict[Any, List[FuncParam]] = defaultdict(list)
         for param in self.params:


Test patch:
diff --git a/tests/test_path.py b/tests/test_path.py
index b082cc6..74d3e9b 100644
--- a/tests/test_path.py
+++ b/tests/test_path.py
@@ -304,7 +304,7 @@ def test_get_path_django(path, expected_status, expected_response):
         assert response.json() == expected_response
 
 
-def test_path_signature_asserts():
+def test_path_signature_asserts_default():
     test_router = Router()
 
     match = "'item_id' is a path param, default not allowed"
@@ -313,3 +313,17 @@ def test_path_signature_asserts():
         @test_router.get("/path/{item_id}")
         def get_path_item_id(request, item_id="1"):
             pass
+
+
+def test_path_signature_warns_missing():
+    test_router = Router()
+
+    match = (
+        r"Field\(s\) \('a_path_param', 'another_path_param'\) are in "
+        r"the view path, but were not found in the view signature."
+    )
+    with pytest.warns(UserWarning, match=match):
+
+        @test_router.get("/path/{a_path_param}/{another_path_param}")
+        def get_path_item_id(request):
+            pass
diff --git a/tests/test_wraps.py b/tests/test_wraps.py
index 283a0af..69b0ee6 100644
--- a/tests/test_wraps.py
+++ b/tests/test_wraps.py
@@ -56,9 +56,7 @@ def get_query_id(request, item_id, query: int):
 
 @router.get("/text-bad")
 @a_bad_test_wrapper
-def get_text_bad(
-    request,
-):
+def get_text_bad(request):
     return "Hello World"
Repository: vitalik/django-ninja
Pull request #382 (2022-03-02 14:20:50)

ConfigError: ModelSchema classes requires a 'Config' subclass
Makes it a bit easier for newbies like me.

Signed-off-by: Sebastian Wagner <sebastian@inter.link>


Patch:
diff --git a/ninja/orm/metaclass.py b/ninja/orm/metaclass.py
index 1300238..e3c7c65 100644
--- a/ninja/orm/metaclass.py
+++ b/ninja/orm/metaclass.py
@@ -24,7 +24,12 @@ class ModelSchemaMetaclass(ResolverMetaclass):
                 and issubclass(base, ModelSchema)
                 and base == ModelSchema
             ):
-                config = namespace["Config"]
+                try:
+                    config = namespace["Config"]
+                except KeyError:
+                    raise ConfigError(
+                        f"ModelSchema class '{name}' requires a 'Config' subclass"
+                    )
 
                 assert issubclass(config.model, DjangoModel)
 


Test patch:
diff --git a/tests/test_orm_metaclass.py b/tests/test_orm_metaclass.py
index cd161c4..911183e 100644
--- a/tests/test_orm_metaclass.py
+++ b/tests/test_orm_metaclass.py
@@ -121,3 +121,13 @@ def test_model_fields_all():
         },
         "required": ["field1"],
     }
+
+
+def test_model_schema_without_config():
+    with pytest.raises(
+        ConfigError,
+        match="ModelSchema class 'NoConfigSchema' requires a 'Config' subclass",
+    ):
+
+        class NoConfigSchema(ModelSchema):
+            pass
Repository: vitalik/django-ninja
Pull request #598 (2022-10-27 12:15:50)

Set correct media type in docs for custom renderer
When specifying a renderer with a custom media type, the generated schema docs don't reflect the media type header.
It always returns application/json.

This patch uses the media_type from the custom renderer to render the response schema media type


Patch:
diff --git a/ninja/openapi/schema.py b/ninja/openapi/schema.py
index 9b9df78..1207da7 100644
--- a/ninja/openapi/schema.py
+++ b/ninja/openapi/schema.py
@@ -252,7 +252,9 @@ class OpenAPISchema(dict):
                 schema = self._create_schema_from_model(
                     model, by_alias=operation.by_alias
                 )[0]
-                details[status]["content"] = {"application/json": {"schema": schema}}
+                details[status]["content"] = {
+                    self.api.renderer.media_type: {"schema": schema}
+                }
             result.update(details)
 
         return result


Test patch:
diff --git a/tests/test_openapi_schema.py b/tests/test_openapi_schema.py
index a5fb00b..b5507be 100644
--- a/tests/test_openapi_schema.py
+++ b/tests/test_openapi_schema.py
@@ -7,6 +7,7 @@ from django.test import Client, override_settings
 
 from ninja import Body, Field, File, Form, NinjaAPI, Query, Schema, UploadedFile
 from ninja.openapi.urls import get_openapi_urls
+from ninja.renderers import JSONRenderer
 
 api = NinjaAPI()
 
@@ -706,3 +707,28 @@ def test_docs_decorator():
         request.build_absolute_uri = lambda: "http://example.com"
         result = ptrn.callback(request)
         assert result.status_code == 302
+
+
+class TestRenderer(JSONRenderer):
+    media_type = "custom/type"
+
+
+def test_renderer_media_type():
+    api = NinjaAPI(renderer=TestRenderer)
+
+    @api.get("/1", response=TypeA)
+    def same_name(
+        request,
+    ):
+        pass
+
+    schema = api.get_openapi_schema()
+    method = schema["paths"]["/api/1"]["get"]
+    assert method["responses"] == {
+        200: {
+            "content": {
+                "custom/type": {"schema": {"$ref": "#/components/schemas/TypeA"}}
+            },
+            "description": "OK",
+        }
+    }
Repository: vitalik/django-ninja
Pull request #653 (2023-01-17 20:02:36)

improve error message in export_openapi_schema
Closes https://github.com/vitalik/django-ninja/issues/381

Currently, if you don't have a NinjaAPI instance at the URL `/api/`, then the `export_openapi_schema` command will fail with an opaque error message. This PR improves the error message in that scenario, telling the user to pass the path to the API using the `--api` flag.

taobojlen: thanks for approving the CI run here!

there was a failing step that complained about missing code coverage. i fixed this with an additional test -- not a super elegant one, but it keeps the code coverage at 100%.
vitalik: > i fixed this with an additional test -- not a super elegant one, but it keeps the code coverage at 100%.

Hi @taobojlen - I do not see this test in PR ? are you sure it's pushed ?

Patch:
diff --git a/ninja/management/commands/export_openapi_schema.py b/ninja/management/commands/export_openapi_schema.py
index fa6a55a..9a2ab0e 100644
--- a/ninja/management/commands/export_openapi_schema.py
+++ b/ninja/management/commands/export_openapi_schema.py
@@ -27,7 +27,12 @@ class Command(BaseCommand):
 
     def _get_api_instance(self, api_path: Optional[str] = None) -> NinjaAPI:
         if not api_path:
-            return resolve("/api/").func.keywords["api"]  # type: ignore
+            try:
+                return resolve("/api/").func.keywords["api"]  # type: ignore
+            except AttributeError:
+                raise CommandError(
+                    "No NinjaAPI instance found; please specify one with --api"
+                )
 
         try:
             api = import_string(api_path)


Test patch:
diff --git a/tests/test_export_openapi_schema.py b/tests/test_export_openapi_schema.py
index bf9fd96..9796f8b 100644
--- a/tests/test_export_openapi_schema.py
+++ b/tests/test_export_openapi_schema.py
@@ -2,6 +2,7 @@ import json
 import os
 import tempfile
 from io import StringIO
+from unittest.mock import patch
 
 import pytest
 from django.core.management import call_command
@@ -45,3 +46,12 @@ def test_export_custom():
 
     call_command(ExportCmd(), api="demo.urls.api_v1")
     call_command(ExportCmd(), api="demo.urls.api_v2")
+
+
+@patch("ninja.management.commands.export_openapi_schema.resolve")
+def test_export_default_without_api_endpoint(mock):
+    mock.side_effect = AttributeError()
+    output = StringIO()
+    with pytest.raises(CommandError) as e:
+        call_command(ExportCmd(), stdout=output)
+    assert str(e.value) == "No NinjaAPI instance found; please specify one with --api"
Repository: vitalik/django-ninja
Pull request #728 (2023-04-03 16:53:49)

fix: allow default example to be included in pagination output in generated API docs.
ref: #502


Patch:
diff --git a/ninja/pagination.py b/ninja/pagination.py
index f02ca4d..9a7a91d 100644
--- a/ninja/pagination.py
+++ b/ninja/pagination.py
@@ -209,7 +209,6 @@ def make_response_paginated(paginator: PaginationBase, op: Operation) -> None:
         (paginator.Output,),
         {
             "__annotations__": {paginator.items_attribute: List[item_schema]},  # type: ignore
-            paginator.items_attribute: [],
         },
     )  # typing: ignore
 


Test patch:
diff --git a/tests/test_openapi_schema.py b/tests/test_openapi_schema.py
index cb99883..5b35ea5 100644
--- a/tests/test_openapi_schema.py
+++ b/tests/test_openapi_schema.py
@@ -1,5 +1,5 @@
 import sys
-from typing import List, Union
+from typing import Any, List, Union
 from unittest.mock import Mock
 
 import pytest
@@ -8,6 +8,7 @@ from django.test import Client, override_settings
 
 from ninja import Body, Field, File, Form, NinjaAPI, Query, Schema, UploadedFile
 from ninja.openapi.urls import get_openapi_urls
+from ninja.pagination import PaginationBase, paginate
 from ninja.renderers import JSONRenderer
 
 api = NinjaAPI()
@@ -906,3 +907,38 @@ def test_all_paths_typed_params_rendered():
     expected_result = {"/api/1": ["post", "get"], "/api/1/{param}": ["get", "delete"]}
     result = {p: list(schema["paths"][p].keys()) for p in schema["paths"].keys()}
     assert expected_result == result
+
+
+def test_no_default_for_custom_items_attribute():
+    api = NinjaAPI(renderer=TestRenderer)
+
+    class EmployeeOut(Schema):
+        id: int
+        first_name: str
+        last_name: str
+
+    class CustomPagination(PaginationBase):
+        class Output(Schema):
+            data: List[Any]  # `items` is a default attribute
+            detail: str
+            total: int
+
+        items_attribute: str = "data"
+
+        def paginate_queryset(self, queryset, pagination, **params):
+            pass
+
+    @api.get(
+        "/employees",
+        auth=["OAuth"],
+        response=List[EmployeeOut],
+    )
+    @paginate(CustomPagination)
+    def get_employees(request):
+        pass
+
+    schema = api.get_openapi_schema()
+
+    paged_employee_out = schema["components"]["schemas"]["PagedEmployeeOut"]
+    # a default value shouldn't be specified automatically
+    assert "default" not in paged_employee_out["properties"]["data"]
Repository: vitalik/django-ninja
Pull request #729 (2023-04-03 21:00:44)

feat: add JSON encoding support for IPv4Address and IPv6Address
ref: https://github.com/vitalik/django-ninja/issues/717


Patch:
diff --git a/ninja/responses.py b/ninja/responses.py
index af36baa..ced1ffb 100644
--- a/ninja/responses.py
+++ b/ninja/responses.py
@@ -1,3 +1,4 @@
+from ipaddress import IPv4Address, IPv6Address
 from typing import Any, FrozenSet
 
 from django.core.serializers.json import DjangoJSONEncoder
@@ -19,6 +20,8 @@ class NinjaJSONEncoder(DjangoJSONEncoder):
     def default(self, o: Any) -> Any:
         if isinstance(o, BaseModel):
             return o.dict()
+        if isinstance(o, (IPv4Address, IPv6Address)):
+            return str(o)
         return super().default(o)
 
 


Test patch:
diff --git a/tests/test_response.py b/tests/test_response.py
index 7d2ae3d..6fc7abf 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -1,3 +1,5 @@
+import json
+from ipaddress import IPv4Address, IPv6Address
 from typing import List, Union
 
 import pytest
@@ -5,6 +7,7 @@ from django.http import HttpResponse
 from pydantic import BaseModel, ValidationError
 
 from ninja import Router
+from ninja.responses import Response
 from ninja.testing import TestClient
 
 router = Router()
@@ -140,3 +143,17 @@ def test_del_cookie():
     assert cookie
     assert cookie["expires"] == "Thu, 01 Jan 1970 00:00:00 GMT"
     assert cookie["max-age"] == 0
+
+
+def test_ipv4address_encoding():
+    data = {"ipv4": IPv4Address("127.0.0.1")}
+    response = Response(data)
+    response_data = json.loads(response.content)
+    assert response_data["ipv4"] == str(data["ipv4"])
+
+
+def test_ipv6address_encoding():
+    data = {"ipv6": IPv6Address("::1")}
+    response = Response(data)
+    response_data = json.loads(response.content)
+    assert response_data["ipv6"] == str(data["ipv6"])