Ensure functions have docstrings for documentation
def custom_generate_unique_id(route: APIRoute):
1import warnings23from fastapi import APIRouter, FastAPI4from fastapi.routing import APIRoute5from fastapi.testclient import TestClient6from inline_snapshot import snapshot7from pydantic import BaseModel8910def custom_generate_unique_id(route: APIRoute):11 return f"foo_{route.name}"121314def custom_generate_unique_id2(route: APIRoute):15 return f"bar_{route.name}"161718def custom_generate_unique_id3(route: APIRoute):19 return f"baz_{route.name}"202122class Item(BaseModel):23 name: str24 price: float252627class Message(BaseModel):28 title: str29 description: str303132def test_top_level_generate_unique_id():33 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)34 router = APIRouter()3536 @app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})37 def post_root(item1: Item, item2: Item):38 return item1, item2 # pragma: nocover3940 @router.post(41 "/router", response_model=list[Item], responses={404: {"model": list[Message]}}42 )43 def post_router(item1: Item, item2: Item):44 return item1, item2 # pragma: nocover4546 app.include_router(router)47 client = TestClient(app)48 response = client.get("/openapi.json")49 assert response.json() == snapshot(50 {51 "openapi": "3.1.0",52 "info": {"title": "FastAPI", "version": "0.1.0"},53 "paths": {54 "/": {55 "post": {56 "summary": "Post Root",57 "operationId": "foo_post_root",58 "requestBody": {59 "content": {60 "application/json": {61 "schema": {62 "$ref": "#/components/schemas/Body_foo_post_root"63 }64 }65 },66 "required": True,67 },68 "responses": {69 "200": {70 "description": "Successful Response",71 "content": {72 "application/json": {73 "schema": {74 "title": "Response Foo Post Root",75 "type": "array",76 "items": {77 "$ref": "#/components/schemas/Item"78 },79 }80 }81 },82 },83 "404": {84 "description": "Not Found",85 "content": {86 "application/json": {87 "schema": {88 "title": "Response 404 Foo Post Root",89 "type": "array",90 "items": {91 "$ref": "#/components/schemas/Message"92 },93 }94 }95 },96 },97 "422": {98 "description": "Validation Error",99 "content": {100 "application/json": {101 "schema": {102 "$ref": "#/components/schemas/HTTPValidationError"103 }104 }105 },106 },107 },108 }109 },110 "/router": {111 "post": {112 "summary": "Post Router",113 "operationId": "foo_post_router",114 "requestBody": {115 "content": {116 "application/json": {117 "schema": {118 "$ref": "#/components/schemas/Body_foo_post_router"119 }120 }121 },122 "required": True,123 },124 "responses": {125 "200": {126 "description": "Successful Response",127 "content": {128 "application/json": {129 "schema": {130 "title": "Response Foo Post Router",131 "type": "array",132 "items": {133 "$ref": "#/components/schemas/Item"134 },135 }136 }137 },138 },139 "404": {140 "description": "Not Found",141 "content": {142 "application/json": {143 "schema": {144 "title": "Response 404 Foo Post Router",145 "type": "array",146 "items": {147 "$ref": "#/components/schemas/Message"148 },149 }150 }151 },152 },153 "422": {154 "description": "Validation Error",155 "content": {156 "application/json": {157 "schema": {158 "$ref": "#/components/schemas/HTTPValidationError"159 }160 }161 },162 },163 },164 }165 },166 },167 "components": {168 "schemas": {169 "Body_foo_post_root": {170 "title": "Body_foo_post_root",171 "required": ["item1", "item2"],172 "type": "object",173 "properties": {174 "item1": {"$ref": "#/components/schemas/Item"},175 "item2": {"$ref": "#/components/schemas/Item"},176 },177 },178 "Body_foo_post_router": {179 "title": "Body_foo_post_router",180 "required": ["item1", "item2"],181 "type": "object",182 "properties": {183 "item1": {"$ref": "#/components/schemas/Item"},184 "item2": {"$ref": "#/components/schemas/Item"},185 },186 },187 "HTTPValidationError": {188 "title": "HTTPValidationError",189 "type": "object",190 "properties": {191 "detail": {192 "title": "Detail",193 "type": "array",194 "items": {195 "$ref": "#/components/schemas/ValidationError"196 },197 }198 },199 },200 "Item": {201 "title": "Item",202 "required": ["name", "price"],203 "type": "object",204 "properties": {205 "name": {"title": "Name", "type": "string"},206 "price": {"title": "Price", "type": "number"},207 },208 },209 "Message": {210 "title": "Message",211 "required": ["title", "description"],212 "type": "object",213 "properties": {214 "title": {"title": "Title", "type": "string"},215 "description": {"title": "Description", "type": "string"},216 },217 },218 "ValidationError": {219 "title": "ValidationError",220 "required": ["loc", "msg", "type"],221 "type": "object",222 "properties": {223 "ctx": {"title": "Context", "type": "object"},224 "input": {"title": "Input"},225 "loc": {226 "title": "Location",227 "type": "array",228 "items": {229 "anyOf": [{"type": "string"}, {"type": "integer"}]230 },231 },232 "msg": {"title": "Message", "type": "string"},233 "type": {"title": "Error Type", "type": "string"},234 },235 },236 }237 },238 }239 )240241242def test_router_overrides_generate_unique_id():243 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)244 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)245246 @app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})247 def post_root(item1: Item, item2: Item):248 return item1, item2 # pragma: nocover249250 @router.post(251 "/router", response_model=list[Item], responses={404: {"model": list[Message]}}252 )253 def post_router(item1: Item, item2: Item):254 return item1, item2 # pragma: nocover255256 app.include_router(router)257 client = TestClient(app)258 response = client.get("/openapi.json")259 assert response.json() == snapshot(260 {261 "openapi": "3.1.0",262 "info": {"title": "FastAPI", "version": "0.1.0"},263 "paths": {264 "/": {265 "post": {266 "summary": "Post Root",267 "operationId": "foo_post_root",268 "requestBody": {269 "content": {270 "application/json": {271 "schema": {272 "$ref": "#/components/schemas/Body_foo_post_root"273 }274 }275 },276 "required": True,277 },278 "responses": {279 "200": {280 "description": "Successful Response",281 "content": {282 "application/json": {283 "schema": {284 "title": "Response Foo Post Root",285 "type": "array",286 "items": {287 "$ref": "#/components/schemas/Item"288 },289 }290 }291 },292 },293 "404": {294 "description": "Not Found",295 "content": {296 "application/json": {297 "schema": {298 "title": "Response 404 Foo Post Root",299 "type": "array",300 "items": {301 "$ref": "#/components/schemas/Message"302 },303 }304 }305 },306 },307 "422": {308 "description": "Validation Error",309 "content": {310 "application/json": {311 "schema": {312 "$ref": "#/components/schemas/HTTPValidationError"313 }314 }315 },316 },317 },318 }319 },320 "/router": {321 "post": {322 "summary": "Post Router",323 "operationId": "bar_post_router",324 "requestBody": {325 "content": {326 "application/json": {327 "schema": {328 "$ref": "#/components/schemas/Body_bar_post_router"329 }330 }331 },332 "required": True,333 },334 "responses": {335 "200": {336 "description": "Successful Response",337 "content": {338 "application/json": {339 "schema": {340 "title": "Response Bar Post Router",341 "type": "array",342 "items": {343 "$ref": "#/components/schemas/Item"344 },345 }346 }347 },348 },349 "404": {350 "description": "Not Found",351 "content": {352 "application/json": {353 "schema": {354 "title": "Response 404 Bar Post Router",355 "type": "array",356 "items": {357 "$ref": "#/components/schemas/Message"358 },359 }360 }361 },362 },363 "422": {364 "description": "Validation Error",365 "content": {366 "application/json": {367 "schema": {368 "$ref": "#/components/schemas/HTTPValidationError"369 }370 }371 },372 },373 },374 }375 },376 },377 "components": {378 "schemas": {379 "Body_bar_post_router": {380 "title": "Body_bar_post_router",381 "required": ["item1", "item2"],382 "type": "object",383 "properties": {384 "item1": {"$ref": "#/components/schemas/Item"},385 "item2": {"$ref": "#/components/schemas/Item"},386 },387 },388 "Body_foo_post_root": {389 "title": "Body_foo_post_root",390 "required": ["item1", "item2"],391 "type": "object",392 "properties": {393 "item1": {"$ref": "#/components/schemas/Item"},394 "item2": {"$ref": "#/components/schemas/Item"},395 },396 },397 "HTTPValidationError": {398 "title": "HTTPValidationError",399 "type": "object",400 "properties": {401 "detail": {402 "title": "Detail",403 "type": "array",404 "items": {405 "$ref": "#/components/schemas/ValidationError"406 },407 }408 },409 },410 "Item": {411 "title": "Item",412 "required": ["name", "price"],413 "type": "object",414 "properties": {415 "name": {"title": "Name", "type": "string"},416 "price": {"title": "Price", "type": "number"},417 },418 },419 "Message": {420 "title": "Message",421 "required": ["title", "description"],422 "type": "object",423 "properties": {424 "title": {"title": "Title", "type": "string"},425 "description": {"title": "Description", "type": "string"},426 },427 },428 "ValidationError": {429 "title": "ValidationError",430 "required": ["loc", "msg", "type"],431 "type": "object",432 "properties": {433 "ctx": {"title": "Context", "type": "object"},434 "input": {"title": "Input"},435 "loc": {436 "title": "Location",437 "type": "array",438 "items": {439 "anyOf": [{"type": "string"}, {"type": "integer"}]440 },441 },442 "msg": {"title": "Message", "type": "string"},443 "type": {"title": "Error Type", "type": "string"},444 },445 },446 }447 },448 }449 )450451452def test_router_include_overrides_generate_unique_id():453 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)454 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)455456 @app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})457 def post_root(item1: Item, item2: Item):458 return item1, item2 # pragma: nocover459460 @router.post(461 "/router", response_model=list[Item], responses={404: {"model": list[Message]}}462 )463 def post_router(item1: Item, item2: Item):464 return item1, item2 # pragma: nocover465466 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3)467 client = TestClient(app)468 response = client.get("/openapi.json")469 assert response.json() == snapshot(470 {471 "openapi": "3.1.0",472 "info": {"title": "FastAPI", "version": "0.1.0"},473 "paths": {474 "/": {475 "post": {476 "summary": "Post Root",477 "operationId": "foo_post_root",478 "requestBody": {479 "content": {480 "application/json": {481 "schema": {482 "$ref": "#/components/schemas/Body_foo_post_root"483 }484 }485 },486 "required": True,487 },488 "responses": {489 "200": {490 "description": "Successful Response",491 "content": {492 "application/json": {493 "schema": {494 "title": "Response Foo Post Root",495 "type": "array",496 "items": {497 "$ref": "#/components/schemas/Item"498 },499 }500 }501 },502 },503 "404": {504 "description": "Not Found",505 "content": {506 "application/json": {507 "schema": {508 "title": "Response 404 Foo Post Root",509 "type": "array",510 "items": {511 "$ref": "#/components/schemas/Message"512 },513 }514 }515 },516 },517 "422": {518 "description": "Validation Error",519 "content": {520 "application/json": {521 "schema": {522 "$ref": "#/components/schemas/HTTPValidationError"523 }524 }525 },526 },527 },528 }529 },530 "/router": {531 "post": {532 "summary": "Post Router",533 "operationId": "bar_post_router",534 "requestBody": {535 "content": {536 "application/json": {537 "schema": {538 "$ref": "#/components/schemas/Body_bar_post_router"539 }540 }541 },542 "required": True,543 },544 "responses": {545 "200": {546 "description": "Successful Response",547 "content": {548 "application/json": {549 "schema": {550 "title": "Response Bar Post Router",551 "type": "array",552 "items": {553 "$ref": "#/components/schemas/Item"554 },555 }556 }557 },558 },559 "404": {560 "description": "Not Found",561 "content": {562 "application/json": {563 "schema": {564 "title": "Response 404 Bar Post Router",565 "type": "array",566 "items": {567 "$ref": "#/components/schemas/Message"568 },569 }570 }571 },572 },573 "422": {574 "description": "Validation Error",575 "content": {576 "application/json": {577 "schema": {578 "$ref": "#/components/schemas/HTTPValidationError"579 }580 }581 },582 },583 },584 }585 },586 },587 "components": {588 "schemas": {589 "Body_bar_post_router": {590 "title": "Body_bar_post_router",591 "required": ["item1", "item2"],592 "type": "object",593 "properties": {594 "item1": {"$ref": "#/components/schemas/Item"},595 "item2": {"$ref": "#/components/schemas/Item"},596 },597 },598 "Body_foo_post_root": {599 "title": "Body_foo_post_root",600 "required": ["item1", "item2"],601 "type": "object",602 "properties": {603 "item1": {"$ref": "#/components/schemas/Item"},604 "item2": {"$ref": "#/components/schemas/Item"},605 },606 },607 "HTTPValidationError": {608 "title": "HTTPValidationError",609 "type": "object",610 "properties": {611 "detail": {612 "title": "Detail",613 "type": "array",614 "items": {615 "$ref": "#/components/schemas/ValidationError"616 },617 }618 },619 },620 "Item": {621 "title": "Item",622 "required": ["name", "price"],623 "type": "object",624 "properties": {625 "name": {"title": "Name", "type": "string"},626 "price": {"title": "Price", "type": "number"},627 },628 },629 "Message": {630 "title": "Message",631 "required": ["title", "description"],632 "type": "object",633 "properties": {634 "title": {"title": "Title", "type": "string"},635 "description": {"title": "Description", "type": "string"},636 },637 },638 "ValidationError": {639 "title": "ValidationError",640 "required": ["loc", "msg", "type"],641 "type": "object",642 "properties": {643 "ctx": {"title": "Context", "type": "object"},644 "input": {"title": "Input"},645 "loc": {646 "title": "Location",647 "type": "array",648 "items": {649 "anyOf": [{"type": "string"}, {"type": "integer"}]650 },651 },652 "msg": {"title": "Message", "type": "string"},653 "type": {"title": "Error Type", "type": "string"},654 },655 },656 }657 },658 }659 )660661662def test_subrouter_top_level_include_overrides_generate_unique_id():663 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)664 router = APIRouter()665 sub_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)666667 @app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})668 def post_root(item1: Item, item2: Item):669 return item1, item2 # pragma: nocover670671 @router.post(672 "/router", response_model=list[Item], responses={404: {"model": list[Message]}}673 )674 def post_router(item1: Item, item2: Item):675 return item1, item2 # pragma: nocover676677 @sub_router.post(678 "/subrouter",679 response_model=list[Item],680 responses={404: {"model": list[Message]}},681 )682 def post_subrouter(item1: Item, item2: Item):683 return item1, item2 # pragma: nocover684685 router.include_router(sub_router)686 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3)687 client = TestClient(app)688 response = client.get("/openapi.json")689 assert response.json() == snapshot(690 {691 "openapi": "3.1.0",692 "info": {"title": "FastAPI", "version": "0.1.0"},693 "paths": {694 "/": {695 "post": {696 "summary": "Post Root",697 "operationId": "foo_post_root",698 "requestBody": {699 "content": {700 "application/json": {701 "schema": {702 "$ref": "#/components/schemas/Body_foo_post_root"703 }704 }705 },706 "required": True,707 },708 "responses": {709 "200": {710 "description": "Successful Response",711 "content": {712 "application/json": {713 "schema": {714 "title": "Response Foo Post Root",715 "type": "array",716 "items": {717 "$ref": "#/components/schemas/Item"718 },719 }720 }721 },722 },723 "404": {724 "description": "Not Found",725 "content": {726 "application/json": {727 "schema": {728 "title": "Response 404 Foo Post Root",729 "type": "array",730 "items": {731 "$ref": "#/components/schemas/Message"732 },733 }734 }735 },736 },737 "422": {738 "description": "Validation Error",739 "content": {740 "application/json": {741 "schema": {742 "$ref": "#/components/schemas/HTTPValidationError"743 }744 }745 },746 },747 },748 }749 },750 "/router": {751 "post": {752 "summary": "Post Router",753 "operationId": "baz_post_router",754 "requestBody": {755 "content": {756 "application/json": {757 "schema": {758 "$ref": "#/components/schemas/Body_baz_post_router"759 }760 }761 },762 "required": True,763 },764 "responses": {765 "200": {766 "description": "Successful Response",767 "content": {768 "application/json": {769 "schema": {770 "title": "Response Baz Post Router",771 "type": "array",772 "items": {773 "$ref": "#/components/schemas/Item"774 },775 }776 }777 },778 },779 "404": {780 "description": "Not Found",781 "content": {782 "application/json": {783 "schema": {784 "title": "Response 404 Baz Post Router",785 "type": "array",786 "items": {787 "$ref": "#/components/schemas/Message"788 },789 }790 }791 },792 },793 "422": {794 "description": "Validation Error",795 "content": {796 "application/json": {797 "schema": {798 "$ref": "#/components/schemas/HTTPValidationError"799 }800 }801 },802 },803 },804 }805 },806 "/subrouter": {807 "post": {808 "summary": "Post Subrouter",809 "operationId": "bar_post_subrouter",810 "requestBody": {811 "content": {812 "application/json": {813 "schema": {814 "$ref": "#/components/schemas/Body_bar_post_subrouter"815 }816 }817 },818 "required": True,819 },820 "responses": {821 "200": {822 "description": "Successful Response",823 "content": {824 "application/json": {825 "schema": {826 "title": "Response Bar Post Subrouter",827 "type": "array",828 "items": {829 "$ref": "#/components/schemas/Item"830 },831 }832 }833 },834 },835 "404": {836 "description": "Not Found",837 "content": {838 "application/json": {839 "schema": {840 "title": "Response 404 Bar Post Subrouter",841 "type": "array",842 "items": {843 "$ref": "#/components/schemas/Message"844 },845 }846 }847 },848 },849 "422": {850 "description": "Validation Error",851 "content": {852 "application/json": {853 "schema": {854 "$ref": "#/components/schemas/HTTPValidationError"855 }856 }857 },858 },859 },860 }861 },862 },863 "components": {864 "schemas": {865 "Body_bar_post_subrouter": {866 "title": "Body_bar_post_subrouter",867 "required": ["item1", "item2"],868 "type": "object",869 "properties": {870 "item1": {"$ref": "#/components/schemas/Item"},871 "item2": {"$ref": "#/components/schemas/Item"},872 },873 },874 "Body_baz_post_router": {875 "title": "Body_baz_post_router",876 "required": ["item1", "item2"],877 "type": "object",878 "properties": {879 "item1": {"$ref": "#/components/schemas/Item"},880 "item2": {"$ref": "#/components/schemas/Item"},881 },882 },883 "Body_foo_post_root": {884 "title": "Body_foo_post_root",885 "required": ["item1", "item2"],886 "type": "object",887 "properties": {888 "item1": {"$ref": "#/components/schemas/Item"},889 "item2": {"$ref": "#/components/schemas/Item"},890 },891 },892 "HTTPValidationError": {893 "title": "HTTPValidationError",894 "type": "object",895 "properties": {896 "detail": {897 "title": "Detail",898 "type": "array",899 "items": {900 "$ref": "#/components/schemas/ValidationError"901 },902 }903 },904 },905 "Item": {906 "title": "Item",907 "required": ["name", "price"],908 "type": "object",909 "properties": {910 "name": {"title": "Name", "type": "string"},911 "price": {"title": "Price", "type": "number"},912 },913 },914 "Message": {915 "title": "Message",916 "required": ["title", "description"],917 "type": "object",918 "properties": {919 "title": {"title": "Title", "type": "string"},920 "description": {"title": "Description", "type": "string"},921 },922 },923 "ValidationError": {924 "title": "ValidationError",925 "required": ["loc", "msg", "type"],926 "type": "object",927 "properties": {928 "ctx": {"title": "Context", "type": "object"},929 "input": {"title": "Input"},930 "loc": {931 "title": "Location",932 "type": "array",933 "items": {934 "anyOf": [{"type": "string"}, {"type": "integer"}]935 },936 },937 "msg": {"title": "Message", "type": "string"},938 "type": {"title": "Error Type", "type": "string"},939 },940 },941 }942 },943 }944 )945946947def test_router_path_operation_overrides_generate_unique_id():948 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)949 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)950951 @app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})952 def post_root(item1: Item, item2: Item):953 return item1, item2 # pragma: nocover954955 @router.post(956 "/router",957 response_model=list[Item],958 responses={404: {"model": list[Message]}},959 generate_unique_id_function=custom_generate_unique_id3,960 )961 def post_router(item1: Item, item2: Item):962 return item1, item2 # pragma: nocover963964 app.include_router(router)965 client = TestClient(app)966 response = client.get("/openapi.json")967 assert response.json() == snapshot(968 {969 "openapi": "3.1.0",970 "info": {"title": "FastAPI", "version": "0.1.0"},971 "paths": {972 "/": {973 "post": {974 "summary": "Post Root",975 "operationId": "foo_post_root",976 "requestBody": {977 "content": {978 "application/json": {979 "schema": {980 "$ref": "#/components/schemas/Body_foo_post_root"981 }982 }983 },984 "required": True,985 },986 "responses": {987 "200": {988 "description": "Successful Response",989 "content": {990 "application/json": {991 "schema": {992 "title": "Response Foo Post Root",993 "type": "array",994 "items": {995 "$ref": "#/components/schemas/Item"996 },997 }998 }999 },1000 },1001 "404": {1002 "description": "Not Found",1003 "content": {1004 "application/json": {1005 "schema": {1006 "title": "Response 404 Foo Post Root",1007 "type": "array",1008 "items": {1009 "$ref": "#/components/schemas/Message"1010 },1011 }1012 }1013 },1014 },1015 "422": {1016 "description": "Validation Error",1017 "content": {1018 "application/json": {1019 "schema": {1020 "$ref": "#/components/schemas/HTTPValidationError"1021 }1022 }1023 },1024 },1025 },1026 }1027 },1028 "/router": {1029 "post": {1030 "summary": "Post Router",1031 "operationId": "baz_post_router",1032 "requestBody": {1033 "content": {1034 "application/json": {1035 "schema": {1036 "$ref": "#/components/schemas/Body_baz_post_router"1037 }1038 }1039 },1040 "required": True,1041 },1042 "responses": {1043 "200": {1044 "description": "Successful Response",1045 "content": {1046 "application/json": {1047 "schema": {1048 "title": "Response Baz Post Router",1049 "type": "array",1050 "items": {1051 "$ref": "#/components/schemas/Item"1052 },1053 }1054 }1055 },1056 },1057 "404": {1058 "description": "Not Found",1059 "content": {1060 "application/json": {1061 "schema": {1062 "title": "Response 404 Baz Post Router",1063 "type": "array",1064 "items": {1065 "$ref": "#/components/schemas/Message"1066 },1067 }1068 }1069 },1070 },1071 "422": {1072 "description": "Validation Error",1073 "content": {1074 "application/json": {1075 "schema": {1076 "$ref": "#/components/schemas/HTTPValidationError"1077 }1078 }1079 },1080 },1081 },1082 }1083 },1084 },1085 "components": {1086 "schemas": {1087 "Body_baz_post_router": {1088 "title": "Body_baz_post_router",1089 "required": ["item1", "item2"],1090 "type": "object",1091 "properties": {1092 "item1": {"$ref": "#/components/schemas/Item"},1093 "item2": {"$ref": "#/components/schemas/Item"},1094 },1095 },1096 "Body_foo_post_root": {1097 "title": "Body_foo_post_root",1098 "required": ["item1", "item2"],1099 "type": "object",1100 "properties": {1101 "item1": {"$ref": "#/components/schemas/Item"},1102 "item2": {"$ref": "#/components/schemas/Item"},1103 },1104 },1105 "HTTPValidationError": {1106 "title": "HTTPValidationError",1107 "type": "object",1108 "properties": {1109 "detail": {1110 "title": "Detail",1111 "type": "array",1112 "items": {1113 "$ref": "#/components/schemas/ValidationError"1114 },1115 }1116 },1117 },1118 "Item": {1119 "title": "Item",1120 "required": ["name", "price"],1121 "type": "object",1122 "properties": {1123 "name": {"title": "Name", "type": "string"},1124 "price": {"title": "Price", "type": "number"},1125 },1126 },1127 "Message": {1128 "title": "Message",1129 "required": ["title", "description"],1130 "type": "object",1131 "properties": {1132 "title": {"title": "Title", "type": "string"},1133 "description": {"title": "Description", "type": "string"},1134 },1135 },1136 "ValidationError": {1137 "title": "ValidationError",1138 "required": ["loc", "msg", "type"],1139 "type": "object",1140 "properties": {1141 "ctx": {"title": "Context", "type": "object"},1142 "input": {"title": "Input"},1143 "loc": {1144 "title": "Location",1145 "type": "array",1146 "items": {1147 "anyOf": [{"type": "string"}, {"type": "integer"}]1148 },1149 },1150 "msg": {"title": "Message", "type": "string"},1151 "type": {"title": "Error Type", "type": "string"},1152 },1153 },1154 }1155 },1156 }1157 )115811591160def test_app_path_operation_overrides_generate_unique_id():1161 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)1162 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)11631164 @app.post(1165 "/",1166 response_model=list[Item],1167 responses={404: {"model": list[Message]}},1168 generate_unique_id_function=custom_generate_unique_id3,1169 )1170 def post_root(item1: Item, item2: Item):1171 return item1, item2 # pragma: nocover11721173 @router.post(1174 "/router",1175 response_model=list[Item],1176 responses={404: {"model": list[Message]}},1177 )1178 def post_router(item1: Item, item2: Item):1179 return item1, item2 # pragma: nocover11801181 app.include_router(router)1182 client = TestClient(app)1183 response = client.get("/openapi.json")1184 assert response.json() == snapshot(1185 {1186 "openapi": "3.1.0",1187 "info": {"title": "FastAPI", "version": "0.1.0"},1188 "paths": {1189 "/": {1190 "post": {1191 "summary": "Post Root",1192 "operationId": "baz_post_root",1193 "requestBody": {1194 "content": {1195 "application/json": {1196 "schema": {1197 "$ref": "#/components/schemas/Body_baz_post_root"1198 }1199 }1200 },1201 "required": True,1202 },1203 "responses": {1204 "200": {1205 "description": "Successful Response",1206 "content": {1207 "application/json": {1208 "schema": {1209 "title": "Response Baz Post Root",1210 "type": "array",1211 "items": {1212 "$ref": "#/components/schemas/Item"1213 },1214 }1215 }1216 },1217 },1218 "404": {1219 "description": "Not Found",1220 "content": {1221 "application/json": {1222 "schema": {1223 "title": "Response 404 Baz Post Root",1224 "type": "array",1225 "items": {1226 "$ref": "#/components/schemas/Message"1227 },1228 }1229 }1230 },1231 },1232 "422": {1233 "description": "Validation Error",1234 "content": {1235 "application/json": {1236 "schema": {1237 "$ref": "#/components/schemas/HTTPValidationError"1238 }1239 }1240 },1241 },1242 },1243 }1244 },1245 "/router": {1246 "post": {1247 "summary": "Post Router",1248 "operationId": "bar_post_router",1249 "requestBody": {1250 "content": {1251 "application/json": {1252 "schema": {1253 "$ref": "#/components/schemas/Body_bar_post_router"1254 }1255 }1256 },1257 "required": True,1258 },1259 "responses": {1260 "200": {1261 "description": "Successful Response",1262 "content": {1263 "application/json": {1264 "schema": {1265 "title": "Response Bar Post Router",1266 "type": "array",1267 "items": {1268 "$ref": "#/components/schemas/Item"1269 },1270 }1271 }1272 },1273 },1274 "404": {1275 "description": "Not Found",1276 "content": {1277 "application/json": {1278 "schema": {1279 "title": "Response 404 Bar Post Router",1280 "type": "array",1281 "items": {1282 "$ref": "#/components/schemas/Message"1283 },1284 }1285 }1286 },1287 },1288 "422": {1289 "description": "Validation Error",1290 "content": {1291 "application/json": {1292 "schema": {1293 "$ref": "#/components/schemas/HTTPValidationError"1294 }1295 }1296 },1297 },1298 },1299 }1300 },1301 },1302 "components": {1303 "schemas": {1304 "Body_bar_post_router": {1305 "title": "Body_bar_post_router",1306 "required": ["item1", "item2"],1307 "type": "object",1308 "properties": {1309 "item1": {"$ref": "#/components/schemas/Item"},1310 "item2": {"$ref": "#/components/schemas/Item"},1311 },1312 },1313 "Body_baz_post_root": {1314 "title": "Body_baz_post_root",1315 "required": ["item1", "item2"],1316 "type": "object",1317 "properties": {1318 "item1": {"$ref": "#/components/schemas/Item"},1319 "item2": {"$ref": "#/components/schemas/Item"},1320 },1321 },1322 "HTTPValidationError": {1323 "title": "HTTPValidationError",1324 "type": "object",1325 "properties": {1326 "detail": {1327 "title": "Detail",1328 "type": "array",1329 "items": {1330 "$ref": "#/components/schemas/ValidationError"1331 },1332 }1333 },1334 },1335 "Item": {1336 "title": "Item",1337 "required": ["name", "price"],1338 "type": "object",1339 "properties": {1340 "name": {"title": "Name", "type": "string"},1341 "price": {"title": "Price", "type": "number"},1342 },1343 },1344 "Message": {1345 "title": "Message",1346 "required": ["title", "description"],1347 "type": "object",1348 "properties": {1349 "title": {"title": "Title", "type": "string"},1350 "description": {"title": "Description", "type": "string"},1351 },1352 },1353 "ValidationError": {1354 "title": "ValidationError",1355 "required": ["loc", "msg", "type"],1356 "type": "object",1357 "properties": {1358 "ctx": {"title": "Context", "type": "object"},1359 "input": {"title": "Input"},1360 "loc": {1361 "title": "Location",1362 "type": "array",1363 "items": {1364 "anyOf": [{"type": "string"}, {"type": "integer"}]1365 },1366 },1367 "msg": {"title": "Message", "type": "string"},1368 "type": {"title": "Error Type", "type": "string"},1369 },1370 },1371 }1372 },1373 }1374 )137513761377def test_callback_override_generate_unique_id():1378 app = FastAPI(generate_unique_id_function=custom_generate_unique_id)1379 callback_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)13801381 @callback_router.post(1382 "/post-callback",1383 response_model=list[Item],1384 responses={404: {"model": list[Message]}},1385 generate_unique_id_function=custom_generate_unique_id3,1386 )1387 def post_callback(item1: Item, item2: Item):1388 return item1, item2 # pragma: nocover13891390 @app.post(1391 "/",1392 response_model=list[Item],1393 responses={404: {"model": list[Message]}},1394 generate_unique_id_function=custom_generate_unique_id3,1395 callbacks=callback_router.routes,1396 )1397 def post_root(item1: Item, item2: Item):1398 return item1, item2 # pragma: nocover13991400 @app.post(1401 "/tocallback",1402 response_model=list[Item],1403 responses={404: {"model": list[Message]}},1404 )1405 def post_with_callback(item1: Item, item2: Item):1406 return item1, item2 # pragma: nocover14071408 client = TestClient(app)1409 response = client.get("/openapi.json")1410 assert response.json() == snapshot(1411 {1412 "openapi": "3.1.0",1413 "info": {"title": "FastAPI", "version": "0.1.0"},1414 "paths": {1415 "/": {1416 "post": {1417 "summary": "Post Root",1418 "operationId": "baz_post_root",1419 "requestBody": {1420 "content": {1421 "application/json": {1422 "schema": {1423 "$ref": "#/components/schemas/Body_baz_post_root"1424 }1425 }1426 },1427 "required": True,1428 },1429 "responses": {1430 "200": {1431 "description": "Successful Response",1432 "content": {1433 "application/json": {1434 "schema": {1435 "title": "Response Baz Post Root",1436 "type": "array",1437 "items": {1438 "$ref": "#/components/schemas/Item"1439 },1440 }1441 }1442 },1443 },1444 "404": {1445 "description": "Not Found",1446 "content": {1447 "application/json": {1448 "schema": {1449 "title": "Response 404 Baz Post Root",1450 "type": "array",1451 "items": {1452 "$ref": "#/components/schemas/Message"1453 },1454 }1455 }1456 },1457 },1458 "422": {1459 "description": "Validation Error",1460 "content": {1461 "application/json": {1462 "schema": {1463 "$ref": "#/components/schemas/HTTPValidationError"1464 }1465 }1466 },1467 },1468 },1469 "callbacks": {1470 "post_callback": {1471 "/post-callback": {1472 "post": {1473 "summary": "Post Callback",1474 "operationId": "baz_post_callback",1475 "requestBody": {1476 "content": {1477 "application/json": {1478 "schema": {1479 "$ref": "#/components/schemas/Body_baz_post_callback"1480 }1481 }1482 },1483 "required": True,1484 },1485 "responses": {1486 "200": {1487 "description": "Successful Response",1488 "content": {1489 "application/json": {1490 "schema": {1491 "title": "Response Baz Post Callback",1492 "type": "array",1493 "items": {1494 "$ref": "#/components/schemas/Item"1495 },1496 }1497 }1498 },1499 },1500 "404": {1501 "description": "Not Found",1502 "content": {1503 "application/json": {1504 "schema": {1505 "title": "Response 404 Baz Post Callback",1506 "type": "array",1507 "items": {1508 "$ref": "#/components/schemas/Message"1509 },1510 }1511 }1512 },1513 },1514 "422": {1515 "description": "Validation Error",1516 "content": {1517 "application/json": {1518 "schema": {1519 "$ref": "#/components/schemas/HTTPValidationError"1520 }1521 }1522 },1523 },1524 },1525 }1526 }1527 }1528 },1529 }1530 },1531 "/tocallback": {1532 "post": {1533 "summary": "Post With Callback",1534 "operationId": "foo_post_with_callback",1535 "requestBody": {1536 "content": {1537 "application/json": {1538 "schema": {1539 "$ref": "#/components/schemas/Body_foo_post_with_callback"1540 }1541 }1542 },1543 "required": True,1544 },1545 "responses": {1546 "200": {1547 "description": "Successful Response",1548 "content": {1549 "application/json": {1550 "schema": {1551 "title": "Response Foo Post With Callback",1552 "type": "array",1553 "items": {1554 "$ref": "#/components/schemas/Item"1555 },1556 }1557 }1558 },1559 },1560 "404": {1561 "description": "Not Found",1562 "content": {1563 "application/json": {1564 "schema": {1565 "title": "Response 404 Foo Post With Callback",1566 "type": "array",1567 "items": {1568 "$ref": "#/components/schemas/Message"1569 },1570 }1571 }1572 },1573 },1574 "422": {1575 "description": "Validation Error",1576 "content": {1577 "application/json": {1578 "schema": {1579 "$ref": "#/components/schemas/HTTPValidationError"1580 }1581 }1582 },1583 },1584 },1585 }1586 },1587 },1588 "components": {1589 "schemas": {1590 "Body_baz_post_callback": {1591 "title": "Body_baz_post_callback",1592 "required": ["item1", "item2"],1593 "type": "object",1594 "properties": {1595 "item1": {"$ref": "#/components/schemas/Item"},1596 "item2": {"$ref": "#/components/schemas/Item"},1597 },1598 },1599 "Body_baz_post_root": {1600 "title": "Body_baz_post_root",1601 "required": ["item1", "item2"],1602 "type": "object",1603 "properties": {1604 "item1": {"$ref": "#/components/schemas/Item"},1605 "item2": {"$ref": "#/components/schemas/Item"},1606 },1607 },1608 "Body_foo_post_with_callback": {1609 "title": "Body_foo_post_with_callback",1610 "required": ["item1", "item2"],1611 "type": "object",1612 "properties": {1613 "item1": {"$ref": "#/components/schemas/Item"},1614 "item2": {"$ref": "#/components/schemas/Item"},1615 },1616 },1617 "HTTPValidationError": {1618 "title": "HTTPValidationError",1619 "type": "object",1620 "properties": {1621 "detail": {1622 "title": "Detail",1623 "type": "array",1624 "items": {1625 "$ref": "#/components/schemas/ValidationError"1626 },1627 }1628 },1629 },1630 "Item": {1631 "title": "Item",1632 "required": ["name", "price"],1633 "type": "object",1634 "properties": {1635 "name": {"title": "Name", "type": "string"},1636 "price": {"title": "Price", "type": "number"},1637 },1638 },1639 "Message": {1640 "title": "Message",1641 "required": ["title", "description"],1642 "type": "object",1643 "properties": {1644 "title": {"title": "Title", "type": "string"},1645 "description": {"title": "Description", "type": "string"},1646 },1647 },1648 "ValidationError": {1649 "title": "ValidationError",1650 "required": ["loc", "msg", "type"],1651 "type": "object",1652 "properties": {1653 "ctx": {"title": "Context", "type": "object"},1654 "input": {"title": "Input"},1655 "loc": {1656 "title": "Location",1657 "type": "array",1658 "items": {1659 "anyOf": [{"type": "string"}, {"type": "integer"}]1660 },1661 },1662 "msg": {"title": "Message", "type": "string"},1663 "type": {"title": "Error Type", "type": "string"},1664 },1665 },1666 }1667 },1668 }1669 )167016711672def test_warn_duplicate_operation_id():1673 def broken_operation_id(route: APIRoute):1674 return "foo"16751676 app = FastAPI(generate_unique_id_function=broken_operation_id)16771678 @app.post("/")1679 def post_root(item1: Item):1680 return item1 # pragma: nocover16811682 @app.post("/second")1683 def post_second(item1: Item):1684 return item1 # pragma: nocover16851686 @app.post("/third")1687 def post_third(item1: Item):1688 return item1 # pragma: nocover16891690 client = TestClient(app)1691 with warnings.catch_warnings(record=True) as w:1692 warnings.simplefilter("always")1693 client.get("/openapi.json")1694 assert len(w) >= 21695 duplicate_warnings = [1696 warning for warning in w if issubclass(warning.category, UserWarning)1697 ]1698 assert len(duplicate_warnings) > 01699 assert "Duplicate Operation ID" in str(duplicate_warnings[0].message)
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.