routing.py 250 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447
  1. import contextlib
  2. import copy
  3. import email.message
  4. import errno
  5. import functools
  6. import inspect
  7. import json
  8. import os
  9. import stat
  10. import threading
  11. import types
  12. import warnings
  13. from collections.abc import (
  14. AsyncIterator,
  15. Awaitable,
  16. Callable,
  17. Collection,
  18. Coroutine,
  19. Generator,
  20. Iterator,
  21. Mapping,
  22. Sequence,
  23. )
  24. from contextlib import (
  25. AbstractAsyncContextManager,
  26. AbstractContextManager,
  27. AsyncExitStack,
  28. asynccontextmanager,
  29. )
  30. from contextvars import ContextVar
  31. from dataclasses import dataclass, field
  32. from enum import Enum, IntEnum
  33. from typing import (
  34. Annotated,
  35. Any,
  36. Literal,
  37. Protocol,
  38. TypeVar,
  39. cast,
  40. )
  41. import anyio
  42. from annotated_doc import Doc
  43. from anyio.abc import ObjectReceiveStream
  44. from fastapi import params
  45. from fastapi._compat import (
  46. ModelField,
  47. Undefined,
  48. lenient_issubclass,
  49. )
  50. from fastapi.datastructures import Default, DefaultPlaceholder
  51. from fastapi.dependencies.models import (
  52. Dependant,
  53. _is_async_gen_callable,
  54. _is_coroutine_callable,
  55. _is_gen_callable,
  56. )
  57. from fastapi.dependencies.utils import (
  58. SolvedDependency,
  59. _get_body_field,
  60. _get_flat_body_params,
  61. _should_embed_body_fields,
  62. get_dependant,
  63. get_parameterless_sub_dependant,
  64. get_stream_item_type,
  65. get_typed_return_annotation,
  66. solve_dependencies,
  67. )
  68. from fastapi.encoders import jsonable_encoder
  69. from fastapi.exceptions import (
  70. EndpointContext,
  71. FastAPIError,
  72. RequestValidationError,
  73. ResponseValidationError,
  74. WebSocketRequestValidationError,
  75. )
  76. from fastapi.sse import (
  77. _PING_INTERVAL,
  78. KEEPALIVE_COMMENT,
  79. EventSourceResponse,
  80. ServerSentEvent,
  81. format_sse_event,
  82. )
  83. from fastapi.types import DecoratedCallable, IncEx
  84. from fastapi.utils import (
  85. create_model_field,
  86. generate_unique_id,
  87. get_value_or_default,
  88. is_body_allowed_for_status_code,
  89. )
  90. from starlette import routing
  91. from starlette._exception_handler import wrap_app_handling_exceptions
  92. from starlette._utils import get_route_path, is_async_callable
  93. from starlette.concurrency import iterate_in_threadpool, run_in_threadpool
  94. from starlette.datastructures import URL, FormData, URLPath
  95. from starlette.exceptions import HTTPException
  96. from starlette.requests import Request
  97. from starlette.responses import (
  98. JSONResponse,
  99. PlainTextResponse,
  100. RedirectResponse,
  101. Response,
  102. StreamingResponse,
  103. )
  104. from starlette.routing import (
  105. BaseRoute,
  106. Match,
  107. NoMatchFound,
  108. compile_path,
  109. get_name,
  110. )
  111. from starlette.routing import Mount as Mount # noqa
  112. from starlette.staticfiles import StaticFiles
  113. from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
  114. from starlette.websockets import WebSocket
  115. from typing_extensions import deprecated
  116. # Copy of starlette.routing.request_response modified to include the
  117. # dependencies' AsyncExitStack
  118. def request_response(
  119. func: Callable[[Request], Awaitable[Response] | Response],
  120. ) -> ASGIApp:
  121. """
  122. Takes a function or coroutine `func(request) -> response`,
  123. and returns an ASGI application.
  124. """
  125. f: Callable[[Request], Awaitable[Response]] = (
  126. func # type: ignore[assignment]
  127. if is_async_callable(func)
  128. else functools.partial(run_in_threadpool, func) # type: ignore[call-arg]
  129. ) # ty: ignore[invalid-assignment]
  130. async def app(scope: Scope, receive: Receive, send: Send) -> None:
  131. request = Request(scope, receive, send)
  132. async def app(scope: Scope, receive: Receive, send: Send) -> None:
  133. # Starts customization
  134. response_awaited = False
  135. async with AsyncExitStack() as request_stack:
  136. scope["fastapi_inner_astack"] = request_stack
  137. async with AsyncExitStack() as function_stack:
  138. scope["fastapi_function_astack"] = function_stack
  139. response = await f(request)
  140. await response(scope, receive, send)
  141. # Continues customization
  142. response_awaited = True
  143. if not response_awaited:
  144. raise FastAPIError(
  145. "Response not awaited. There's a high chance that the "
  146. "application code is raising an exception and a dependency with yield "
  147. "has a block with a bare except, or a block with except Exception, "
  148. "and is not raising the exception again. Read more about it in the "
  149. "docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except"
  150. )
  151. # Same as in Starlette
  152. await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  153. return app
  154. # Copy of starlette.routing.websocket_session modified to include the
  155. # dependencies' AsyncExitStack
  156. def websocket_session(
  157. func: Callable[[WebSocket], Awaitable[None]],
  158. ) -> ASGIApp:
  159. """
  160. Takes a coroutine `func(session)`, and returns an ASGI application.
  161. """
  162. # assert asyncio.iscoroutinefunction(func), "WebSocket endpoints must be async"
  163. async def app(scope: Scope, receive: Receive, send: Send) -> None:
  164. session = WebSocket(scope, receive=receive, send=send)
  165. async def app(scope: Scope, receive: Receive, send: Send) -> None:
  166. async with AsyncExitStack() as request_stack:
  167. scope["fastapi_inner_astack"] = request_stack
  168. async with AsyncExitStack() as function_stack:
  169. scope["fastapi_function_astack"] = function_stack
  170. await func(session)
  171. # Same as in Starlette
  172. await wrap_app_handling_exceptions(app, session)(scope, receive, send)
  173. return app
  174. _T = TypeVar("_T")
  175. # Vendored from starlette.routing to avoid importing private symbols
  176. class _AsyncLiftContextManager(AbstractAsyncContextManager[_T]):
  177. """
  178. Wraps a synchronous context manager to make it async.
  179. This is vendored from Starlette to avoid importing private symbols.
  180. """
  181. def __init__(self, cm: AbstractContextManager[_T]) -> None:
  182. self._cm = cm
  183. async def __aenter__(self) -> _T:
  184. return self._cm.__enter__()
  185. async def __aexit__(
  186. self,
  187. exc_type: type[BaseException] | None,
  188. exc_value: BaseException | None,
  189. traceback: types.TracebackType | None,
  190. ) -> bool | None:
  191. return self._cm.__exit__(exc_type, exc_value, traceback)
  192. # Vendored from starlette.routing to avoid importing private symbols
  193. def _wrap_gen_lifespan_context(
  194. lifespan_context: Callable[[Any], Generator[Any, Any, Any]],
  195. ) -> Callable[[Any], AbstractAsyncContextManager[Any]]:
  196. """
  197. Wrap a generator-based lifespan context into an async context manager.
  198. This is vendored from Starlette to avoid importing private symbols.
  199. """
  200. cmgr = contextlib.contextmanager(lifespan_context)
  201. @functools.wraps(cmgr)
  202. def wrapper(app: Any) -> _AsyncLiftContextManager[Any]:
  203. return _AsyncLiftContextManager(cmgr(app))
  204. return wrapper
  205. def _merge_lifespan_context(
  206. original_context: Lifespan[Any], nested_context: Lifespan[Any]
  207. ) -> Lifespan[Any]:
  208. @asynccontextmanager
  209. async def merged_lifespan(
  210. app: AppType,
  211. ) -> AsyncIterator[Mapping[str, Any] | None]:
  212. async with original_context(app) as maybe_original_state:
  213. async with nested_context(app) as maybe_nested_state:
  214. if maybe_nested_state is None and maybe_original_state is None:
  215. yield None # old ASGI compatibility
  216. else:
  217. yield {**(maybe_nested_state or {}), **(maybe_original_state or {})}
  218. return merged_lifespan # type: ignore[return-value] # ty: ignore[invalid-return-type]
  219. class _DefaultLifespan:
  220. """
  221. Default lifespan context manager that runs on_startup and on_shutdown handlers.
  222. This is a copy of the Starlette _DefaultLifespan class that was removed
  223. in Starlette. FastAPI keeps it to maintain backward compatibility with
  224. on_startup and on_shutdown event handlers.
  225. Ref: https://github.com/Kludex/starlette/pull/3117
  226. """
  227. def __init__(self, router: "APIRouter") -> None:
  228. self._router = router
  229. async def __aenter__(self) -> None:
  230. await self._router._startup()
  231. async def __aexit__(self, *exc_info: object) -> None:
  232. await self._router._shutdown()
  233. def __call__(self: _T, app: object) -> _T:
  234. return self
  235. # Cache for endpoint context to avoid re-extracting on every request
  236. _endpoint_context_cache: dict[int, EndpointContext] = {}
  237. def _extract_endpoint_context(func: Any) -> EndpointContext:
  238. """Extract endpoint context with caching to avoid repeated file I/O."""
  239. func_id = id(func)
  240. if func_id in _endpoint_context_cache:
  241. return _endpoint_context_cache[func_id]
  242. try:
  243. ctx: EndpointContext = {}
  244. if (source_file := inspect.getsourcefile(func)) is not None:
  245. ctx["file"] = source_file
  246. if (line_number := inspect.getsourcelines(func)[1]) is not None:
  247. ctx["line"] = line_number
  248. if (func_name := getattr(func, "__name__", None)) is not None:
  249. ctx["function"] = func_name
  250. except Exception:
  251. ctx = EndpointContext()
  252. _endpoint_context_cache[func_id] = ctx
  253. return ctx
  254. async def serialize_response(
  255. *,
  256. field: ModelField | None = None,
  257. response_content: Any,
  258. include: IncEx | None = None,
  259. exclude: IncEx | None = None,
  260. by_alias: bool = True,
  261. exclude_unset: bool = False,
  262. exclude_defaults: bool = False,
  263. exclude_none: bool = False,
  264. is_coroutine: bool = True,
  265. endpoint_ctx: EndpointContext | None = None,
  266. dump_json: bool = False,
  267. ) -> Any:
  268. if field:
  269. if is_coroutine:
  270. value, errors = field.validate(response_content, {}, loc=("response",))
  271. else:
  272. value, errors = await run_in_threadpool(
  273. field.validate, response_content, {}, loc=("response",)
  274. )
  275. if errors:
  276. ctx = endpoint_ctx or EndpointContext()
  277. raise ResponseValidationError(
  278. errors=errors,
  279. body=response_content,
  280. endpoint_ctx=ctx,
  281. )
  282. serializer = field.serialize_json if dump_json else field.serialize
  283. return serializer(
  284. value,
  285. include=include,
  286. exclude=exclude,
  287. by_alias=by_alias,
  288. exclude_unset=exclude_unset,
  289. exclude_defaults=exclude_defaults,
  290. exclude_none=exclude_none,
  291. )
  292. else:
  293. return jsonable_encoder(response_content)
  294. async def run_endpoint_function(
  295. *, dependant: Dependant, values: dict[str, Any], is_coroutine: bool
  296. ) -> Any:
  297. # Only called by get_request_handler. Has been split into its own function to
  298. # facilitate profiling endpoints, since inner functions are harder to profile.
  299. assert dependant.call is not None, "dependant.call must be a function"
  300. if is_coroutine:
  301. return await dependant.call(**values)
  302. else:
  303. return await run_in_threadpool(dependant.call, **values)
  304. def _build_response_args(
  305. *, status_code: int | None, solved_result: Any
  306. ) -> dict[str, Any]:
  307. response_args: dict[str, Any] = {
  308. "background": solved_result.background_tasks,
  309. }
  310. # If status_code was set, use it, otherwise use the default from the
  311. # response class, in the case of redirect it's 307
  312. current_status_code = (
  313. status_code if status_code else solved_result.response.status_code
  314. )
  315. if current_status_code is not None:
  316. response_args["status_code"] = current_status_code
  317. if solved_result.response.status_code:
  318. response_args["status_code"] = solved_result.response.status_code
  319. return response_args
  320. def get_request_handler(
  321. dependant: Dependant,
  322. body_field: ModelField | None = None,
  323. status_code: int | None = None,
  324. response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
  325. response_field: ModelField | None = None,
  326. response_model_include: IncEx | None = None,
  327. response_model_exclude: IncEx | None = None,
  328. response_model_by_alias: bool = True,
  329. response_model_exclude_unset: bool = False,
  330. response_model_exclude_defaults: bool = False,
  331. response_model_exclude_none: bool = False,
  332. dependency_overrides_provider: Any | None = None,
  333. embed_body_fields: bool = False,
  334. strict_content_type: bool | DefaultPlaceholder = Default(True),
  335. stream_item_field: ModelField | None = None,
  336. is_json_stream: bool = False,
  337. ) -> Callable[[Request], Coroutine[Any, Any, Response]]:
  338. assert dependant.call is not None, "dependant.call must be a function"
  339. is_coroutine = _is_coroutine_callable(dependant.call)
  340. is_body_form = body_field and isinstance(body_field.field_info, params.Form)
  341. if isinstance(response_class, DefaultPlaceholder):
  342. actual_response_class: type[Response] = response_class.value
  343. else:
  344. actual_response_class = response_class
  345. is_sse_stream = lenient_issubclass(actual_response_class, EventSourceResponse)
  346. if isinstance(strict_content_type, DefaultPlaceholder):
  347. actual_strict_content_type: bool = strict_content_type.value
  348. else:
  349. actual_strict_content_type = strict_content_type
  350. async def app(request: Request) -> Response:
  351. response: Response | None = None
  352. file_stack = request.scope.get("fastapi_middleware_astack")
  353. assert isinstance(file_stack, AsyncExitStack), (
  354. "fastapi_middleware_astack not found in request scope"
  355. )
  356. # Extract endpoint context for error messages
  357. endpoint_ctx = (
  358. _extract_endpoint_context(dependant.call)
  359. if dependant.call
  360. else EndpointContext()
  361. )
  362. if dependant.path:
  363. # For mounted sub-apps, include the mount path prefix
  364. mount_path = request.scope.get("root_path", "").rstrip("/")
  365. endpoint_ctx["path"] = f"{request.method} {mount_path}{dependant.path}"
  366. # Read body and auto-close files
  367. try:
  368. body: Any = None
  369. if body_field:
  370. if is_body_form:
  371. body = await request.form()
  372. file_stack.push_async_callback(body.close)
  373. else:
  374. body_bytes = await request.body()
  375. if body_bytes:
  376. json_body: Any = Undefined
  377. content_type_value = request.headers.get("content-type")
  378. if not content_type_value:
  379. if not actual_strict_content_type:
  380. json_body = await request.json()
  381. else:
  382. message = email.message.Message()
  383. message["content-type"] = content_type_value
  384. if message.get_content_maintype() == "application":
  385. subtype = message.get_content_subtype()
  386. if subtype == "json" or subtype.endswith("+json"):
  387. json_body = await request.json()
  388. if json_body != Undefined:
  389. body = json_body
  390. else:
  391. body = body_bytes
  392. except json.JSONDecodeError as e:
  393. validation_error = RequestValidationError(
  394. [
  395. {
  396. "type": "json_invalid",
  397. "loc": ("body", e.pos),
  398. "msg": "JSON decode error",
  399. "input": {},
  400. "ctx": {"error": e.msg},
  401. }
  402. ],
  403. body=e.doc,
  404. endpoint_ctx=endpoint_ctx,
  405. )
  406. raise validation_error from e
  407. except HTTPException:
  408. # If a middleware raises an HTTPException, it should be raised again
  409. raise
  410. except Exception as e:
  411. http_error = HTTPException(
  412. status_code=400, detail="There was an error parsing the body"
  413. )
  414. raise http_error from e
  415. # Solve dependencies and run path operation function, auto-closing dependencies
  416. errors: list[Any] = []
  417. async_exit_stack = request.scope.get("fastapi_inner_astack")
  418. assert isinstance(async_exit_stack, AsyncExitStack), (
  419. "fastapi_inner_astack not found in request scope"
  420. )
  421. solved_result = await solve_dependencies(
  422. request=request,
  423. dependant=dependant,
  424. body=cast(dict[str, Any] | FormData | bytes | None, body),
  425. dependency_overrides_provider=dependency_overrides_provider,
  426. async_exit_stack=async_exit_stack,
  427. embed_body_fields=embed_body_fields,
  428. )
  429. errors = solved_result.errors
  430. assert dependant.call # For types
  431. if not errors:
  432. # Shared serializer for stream items (JSONL and SSE).
  433. # Validates against stream_item_field when set, then
  434. # serializes to JSON bytes.
  435. def _serialize_data(data: Any) -> bytes:
  436. if stream_item_field:
  437. value, errors_ = stream_item_field.validate(
  438. data, {}, loc=("response",)
  439. )
  440. if errors_:
  441. ctx = endpoint_ctx or EndpointContext()
  442. raise ResponseValidationError(
  443. errors=errors_,
  444. body=data,
  445. endpoint_ctx=ctx,
  446. )
  447. return stream_item_field.serialize_json(
  448. value,
  449. include=response_model_include,
  450. exclude=response_model_exclude,
  451. by_alias=response_model_by_alias,
  452. exclude_unset=response_model_exclude_unset,
  453. exclude_defaults=response_model_exclude_defaults,
  454. exclude_none=response_model_exclude_none,
  455. )
  456. else:
  457. data = jsonable_encoder(data)
  458. return json.dumps(data).encode("utf-8")
  459. if is_sse_stream:
  460. # Generator endpoint: stream as Server-Sent Events
  461. gen = dependant.call(**solved_result.values)
  462. def _serialize_sse_item(item: Any) -> bytes:
  463. if isinstance(item, ServerSentEvent):
  464. # User controls the event structure.
  465. # Serialize the data payload if present.
  466. # For ServerSentEvent items we skip stream_item_field
  467. # validation (the user may mix types intentionally).
  468. if item.raw_data is not None:
  469. data_str: str | None = item.raw_data
  470. elif item.data is not None:
  471. if hasattr(item.data, "model_dump_json"):
  472. data_str = item.data.model_dump_json()
  473. else:
  474. data_str = json.dumps(jsonable_encoder(item.data))
  475. else:
  476. data_str = None
  477. return format_sse_event(
  478. data_str=data_str,
  479. event=item.event,
  480. id=item.id,
  481. retry=item.retry,
  482. comment=item.comment,
  483. )
  484. else:
  485. # Plain object: validate + serialize via
  486. # stream_item_field (if set) and wrap in data field
  487. return format_sse_event(
  488. data_str=_serialize_data(item).decode("utf-8")
  489. )
  490. if _is_async_gen_callable(dependant.call):
  491. sse_aiter: AsyncIterator[Any] = gen.__aiter__()
  492. else:
  493. sse_aiter = iterate_in_threadpool(gen)
  494. @asynccontextmanager
  495. async def _sse_producer_cm() -> AsyncIterator[
  496. ObjectReceiveStream[bytes]
  497. ]:
  498. # Use a memory stream to decouple generator iteration
  499. # from the keepalive timer. A producer task pulls items
  500. # from the generator independently, so
  501. # `anyio.fail_after` never wraps the generator's
  502. # `__anext__` directly - avoiding CancelledError that
  503. # would finalize the generator and also working for sync
  504. # generators running in a thread pool.
  505. #
  506. # This context manager is entered on the request-scoped
  507. # AsyncExitStack so its __aexit__ (which cancels the
  508. # task group) is called by the exit stack after the
  509. # streaming response completes — not by async generator
  510. # finalization via GeneratorExit.
  511. # Ref: https://peps.python.org/pep-0789/
  512. send_stream, receive_stream = anyio.create_memory_object_stream[
  513. bytes
  514. ](max_buffer_size=1)
  515. async def _producer() -> None:
  516. async with send_stream:
  517. async for raw_item in sse_aiter:
  518. await send_stream.send(_serialize_sse_item(raw_item))
  519. send_keepalive, receive_keepalive = (
  520. anyio.create_memory_object_stream[bytes](max_buffer_size=1)
  521. )
  522. async def _keepalive_inserter() -> None:
  523. """Read from the producer and forward to the output,
  524. inserting keepalive comments on timeout."""
  525. async with send_keepalive, receive_stream:
  526. try:
  527. while True:
  528. try:
  529. with anyio.fail_after(_PING_INTERVAL):
  530. data = await receive_stream.receive()
  531. await send_keepalive.send(data)
  532. except TimeoutError:
  533. await send_keepalive.send(KEEPALIVE_COMMENT)
  534. except anyio.EndOfStream:
  535. pass
  536. async with anyio.create_task_group() as tg:
  537. tg.start_soon(_producer)
  538. tg.start_soon(_keepalive_inserter)
  539. yield receive_keepalive
  540. tg.cancel_scope.cancel()
  541. # Enter the SSE context manager on the request-scoped
  542. # exit stack. The stack outlives the streaming response,
  543. # so __aexit__ runs via proper structured teardown, not
  544. # via GeneratorExit thrown into an async generator.
  545. sse_receive_stream = await async_exit_stack.enter_async_context(
  546. _sse_producer_cm()
  547. )
  548. # Ensure the receive stream is closed when the exit stack
  549. # unwinds, preventing ResourceWarning from __del__.
  550. async_exit_stack.push_async_callback(sse_receive_stream.aclose)
  551. async def _sse_with_checkpoints(
  552. stream: ObjectReceiveStream[bytes],
  553. ) -> AsyncIterator[bytes]:
  554. async for data in stream:
  555. yield data
  556. # Guarantee a checkpoint so cancellation can be
  557. # delivered even when the producer is faster than
  558. # the consumer and receive() never suspends.
  559. await anyio.sleep(0)
  560. sse_stream_content: AsyncIterator[bytes] | Iterator[bytes] = (
  561. _sse_with_checkpoints(sse_receive_stream)
  562. )
  563. response_args = _build_response_args(
  564. status_code=status_code, solved_result=solved_result
  565. )
  566. response = StreamingResponse(
  567. sse_stream_content,
  568. media_type="text/event-stream",
  569. **response_args,
  570. )
  571. response.headers["Cache-Control"] = "no-cache"
  572. # For Nginx proxies to not buffer server sent events
  573. response.headers["X-Accel-Buffering"] = "no"
  574. response.headers.raw.extend(solved_result.response.headers.raw)
  575. elif is_json_stream:
  576. # Generator endpoint: stream as JSONL
  577. gen = dependant.call(**solved_result.values)
  578. def _serialize_item(item: Any) -> bytes:
  579. return _serialize_data(item) + b"\n"
  580. if _is_async_gen_callable(dependant.call):
  581. async def _async_stream_jsonl() -> AsyncIterator[bytes]:
  582. async for item in gen:
  583. yield _serialize_item(item)
  584. # To allow for cancellation to trigger
  585. # Ref: https://github.com/fastapi/fastapi/issues/14680
  586. await anyio.sleep(0)
  587. jsonl_stream_content: AsyncIterator[bytes] | Iterator[bytes] = (
  588. _async_stream_jsonl()
  589. )
  590. else:
  591. def _sync_stream_jsonl() -> Iterator[bytes]:
  592. for item in gen: # ty: ignore[not-iterable]
  593. yield _serialize_item(item)
  594. jsonl_stream_content = _sync_stream_jsonl()
  595. response_args = _build_response_args(
  596. status_code=status_code, solved_result=solved_result
  597. )
  598. response = StreamingResponse(
  599. jsonl_stream_content,
  600. media_type="application/jsonl",
  601. **response_args,
  602. )
  603. response.headers.raw.extend(solved_result.response.headers.raw)
  604. elif _is_async_gen_callable(dependant.call) or _is_gen_callable(
  605. dependant.call
  606. ):
  607. # Raw streaming with explicit response_class (e.g. StreamingResponse)
  608. gen = dependant.call(**solved_result.values)
  609. if _is_async_gen_callable(dependant.call):
  610. async def _async_stream_raw(
  611. async_gen: AsyncIterator[Any],
  612. ) -> AsyncIterator[Any]:
  613. async for chunk in async_gen:
  614. yield chunk
  615. # To allow for cancellation to trigger
  616. # Ref: https://github.com/fastapi/fastapi/issues/14680
  617. await anyio.sleep(0)
  618. gen = _async_stream_raw(gen)
  619. response_args = _build_response_args(
  620. status_code=status_code, solved_result=solved_result
  621. )
  622. response = actual_response_class(content=gen, **response_args)
  623. response.headers.raw.extend(solved_result.response.headers.raw)
  624. else:
  625. raw_response = await run_endpoint_function(
  626. dependant=dependant,
  627. values=solved_result.values,
  628. is_coroutine=is_coroutine,
  629. )
  630. if isinstance(raw_response, Response):
  631. if raw_response.background is None:
  632. raw_response.background = solved_result.background_tasks
  633. response = raw_response
  634. else:
  635. response_args = _build_response_args(
  636. status_code=status_code, solved_result=solved_result
  637. )
  638. # Use the fast path (dump_json) when no custom response
  639. # class was set and a response field with a TypeAdapter
  640. # exists. Serializes directly to JSON bytes via Pydantic's
  641. # Rust core, skipping the intermediate Python dict +
  642. # json.dumps() step.
  643. use_dump_json = response_field is not None and isinstance(
  644. response_class, DefaultPlaceholder
  645. )
  646. content = await serialize_response(
  647. field=response_field,
  648. response_content=raw_response,
  649. include=response_model_include,
  650. exclude=response_model_exclude,
  651. by_alias=response_model_by_alias,
  652. exclude_unset=response_model_exclude_unset,
  653. exclude_defaults=response_model_exclude_defaults,
  654. exclude_none=response_model_exclude_none,
  655. is_coroutine=is_coroutine,
  656. endpoint_ctx=endpoint_ctx,
  657. dump_json=use_dump_json,
  658. )
  659. if use_dump_json:
  660. response = Response(
  661. content=content,
  662. media_type="application/json",
  663. **response_args,
  664. )
  665. else:
  666. response = actual_response_class(content, **response_args)
  667. if not is_body_allowed_for_status_code(response.status_code):
  668. response.body = b""
  669. response.headers.raw.extend(solved_result.response.headers.raw)
  670. if errors:
  671. validation_error = RequestValidationError(
  672. errors, body=body, endpoint_ctx=endpoint_ctx
  673. )
  674. raise validation_error
  675. # Return response
  676. assert response
  677. return response
  678. return app
  679. def get_websocket_app(
  680. dependant: Dependant,
  681. dependency_overrides_provider: Any | None = None,
  682. embed_body_fields: bool = False,
  683. ) -> Callable[[WebSocket], Coroutine[Any, Any, Any]]:
  684. async def app(websocket: WebSocket) -> None:
  685. endpoint_ctx = (
  686. _extract_endpoint_context(dependant.call)
  687. if dependant.call
  688. else EndpointContext()
  689. )
  690. if dependant.path:
  691. # For mounted sub-apps, include the mount path prefix
  692. mount_path = websocket.scope.get("root_path", "").rstrip("/")
  693. endpoint_ctx["path"] = f"WS {mount_path}{dependant.path}"
  694. async_exit_stack = websocket.scope.get("fastapi_inner_astack")
  695. assert isinstance(async_exit_stack, AsyncExitStack), (
  696. "fastapi_inner_astack not found in request scope"
  697. )
  698. solved_result = await solve_dependencies(
  699. request=websocket,
  700. dependant=dependant,
  701. dependency_overrides_provider=dependency_overrides_provider,
  702. async_exit_stack=async_exit_stack,
  703. embed_body_fields=embed_body_fields,
  704. )
  705. if solved_result.errors:
  706. raise WebSocketRequestValidationError(
  707. solved_result.errors,
  708. endpoint_ctx=endpoint_ctx,
  709. )
  710. assert dependant.call is not None, "dependant.call must be a function"
  711. await dependant.call(**solved_result.values)
  712. return app
  713. class APIWebSocketRoute(routing.WebSocketRoute):
  714. def __init__(
  715. self,
  716. path: str,
  717. endpoint: Callable[..., Any],
  718. *,
  719. name: str | None = None,
  720. dependencies: Sequence[params.Depends] | None = None,
  721. dependency_overrides_provider: Any | None = None,
  722. ) -> None:
  723. self.path = path
  724. self.endpoint = endpoint
  725. self.name = get_name(endpoint) if name is None else name
  726. self.dependencies = list(dependencies or [])
  727. self.path_regex, self.path_format, self.param_convertors = compile_path(path)
  728. (
  729. self.dependant,
  730. _,
  731. self._embed_body_fields,
  732. ) = _build_dependant_with_parameterless_dependencies(
  733. path=self.path_format,
  734. call=self.endpoint,
  735. dependencies=self.dependencies,
  736. )
  737. self.app = websocket_session(
  738. get_websocket_app(
  739. dependant=self.dependant,
  740. dependency_overrides_provider=dependency_overrides_provider,
  741. embed_body_fields=self._embed_body_fields,
  742. )
  743. )
  744. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  745. match, child_scope = super().matches(scope)
  746. if match != Match.NONE:
  747. child_scope["route"] = self
  748. return match, child_scope
  749. _FASTAPI_SCOPE_KEY = "fastapi"
  750. _FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY = "effective_route_context"
  751. _FASTAPI_FRONTEND_PATH_KEY = "frontend_path"
  752. _FASTAPI_FRONTEND_SPECIFICITY_KEY = "frontend_specificity"
  753. _FASTAPI_INCLUDED_ROUTER_KEY = "included_router"
  754. _effective_route_context_var: ContextVar[Any | None] = ContextVar(
  755. "fastapi_effective_route_context", default=None
  756. )
  757. _SCOPE_MISSING = object()
  758. def _frontend_dependency_endpoint() -> None:
  759. pass # pragma: no cover
  760. def _build_dependant_with_parameterless_dependencies(
  761. *,
  762. path: str,
  763. call: Callable[..., Any],
  764. dependencies: Sequence[params.Depends],
  765. ) -> tuple[Dependant, list[ModelField], bool]:
  766. dependant = get_dependant(path=path, call=call, scope="function")
  767. for depends in dependencies[::-1]:
  768. dependant.dependencies.insert(
  769. 0,
  770. get_parameterless_sub_dependant(depends=depends, path=path),
  771. )
  772. body_params = _get_flat_body_params(dependant)
  773. embed_body_fields = _should_embed_body_fields(body_params)
  774. return dependant, body_params, embed_body_fields
  775. class _RouteWithPath(Protocol):
  776. path: str
  777. def _get_fastapi_scope(scope: Scope) -> dict[str, Any]:
  778. fastapi_scope = scope.setdefault(_FASTAPI_SCOPE_KEY, {})
  779. assert isinstance(fastapi_scope, dict)
  780. return fastapi_scope
  781. def _update_scope(scope: Scope, child_scope: Scope) -> None:
  782. fastapi_child_scope = child_scope.get(_FASTAPI_SCOPE_KEY)
  783. for key, value in child_scope.items():
  784. if key != _FASTAPI_SCOPE_KEY:
  785. scope[key] = value
  786. if isinstance(fastapi_child_scope, dict):
  787. _get_fastapi_scope(scope).update(fastapi_child_scope)
  788. def _get_scope_effective_route_context(scope: Scope) -> Any | None:
  789. return scope.get(_FASTAPI_SCOPE_KEY, {}).get(_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY)
  790. def _get_scope_included_router(scope: Scope) -> Any | None:
  791. return scope.get(_FASTAPI_SCOPE_KEY, {}).get(_FASTAPI_INCLUDED_ROUTER_KEY)
  792. def _frontend_scope_specificity(scope: Scope) -> int | None:
  793. specificity = scope.get(_FASTAPI_SCOPE_KEY, {}).get(
  794. _FASTAPI_FRONTEND_SPECIFICITY_KEY
  795. )
  796. if isinstance(specificity, int):
  797. return specificity
  798. return None
  799. def _restore_fastapi_scope_key(scope: Scope, key: str, previous: Any) -> None:
  800. fastapi_scope = scope.get(_FASTAPI_SCOPE_KEY)
  801. if not isinstance(fastapi_scope, dict):
  802. return
  803. if previous is _SCOPE_MISSING:
  804. fastapi_scope.pop(key, None)
  805. else:
  806. fastapi_scope[key] = previous
  807. class _APIRouteLike(Protocol):
  808. path: str
  809. endpoint: Callable[..., Any]
  810. stream_item_type: Any | None
  811. response_model: Any
  812. summary: str | None
  813. response_description: str
  814. deprecated: bool | None
  815. operation_id: str | None
  816. response_model_include: IncEx | None
  817. response_model_exclude: IncEx | None
  818. response_model_by_alias: bool
  819. response_model_exclude_unset: bool
  820. response_model_exclude_defaults: bool
  821. response_model_exclude_none: bool
  822. include_in_schema: bool
  823. response_class: type[Response] | DefaultPlaceholder
  824. dependency_overrides_provider: Any | None
  825. callbacks: list[BaseRoute] | None
  826. openapi_extra: dict[str, Any] | None
  827. generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder
  828. strict_content_type: bool | DefaultPlaceholder
  829. tags: list[str | Enum]
  830. responses: dict[int | str, dict[str, Any]]
  831. name: str
  832. path_regex: Any
  833. path_format: str
  834. param_convertors: dict[str, Any]
  835. methods: set[str]
  836. unique_id: str
  837. status_code: int | None
  838. response_field: ModelField | None
  839. stream_item_field: ModelField | None
  840. dependencies: list[params.Depends]
  841. description: str
  842. response_fields: dict[int | str, ModelField]
  843. dependant: Dependant
  844. _embed_body_fields: bool
  845. body_field: ModelField | None
  846. is_sse_stream: bool
  847. is_json_stream: bool
  848. def _populate_api_route_state(
  849. route: _APIRouteLike,
  850. path: str,
  851. endpoint: Callable[..., Any],
  852. *,
  853. response_model: Any = Default(None),
  854. status_code: int | None = None,
  855. tags: list[str | Enum] | None = None,
  856. dependencies: Sequence[params.Depends] | None = None,
  857. summary: str | None = None,
  858. description: str | None = None,
  859. response_description: str = "Successful Response",
  860. responses: dict[int | str, dict[str, Any]] | None = None,
  861. deprecated: bool | None = None,
  862. name: str | None = None,
  863. methods: set[str] | list[str] | None = None,
  864. operation_id: str | None = None,
  865. response_model_include: IncEx | None = None,
  866. response_model_exclude: IncEx | None = None,
  867. response_model_by_alias: bool = True,
  868. response_model_exclude_unset: bool = False,
  869. response_model_exclude_defaults: bool = False,
  870. response_model_exclude_none: bool = False,
  871. include_in_schema: bool = True,
  872. response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
  873. dependency_overrides_provider: Any | None = None,
  874. callbacks: list[BaseRoute] | None = None,
  875. openapi_extra: dict[str, Any] | None = None,
  876. generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder = Default(
  877. generate_unique_id
  878. ),
  879. strict_content_type: bool | DefaultPlaceholder = Default(True),
  880. stream_item_type: Any | None = None,
  881. ) -> None:
  882. route.path = path
  883. route.endpoint = endpoint
  884. route.stream_item_type = stream_item_type
  885. route.summary = summary
  886. route.response_description = response_description
  887. route.deprecated = deprecated
  888. route.operation_id = operation_id
  889. route.response_model_include = response_model_include
  890. route.response_model_exclude = response_model_exclude
  891. route.response_model_by_alias = response_model_by_alias
  892. route.response_model_exclude_unset = response_model_exclude_unset
  893. route.response_model_exclude_defaults = response_model_exclude_defaults
  894. route.response_model_exclude_none = response_model_exclude_none
  895. route.include_in_schema = include_in_schema
  896. route.response_class = response_class
  897. route.dependency_overrides_provider = dependency_overrides_provider
  898. route.callbacks = callbacks
  899. route.openapi_extra = openapi_extra
  900. route.generate_unique_id_function = generate_unique_id_function
  901. route.strict_content_type = strict_content_type
  902. route.tags = tags or []
  903. route.responses = responses or {}
  904. route.name = get_name(endpoint) if name is None else name
  905. route.path_regex, route.path_format, route.param_convertors = compile_path(path)
  906. if methods is None:
  907. methods = ["GET"]
  908. route.methods = {method.upper() for method in methods}
  909. if isinstance(generate_unique_id_function, DefaultPlaceholder):
  910. current_generate_unique_id: Callable[[Any], str] = (
  911. generate_unique_id_function.value
  912. )
  913. else:
  914. current_generate_unique_id = generate_unique_id_function
  915. route.unique_id = route.operation_id or current_generate_unique_id(route)
  916. # normalize enums e.g. http.HTTPStatus
  917. if isinstance(status_code, IntEnum):
  918. status_code = int(status_code)
  919. route.status_code = status_code
  920. route.dependencies = list(dependencies or [])
  921. route.description = description or inspect.cleandoc(route.endpoint.__doc__ or "")
  922. # if a "form feed" character (page break) is found in the description text,
  923. # truncate description text to the content preceding the first "form feed"
  924. route.description = route.description.split("\f")[0].strip()
  925. response_fields = {}
  926. for additional_status_code, response in route.responses.items():
  927. assert isinstance(response, dict), "An additional response must be a dict"
  928. model = response.get("model")
  929. if model:
  930. assert is_body_allowed_for_status_code(additional_status_code), (
  931. f"Status code {additional_status_code} must not have a response body"
  932. )
  933. response_name = f"Response_{additional_status_code}_{route.unique_id}"
  934. response_field = create_model_field(
  935. name=response_name, type_=model, mode="serialization"
  936. )
  937. response_fields[additional_status_code] = response_field
  938. if response_fields:
  939. route.response_fields = response_fields
  940. else:
  941. route.response_fields = {}
  942. assert callable(endpoint), "An endpoint must be a callable"
  943. (
  944. route.dependant,
  945. body_params,
  946. route._embed_body_fields,
  947. ) = _build_dependant_with_parameterless_dependencies(
  948. path=route.path_format,
  949. call=route.endpoint,
  950. dependencies=route.dependencies,
  951. )
  952. route.body_field = _get_body_field(
  953. body_params=body_params,
  954. name=route.unique_id,
  955. embed_body_fields=route._embed_body_fields,
  956. )
  957. # Detect generator endpoints that should stream as JSONL or SSE
  958. is_generator = _is_async_gen_callable(route.dependant.call) or _is_gen_callable(
  959. route.dependant.call
  960. )
  961. route.is_sse_stream = is_generator and lenient_issubclass(
  962. response_class, EventSourceResponse
  963. )
  964. route.is_json_stream = is_generator and isinstance(
  965. response_class, DefaultPlaceholder
  966. )
  967. if isinstance(response_model, DefaultPlaceholder):
  968. return_annotation = get_typed_return_annotation(endpoint)
  969. if lenient_issubclass(return_annotation, Response):
  970. response_model = None
  971. else:
  972. stream_item = get_stream_item_type(return_annotation)
  973. if stream_item is not None and is_generator:
  974. # Extract item type for JSONL or SSE streaming for
  975. # generator endpoints when response_class is
  976. # DefaultPlaceholder (JSONL) or EventSourceResponse (SSE).
  977. # ServerSentEvent is excluded: it's a transport
  978. # wrapper, not a data model, so it shouldn't feed
  979. # into validation or OpenAPI schema generation.
  980. if (
  981. isinstance(response_class, DefaultPlaceholder)
  982. or lenient_issubclass(response_class, EventSourceResponse)
  983. ) and not lenient_issubclass(stream_item, ServerSentEvent):
  984. route.stream_item_type = stream_item
  985. response_model = None
  986. else:
  987. response_model = return_annotation
  988. route.response_model = response_model
  989. if route.response_model:
  990. assert is_body_allowed_for_status_code(status_code), (
  991. f"Status code {status_code} must not have a response body"
  992. )
  993. response_name = "Response_" + route.unique_id
  994. route.response_field = create_model_field(
  995. name=response_name,
  996. type_=route.response_model,
  997. mode="serialization",
  998. )
  999. else:
  1000. route.response_field = None
  1001. if route.stream_item_type:
  1002. stream_item_name = "StreamItem_" + route.unique_id
  1003. route.stream_item_field = create_model_field(
  1004. name=stream_item_name,
  1005. type_=route.stream_item_type,
  1006. mode="serialization",
  1007. )
  1008. else:
  1009. route.stream_item_field = None
  1010. class APIRoute(routing.Route):
  1011. stream_item_type: Any | None
  1012. response_model: Any
  1013. summary: str | None
  1014. response_description: str
  1015. deprecated: bool | None
  1016. operation_id: str | None
  1017. response_model_include: IncEx | None
  1018. response_model_exclude: IncEx | None
  1019. response_model_by_alias: bool
  1020. response_model_exclude_unset: bool
  1021. response_model_exclude_defaults: bool
  1022. response_model_exclude_none: bool
  1023. include_in_schema: bool
  1024. response_class: type[Response] | DefaultPlaceholder
  1025. dependency_overrides_provider: Any | None
  1026. callbacks: list[BaseRoute] | None
  1027. openapi_extra: dict[str, Any] | None
  1028. generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder
  1029. strict_content_type: bool | DefaultPlaceholder
  1030. tags: list[str | Enum]
  1031. responses: dict[int | str, dict[str, Any]]
  1032. unique_id: str
  1033. status_code: int | None
  1034. response_field: ModelField | None
  1035. stream_item_field: ModelField | None
  1036. dependencies: list[params.Depends]
  1037. description: str
  1038. response_fields: dict[int | str, ModelField]
  1039. dependant: Dependant
  1040. _embed_body_fields: bool
  1041. body_field: ModelField | None
  1042. is_sse_stream: bool
  1043. is_json_stream: bool
  1044. def __init__(
  1045. self,
  1046. path: str,
  1047. endpoint: Callable[..., Any],
  1048. *,
  1049. response_model: Any = Default(None),
  1050. status_code: int | None = None,
  1051. tags: list[str | Enum] | None = None,
  1052. dependencies: Sequence[params.Depends] | None = None,
  1053. summary: str | None = None,
  1054. description: str | None = None,
  1055. response_description: str = "Successful Response",
  1056. responses: dict[int | str, dict[str, Any]] | None = None,
  1057. deprecated: bool | None = None,
  1058. name: str | None = None,
  1059. methods: set[str] | list[str] | None = None,
  1060. operation_id: str | None = None,
  1061. response_model_include: IncEx | None = None,
  1062. response_model_exclude: IncEx | None = None,
  1063. response_model_by_alias: bool = True,
  1064. response_model_exclude_unset: bool = False,
  1065. response_model_exclude_defaults: bool = False,
  1066. response_model_exclude_none: bool = False,
  1067. include_in_schema: bool = True,
  1068. response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
  1069. dependency_overrides_provider: Any | None = None,
  1070. callbacks: list[BaseRoute] | None = None,
  1071. openapi_extra: dict[str, Any] | None = None,
  1072. generate_unique_id_function: Callable[["APIRoute"], str]
  1073. | DefaultPlaceholder = Default(generate_unique_id),
  1074. strict_content_type: bool | DefaultPlaceholder = Default(True),
  1075. ) -> None:
  1076. _populate_api_route_state(
  1077. cast(_APIRouteLike, self),
  1078. path,
  1079. endpoint,
  1080. response_model=response_model,
  1081. status_code=status_code,
  1082. tags=tags,
  1083. dependencies=dependencies,
  1084. summary=summary,
  1085. description=description,
  1086. response_description=response_description,
  1087. responses=responses,
  1088. deprecated=deprecated,
  1089. name=name,
  1090. methods=methods,
  1091. operation_id=operation_id,
  1092. response_model_include=response_model_include,
  1093. response_model_exclude=response_model_exclude,
  1094. response_model_by_alias=response_model_by_alias,
  1095. response_model_exclude_unset=response_model_exclude_unset,
  1096. response_model_exclude_defaults=response_model_exclude_defaults,
  1097. response_model_exclude_none=response_model_exclude_none,
  1098. include_in_schema=include_in_schema,
  1099. response_class=response_class,
  1100. dependency_overrides_provider=dependency_overrides_provider,
  1101. callbacks=callbacks,
  1102. openapi_extra=openapi_extra,
  1103. generate_unique_id_function=generate_unique_id_function,
  1104. strict_content_type=strict_content_type,
  1105. )
  1106. self.app = request_response(self.get_route_handler())
  1107. def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
  1108. route = cast(_APIRouteLike, self)
  1109. # TODO: Replace or deprecate this no-scope hook so included-route
  1110. # effective context can be passed explicitly instead of via ContextVar.
  1111. effective_context = _effective_route_context_var.get()
  1112. if effective_context is not None and effective_context.original_route is self:
  1113. route = cast(_APIRouteLike, effective_context)
  1114. return get_request_handler(
  1115. dependant=route.dependant,
  1116. body_field=route.body_field,
  1117. status_code=route.status_code,
  1118. response_class=route.response_class,
  1119. response_field=route.response_field,
  1120. response_model_include=route.response_model_include,
  1121. response_model_exclude=route.response_model_exclude,
  1122. response_model_by_alias=route.response_model_by_alias,
  1123. response_model_exclude_unset=route.response_model_exclude_unset,
  1124. response_model_exclude_defaults=route.response_model_exclude_defaults,
  1125. response_model_exclude_none=route.response_model_exclude_none,
  1126. dependency_overrides_provider=route.dependency_overrides_provider,
  1127. embed_body_fields=route._embed_body_fields,
  1128. strict_content_type=route.strict_content_type,
  1129. stream_item_field=route.stream_item_field,
  1130. is_json_stream=route.is_json_stream,
  1131. )
  1132. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  1133. effective_context = _get_scope_effective_route_context(scope)
  1134. if effective_context is not None and effective_context.original_route is self:
  1135. match, child_scope = effective_context.matches(scope)
  1136. else:
  1137. match, child_scope = super().matches(scope)
  1138. if match != Match.NONE:
  1139. child_scope["route"] = self
  1140. return match, child_scope
  1141. async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
  1142. effective_context = _get_scope_effective_route_context(scope)
  1143. if effective_context is not None and effective_context.original_route is self:
  1144. methods = effective_context.methods
  1145. if methods and scope["method"] not in methods:
  1146. headers = {"Allow": ", ".join(methods)}
  1147. if "app" in scope:
  1148. raise HTTPException(status_code=405, headers=headers)
  1149. response = PlainTextResponse(
  1150. "Method Not Allowed", status_code=405, headers=headers
  1151. )
  1152. await response(scope, receive, send)
  1153. return
  1154. token = _effective_route_context_var.set(effective_context)
  1155. try:
  1156. app = request_response(self.get_route_handler())
  1157. finally:
  1158. _effective_route_context_var.reset(token)
  1159. await app(scope, receive, send)
  1160. return
  1161. await super().handle(scope, receive, send)
  1162. @dataclass
  1163. class _RouterIncludeContext:
  1164. included_router: "APIRouter"
  1165. prefix: str = ""
  1166. tags: list[str | Enum] = field(default_factory=list)
  1167. dependencies: list[params.Depends] = field(default_factory=list)
  1168. default_response_class: type[Response] | DefaultPlaceholder = field(
  1169. default_factory=lambda: Default(JSONResponse)
  1170. )
  1171. responses: dict[int | str, dict[str, Any]] = field(default_factory=dict)
  1172. callbacks: list[BaseRoute] = field(default_factory=list)
  1173. deprecated: bool | None = None
  1174. include_in_schema: bool = True
  1175. generate_unique_id_function: Callable[[APIRoute], str] | DefaultPlaceholder = field(
  1176. default_factory=lambda: Default(generate_unique_id)
  1177. )
  1178. strict_content_type: bool | DefaultPlaceholder = field(
  1179. default_factory=lambda: Default(True)
  1180. )
  1181. dependency_overrides_provider: Any | None = None
  1182. @classmethod
  1183. def for_include(
  1184. cls,
  1185. *,
  1186. parent_router: "APIRouter",
  1187. included_router: "APIRouter",
  1188. prefix: str = "",
  1189. tags: list[str | Enum] | None = None,
  1190. dependencies: Sequence[params.Depends] | None = None,
  1191. default_response_class: type[Response] | DefaultPlaceholder = Default(
  1192. JSONResponse
  1193. ),
  1194. responses: dict[int | str, dict[str, Any]] | None = None,
  1195. callbacks: list[BaseRoute] | None = None,
  1196. deprecated: bool | None = None,
  1197. include_in_schema: bool = True,
  1198. generate_unique_id_function: Callable[[APIRoute], str]
  1199. | DefaultPlaceholder = Default(generate_unique_id),
  1200. ) -> "_RouterIncludeContext":
  1201. return cls(
  1202. included_router=included_router,
  1203. prefix=parent_router.prefix + prefix,
  1204. tags=[*parent_router.tags, *(tags or [])],
  1205. dependencies=[*parent_router.dependencies, *(dependencies or [])],
  1206. default_response_class=get_value_or_default(
  1207. default_response_class, parent_router.default_response_class
  1208. ),
  1209. responses={**parent_router.responses, **(responses or {})},
  1210. callbacks=[*parent_router.callbacks, *(callbacks or [])],
  1211. deprecated=deprecated or parent_router.deprecated,
  1212. include_in_schema=parent_router.include_in_schema and include_in_schema,
  1213. generate_unique_id_function=get_value_or_default(
  1214. generate_unique_id_function, parent_router.generate_unique_id_function
  1215. ),
  1216. strict_content_type=parent_router.strict_content_type,
  1217. dependency_overrides_provider=parent_router.dependency_overrides_provider,
  1218. )
  1219. def combine(
  1220. self, child_context: "_RouterIncludeContext"
  1221. ) -> "_RouterIncludeContext":
  1222. return _RouterIncludeContext(
  1223. included_router=child_context.included_router,
  1224. prefix=self.prefix + child_context.prefix,
  1225. tags=[*self.tags, *child_context.tags],
  1226. dependencies=[*self.dependencies, *child_context.dependencies],
  1227. default_response_class=get_value_or_default(
  1228. child_context.default_response_class, self.default_response_class
  1229. ),
  1230. responses={**self.responses, **child_context.responses},
  1231. callbacks=[*self.callbacks, *child_context.callbacks],
  1232. deprecated=self.deprecated or child_context.deprecated,
  1233. include_in_schema=self.include_in_schema
  1234. and child_context.include_in_schema,
  1235. generate_unique_id_function=get_value_or_default(
  1236. child_context.generate_unique_id_function,
  1237. self.generate_unique_id_function,
  1238. ),
  1239. strict_content_type=get_value_or_default(
  1240. child_context.strict_content_type, self.strict_content_type
  1241. ),
  1242. dependency_overrides_provider=self.dependency_overrides_provider,
  1243. )
  1244. def path_for(self, route: _RouteWithPath) -> str:
  1245. return self.prefix + route.path
  1246. @dataclass
  1247. class _EffectiveRouteContext:
  1248. original_route: BaseRoute
  1249. starlette_route: BaseRoute | None = None
  1250. frontend_prefix: str = ""
  1251. path: str = ""
  1252. endpoint: Callable[..., Any] | None = None
  1253. stream_item_type: Any | None = None
  1254. response_model: Any = None
  1255. summary: str | None = None
  1256. response_description: str = "Successful Response"
  1257. deprecated: bool | None = None
  1258. operation_id: str | None = None
  1259. response_model_include: IncEx | None = None
  1260. response_model_exclude: IncEx | None = None
  1261. response_model_by_alias: bool = True
  1262. response_model_exclude_unset: bool = False
  1263. response_model_exclude_defaults: bool = False
  1264. response_model_exclude_none: bool = False
  1265. include_in_schema: bool = True
  1266. response_class: type[Response] | DefaultPlaceholder = field(
  1267. default_factory=lambda: Default(JSONResponse)
  1268. )
  1269. dependency_overrides_provider: Any | None = None
  1270. callbacks: list[BaseRoute] | None = None
  1271. openapi_extra: dict[str, Any] | None = None
  1272. generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder = field(
  1273. default_factory=lambda: Default(generate_unique_id)
  1274. )
  1275. strict_content_type: bool | DefaultPlaceholder = field(
  1276. default_factory=lambda: Default(True)
  1277. )
  1278. tags: list[str | Enum] = field(default_factory=list)
  1279. responses: dict[int | str, dict[str, Any]] = field(default_factory=dict)
  1280. name: str = ""
  1281. path_regex: Any = None
  1282. path_format: str = ""
  1283. param_convertors: dict[str, Any] = field(default_factory=dict)
  1284. methods: set[str] = field(default_factory=set)
  1285. unique_id: str = ""
  1286. status_code: int | None = None
  1287. response_field: ModelField | None = None
  1288. stream_item_field: ModelField | None = None
  1289. dependencies: list[params.Depends] = field(default_factory=list)
  1290. description: str = ""
  1291. response_fields: dict[int | str, ModelField] = field(default_factory=dict)
  1292. dependant: Dependant | None = None
  1293. _embed_body_fields: bool = False
  1294. body_field: ModelField | None = None
  1295. is_sse_stream: bool = False
  1296. is_json_stream: bool = False
  1297. @classmethod
  1298. def from_api_route(
  1299. cls,
  1300. *,
  1301. original_route: APIRoute,
  1302. include_context: _RouterIncludeContext,
  1303. ) -> "_EffectiveRouteContext":
  1304. route = cast(_APIRouteLike, original_route)
  1305. context = cls(original_route=original_route)
  1306. _populate_api_route_state(
  1307. cast(_APIRouteLike, context),
  1308. include_context.path_for(original_route),
  1309. route.endpoint,
  1310. response_model=route.response_model,
  1311. status_code=route.status_code,
  1312. tags=[*include_context.tags, *route.tags],
  1313. dependencies=[*include_context.dependencies, *route.dependencies],
  1314. summary=route.summary,
  1315. description=route.description,
  1316. response_description=route.response_description,
  1317. responses={**include_context.responses, **route.responses},
  1318. deprecated=route.deprecated or include_context.deprecated,
  1319. methods=route.methods,
  1320. operation_id=route.operation_id,
  1321. response_model_include=route.response_model_include,
  1322. response_model_exclude=route.response_model_exclude,
  1323. response_model_by_alias=route.response_model_by_alias,
  1324. response_model_exclude_unset=route.response_model_exclude_unset,
  1325. response_model_exclude_defaults=route.response_model_exclude_defaults,
  1326. response_model_exclude_none=route.response_model_exclude_none,
  1327. include_in_schema=route.include_in_schema
  1328. and include_context.include_in_schema,
  1329. response_class=get_value_or_default(
  1330. route.response_class,
  1331. include_context.included_router.default_response_class,
  1332. include_context.default_response_class,
  1333. ),
  1334. name=route.name,
  1335. dependency_overrides_provider=include_context.dependency_overrides_provider,
  1336. callbacks=[*include_context.callbacks, *(route.callbacks or [])],
  1337. openapi_extra=route.openapi_extra,
  1338. generate_unique_id_function=get_value_or_default(
  1339. route.generate_unique_id_function,
  1340. include_context.included_router.generate_unique_id_function,
  1341. include_context.generate_unique_id_function,
  1342. ),
  1343. strict_content_type=get_value_or_default(
  1344. route.strict_content_type,
  1345. include_context.included_router.strict_content_type,
  1346. include_context.strict_content_type,
  1347. ),
  1348. stream_item_type=route.stream_item_type,
  1349. )
  1350. return context
  1351. @classmethod
  1352. def from_frontend_route_group(
  1353. cls,
  1354. *,
  1355. original_route: "_FrontendRouteGroup",
  1356. include_context: _RouterIncludeContext,
  1357. ) -> "_EffectiveRouteContext":
  1358. dependencies = [*include_context.dependencies, *original_route.dependencies]
  1359. context = cls(
  1360. original_route=original_route,
  1361. frontend_prefix=include_context.prefix,
  1362. dependencies=dependencies,
  1363. dependency_overrides_provider=include_context.dependency_overrides_provider,
  1364. )
  1365. (
  1366. context.dependant,
  1367. _,
  1368. context._embed_body_fields,
  1369. ) = _build_dependant_with_parameterless_dependencies(
  1370. path="",
  1371. call=_frontend_dependency_endpoint,
  1372. dependencies=dependencies,
  1373. )
  1374. return context
  1375. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  1376. if isinstance(self.original_route, _FrontendRouteGroup):
  1377. return self.original_route.matches_with_prefix(scope, self.frontend_prefix)
  1378. if not isinstance(self.original_route, APIRoute):
  1379. assert self.starlette_route is not None
  1380. return self.starlette_route.matches(scope)
  1381. if scope["type"] != "http":
  1382. return Match.NONE, {}
  1383. route_path = get_route_path(scope)
  1384. match = self.path_regex.match(route_path)
  1385. if not match:
  1386. return Match.NONE, {}
  1387. matched_params = match.groupdict()
  1388. for key, value in matched_params.items():
  1389. matched_params[key] = self.param_convertors[key].convert(value)
  1390. path_params = dict(scope.get("path_params", {}))
  1391. path_params.update(matched_params)
  1392. child_scope = {"endpoint": self.endpoint, "path_params": path_params}
  1393. methods = self.methods
  1394. if methods and scope["method"] not in methods:
  1395. return Match.PARTIAL, child_scope
  1396. return Match.FULL, child_scope
  1397. def url_path_for(self, name: str, /, **path_params: Any) -> Any:
  1398. if not isinstance(self.original_route, APIRoute):
  1399. assert self.starlette_route is not None
  1400. return self.starlette_route.url_path_for(name, **path_params)
  1401. seen_params = set(path_params.keys())
  1402. param_convertors = self.param_convertors
  1403. expected_params = set(param_convertors.keys())
  1404. if name != self.name or seen_params != expected_params:
  1405. raise routing.NoMatchFound(name, path_params)
  1406. path, remaining_params = routing.replace_params(
  1407. self.path_format, param_convertors, path_params
  1408. )
  1409. assert not remaining_params
  1410. return URLPath(path=path, protocol="http")
  1411. @dataclass(frozen=True)
  1412. class RouteContext:
  1413. route: BaseRoute
  1414. _route_context: _EffectiveRouteContext | None = field(default=None, repr=False)
  1415. @property
  1416. def original_route(self) -> BaseRoute:
  1417. if self._route_context is not None:
  1418. return self._route_context.original_route
  1419. return self.route
  1420. @property
  1421. def _effective_route(self) -> BaseRoute | _EffectiveRouteContext:
  1422. if self._route_context is not None:
  1423. return self._route_context
  1424. return self.route
  1425. @property
  1426. def path(self) -> str | None:
  1427. return getattr(self._effective_route, "path", None)
  1428. @property
  1429. def path_format(self) -> str | None:
  1430. return getattr(self._effective_route, "path_format", None)
  1431. @property
  1432. def name(self) -> str | None:
  1433. return getattr(self._effective_route, "name", None)
  1434. @property
  1435. def methods(self) -> set[str] | None:
  1436. return getattr(self._effective_route, "methods", None)
  1437. @property
  1438. def endpoint(self) -> Callable[..., Any] | None:
  1439. return getattr(self._effective_route, "endpoint", None)
  1440. def __getattr__(self, name: str) -> Any:
  1441. return getattr(self._effective_route, name)
  1442. @dataclass
  1443. class _IncludedRouter(BaseRoute):
  1444. original_router: "APIRouter"
  1445. include_context: _RouterIncludeContext
  1446. _effective_routes_lock: Any = field(
  1447. default_factory=threading.Lock, repr=False, compare=False
  1448. )
  1449. _effective_candidates: list["_EffectiveRouteContext | _IncludedRouter"] = field(
  1450. default_factory=list
  1451. )
  1452. _effective_candidates_version: int | None = None
  1453. _effective_low_priority_routes: list["_EffectiveRouteContext"] = field(
  1454. default_factory=list
  1455. )
  1456. _effective_low_priority_routes_version: int | None = None
  1457. def effective_candidates(self) -> list["_EffectiveRouteContext | _IncludedRouter"]:
  1458. routes_version = self.original_router._get_routes_version()
  1459. if routes_version == self._effective_candidates_version:
  1460. return self._effective_candidates
  1461. with self._effective_routes_lock:
  1462. routes_version = self.original_router._get_routes_version()
  1463. if routes_version == self._effective_candidates_version:
  1464. return self._effective_candidates
  1465. effective_candidates: list[_EffectiveRouteContext | _IncludedRouter] = []
  1466. for route in self.original_router.routes:
  1467. if isinstance(route, _IncludedRouter):
  1468. child_context = self.include_context.combine(route.include_context)
  1469. child_branch = _IncludedRouter(
  1470. original_router=route.original_router,
  1471. include_context=child_context,
  1472. )
  1473. effective_candidates.append(child_branch)
  1474. continue
  1475. route_context = self._build_effective_context(route)
  1476. if route_context is not None:
  1477. effective_candidates.append(route_context)
  1478. self._effective_candidates = effective_candidates
  1479. self._effective_candidates_version = routes_version
  1480. return effective_candidates
  1481. def effective_low_priority_routes(self) -> list["_EffectiveRouteContext"]:
  1482. routes_version = self.original_router._get_routes_version()
  1483. if routes_version == self._effective_low_priority_routes_version:
  1484. return self._effective_low_priority_routes
  1485. with self._effective_routes_lock:
  1486. routes_version = self.original_router._get_routes_version()
  1487. if routes_version == self._effective_low_priority_routes_version:
  1488. return self._effective_low_priority_routes
  1489. effective_low_priority_routes: list[_EffectiveRouteContext] = []
  1490. for route in self.original_router._low_priority_routes:
  1491. route_context = self._build_effective_context(route)
  1492. if route_context is not None:
  1493. effective_low_priority_routes.append(route_context)
  1494. for route in self.original_router.routes:
  1495. if isinstance(route, _IncludedRouter):
  1496. child_context = self.include_context.combine(route.include_context)
  1497. child_branch = _IncludedRouter(
  1498. original_router=route.original_router,
  1499. include_context=child_context,
  1500. )
  1501. effective_low_priority_routes.extend(
  1502. child_branch.effective_low_priority_routes()
  1503. )
  1504. self._effective_low_priority_routes = effective_low_priority_routes
  1505. self._effective_low_priority_routes_version = routes_version
  1506. return effective_low_priority_routes
  1507. def _build_effective_context(
  1508. self, route: BaseRoute
  1509. ) -> _EffectiveRouteContext | None:
  1510. if isinstance(route, APIRoute):
  1511. return _EffectiveRouteContext.from_api_route(
  1512. original_route=route,
  1513. include_context=self.include_context,
  1514. )
  1515. if isinstance(route, _FrontendRouteGroup):
  1516. return _EffectiveRouteContext.from_frontend_route_group(
  1517. original_route=route,
  1518. include_context=self.include_context,
  1519. )
  1520. if isinstance(route, routing.Route):
  1521. starlette_route: BaseRoute = routing.Route(
  1522. self.include_context.path_for(route),
  1523. endpoint=route.endpoint,
  1524. methods=list(route.methods or []),
  1525. name=route.name,
  1526. include_in_schema=route.include_in_schema,
  1527. )
  1528. return _EffectiveRouteContext(
  1529. original_route=route,
  1530. starlette_route=starlette_route,
  1531. )
  1532. if isinstance(route, APIWebSocketRoute):
  1533. starlette_route = APIWebSocketRoute(
  1534. self.include_context.path_for(route),
  1535. endpoint=route.endpoint,
  1536. name=route.name,
  1537. dependencies=[*self.include_context.dependencies, *route.dependencies],
  1538. dependency_overrides_provider=(
  1539. self.include_context.dependency_overrides_provider
  1540. ),
  1541. )
  1542. return _EffectiveRouteContext(
  1543. original_route=route,
  1544. starlette_route=starlette_route,
  1545. )
  1546. if isinstance(route, routing.WebSocketRoute):
  1547. starlette_route = routing.WebSocketRoute(
  1548. self.include_context.path_for(route), route.endpoint, name=route.name
  1549. )
  1550. return _EffectiveRouteContext(
  1551. original_route=route,
  1552. starlette_route=starlette_route,
  1553. )
  1554. if isinstance(route, routing.Mount):
  1555. starlette_route = copy.copy(route)
  1556. starlette_route.path = self.include_context.path_for(route).rstrip("/")
  1557. (
  1558. starlette_route.path_regex,
  1559. starlette_route.path_format,
  1560. starlette_route.param_convertors,
  1561. ) = compile_path(starlette_route.path + "/{path:path}")
  1562. return _EffectiveRouteContext(
  1563. original_route=route,
  1564. starlette_route=starlette_route,
  1565. )
  1566. if isinstance(route, routing.Host):
  1567. if self.include_context.prefix:
  1568. prefixed_app: ASGIApp = routing.Router(
  1569. routes=[routing.Mount(self.include_context.prefix, app=route.app)]
  1570. )
  1571. else:
  1572. prefixed_app = route.app
  1573. starlette_route = routing.Host(
  1574. route.host, app=prefixed_app, name=route.name
  1575. )
  1576. return _EffectiveRouteContext(
  1577. original_route=route,
  1578. starlette_route=starlette_route,
  1579. )
  1580. return None
  1581. def _match(
  1582. self, scope: Scope
  1583. ) -> tuple[Match, Scope, BaseRoute | None, _EffectiveRouteContext | None]:
  1584. partial: tuple[Scope, BaseRoute, _EffectiveRouteContext | None] | None = None
  1585. for candidate in self.effective_candidates():
  1586. if isinstance(candidate, _IncludedRouter):
  1587. match, child_scope = candidate.matches(scope)
  1588. route: BaseRoute = candidate
  1589. route_context = None
  1590. elif isinstance(candidate.original_route, APIRoute):
  1591. route_context = candidate
  1592. fastapi_scope = _get_fastapi_scope(scope)
  1593. previous_context = fastapi_scope.get(
  1594. _FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY, _SCOPE_MISSING
  1595. )
  1596. fastapi_scope[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = route_context
  1597. try:
  1598. match, child_scope = candidate.original_route.matches(scope)
  1599. finally:
  1600. _restore_fastapi_scope_key(
  1601. scope, _FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY, previous_context
  1602. )
  1603. route = candidate.original_route
  1604. else:
  1605. route_context = candidate
  1606. match, child_scope = candidate.matches(scope)
  1607. route = candidate.starlette_route or candidate.original_route
  1608. if match == Match.FULL:
  1609. return match, child_scope, route, route_context
  1610. if match == Match.PARTIAL and partial is None:
  1611. partial = (child_scope, route, route_context)
  1612. if partial is not None:
  1613. child_scope, route, route_context = partial
  1614. return Match.PARTIAL, child_scope, route, route_context
  1615. return Match.NONE, {}, None, None
  1616. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  1617. fastapi_scope = _get_fastapi_scope(scope)
  1618. previous_router = fastapi_scope.get(
  1619. _FASTAPI_INCLUDED_ROUTER_KEY, _SCOPE_MISSING
  1620. )
  1621. fastapi_scope[_FASTAPI_INCLUDED_ROUTER_KEY] = self
  1622. try:
  1623. match, _ = self.original_router.matches(scope)
  1624. return match, {}
  1625. finally:
  1626. _restore_fastapi_scope_key(
  1627. scope, _FASTAPI_INCLUDED_ROUTER_KEY, previous_router
  1628. )
  1629. async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
  1630. _get_fastapi_scope(scope)[_FASTAPI_INCLUDED_ROUTER_KEY] = self
  1631. await self.original_router.handle(scope, receive, send)
  1632. async def _handle_selected(
  1633. self, scope: Scope, receive: Receive, send: Send
  1634. ) -> None:
  1635. match, child_scope, route, effective_context = self._match(scope)
  1636. if match == Match.NONE or route is None:
  1637. await self.original_router.default(scope, receive, send)
  1638. return
  1639. scope.update(child_scope)
  1640. if isinstance(route, _IncludedRouter):
  1641. await route.handle(scope, receive, send)
  1642. return
  1643. if effective_context is not None:
  1644. _get_fastapi_scope(scope)[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = (
  1645. effective_context
  1646. )
  1647. original_route = effective_context.original_route
  1648. if isinstance(original_route, APIRoute):
  1649. scope["route"] = original_route
  1650. await original_route.handle(scope, receive, send)
  1651. return
  1652. await route.handle(scope, receive, send)
  1653. def effective_route_contexts(self) -> Iterator[_EffectiveRouteContext]:
  1654. for candidate in self.effective_candidates():
  1655. if isinstance(candidate, _IncludedRouter):
  1656. yield from candidate.effective_route_contexts()
  1657. else:
  1658. yield candidate
  1659. def url_path_for(self, name: str, /, **path_params: Any) -> Any:
  1660. for route_context in self.effective_route_contexts():
  1661. try:
  1662. return route_context.url_path_for(name, **path_params)
  1663. except routing.NoMatchFound:
  1664. pass
  1665. raise routing.NoMatchFound(name, path_params)
  1666. def _iter_included_route_candidates(routes: Sequence[BaseRoute]) -> Iterator[BaseRoute]:
  1667. for route, route_context in _iter_routes_with_context(routes):
  1668. if route_context is not None and route_context.starlette_route is not None:
  1669. yield route_context.starlette_route
  1670. else:
  1671. yield route
  1672. def iter_route_contexts(
  1673. routes: Sequence[BaseRoute | RouteContext],
  1674. ) -> Iterator[RouteContext]:
  1675. for route in routes:
  1676. if isinstance(route, RouteContext):
  1677. yield route
  1678. continue
  1679. for original_route, route_context in _iter_routes_with_context([route]):
  1680. if route_context is None:
  1681. yield RouteContext(original_route)
  1682. else:
  1683. yield RouteContext(original_route, route_context)
  1684. def _iter_routes_with_context(
  1685. routes: Sequence[BaseRoute],
  1686. ) -> Iterator[tuple[BaseRoute, _EffectiveRouteContext | None]]:
  1687. for route in routes:
  1688. if isinstance(route, _IncludedRouter):
  1689. for route_context in route.effective_route_contexts():
  1690. yield route_context.original_route, route_context
  1691. else:
  1692. yield route, None
  1693. def _normalize_frontend_path(path: str) -> str:
  1694. if not path:
  1695. raise AssertionError("A frontend path cannot be empty")
  1696. if not path.startswith("/"):
  1697. raise AssertionError("A frontend path must start with '/'")
  1698. if path != "/":
  1699. path = path.rstrip("/")
  1700. return path
  1701. def _join_frontend_paths(prefix: str, path: str) -> str:
  1702. if not prefix:
  1703. return path
  1704. if path == "/":
  1705. return prefix
  1706. return prefix + path
  1707. def _frontend_path_specificity(path: str) -> int:
  1708. if path == "/":
  1709. return 0
  1710. return len(path)
  1711. def _get_resolved_absolute_path(path: str | os.PathLike[str]) -> str:
  1712. return os.path.realpath(os.fspath(path))
  1713. def _resolve_frontend_check_dir(
  1714. *,
  1715. directory: str | os.PathLike[str],
  1716. check_dir: bool | Literal["auto"],
  1717. ) -> bool:
  1718. if check_dir != "auto":
  1719. return check_dir
  1720. if os.environ.get("FASTAPI_ENV") != "development":
  1721. return True
  1722. if not os.path.isdir(directory):
  1723. warnings.warn(
  1724. f"Frontend directory '{directory}' does not exist. "
  1725. f"Resolved absolute path: '{_get_resolved_absolute_path(directory)}'",
  1726. stacklevel=3,
  1727. )
  1728. return False
  1729. class _FrontendStaticFiles(StaticFiles):
  1730. def __init__(
  1731. self,
  1732. *,
  1733. directory: str | os.PathLike[str],
  1734. fallback: Literal["auto", "index.html", "404.html"] | None,
  1735. check_dir: bool,
  1736. ) -> None:
  1737. self.fallback = fallback
  1738. if check_dir and not os.path.isdir(directory):
  1739. raise RuntimeError(
  1740. f"Frontend directory '{directory}' does not exist. "
  1741. f"Resolved absolute path: '{_get_resolved_absolute_path(directory)}'"
  1742. )
  1743. super().__init__(
  1744. directory=directory,
  1745. html=True,
  1746. check_dir=check_dir,
  1747. follow_symlink=False,
  1748. )
  1749. if check_dir and fallback in {"index.html", "404.html"}:
  1750. self._check_fallback_file(fallback)
  1751. def _check_fallback_file(self, fallback: str) -> None:
  1752. _, stat_result = self.lookup_path(fallback)
  1753. if stat_result is None or not stat.S_ISREG(stat_result.st_mode):
  1754. raise RuntimeError(
  1755. f"Frontend fallback file '{fallback}' does not exist in "
  1756. f"directory '{self.directory}'. Resolved absolute directory: "
  1757. f"'{self._get_resolved_directory()}'"
  1758. )
  1759. def _get_resolved_directory(self) -> str:
  1760. assert self.directory is not None
  1761. return _get_resolved_absolute_path(self.directory)
  1762. def get_path(self, scope: Scope) -> str:
  1763. path = _get_fastapi_scope(scope).get(_FASTAPI_FRONTEND_PATH_KEY, "")
  1764. assert isinstance(path, str)
  1765. return os.path.normpath(os.path.join(*path.split("/")))
  1766. async def get_response_for_scope(self, scope: Scope) -> Response:
  1767. if not self.config_checked:
  1768. await self.check_config()
  1769. self.config_checked = True
  1770. return await self.get_response(self.get_path(scope), scope)
  1771. async def get_response(self, path: str, scope: Scope) -> Response:
  1772. if scope["method"] not in ("GET", "HEAD"):
  1773. if await self._lookup_static_resource(path) is not None:
  1774. raise HTTPException(status_code=405)
  1775. raise HTTPException(status_code=404)
  1776. static_resource = await self._lookup_static_resource(path)
  1777. if static_resource is not None:
  1778. full_path, stat_result, is_directory_index = static_resource
  1779. if is_directory_index and not scope["path"].endswith("/"):
  1780. url = URL(scope=scope)
  1781. url = url.replace(path=url.path + "/")
  1782. return RedirectResponse(url=url)
  1783. return self.file_response(full_path, stat_result, scope)
  1784. if self.fallback == "404.html" or (
  1785. self.fallback == "auto" and self._fallback_file_exists("404.html")
  1786. ):
  1787. return await self._fallback_response("404.html", scope, status_code=404)
  1788. if (
  1789. self.fallback == "index.html"
  1790. or (self.fallback == "auto" and self._fallback_file_exists("index.html"))
  1791. ) and _is_frontend_navigation_request(scope):
  1792. return await self._fallback_response("index.html", scope, status_code=200)
  1793. raise HTTPException(status_code=404)
  1794. async def _lookup_path(self, path: str) -> tuple[str, os.stat_result | None]:
  1795. try:
  1796. return await run_in_threadpool(self.lookup_path, path)
  1797. except PermissionError:
  1798. raise HTTPException(status_code=401) from None
  1799. except OSError as exc:
  1800. if exc.errno == errno.ENAMETOOLONG:
  1801. raise HTTPException(status_code=404) from None
  1802. raise exc
  1803. except ValueError:
  1804. raise HTTPException(status_code=404) from None
  1805. async def _lookup_static_resource(
  1806. self, path: str
  1807. ) -> tuple[str, os.stat_result, bool] | None:
  1808. full_path, stat_result = await self._lookup_path(path)
  1809. if stat_result is None:
  1810. return None
  1811. if stat.S_ISREG(stat_result.st_mode):
  1812. return full_path, stat_result, False
  1813. if stat.S_ISDIR(stat_result.st_mode):
  1814. index_path = os.path.join(path, "index.html")
  1815. full_path, stat_result = await self._lookup_path(index_path)
  1816. if stat_result is not None and stat.S_ISREG(stat_result.st_mode):
  1817. return full_path, stat_result, True
  1818. return None
  1819. def _fallback_file_exists(self, fallback: str) -> bool:
  1820. _, stat_result = self.lookup_path(fallback)
  1821. return stat_result is not None and stat.S_ISREG(stat_result.st_mode)
  1822. async def _fallback_response(
  1823. self, fallback: str, scope: Scope, *, status_code: int
  1824. ) -> Response:
  1825. full_path, stat_result = await run_in_threadpool(self.lookup_path, fallback)
  1826. if stat_result is None or not stat.S_ISREG(stat_result.st_mode):
  1827. raise RuntimeError(
  1828. f"Frontend fallback file '{fallback}' does not exist in "
  1829. f"directory '{self.directory}'. Resolved absolute directory: "
  1830. f"'{self._get_resolved_directory()}'"
  1831. )
  1832. return self.file_response(
  1833. full_path, stat_result, scope, status_code=status_code
  1834. )
  1835. def _iter_accept_media_types(accept: str) -> Iterator[tuple[str, float]]:
  1836. for raw_value in accept.split(","):
  1837. message = email.message.Message()
  1838. message["content-type"] = raw_value.strip()
  1839. q = message.get_param("q")
  1840. quality = 1.0
  1841. if isinstance(q, str):
  1842. try:
  1843. quality = float(q)
  1844. except ValueError:
  1845. pass
  1846. yield (
  1847. f"{message.get_content_maintype()}/{message.get_content_subtype()}",
  1848. quality,
  1849. )
  1850. def _is_frontend_navigation_request(scope: Scope) -> bool:
  1851. request = Request(scope)
  1852. for media_type, quality in _iter_accept_media_types(
  1853. request.headers.get("accept", "")
  1854. ):
  1855. if media_type in {"text/html", "application/xhtml+xml"} and quality != 0:
  1856. return True
  1857. return False
  1858. class _FrontendRoute(BaseRoute):
  1859. def __init__(
  1860. self,
  1861. path: str,
  1862. *,
  1863. directory: str | os.PathLike[str],
  1864. fallback: Literal["auto", "index.html", "404.html"] | None = "auto",
  1865. check_dir: bool,
  1866. ) -> None:
  1867. if fallback not in {"auto", "index.html", "404.html", None}:
  1868. raise AssertionError(
  1869. "fallback must be 'auto', 'index.html', '404.html', or None"
  1870. )
  1871. self.path = _normalize_frontend_path(path)
  1872. self.methods = {"GET", "HEAD"}
  1873. self.app = _FrontendStaticFiles(
  1874. directory=directory, fallback=fallback, check_dir=check_dir
  1875. )
  1876. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  1877. return self.matches_with_path(scope, self.path)
  1878. def matches_with_path(self, scope: Scope, path: str) -> tuple[Match, Scope]:
  1879. if scope["type"] != "http":
  1880. return Match.NONE, {}
  1881. frontend_path = self._get_frontend_path(path, get_route_path(scope))
  1882. if frontend_path is None:
  1883. return Match.NONE, {}
  1884. child_scope = {
  1885. _FASTAPI_SCOPE_KEY: {
  1886. _FASTAPI_FRONTEND_PATH_KEY: frontend_path,
  1887. _FASTAPI_FRONTEND_SPECIFICITY_KEY: _frontend_path_specificity(path),
  1888. }
  1889. }
  1890. if scope["method"] not in self.methods:
  1891. return Match.PARTIAL, child_scope
  1892. return Match.FULL, child_scope
  1893. def _get_frontend_path(self, path: str, route_path: str) -> str | None:
  1894. if path == "/":
  1895. return route_path.lstrip("/")
  1896. if route_path == path:
  1897. return ""
  1898. prefix = path + "/"
  1899. if route_path.startswith(prefix):
  1900. return route_path[len(prefix) :]
  1901. return None
  1902. async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
  1903. response = await self.app.get_response_for_scope(scope)
  1904. await response(scope, receive, send)
  1905. def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
  1906. raise NoMatchFound(name, path_params)
  1907. class _FrontendRouteGroup(BaseRoute):
  1908. def __init__(
  1909. self,
  1910. *,
  1911. dependencies: Sequence[params.Depends] | None = None,
  1912. dependency_overrides_provider: Any | None = None,
  1913. ) -> None:
  1914. self.routes: list[_FrontendRoute] = []
  1915. self.dependencies = list(dependencies or [])
  1916. self.dependency_overrides_provider = dependency_overrides_provider
  1917. (
  1918. self.dependant,
  1919. _,
  1920. self._embed_body_fields,
  1921. ) = _build_dependant_with_parameterless_dependencies(
  1922. path="",
  1923. call=_frontend_dependency_endpoint,
  1924. dependencies=self.dependencies,
  1925. )
  1926. def add_frontend_route(
  1927. self,
  1928. path: str,
  1929. *,
  1930. directory: str | os.PathLike[str],
  1931. fallback: Literal["auto", "index.html", "404.html"] | None = "auto",
  1932. check_dir: bool,
  1933. ) -> None:
  1934. self.routes.append(
  1935. _FrontendRoute(
  1936. path,
  1937. directory=directory,
  1938. fallback=fallback,
  1939. check_dir=check_dir,
  1940. )
  1941. )
  1942. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  1943. match, child_scope, _ = self._match(scope, prefix="")
  1944. return match, child_scope
  1945. def matches_with_prefix(self, scope: Scope, prefix: str) -> tuple[Match, Scope]:
  1946. match, child_scope, _ = self._match(scope, prefix=prefix)
  1947. return match, child_scope
  1948. def _match(
  1949. self, scope: Scope, *, prefix: str
  1950. ) -> tuple[Match, Scope, _FrontendRoute | None]:
  1951. full: tuple[Scope, _FrontendRoute, int] | None = None
  1952. partial: tuple[Scope, _FrontendRoute, int] | None = None
  1953. for route in self.routes:
  1954. path = _join_frontend_paths(prefix, route.path)
  1955. match, child_scope = route.matches_with_path(scope, path)
  1956. specificity = _frontend_path_specificity(path)
  1957. if match == Match.FULL:
  1958. if full is None or specificity > full[2]:
  1959. full = (child_scope, route, specificity)
  1960. elif match == Match.PARTIAL:
  1961. if partial is None or specificity > partial[2]:
  1962. partial = (child_scope, route, specificity)
  1963. if full is not None:
  1964. child_scope, route, _ = full
  1965. return Match.FULL, child_scope, route
  1966. if partial is not None:
  1967. child_scope, route, _ = partial
  1968. return Match.PARTIAL, child_scope, route
  1969. return Match.NONE, {}, None
  1970. async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
  1971. effective_context = _get_scope_effective_route_context(scope)
  1972. if (
  1973. isinstance(effective_context, _EffectiveRouteContext)
  1974. and effective_context.original_route is self
  1975. ):
  1976. prefix = effective_context.frontend_prefix
  1977. dependant = effective_context.dependant
  1978. dependency_overrides_provider = (
  1979. effective_context.dependency_overrides_provider
  1980. )
  1981. embed_body_fields = effective_context._embed_body_fields
  1982. else:
  1983. prefix = ""
  1984. dependant = self.dependant
  1985. dependency_overrides_provider = self.dependency_overrides_provider
  1986. embed_body_fields = self._embed_body_fields
  1987. match, child_scope, route = self._match(scope, prefix=prefix)
  1988. if match == Match.NONE or route is None:
  1989. raise HTTPException(status_code=404)
  1990. _update_scope(scope, child_scope)
  1991. if match == Match.FULL and dependant and dependant.dependencies:
  1992. async with self._solve_dependencies(
  1993. scope,
  1994. receive,
  1995. send,
  1996. dependant=dependant,
  1997. dependency_overrides_provider=dependency_overrides_provider,
  1998. embed_body_fields=embed_body_fields,
  1999. ) as solved_result:
  2000. response = await route.app.get_response_for_scope(scope)
  2001. if response.background is None:
  2002. response.background = solved_result.background_tasks
  2003. response.headers.raw.extend(solved_result.response.headers.raw)
  2004. await response(scope, receive, send)
  2005. return
  2006. await route.handle(scope, receive, send)
  2007. def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
  2008. raise NoMatchFound(name, path_params)
  2009. # TODO: probably move this out of the Route / Route Group, same in APIRoute
  2010. # this should probably be top level FastAPI logic, not part of APIRoute and
  2011. # duplicated here
  2012. @asynccontextmanager
  2013. async def _solve_dependencies(
  2014. self,
  2015. scope: Scope,
  2016. receive: Receive,
  2017. send: Send,
  2018. *,
  2019. dependant: Dependant,
  2020. dependency_overrides_provider: Any | None,
  2021. embed_body_fields: bool,
  2022. ) -> AsyncIterator[SolvedDependency]:
  2023. request = Request(scope, receive, send)
  2024. previous_inner_astack = scope.get("fastapi_inner_astack", _SCOPE_MISSING)
  2025. previous_function_astack = scope.get("fastapi_function_astack", _SCOPE_MISSING)
  2026. try:
  2027. async with AsyncExitStack() as request_stack:
  2028. scope["fastapi_inner_astack"] = request_stack
  2029. async with AsyncExitStack() as function_stack:
  2030. scope["fastapi_function_astack"] = function_stack
  2031. solved_result = await solve_dependencies(
  2032. request=request,
  2033. dependant=dependant,
  2034. dependency_overrides_provider=dependency_overrides_provider,
  2035. async_exit_stack=request_stack,
  2036. embed_body_fields=embed_body_fields,
  2037. )
  2038. if solved_result.errors:
  2039. raise RequestValidationError(solved_result.errors)
  2040. yield solved_result
  2041. finally:
  2042. if previous_inner_astack is _SCOPE_MISSING:
  2043. scope.pop("fastapi_inner_astack", None)
  2044. else:
  2045. scope["fastapi_inner_astack"] = previous_inner_astack
  2046. if previous_function_astack is _SCOPE_MISSING:
  2047. scope.pop("fastapi_function_astack", None)
  2048. else:
  2049. scope["fastapi_function_astack"] = previous_function_astack
  2050. class APIRouter(routing.Router):
  2051. """
  2052. `APIRouter` class, used to group *path operations*, for example to structure
  2053. an app in multiple files. It would then be included in the `FastAPI` app, or
  2054. in another `APIRouter` (ultimately included in the app).
  2055. Read more about it in the
  2056. [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/).
  2057. ## Example
  2058. ```python
  2059. from fastapi import APIRouter, FastAPI
  2060. app = FastAPI()
  2061. router = APIRouter()
  2062. @router.get("/users/", tags=["users"])
  2063. async def read_users():
  2064. return [{"username": "Rick"}, {"username": "Morty"}]
  2065. app.include_router(router)
  2066. ```
  2067. """
  2068. def __init__(
  2069. self,
  2070. *,
  2071. prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "",
  2072. tags: Annotated[
  2073. list[str | Enum] | None,
  2074. Doc(
  2075. """
  2076. A list of tags to be applied to all the *path operations* in this
  2077. router.
  2078. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2079. Read more about it in the
  2080. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  2081. """
  2082. ),
  2083. ] = None,
  2084. dependencies: Annotated[
  2085. Sequence[params.Depends] | None,
  2086. Doc(
  2087. """
  2088. A list of dependencies (using `Depends()`) to be applied to all the
  2089. *path operations* in this router.
  2090. Read more about it in the
  2091. [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
  2092. """
  2093. ),
  2094. ] = None,
  2095. default_response_class: Annotated[
  2096. type[Response],
  2097. Doc(
  2098. """
  2099. The default response class to be used.
  2100. Read more in the
  2101. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class).
  2102. """
  2103. ),
  2104. ] = Default(JSONResponse),
  2105. responses: Annotated[
  2106. dict[int | str, dict[str, Any]] | None,
  2107. Doc(
  2108. """
  2109. Additional responses to be shown in OpenAPI.
  2110. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2111. Read more about it in the
  2112. [FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/).
  2113. And in the
  2114. [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
  2115. """
  2116. ),
  2117. ] = None,
  2118. callbacks: Annotated[
  2119. list[BaseRoute] | None,
  2120. Doc(
  2121. """
  2122. OpenAPI callbacks that should apply to all *path operations* in this
  2123. router.
  2124. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2125. Read more about it in the
  2126. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  2127. """
  2128. ),
  2129. ] = None,
  2130. routes: Annotated[
  2131. list[BaseRoute] | None,
  2132. Doc(
  2133. """
  2134. **Note**: you probably shouldn't use this parameter, it is inherited
  2135. from Starlette and supported for compatibility.
  2136. ---
  2137. A list of routes to serve incoming HTTP and WebSocket requests.
  2138. """
  2139. ),
  2140. deprecated(
  2141. """
  2142. You normally wouldn't use this parameter with FastAPI, it is inherited
  2143. from Starlette and supported for compatibility.
  2144. In FastAPI, you normally would use the *path operation methods*,
  2145. like `router.get()`, `router.post()`, etc.
  2146. """
  2147. ),
  2148. ] = None,
  2149. redirect_slashes: Annotated[
  2150. bool,
  2151. Doc(
  2152. """
  2153. Whether to detect and redirect slashes in URLs when the client doesn't
  2154. use the same format.
  2155. """
  2156. ),
  2157. ] = True,
  2158. default: Annotated[
  2159. ASGIApp | None,
  2160. Doc(
  2161. """
  2162. Default function handler for this router. Used to handle
  2163. 404 Not Found errors.
  2164. """
  2165. ),
  2166. ] = None,
  2167. dependency_overrides_provider: Annotated[
  2168. Any | None,
  2169. Doc(
  2170. """
  2171. Only used internally by FastAPI to handle dependency overrides.
  2172. You shouldn't need to use it. It normally points to the `FastAPI` app
  2173. object.
  2174. """
  2175. ),
  2176. ] = None,
  2177. route_class: Annotated[
  2178. type[APIRoute],
  2179. Doc(
  2180. """
  2181. Custom route (*path operation*) class to be used by this router.
  2182. Read more about it in the
  2183. [FastAPI docs for Custom Request and APIRoute class](https://fastapi.tiangolo.com/how-to/custom-request-and-route/#custom-apiroute-class-in-a-router).
  2184. """
  2185. ),
  2186. ] = APIRoute,
  2187. on_startup: Annotated[
  2188. Sequence[Callable[[], Any]] | None,
  2189. Doc(
  2190. """
  2191. A list of startup event handler functions.
  2192. You should instead use the `lifespan` handlers.
  2193. Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
  2194. """
  2195. ),
  2196. ] = None,
  2197. on_shutdown: Annotated[
  2198. Sequence[Callable[[], Any]] | None,
  2199. Doc(
  2200. """
  2201. A list of shutdown event handler functions.
  2202. You should instead use the `lifespan` handlers.
  2203. Read more in the
  2204. [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
  2205. """
  2206. ),
  2207. ] = None,
  2208. # the generic to Lifespan[AppType] is the type of the top level application
  2209. # which the router cannot know statically, so we use typing.Any
  2210. lifespan: Annotated[
  2211. Lifespan[Any] | None,
  2212. Doc(
  2213. """
  2214. A `Lifespan` context manager handler. This replaces `startup` and
  2215. `shutdown` functions with a single context manager.
  2216. Read more in the
  2217. [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
  2218. """
  2219. ),
  2220. ] = None,
  2221. deprecated: Annotated[
  2222. bool | None,
  2223. Doc(
  2224. """
  2225. Mark all *path operations* in this router as deprecated.
  2226. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2227. Read more about it in the
  2228. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  2229. """
  2230. ),
  2231. ] = None,
  2232. include_in_schema: Annotated[
  2233. bool,
  2234. Doc(
  2235. """
  2236. To include (or not) all the *path operations* in this router in the
  2237. generated OpenAPI.
  2238. This affects the generated OpenAPI (e.g. visible at `/docs`).
  2239. Read more about it in the
  2240. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  2241. """
  2242. ),
  2243. ] = True,
  2244. generate_unique_id_function: Annotated[
  2245. Callable[[APIRoute], str],
  2246. Doc(
  2247. """
  2248. Customize the function used to generate unique IDs for the *path
  2249. operations* shown in the generated OpenAPI.
  2250. This is particularly useful when automatically generating clients or
  2251. SDKs for your API.
  2252. Read more about it in the
  2253. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  2254. """
  2255. ),
  2256. ] = Default(generate_unique_id),
  2257. strict_content_type: Annotated[
  2258. bool,
  2259. Doc(
  2260. """
  2261. Enable strict checking for request Content-Type headers.
  2262. When `True` (the default), requests with a body that do not include
  2263. a `Content-Type` header will **not** be parsed as JSON.
  2264. This prevents potential cross-site request forgery (CSRF) attacks
  2265. that exploit the browser's ability to send requests without a
  2266. Content-Type header, bypassing CORS preflight checks. In particular
  2267. applicable for apps that need to be run locally (in localhost).
  2268. When `False`, requests without a `Content-Type` header will have
  2269. their body parsed as JSON, which maintains compatibility with
  2270. certain clients that don't send `Content-Type` headers.
  2271. Read more about it in the
  2272. [FastAPI docs for Strict Content-Type](https://fastapi.tiangolo.com/advanced/strict-content-type/).
  2273. """
  2274. ),
  2275. ] = Default(True),
  2276. ) -> None:
  2277. # Determine the lifespan context to use
  2278. if lifespan is None:
  2279. # Use the default lifespan that runs on_startup/on_shutdown handlers
  2280. lifespan_context: Lifespan[Any] = _DefaultLifespan(self)
  2281. elif inspect.isasyncgenfunction(lifespan):
  2282. lifespan_context = asynccontextmanager(lifespan)
  2283. elif inspect.isgeneratorfunction(lifespan):
  2284. lifespan_context = _wrap_gen_lifespan_context(lifespan)
  2285. else:
  2286. lifespan_context = lifespan
  2287. self.lifespan_context = lifespan_context
  2288. super().__init__(
  2289. routes=routes,
  2290. redirect_slashes=redirect_slashes,
  2291. default=default,
  2292. lifespan=lifespan_context,
  2293. )
  2294. if prefix:
  2295. assert prefix.startswith("/"), "A path prefix must start with '/'"
  2296. assert not prefix.endswith("/"), (
  2297. "A path prefix must not end with '/', as the routes will start with '/'"
  2298. )
  2299. # Handle on_startup/on_shutdown locally since Starlette removed support
  2300. # Ref: https://github.com/Kludex/starlette/pull/3117
  2301. # TODO: deprecate this once the lifespan (or alternative) interface is improved
  2302. self.on_startup: list[Callable[[], Any]] = (
  2303. [] if on_startup is None else list(on_startup)
  2304. )
  2305. self.on_shutdown: list[Callable[[], Any]] = (
  2306. [] if on_shutdown is None else list(on_shutdown)
  2307. )
  2308. self.prefix = prefix
  2309. self.tags: list[str | Enum] = tags or []
  2310. self.dependencies = list(dependencies or [])
  2311. self.deprecated = deprecated
  2312. self.include_in_schema = include_in_schema
  2313. self.responses = responses or {}
  2314. self.callbacks = callbacks or []
  2315. self.dependency_overrides_provider = dependency_overrides_provider
  2316. self.route_class = route_class
  2317. self.default_response_class = default_response_class
  2318. self.generate_unique_id_function = generate_unique_id_function
  2319. self.strict_content_type = strict_content_type
  2320. self._routes_version = 0
  2321. self._low_priority_routes: list[BaseRoute] = []
  2322. self._frontend_routes: _FrontendRouteGroup | None = None
  2323. def _mark_routes_changed(self) -> None:
  2324. self._routes_version += 1
  2325. def _get_routes_version(self, seen: set[int] | None = None) -> int:
  2326. if seen is None:
  2327. seen = set()
  2328. router_id = id(self)
  2329. if router_id in seen:
  2330. return self._routes_version
  2331. seen.add(router_id)
  2332. version = self._routes_version
  2333. for route in self.routes:
  2334. if isinstance(route, _IncludedRouter):
  2335. version += route.original_router._get_routes_version(seen)
  2336. return version
  2337. def _contains_router(
  2338. self, router: "APIRouter", seen: set[int] | None = None
  2339. ) -> bool:
  2340. if seen is None:
  2341. seen = set()
  2342. router_id = id(self)
  2343. if router_id in seen:
  2344. return False
  2345. seen.add(router_id)
  2346. for route in self.routes:
  2347. if not isinstance(route, _IncludedRouter):
  2348. continue
  2349. if route.original_router is router:
  2350. return True
  2351. if route.original_router._contains_router(router, seen):
  2352. return True
  2353. return False
  2354. def add_route(
  2355. self,
  2356. path: str,
  2357. endpoint: Callable[[Request], Awaitable[Response] | Response],
  2358. methods: Collection[str] | None = None,
  2359. name: str | None = None,
  2360. include_in_schema: bool = True,
  2361. ) -> None:
  2362. super().add_route(
  2363. path,
  2364. endpoint,
  2365. methods=methods,
  2366. name=name,
  2367. include_in_schema=include_in_schema,
  2368. )
  2369. self._mark_routes_changed()
  2370. def add_websocket_route(
  2371. self,
  2372. path: str,
  2373. endpoint: Callable[[WebSocket], Awaitable[None]],
  2374. name: str | None = None,
  2375. ) -> None:
  2376. super().add_websocket_route(path, endpoint, name=name)
  2377. self._mark_routes_changed()
  2378. def frontend(
  2379. self,
  2380. path: Annotated[
  2381. str,
  2382. Doc(
  2383. """
  2384. The URL path prefix where the frontend build should be served.
  2385. """
  2386. ),
  2387. ],
  2388. *,
  2389. directory: Annotated[
  2390. str | os.PathLike[str],
  2391. Doc(
  2392. """
  2393. The directory containing the static frontend build output.
  2394. """
  2395. ),
  2396. ],
  2397. fallback: Annotated[
  2398. Literal["auto", "index.html", "404.html"] | None,
  2399. Doc(
  2400. """
  2401. The fallback file behavior for missing frontend paths.
  2402. """
  2403. ),
  2404. ] = "auto",
  2405. check_dir: Annotated[
  2406. bool | Literal["auto"],
  2407. Doc(
  2408. """
  2409. Check that the frontend directory exists when the app is created. When
  2410. set to `"auto"`, skip the check with a warning when `FASTAPI_ENV` is
  2411. `"development"`, and check it otherwise. The `fastapi dev` command
  2412. sets `FASTAPI_ENV` to `"development"` if it is not already set.
  2413. """
  2414. ),
  2415. ] = "auto",
  2416. ) -> None:
  2417. """
  2418. Serve a static frontend build as low-priority routes.
  2419. Use this for frontend tools that build static files into a directory,
  2420. such as `dist`. **FastAPI** path operations are checked first, and
  2421. the frontend files are checked only if no normal route matched.
  2422. A typical project could look like this:
  2423. ```text
  2424. .
  2425. ├── pyproject.toml
  2426. ├── app
  2427. │ ├── __init__.py
  2428. │ └── main.py
  2429. └── dist
  2430. ├── index.html
  2431. └── assets
  2432. └── app.js
  2433. ```
  2434. Then in `app/main.py`:
  2435. ```python
  2436. from fastapi import APIRouter, FastAPI
  2437. app = FastAPI()
  2438. router = APIRouter()
  2439. router.frontend("/", directory="dist")
  2440. app.include_router(router)
  2441. ```
  2442. """
  2443. check_dir = _resolve_frontend_check_dir(
  2444. directory=directory, check_dir=check_dir
  2445. )
  2446. normalized_path = _normalize_frontend_path(path)
  2447. if self._frontend_routes is None:
  2448. self._frontend_routes = _FrontendRouteGroup(
  2449. dependencies=self.dependencies,
  2450. dependency_overrides_provider=self.dependency_overrides_provider,
  2451. )
  2452. self._low_priority_routes.append(self._frontend_routes)
  2453. self._frontend_routes.add_frontend_route(
  2454. _join_frontend_paths(self.prefix, normalized_path),
  2455. directory=directory,
  2456. fallback=fallback,
  2457. check_dir=check_dir,
  2458. )
  2459. self._mark_routes_changed()
  2460. async def app(self, scope: Scope, receive: Receive, send: Send) -> None:
  2461. assert scope["type"] in ("http", "websocket", "lifespan")
  2462. if "router" not in scope:
  2463. scope["router"] = self
  2464. if scope["type"] == "lifespan":
  2465. await self.lifespan(scope, receive, send)
  2466. return
  2467. partial: tuple[BaseRoute, Scope] | None = None
  2468. for route in self.routes:
  2469. match, child_scope = route.matches(scope)
  2470. if match == Match.FULL:
  2471. scope.update(child_scope)
  2472. await route.handle(scope, receive, send)
  2473. return
  2474. if match == Match.PARTIAL and partial is None:
  2475. partial = (route, child_scope)
  2476. if partial is not None:
  2477. route, child_scope = partial
  2478. scope.update(child_scope)
  2479. await route.handle(scope, receive, send)
  2480. return
  2481. route_path = get_route_path(scope)
  2482. if scope["type"] == "http" and self.redirect_slashes and route_path != "/":
  2483. redirect_scope = dict(scope)
  2484. if route_path.endswith("/"):
  2485. redirect_scope["path"] = redirect_scope["path"].rstrip("/")
  2486. else:
  2487. redirect_scope["path"] = redirect_scope["path"] + "/"
  2488. for route in self.routes:
  2489. match, _ = route.matches(redirect_scope)
  2490. if match != Match.NONE:
  2491. redirect_url = URL(scope=redirect_scope)
  2492. response = RedirectResponse(url=str(redirect_url))
  2493. await response(scope, receive, send)
  2494. return
  2495. (
  2496. low_priority_match,
  2497. low_priority_scope,
  2498. low_priority_route,
  2499. low_priority_context,
  2500. ) = self._match_low_priority(scope)
  2501. if low_priority_match != Match.NONE and low_priority_route is not None:
  2502. _update_scope(scope, low_priority_scope)
  2503. if low_priority_context is not None:
  2504. _get_fastapi_scope(scope)[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = (
  2505. low_priority_context
  2506. )
  2507. original_route = low_priority_context.original_route
  2508. if isinstance(original_route, APIRoute):
  2509. scope["route"] = original_route
  2510. await original_route.handle(scope, receive, send)
  2511. return
  2512. await low_priority_route.handle(scope, receive, send)
  2513. return
  2514. await self.default(scope, receive, send)
  2515. async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
  2516. included_router = _get_scope_included_router(scope)
  2517. if (
  2518. isinstance(included_router, _IncludedRouter)
  2519. and included_router.original_router is self
  2520. ):
  2521. await included_router._handle_selected(scope, receive, send)
  2522. return
  2523. await self.app(scope, receive, send)
  2524. def matches(self, scope: Scope) -> tuple[Match, Scope]:
  2525. included_router = _get_scope_included_router(scope)
  2526. if (
  2527. isinstance(included_router, _IncludedRouter)
  2528. and included_router.original_router is self
  2529. ):
  2530. match, child_scope, _, _ = included_router._match(scope)
  2531. return match, child_scope
  2532. return Match.NONE, {}
  2533. def _iter_low_priority_routes(
  2534. self,
  2535. ) -> Iterator[BaseRoute | _EffectiveRouteContext]:
  2536. yield from self._low_priority_routes
  2537. for route in self.routes:
  2538. if isinstance(route, _IncludedRouter):
  2539. yield from route.effective_low_priority_routes()
  2540. def _match_low_priority(
  2541. self, scope: Scope
  2542. ) -> tuple[Match, Scope, BaseRoute | None, _EffectiveRouteContext | None]:
  2543. full: tuple[Scope, BaseRoute, _EffectiveRouteContext | None] | None = None
  2544. partial: tuple[Scope, BaseRoute, _EffectiveRouteContext | None] | None = None
  2545. for candidate in self._iter_low_priority_routes():
  2546. route: BaseRoute
  2547. if isinstance(candidate, _EffectiveRouteContext):
  2548. route_context: _EffectiveRouteContext | None = candidate
  2549. original_route = candidate.original_route
  2550. if isinstance(original_route, APIRoute):
  2551. fastapi_scope = _get_fastapi_scope(scope)
  2552. previous_context = fastapi_scope.get(
  2553. _FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY, _SCOPE_MISSING
  2554. )
  2555. fastapi_scope[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = route_context
  2556. try:
  2557. match, child_scope = original_route.matches(scope)
  2558. finally:
  2559. _restore_fastapi_scope_key(
  2560. scope,
  2561. _FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY,
  2562. previous_context,
  2563. )
  2564. route = original_route
  2565. else:
  2566. match, child_scope = candidate.matches(scope)
  2567. route = candidate.starlette_route or original_route
  2568. else:
  2569. route_context = None
  2570. match, child_scope = candidate.matches(scope)
  2571. route = candidate
  2572. if match == Match.FULL:
  2573. if full is None or self._frontend_match_is_more_specific(
  2574. child_scope, full[0]
  2575. ):
  2576. full = (child_scope, route, route_context)
  2577. elif match == Match.PARTIAL:
  2578. if partial is None or self._frontend_match_is_more_specific(
  2579. child_scope, partial[0]
  2580. ):
  2581. partial = (child_scope, route, route_context)
  2582. if full is not None:
  2583. child_scope, route, route_context = full
  2584. return Match.FULL, child_scope, route, route_context
  2585. if partial is not None:
  2586. child_scope, route, route_context = partial
  2587. return Match.PARTIAL, child_scope, route, route_context
  2588. return Match.NONE, {}, None, None
  2589. def _frontend_match_is_more_specific(
  2590. self, child_scope: Scope, previous_child_scope: Scope
  2591. ) -> bool:
  2592. specificity = _frontend_scope_specificity(child_scope)
  2593. previous_specificity = _frontend_scope_specificity(previous_child_scope)
  2594. if specificity is None or previous_specificity is None:
  2595. return False
  2596. return specificity > previous_specificity
  2597. def route(
  2598. self,
  2599. path: str,
  2600. methods: Collection[str] | None = None,
  2601. name: str | None = None,
  2602. include_in_schema: bool = True,
  2603. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  2604. def decorator(func: DecoratedCallable) -> DecoratedCallable:
  2605. self.add_route(
  2606. path,
  2607. func,
  2608. methods=methods,
  2609. name=name,
  2610. include_in_schema=include_in_schema,
  2611. )
  2612. return func
  2613. return decorator
  2614. def add_api_route(
  2615. self,
  2616. path: str,
  2617. endpoint: Callable[..., Any],
  2618. *,
  2619. response_model: Any = Default(None),
  2620. status_code: int | None = None,
  2621. tags: list[str | Enum] | None = None,
  2622. dependencies: Sequence[params.Depends] | None = None,
  2623. summary: str | None = None,
  2624. description: str | None = None,
  2625. response_description: str = "Successful Response",
  2626. responses: dict[int | str, dict[str, Any]] | None = None,
  2627. deprecated: bool | None = None,
  2628. methods: set[str] | list[str] | None = None,
  2629. operation_id: str | None = None,
  2630. response_model_include: IncEx | None = None,
  2631. response_model_exclude: IncEx | None = None,
  2632. response_model_by_alias: bool = True,
  2633. response_model_exclude_unset: bool = False,
  2634. response_model_exclude_defaults: bool = False,
  2635. response_model_exclude_none: bool = False,
  2636. include_in_schema: bool = True,
  2637. response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
  2638. name: str | None = None,
  2639. route_class_override: type[APIRoute] | None = None,
  2640. callbacks: list[BaseRoute] | None = None,
  2641. openapi_extra: dict[str, Any] | None = None,
  2642. generate_unique_id_function: Callable[[APIRoute], str]
  2643. | DefaultPlaceholder = Default(generate_unique_id),
  2644. strict_content_type: bool | DefaultPlaceholder = Default(True),
  2645. ) -> None:
  2646. route_class = route_class_override or self.route_class
  2647. responses = responses or {}
  2648. combined_responses = {**self.responses, **responses}
  2649. current_response_class = get_value_or_default(
  2650. response_class, self.default_response_class
  2651. )
  2652. current_tags = self.tags.copy()
  2653. if tags:
  2654. current_tags.extend(tags)
  2655. current_dependencies = self.dependencies.copy()
  2656. if dependencies:
  2657. current_dependencies.extend(dependencies)
  2658. current_callbacks = self.callbacks.copy()
  2659. if callbacks:
  2660. current_callbacks.extend(callbacks)
  2661. current_generate_unique_id = get_value_or_default(
  2662. generate_unique_id_function, self.generate_unique_id_function
  2663. )
  2664. route = route_class(
  2665. self.prefix + path,
  2666. endpoint=endpoint,
  2667. response_model=response_model,
  2668. status_code=status_code,
  2669. tags=current_tags,
  2670. dependencies=current_dependencies,
  2671. summary=summary,
  2672. description=description,
  2673. response_description=response_description,
  2674. responses=combined_responses,
  2675. deprecated=deprecated or self.deprecated,
  2676. methods=methods,
  2677. operation_id=operation_id,
  2678. response_model_include=response_model_include,
  2679. response_model_exclude=response_model_exclude,
  2680. response_model_by_alias=response_model_by_alias,
  2681. response_model_exclude_unset=response_model_exclude_unset,
  2682. response_model_exclude_defaults=response_model_exclude_defaults,
  2683. response_model_exclude_none=response_model_exclude_none,
  2684. include_in_schema=include_in_schema and self.include_in_schema,
  2685. response_class=current_response_class,
  2686. name=name,
  2687. dependency_overrides_provider=self.dependency_overrides_provider,
  2688. callbacks=current_callbacks,
  2689. openapi_extra=openapi_extra,
  2690. generate_unique_id_function=current_generate_unique_id,
  2691. strict_content_type=get_value_or_default(
  2692. strict_content_type, self.strict_content_type
  2693. ),
  2694. )
  2695. self.routes.append(route)
  2696. self._mark_routes_changed()
  2697. def api_route(
  2698. self,
  2699. path: str,
  2700. *,
  2701. response_model: Any = Default(None),
  2702. status_code: int | None = None,
  2703. tags: list[str | Enum] | None = None,
  2704. dependencies: Sequence[params.Depends] | None = None,
  2705. summary: str | None = None,
  2706. description: str | None = None,
  2707. response_description: str = "Successful Response",
  2708. responses: dict[int | str, dict[str, Any]] | None = None,
  2709. deprecated: bool | None = None,
  2710. methods: list[str] | None = None,
  2711. operation_id: str | None = None,
  2712. response_model_include: IncEx | None = None,
  2713. response_model_exclude: IncEx | None = None,
  2714. response_model_by_alias: bool = True,
  2715. response_model_exclude_unset: bool = False,
  2716. response_model_exclude_defaults: bool = False,
  2717. response_model_exclude_none: bool = False,
  2718. include_in_schema: bool = True,
  2719. response_class: type[Response] = Default(JSONResponse),
  2720. name: str | None = None,
  2721. callbacks: list[BaseRoute] | None = None,
  2722. openapi_extra: dict[str, Any] | None = None,
  2723. generate_unique_id_function: Callable[[APIRoute], str] = Default(
  2724. generate_unique_id
  2725. ),
  2726. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  2727. def decorator(func: DecoratedCallable) -> DecoratedCallable:
  2728. self.add_api_route(
  2729. path,
  2730. func,
  2731. response_model=response_model,
  2732. status_code=status_code,
  2733. tags=tags,
  2734. dependencies=dependencies,
  2735. summary=summary,
  2736. description=description,
  2737. response_description=response_description,
  2738. responses=responses,
  2739. deprecated=deprecated,
  2740. methods=methods,
  2741. operation_id=operation_id,
  2742. response_model_include=response_model_include,
  2743. response_model_exclude=response_model_exclude,
  2744. response_model_by_alias=response_model_by_alias,
  2745. response_model_exclude_unset=response_model_exclude_unset,
  2746. response_model_exclude_defaults=response_model_exclude_defaults,
  2747. response_model_exclude_none=response_model_exclude_none,
  2748. include_in_schema=include_in_schema,
  2749. response_class=response_class,
  2750. name=name,
  2751. callbacks=callbacks,
  2752. openapi_extra=openapi_extra,
  2753. generate_unique_id_function=generate_unique_id_function,
  2754. )
  2755. return func
  2756. return decorator
  2757. def add_api_websocket_route(
  2758. self,
  2759. path: str,
  2760. endpoint: Callable[..., Any],
  2761. name: str | None = None,
  2762. *,
  2763. dependencies: Sequence[params.Depends] | None = None,
  2764. ) -> None:
  2765. current_dependencies = self.dependencies.copy()
  2766. if dependencies:
  2767. current_dependencies.extend(dependencies)
  2768. route = APIWebSocketRoute(
  2769. self.prefix + path,
  2770. endpoint=endpoint,
  2771. name=name,
  2772. dependencies=current_dependencies,
  2773. dependency_overrides_provider=self.dependency_overrides_provider,
  2774. )
  2775. self.routes.append(route)
  2776. self._mark_routes_changed()
  2777. def websocket(
  2778. self,
  2779. path: Annotated[
  2780. str,
  2781. Doc(
  2782. """
  2783. WebSocket path.
  2784. """
  2785. ),
  2786. ],
  2787. name: Annotated[
  2788. str | None,
  2789. Doc(
  2790. """
  2791. A name for the WebSocket. Only used internally.
  2792. """
  2793. ),
  2794. ] = None,
  2795. *,
  2796. dependencies: Annotated[
  2797. Sequence[params.Depends] | None,
  2798. Doc(
  2799. """
  2800. A list of dependencies (using `Depends()`) to be used for this
  2801. WebSocket.
  2802. Read more about it in the
  2803. [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/).
  2804. """
  2805. ),
  2806. ] = None,
  2807. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  2808. """
  2809. Decorate a WebSocket function.
  2810. Read more about it in the
  2811. [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/).
  2812. **Example**
  2813. ## Example
  2814. ```python
  2815. from fastapi import APIRouter, FastAPI, WebSocket
  2816. app = FastAPI()
  2817. router = APIRouter()
  2818. @router.websocket("/ws")
  2819. async def websocket_endpoint(websocket: WebSocket):
  2820. await websocket.accept()
  2821. while True:
  2822. data = await websocket.receive_text()
  2823. await websocket.send_text(f"Message text was: {data}")
  2824. app.include_router(router)
  2825. ```
  2826. """
  2827. def decorator(func: DecoratedCallable) -> DecoratedCallable:
  2828. self.add_api_websocket_route(
  2829. path, func, name=name, dependencies=dependencies
  2830. )
  2831. return func
  2832. return decorator
  2833. def websocket_route(
  2834. self, path: str, name: str | None = None
  2835. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  2836. def decorator(func: DecoratedCallable) -> DecoratedCallable:
  2837. self.add_websocket_route(path, func, name=name)
  2838. return func
  2839. return decorator
  2840. def include_router(
  2841. self,
  2842. router: Annotated["APIRouter", Doc("The `APIRouter` to include.")],
  2843. *,
  2844. prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "",
  2845. tags: Annotated[
  2846. list[str | Enum] | None,
  2847. Doc(
  2848. """
  2849. A list of tags to be applied to all the *path operations* in this
  2850. router.
  2851. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2852. Read more about it in the
  2853. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  2854. """
  2855. ),
  2856. ] = None,
  2857. dependencies: Annotated[
  2858. Sequence[params.Depends] | None,
  2859. Doc(
  2860. """
  2861. A list of dependencies (using `Depends()`) to be applied to all the
  2862. *path operations* in this router.
  2863. Read more about it in the
  2864. [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
  2865. """
  2866. ),
  2867. ] = None,
  2868. default_response_class: Annotated[
  2869. type[Response],
  2870. Doc(
  2871. """
  2872. The default response class to be used.
  2873. Read more in the
  2874. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class).
  2875. """
  2876. ),
  2877. ] = Default(JSONResponse),
  2878. responses: Annotated[
  2879. dict[int | str, dict[str, Any]] | None,
  2880. Doc(
  2881. """
  2882. Additional responses to be shown in OpenAPI.
  2883. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2884. Read more about it in the
  2885. [FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/).
  2886. And in the
  2887. [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
  2888. """
  2889. ),
  2890. ] = None,
  2891. callbacks: Annotated[
  2892. list[BaseRoute] | None,
  2893. Doc(
  2894. """
  2895. OpenAPI callbacks that should apply to all *path operations* in this
  2896. router.
  2897. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2898. Read more about it in the
  2899. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  2900. """
  2901. ),
  2902. ] = None,
  2903. deprecated: Annotated[
  2904. bool | None,
  2905. Doc(
  2906. """
  2907. Mark all *path operations* in this router as deprecated.
  2908. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  2909. Read more about it in the
  2910. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  2911. """
  2912. ),
  2913. ] = None,
  2914. include_in_schema: Annotated[
  2915. bool,
  2916. Doc(
  2917. """
  2918. Include (or not) all the *path operations* in this router in the
  2919. generated OpenAPI schema.
  2920. This affects the generated OpenAPI (e.g. visible at `/docs`).
  2921. """
  2922. ),
  2923. ] = True,
  2924. generate_unique_id_function: Annotated[
  2925. Callable[[APIRoute], str],
  2926. Doc(
  2927. """
  2928. Customize the function used to generate unique IDs for the *path
  2929. operations* shown in the generated OpenAPI.
  2930. This is particularly useful when automatically generating clients or
  2931. SDKs for your API.
  2932. Read more about it in the
  2933. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  2934. """
  2935. ),
  2936. ] = Default(generate_unique_id),
  2937. ) -> None:
  2938. """
  2939. Include another `APIRouter` in the same current `APIRouter`.
  2940. Read more about it in the
  2941. [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/).
  2942. ## Example
  2943. ```python
  2944. from fastapi import APIRouter, FastAPI
  2945. app = FastAPI()
  2946. internal_router = APIRouter()
  2947. users_router = APIRouter()
  2948. @users_router.get("/users/")
  2949. def read_users():
  2950. return [{"name": "Rick"}, {"name": "Morty"}]
  2951. internal_router.include_router(users_router)
  2952. app.include_router(internal_router)
  2953. ```
  2954. """
  2955. assert self is not router, (
  2956. "Cannot include the same APIRouter instance into itself. "
  2957. "Did you mean to include a different router?"
  2958. )
  2959. assert not router._contains_router(self), (
  2960. "Cannot include an APIRouter instance that already includes this router. "
  2961. "Did you mean to include a different router?"
  2962. )
  2963. if prefix:
  2964. assert prefix.startswith("/"), "A path prefix must start with '/'"
  2965. assert not prefix.endswith("/"), (
  2966. "A path prefix must not end with '/', as the routes will start with '/'"
  2967. )
  2968. else:
  2969. for route, route_context in _iter_routes_with_context(router.routes):
  2970. if route_context is None:
  2971. path = getattr(route, "path", None)
  2972. name = getattr(route, "name", "unknown")
  2973. elif route_context.starlette_route is not None:
  2974. path = getattr(route_context.starlette_route, "path", None)
  2975. name = getattr(route_context.starlette_route, "name", "unknown")
  2976. else:
  2977. path = route_context.path
  2978. name = route_context.name
  2979. if path is not None and not path:
  2980. raise FastAPIError(
  2981. f"Prefix and path cannot be both empty (path operation: {name})"
  2982. )
  2983. include_context = _RouterIncludeContext.for_include(
  2984. parent_router=self,
  2985. included_router=router,
  2986. prefix=prefix,
  2987. tags=tags,
  2988. dependencies=dependencies,
  2989. default_response_class=default_response_class,
  2990. responses=responses,
  2991. callbacks=callbacks,
  2992. deprecated=deprecated,
  2993. include_in_schema=include_in_schema,
  2994. generate_unique_id_function=generate_unique_id_function,
  2995. )
  2996. self.routes.append(
  2997. _IncludedRouter(original_router=router, include_context=include_context)
  2998. )
  2999. self._mark_routes_changed()
  3000. for handler in router.on_startup:
  3001. self.add_event_handler("startup", handler)
  3002. for handler in router.on_shutdown:
  3003. self.add_event_handler("shutdown", handler)
  3004. self.lifespan_context = _merge_lifespan_context(
  3005. self.lifespan_context,
  3006. router.lifespan_context,
  3007. )
  3008. def get(
  3009. self,
  3010. path: Annotated[
  3011. str,
  3012. Doc(
  3013. """
  3014. The URL path to be used for this *path operation*.
  3015. For example, in `http://example.com/items`, the path is `/items`.
  3016. """
  3017. ),
  3018. ],
  3019. *,
  3020. response_model: Annotated[
  3021. Any,
  3022. Doc(
  3023. """
  3024. The type to use for the response.
  3025. It could be any valid Pydantic *field* type. So, it doesn't have to
  3026. be a Pydantic model, it could be other things, like a `list`, `dict`,
  3027. etc.
  3028. It will be used for:
  3029. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  3030. show it as the response (JSON Schema).
  3031. * Serialization: you could return an arbitrary object and the
  3032. `response_model` would be used to serialize that object into the
  3033. corresponding JSON.
  3034. * Filtering: the JSON sent to the client will only contain the data
  3035. (fields) defined in the `response_model`. If you returned an object
  3036. that contains an attribute `password` but the `response_model` does
  3037. not include that field, the JSON sent to the client would not have
  3038. that `password`.
  3039. * Validation: whatever you return will be serialized with the
  3040. `response_model`, converting any data as necessary to generate the
  3041. corresponding JSON. But if the data in the object returned is not
  3042. valid, that would mean a violation of the contract with the client,
  3043. so it's an error from the API developer. So, FastAPI will raise an
  3044. error and return a 500 error code (Internal Server Error).
  3045. Read more about it in the
  3046. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  3047. """
  3048. ),
  3049. ] = Default(None),
  3050. status_code: Annotated[
  3051. int | None,
  3052. Doc(
  3053. """
  3054. The default status code to be used for the response.
  3055. You could override the status code by returning a response directly.
  3056. Read more about it in the
  3057. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  3058. """
  3059. ),
  3060. ] = None,
  3061. tags: Annotated[
  3062. list[str | Enum] | None,
  3063. Doc(
  3064. """
  3065. A list of tags to be applied to the *path operation*.
  3066. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3067. Read more about it in the
  3068. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  3069. """
  3070. ),
  3071. ] = None,
  3072. dependencies: Annotated[
  3073. Sequence[params.Depends] | None,
  3074. Doc(
  3075. """
  3076. A list of dependencies (using `Depends()`) to be applied to the
  3077. *path operation*.
  3078. Read more about it in the
  3079. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  3080. """
  3081. ),
  3082. ] = None,
  3083. summary: Annotated[
  3084. str | None,
  3085. Doc(
  3086. """
  3087. A summary for the *path operation*.
  3088. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3089. Read more about it in the
  3090. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  3091. """
  3092. ),
  3093. ] = None,
  3094. description: Annotated[
  3095. str | None,
  3096. Doc(
  3097. """
  3098. A description for the *path operation*.
  3099. If not provided, it will be extracted automatically from the docstring
  3100. of the *path operation function*.
  3101. It can contain Markdown.
  3102. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3103. Read more about it in the
  3104. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  3105. """
  3106. ),
  3107. ] = None,
  3108. response_description: Annotated[
  3109. str,
  3110. Doc(
  3111. """
  3112. The description for the default response.
  3113. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3114. """
  3115. ),
  3116. ] = "Successful Response",
  3117. responses: Annotated[
  3118. dict[int | str, dict[str, Any]] | None,
  3119. Doc(
  3120. """
  3121. Additional responses that could be returned by this *path operation*.
  3122. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3123. """
  3124. ),
  3125. ] = None,
  3126. deprecated: Annotated[
  3127. bool | None,
  3128. Doc(
  3129. """
  3130. Mark this *path operation* as deprecated.
  3131. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3132. """
  3133. ),
  3134. ] = None,
  3135. operation_id: Annotated[
  3136. str | None,
  3137. Doc(
  3138. """
  3139. Custom operation ID to be used by this *path operation*.
  3140. By default, it is generated automatically.
  3141. If you provide a custom operation ID, you need to make sure it is
  3142. unique for the whole API.
  3143. You can customize the
  3144. operation ID generation with the parameter
  3145. `generate_unique_id_function` in the `FastAPI` class.
  3146. Read more about it in the
  3147. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  3148. """
  3149. ),
  3150. ] = None,
  3151. response_model_include: Annotated[
  3152. IncEx | None,
  3153. Doc(
  3154. """
  3155. Configuration passed to Pydantic to include only certain fields in the
  3156. response data.
  3157. Read more about it in the
  3158. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3159. """
  3160. ),
  3161. ] = None,
  3162. response_model_exclude: Annotated[
  3163. IncEx | None,
  3164. Doc(
  3165. """
  3166. Configuration passed to Pydantic to exclude certain fields in the
  3167. response data.
  3168. Read more about it in the
  3169. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3170. """
  3171. ),
  3172. ] = None,
  3173. response_model_by_alias: Annotated[
  3174. bool,
  3175. Doc(
  3176. """
  3177. Configuration passed to Pydantic to define if the response model
  3178. should be serialized by alias when an alias is used.
  3179. Read more about it in the
  3180. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3181. """
  3182. ),
  3183. ] = True,
  3184. response_model_exclude_unset: Annotated[
  3185. bool,
  3186. Doc(
  3187. """
  3188. Configuration passed to Pydantic to define if the response data
  3189. should have all the fields, including the ones that were not set and
  3190. have their default values. This is different from
  3191. `response_model_exclude_defaults` in that if the fields are set,
  3192. they will be included in the response, even if the value is the same
  3193. as the default.
  3194. When `True`, default values are omitted from the response.
  3195. Read more about it in the
  3196. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  3197. """
  3198. ),
  3199. ] = False,
  3200. response_model_exclude_defaults: Annotated[
  3201. bool,
  3202. Doc(
  3203. """
  3204. Configuration passed to Pydantic to define if the response data
  3205. should have all the fields, including the ones that have the same value
  3206. as the default. This is different from `response_model_exclude_unset`
  3207. in that if the fields are set but contain the same default values,
  3208. they will be excluded from the response.
  3209. When `True`, default values are omitted from the response.
  3210. Read more about it in the
  3211. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  3212. """
  3213. ),
  3214. ] = False,
  3215. response_model_exclude_none: Annotated[
  3216. bool,
  3217. Doc(
  3218. """
  3219. Configuration passed to Pydantic to define if the response data should
  3220. exclude fields set to `None`.
  3221. This is much simpler (less smart) than `response_model_exclude_unset`
  3222. and `response_model_exclude_defaults`. You probably want to use one of
  3223. those two instead of this one, as those allow returning `None` values
  3224. when it makes sense.
  3225. Read more about it in the
  3226. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  3227. """
  3228. ),
  3229. ] = False,
  3230. include_in_schema: Annotated[
  3231. bool,
  3232. Doc(
  3233. """
  3234. Include this *path operation* in the generated OpenAPI schema.
  3235. This affects the generated OpenAPI (e.g. visible at `/docs`).
  3236. Read more about it in the
  3237. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  3238. """
  3239. ),
  3240. ] = True,
  3241. response_class: Annotated[
  3242. type[Response],
  3243. Doc(
  3244. """
  3245. Response class to be used for this *path operation*.
  3246. This will not be used if you return a response directly.
  3247. Read more about it in the
  3248. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  3249. """
  3250. ),
  3251. ] = Default(JSONResponse),
  3252. name: Annotated[
  3253. str | None,
  3254. Doc(
  3255. """
  3256. Name for this *path operation*. Only used internally.
  3257. """
  3258. ),
  3259. ] = None,
  3260. callbacks: Annotated[
  3261. list[BaseRoute] | None,
  3262. Doc(
  3263. """
  3264. List of *path operations* that will be used as OpenAPI callbacks.
  3265. This is only for OpenAPI documentation, the callbacks won't be used
  3266. directly.
  3267. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3268. Read more about it in the
  3269. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  3270. """
  3271. ),
  3272. ] = None,
  3273. openapi_extra: Annotated[
  3274. dict[str, Any] | None,
  3275. Doc(
  3276. """
  3277. Extra metadata to be included in the OpenAPI schema for this *path
  3278. operation*.
  3279. Read more about it in the
  3280. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  3281. """
  3282. ),
  3283. ] = None,
  3284. generate_unique_id_function: Annotated[
  3285. Callable[[APIRoute], str],
  3286. Doc(
  3287. """
  3288. Customize the function used to generate unique IDs for the *path
  3289. operations* shown in the generated OpenAPI.
  3290. This is particularly useful when automatically generating clients or
  3291. SDKs for your API.
  3292. Read more about it in the
  3293. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  3294. """
  3295. ),
  3296. ] = Default(generate_unique_id),
  3297. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  3298. """
  3299. Add a *path operation* using an HTTP GET operation.
  3300. ## Example
  3301. ```python
  3302. from fastapi import APIRouter, FastAPI
  3303. app = FastAPI()
  3304. router = APIRouter()
  3305. @router.get("/items/")
  3306. def read_items():
  3307. return [{"name": "Empanada"}, {"name": "Arepa"}]
  3308. app.include_router(router)
  3309. ```
  3310. """
  3311. return self.api_route(
  3312. path=path,
  3313. response_model=response_model,
  3314. status_code=status_code,
  3315. tags=tags,
  3316. dependencies=dependencies,
  3317. summary=summary,
  3318. description=description,
  3319. response_description=response_description,
  3320. responses=responses,
  3321. deprecated=deprecated,
  3322. methods=["GET"],
  3323. operation_id=operation_id,
  3324. response_model_include=response_model_include,
  3325. response_model_exclude=response_model_exclude,
  3326. response_model_by_alias=response_model_by_alias,
  3327. response_model_exclude_unset=response_model_exclude_unset,
  3328. response_model_exclude_defaults=response_model_exclude_defaults,
  3329. response_model_exclude_none=response_model_exclude_none,
  3330. include_in_schema=include_in_schema,
  3331. response_class=response_class,
  3332. name=name,
  3333. callbacks=callbacks,
  3334. openapi_extra=openapi_extra,
  3335. generate_unique_id_function=generate_unique_id_function,
  3336. )
  3337. def put(
  3338. self,
  3339. path: Annotated[
  3340. str,
  3341. Doc(
  3342. """
  3343. The URL path to be used for this *path operation*.
  3344. For example, in `http://example.com/items`, the path is `/items`.
  3345. """
  3346. ),
  3347. ],
  3348. *,
  3349. response_model: Annotated[
  3350. Any,
  3351. Doc(
  3352. """
  3353. The type to use for the response.
  3354. It could be any valid Pydantic *field* type. So, it doesn't have to
  3355. be a Pydantic model, it could be other things, like a `list`, `dict`,
  3356. etc.
  3357. It will be used for:
  3358. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  3359. show it as the response (JSON Schema).
  3360. * Serialization: you could return an arbitrary object and the
  3361. `response_model` would be used to serialize that object into the
  3362. corresponding JSON.
  3363. * Filtering: the JSON sent to the client will only contain the data
  3364. (fields) defined in the `response_model`. If you returned an object
  3365. that contains an attribute `password` but the `response_model` does
  3366. not include that field, the JSON sent to the client would not have
  3367. that `password`.
  3368. * Validation: whatever you return will be serialized with the
  3369. `response_model`, converting any data as necessary to generate the
  3370. corresponding JSON. But if the data in the object returned is not
  3371. valid, that would mean a violation of the contract with the client,
  3372. so it's an error from the API developer. So, FastAPI will raise an
  3373. error and return a 500 error code (Internal Server Error).
  3374. Read more about it in the
  3375. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  3376. """
  3377. ),
  3378. ] = Default(None),
  3379. status_code: Annotated[
  3380. int | None,
  3381. Doc(
  3382. """
  3383. The default status code to be used for the response.
  3384. You could override the status code by returning a response directly.
  3385. Read more about it in the
  3386. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  3387. """
  3388. ),
  3389. ] = None,
  3390. tags: Annotated[
  3391. list[str | Enum] | None,
  3392. Doc(
  3393. """
  3394. A list of tags to be applied to the *path operation*.
  3395. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3396. Read more about it in the
  3397. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  3398. """
  3399. ),
  3400. ] = None,
  3401. dependencies: Annotated[
  3402. Sequence[params.Depends] | None,
  3403. Doc(
  3404. """
  3405. A list of dependencies (using `Depends()`) to be applied to the
  3406. *path operation*.
  3407. Read more about it in the
  3408. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  3409. """
  3410. ),
  3411. ] = None,
  3412. summary: Annotated[
  3413. str | None,
  3414. Doc(
  3415. """
  3416. A summary for the *path operation*.
  3417. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3418. Read more about it in the
  3419. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  3420. """
  3421. ),
  3422. ] = None,
  3423. description: Annotated[
  3424. str | None,
  3425. Doc(
  3426. """
  3427. A description for the *path operation*.
  3428. If not provided, it will be extracted automatically from the docstring
  3429. of the *path operation function*.
  3430. It can contain Markdown.
  3431. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3432. Read more about it in the
  3433. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  3434. """
  3435. ),
  3436. ] = None,
  3437. response_description: Annotated[
  3438. str,
  3439. Doc(
  3440. """
  3441. The description for the default response.
  3442. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3443. """
  3444. ),
  3445. ] = "Successful Response",
  3446. responses: Annotated[
  3447. dict[int | str, dict[str, Any]] | None,
  3448. Doc(
  3449. """
  3450. Additional responses that could be returned by this *path operation*.
  3451. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3452. """
  3453. ),
  3454. ] = None,
  3455. deprecated: Annotated[
  3456. bool | None,
  3457. Doc(
  3458. """
  3459. Mark this *path operation* as deprecated.
  3460. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3461. """
  3462. ),
  3463. ] = None,
  3464. operation_id: Annotated[
  3465. str | None,
  3466. Doc(
  3467. """
  3468. Custom operation ID to be used by this *path operation*.
  3469. By default, it is generated automatically.
  3470. If you provide a custom operation ID, you need to make sure it is
  3471. unique for the whole API.
  3472. You can customize the
  3473. operation ID generation with the parameter
  3474. `generate_unique_id_function` in the `FastAPI` class.
  3475. Read more about it in the
  3476. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  3477. """
  3478. ),
  3479. ] = None,
  3480. response_model_include: Annotated[
  3481. IncEx | None,
  3482. Doc(
  3483. """
  3484. Configuration passed to Pydantic to include only certain fields in the
  3485. response data.
  3486. Read more about it in the
  3487. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3488. """
  3489. ),
  3490. ] = None,
  3491. response_model_exclude: Annotated[
  3492. IncEx | None,
  3493. Doc(
  3494. """
  3495. Configuration passed to Pydantic to exclude certain fields in the
  3496. response data.
  3497. Read more about it in the
  3498. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3499. """
  3500. ),
  3501. ] = None,
  3502. response_model_by_alias: Annotated[
  3503. bool,
  3504. Doc(
  3505. """
  3506. Configuration passed to Pydantic to define if the response model
  3507. should be serialized by alias when an alias is used.
  3508. Read more about it in the
  3509. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3510. """
  3511. ),
  3512. ] = True,
  3513. response_model_exclude_unset: Annotated[
  3514. bool,
  3515. Doc(
  3516. """
  3517. Configuration passed to Pydantic to define if the response data
  3518. should have all the fields, including the ones that were not set and
  3519. have their default values. This is different from
  3520. `response_model_exclude_defaults` in that if the fields are set,
  3521. they will be included in the response, even if the value is the same
  3522. as the default.
  3523. When `True`, default values are omitted from the response.
  3524. Read more about it in the
  3525. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  3526. """
  3527. ),
  3528. ] = False,
  3529. response_model_exclude_defaults: Annotated[
  3530. bool,
  3531. Doc(
  3532. """
  3533. Configuration passed to Pydantic to define if the response data
  3534. should have all the fields, including the ones that have the same value
  3535. as the default. This is different from `response_model_exclude_unset`
  3536. in that if the fields are set but contain the same default values,
  3537. they will be excluded from the response.
  3538. When `True`, default values are omitted from the response.
  3539. Read more about it in the
  3540. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  3541. """
  3542. ),
  3543. ] = False,
  3544. response_model_exclude_none: Annotated[
  3545. bool,
  3546. Doc(
  3547. """
  3548. Configuration passed to Pydantic to define if the response data should
  3549. exclude fields set to `None`.
  3550. This is much simpler (less smart) than `response_model_exclude_unset`
  3551. and `response_model_exclude_defaults`. You probably want to use one of
  3552. those two instead of this one, as those allow returning `None` values
  3553. when it makes sense.
  3554. Read more about it in the
  3555. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  3556. """
  3557. ),
  3558. ] = False,
  3559. include_in_schema: Annotated[
  3560. bool,
  3561. Doc(
  3562. """
  3563. Include this *path operation* in the generated OpenAPI schema.
  3564. This affects the generated OpenAPI (e.g. visible at `/docs`).
  3565. Read more about it in the
  3566. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  3567. """
  3568. ),
  3569. ] = True,
  3570. response_class: Annotated[
  3571. type[Response],
  3572. Doc(
  3573. """
  3574. Response class to be used for this *path operation*.
  3575. This will not be used if you return a response directly.
  3576. Read more about it in the
  3577. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  3578. """
  3579. ),
  3580. ] = Default(JSONResponse),
  3581. name: Annotated[
  3582. str | None,
  3583. Doc(
  3584. """
  3585. Name for this *path operation*. Only used internally.
  3586. """
  3587. ),
  3588. ] = None,
  3589. callbacks: Annotated[
  3590. list[BaseRoute] | None,
  3591. Doc(
  3592. """
  3593. List of *path operations* that will be used as OpenAPI callbacks.
  3594. This is only for OpenAPI documentation, the callbacks won't be used
  3595. directly.
  3596. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3597. Read more about it in the
  3598. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  3599. """
  3600. ),
  3601. ] = None,
  3602. openapi_extra: Annotated[
  3603. dict[str, Any] | None,
  3604. Doc(
  3605. """
  3606. Extra metadata to be included in the OpenAPI schema for this *path
  3607. operation*.
  3608. Read more about it in the
  3609. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  3610. """
  3611. ),
  3612. ] = None,
  3613. generate_unique_id_function: Annotated[
  3614. Callable[[APIRoute], str],
  3615. Doc(
  3616. """
  3617. Customize the function used to generate unique IDs for the *path
  3618. operations* shown in the generated OpenAPI.
  3619. This is particularly useful when automatically generating clients or
  3620. SDKs for your API.
  3621. Read more about it in the
  3622. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  3623. """
  3624. ),
  3625. ] = Default(generate_unique_id),
  3626. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  3627. """
  3628. Add a *path operation* using an HTTP PUT operation.
  3629. ## Example
  3630. ```python
  3631. from fastapi import APIRouter, FastAPI
  3632. from pydantic import BaseModel
  3633. class Item(BaseModel):
  3634. name: str
  3635. description: str | None = None
  3636. app = FastAPI()
  3637. router = APIRouter()
  3638. @router.put("/items/{item_id}")
  3639. def replace_item(item_id: str, item: Item):
  3640. return {"message": "Item replaced", "id": item_id}
  3641. app.include_router(router)
  3642. ```
  3643. """
  3644. return self.api_route(
  3645. path=path,
  3646. response_model=response_model,
  3647. status_code=status_code,
  3648. tags=tags,
  3649. dependencies=dependencies,
  3650. summary=summary,
  3651. description=description,
  3652. response_description=response_description,
  3653. responses=responses,
  3654. deprecated=deprecated,
  3655. methods=["PUT"],
  3656. operation_id=operation_id,
  3657. response_model_include=response_model_include,
  3658. response_model_exclude=response_model_exclude,
  3659. response_model_by_alias=response_model_by_alias,
  3660. response_model_exclude_unset=response_model_exclude_unset,
  3661. response_model_exclude_defaults=response_model_exclude_defaults,
  3662. response_model_exclude_none=response_model_exclude_none,
  3663. include_in_schema=include_in_schema,
  3664. response_class=response_class,
  3665. name=name,
  3666. callbacks=callbacks,
  3667. openapi_extra=openapi_extra,
  3668. generate_unique_id_function=generate_unique_id_function,
  3669. )
  3670. def post(
  3671. self,
  3672. path: Annotated[
  3673. str,
  3674. Doc(
  3675. """
  3676. The URL path to be used for this *path operation*.
  3677. For example, in `http://example.com/items`, the path is `/items`.
  3678. """
  3679. ),
  3680. ],
  3681. *,
  3682. response_model: Annotated[
  3683. Any,
  3684. Doc(
  3685. """
  3686. The type to use for the response.
  3687. It could be any valid Pydantic *field* type. So, it doesn't have to
  3688. be a Pydantic model, it could be other things, like a `list`, `dict`,
  3689. etc.
  3690. It will be used for:
  3691. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  3692. show it as the response (JSON Schema).
  3693. * Serialization: you could return an arbitrary object and the
  3694. `response_model` would be used to serialize that object into the
  3695. corresponding JSON.
  3696. * Filtering: the JSON sent to the client will only contain the data
  3697. (fields) defined in the `response_model`. If you returned an object
  3698. that contains an attribute `password` but the `response_model` does
  3699. not include that field, the JSON sent to the client would not have
  3700. that `password`.
  3701. * Validation: whatever you return will be serialized with the
  3702. `response_model`, converting any data as necessary to generate the
  3703. corresponding JSON. But if the data in the object returned is not
  3704. valid, that would mean a violation of the contract with the client,
  3705. so it's an error from the API developer. So, FastAPI will raise an
  3706. error and return a 500 error code (Internal Server Error).
  3707. Read more about it in the
  3708. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  3709. """
  3710. ),
  3711. ] = Default(None),
  3712. status_code: Annotated[
  3713. int | None,
  3714. Doc(
  3715. """
  3716. The default status code to be used for the response.
  3717. You could override the status code by returning a response directly.
  3718. Read more about it in the
  3719. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  3720. """
  3721. ),
  3722. ] = None,
  3723. tags: Annotated[
  3724. list[str | Enum] | None,
  3725. Doc(
  3726. """
  3727. A list of tags to be applied to the *path operation*.
  3728. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3729. Read more about it in the
  3730. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  3731. """
  3732. ),
  3733. ] = None,
  3734. dependencies: Annotated[
  3735. Sequence[params.Depends] | None,
  3736. Doc(
  3737. """
  3738. A list of dependencies (using `Depends()`) to be applied to the
  3739. *path operation*.
  3740. Read more about it in the
  3741. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  3742. """
  3743. ),
  3744. ] = None,
  3745. summary: Annotated[
  3746. str | None,
  3747. Doc(
  3748. """
  3749. A summary for the *path operation*.
  3750. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3751. Read more about it in the
  3752. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  3753. """
  3754. ),
  3755. ] = None,
  3756. description: Annotated[
  3757. str | None,
  3758. Doc(
  3759. """
  3760. A description for the *path operation*.
  3761. If not provided, it will be extracted automatically from the docstring
  3762. of the *path operation function*.
  3763. It can contain Markdown.
  3764. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3765. Read more about it in the
  3766. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  3767. """
  3768. ),
  3769. ] = None,
  3770. response_description: Annotated[
  3771. str,
  3772. Doc(
  3773. """
  3774. The description for the default response.
  3775. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3776. """
  3777. ),
  3778. ] = "Successful Response",
  3779. responses: Annotated[
  3780. dict[int | str, dict[str, Any]] | None,
  3781. Doc(
  3782. """
  3783. Additional responses that could be returned by this *path operation*.
  3784. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3785. """
  3786. ),
  3787. ] = None,
  3788. deprecated: Annotated[
  3789. bool | None,
  3790. Doc(
  3791. """
  3792. Mark this *path operation* as deprecated.
  3793. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3794. """
  3795. ),
  3796. ] = None,
  3797. operation_id: Annotated[
  3798. str | None,
  3799. Doc(
  3800. """
  3801. Custom operation ID to be used by this *path operation*.
  3802. By default, it is generated automatically.
  3803. If you provide a custom operation ID, you need to make sure it is
  3804. unique for the whole API.
  3805. You can customize the
  3806. operation ID generation with the parameter
  3807. `generate_unique_id_function` in the `FastAPI` class.
  3808. Read more about it in the
  3809. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  3810. """
  3811. ),
  3812. ] = None,
  3813. response_model_include: Annotated[
  3814. IncEx | None,
  3815. Doc(
  3816. """
  3817. Configuration passed to Pydantic to include only certain fields in the
  3818. response data.
  3819. Read more about it in the
  3820. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3821. """
  3822. ),
  3823. ] = None,
  3824. response_model_exclude: Annotated[
  3825. IncEx | None,
  3826. Doc(
  3827. """
  3828. Configuration passed to Pydantic to exclude certain fields in the
  3829. response data.
  3830. Read more about it in the
  3831. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3832. """
  3833. ),
  3834. ] = None,
  3835. response_model_by_alias: Annotated[
  3836. bool,
  3837. Doc(
  3838. """
  3839. Configuration passed to Pydantic to define if the response model
  3840. should be serialized by alias when an alias is used.
  3841. Read more about it in the
  3842. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  3843. """
  3844. ),
  3845. ] = True,
  3846. response_model_exclude_unset: Annotated[
  3847. bool,
  3848. Doc(
  3849. """
  3850. Configuration passed to Pydantic to define if the response data
  3851. should have all the fields, including the ones that were not set and
  3852. have their default values. This is different from
  3853. `response_model_exclude_defaults` in that if the fields are set,
  3854. they will be included in the response, even if the value is the same
  3855. as the default.
  3856. When `True`, default values are omitted from the response.
  3857. Read more about it in the
  3858. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  3859. """
  3860. ),
  3861. ] = False,
  3862. response_model_exclude_defaults: Annotated[
  3863. bool,
  3864. Doc(
  3865. """
  3866. Configuration passed to Pydantic to define if the response data
  3867. should have all the fields, including the ones that have the same value
  3868. as the default. This is different from `response_model_exclude_unset`
  3869. in that if the fields are set but contain the same default values,
  3870. they will be excluded from the response.
  3871. When `True`, default values are omitted from the response.
  3872. Read more about it in the
  3873. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  3874. """
  3875. ),
  3876. ] = False,
  3877. response_model_exclude_none: Annotated[
  3878. bool,
  3879. Doc(
  3880. """
  3881. Configuration passed to Pydantic to define if the response data should
  3882. exclude fields set to `None`.
  3883. This is much simpler (less smart) than `response_model_exclude_unset`
  3884. and `response_model_exclude_defaults`. You probably want to use one of
  3885. those two instead of this one, as those allow returning `None` values
  3886. when it makes sense.
  3887. Read more about it in the
  3888. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  3889. """
  3890. ),
  3891. ] = False,
  3892. include_in_schema: Annotated[
  3893. bool,
  3894. Doc(
  3895. """
  3896. Include this *path operation* in the generated OpenAPI schema.
  3897. This affects the generated OpenAPI (e.g. visible at `/docs`).
  3898. Read more about it in the
  3899. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  3900. """
  3901. ),
  3902. ] = True,
  3903. response_class: Annotated[
  3904. type[Response],
  3905. Doc(
  3906. """
  3907. Response class to be used for this *path operation*.
  3908. This will not be used if you return a response directly.
  3909. Read more about it in the
  3910. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  3911. """
  3912. ),
  3913. ] = Default(JSONResponse),
  3914. name: Annotated[
  3915. str | None,
  3916. Doc(
  3917. """
  3918. Name for this *path operation*. Only used internally.
  3919. """
  3920. ),
  3921. ] = None,
  3922. callbacks: Annotated[
  3923. list[BaseRoute] | None,
  3924. Doc(
  3925. """
  3926. List of *path operations* that will be used as OpenAPI callbacks.
  3927. This is only for OpenAPI documentation, the callbacks won't be used
  3928. directly.
  3929. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  3930. Read more about it in the
  3931. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  3932. """
  3933. ),
  3934. ] = None,
  3935. openapi_extra: Annotated[
  3936. dict[str, Any] | None,
  3937. Doc(
  3938. """
  3939. Extra metadata to be included in the OpenAPI schema for this *path
  3940. operation*.
  3941. Read more about it in the
  3942. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  3943. """
  3944. ),
  3945. ] = None,
  3946. generate_unique_id_function: Annotated[
  3947. Callable[[APIRoute], str],
  3948. Doc(
  3949. """
  3950. Customize the function used to generate unique IDs for the *path
  3951. operations* shown in the generated OpenAPI.
  3952. This is particularly useful when automatically generating clients or
  3953. SDKs for your API.
  3954. Read more about it in the
  3955. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  3956. """
  3957. ),
  3958. ] = Default(generate_unique_id),
  3959. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  3960. """
  3961. Add a *path operation* using an HTTP POST operation.
  3962. ## Example
  3963. ```python
  3964. from fastapi import APIRouter, FastAPI
  3965. from pydantic import BaseModel
  3966. class Item(BaseModel):
  3967. name: str
  3968. description: str | None = None
  3969. app = FastAPI()
  3970. router = APIRouter()
  3971. @router.post("/items/")
  3972. def create_item(item: Item):
  3973. return {"message": "Item created"}
  3974. app.include_router(router)
  3975. ```
  3976. """
  3977. return self.api_route(
  3978. path=path,
  3979. response_model=response_model,
  3980. status_code=status_code,
  3981. tags=tags,
  3982. dependencies=dependencies,
  3983. summary=summary,
  3984. description=description,
  3985. response_description=response_description,
  3986. responses=responses,
  3987. deprecated=deprecated,
  3988. methods=["POST"],
  3989. operation_id=operation_id,
  3990. response_model_include=response_model_include,
  3991. response_model_exclude=response_model_exclude,
  3992. response_model_by_alias=response_model_by_alias,
  3993. response_model_exclude_unset=response_model_exclude_unset,
  3994. response_model_exclude_defaults=response_model_exclude_defaults,
  3995. response_model_exclude_none=response_model_exclude_none,
  3996. include_in_schema=include_in_schema,
  3997. response_class=response_class,
  3998. name=name,
  3999. callbacks=callbacks,
  4000. openapi_extra=openapi_extra,
  4001. generate_unique_id_function=generate_unique_id_function,
  4002. )
  4003. def delete(
  4004. self,
  4005. path: Annotated[
  4006. str,
  4007. Doc(
  4008. """
  4009. The URL path to be used for this *path operation*.
  4010. For example, in `http://example.com/items`, the path is `/items`.
  4011. """
  4012. ),
  4013. ],
  4014. *,
  4015. response_model: Annotated[
  4016. Any,
  4017. Doc(
  4018. """
  4019. The type to use for the response.
  4020. It could be any valid Pydantic *field* type. So, it doesn't have to
  4021. be a Pydantic model, it could be other things, like a `list`, `dict`,
  4022. etc.
  4023. It will be used for:
  4024. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  4025. show it as the response (JSON Schema).
  4026. * Serialization: you could return an arbitrary object and the
  4027. `response_model` would be used to serialize that object into the
  4028. corresponding JSON.
  4029. * Filtering: the JSON sent to the client will only contain the data
  4030. (fields) defined in the `response_model`. If you returned an object
  4031. that contains an attribute `password` but the `response_model` does
  4032. not include that field, the JSON sent to the client would not have
  4033. that `password`.
  4034. * Validation: whatever you return will be serialized with the
  4035. `response_model`, converting any data as necessary to generate the
  4036. corresponding JSON. But if the data in the object returned is not
  4037. valid, that would mean a violation of the contract with the client,
  4038. so it's an error from the API developer. So, FastAPI will raise an
  4039. error and return a 500 error code (Internal Server Error).
  4040. Read more about it in the
  4041. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  4042. """
  4043. ),
  4044. ] = Default(None),
  4045. status_code: Annotated[
  4046. int | None,
  4047. Doc(
  4048. """
  4049. The default status code to be used for the response.
  4050. You could override the status code by returning a response directly.
  4051. Read more about it in the
  4052. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  4053. """
  4054. ),
  4055. ] = None,
  4056. tags: Annotated[
  4057. list[str | Enum] | None,
  4058. Doc(
  4059. """
  4060. A list of tags to be applied to the *path operation*.
  4061. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4062. Read more about it in the
  4063. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  4064. """
  4065. ),
  4066. ] = None,
  4067. dependencies: Annotated[
  4068. Sequence[params.Depends] | None,
  4069. Doc(
  4070. """
  4071. A list of dependencies (using `Depends()`) to be applied to the
  4072. *path operation*.
  4073. Read more about it in the
  4074. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  4075. """
  4076. ),
  4077. ] = None,
  4078. summary: Annotated[
  4079. str | None,
  4080. Doc(
  4081. """
  4082. A summary for the *path operation*.
  4083. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4084. Read more about it in the
  4085. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  4086. """
  4087. ),
  4088. ] = None,
  4089. description: Annotated[
  4090. str | None,
  4091. Doc(
  4092. """
  4093. A description for the *path operation*.
  4094. If not provided, it will be extracted automatically from the docstring
  4095. of the *path operation function*.
  4096. It can contain Markdown.
  4097. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4098. Read more about it in the
  4099. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  4100. """
  4101. ),
  4102. ] = None,
  4103. response_description: Annotated[
  4104. str,
  4105. Doc(
  4106. """
  4107. The description for the default response.
  4108. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4109. """
  4110. ),
  4111. ] = "Successful Response",
  4112. responses: Annotated[
  4113. dict[int | str, dict[str, Any]] | None,
  4114. Doc(
  4115. """
  4116. Additional responses that could be returned by this *path operation*.
  4117. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4118. """
  4119. ),
  4120. ] = None,
  4121. deprecated: Annotated[
  4122. bool | None,
  4123. Doc(
  4124. """
  4125. Mark this *path operation* as deprecated.
  4126. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4127. """
  4128. ),
  4129. ] = None,
  4130. operation_id: Annotated[
  4131. str | None,
  4132. Doc(
  4133. """
  4134. Custom operation ID to be used by this *path operation*.
  4135. By default, it is generated automatically.
  4136. If you provide a custom operation ID, you need to make sure it is
  4137. unique for the whole API.
  4138. You can customize the
  4139. operation ID generation with the parameter
  4140. `generate_unique_id_function` in the `FastAPI` class.
  4141. Read more about it in the
  4142. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  4143. """
  4144. ),
  4145. ] = None,
  4146. response_model_include: Annotated[
  4147. IncEx | None,
  4148. Doc(
  4149. """
  4150. Configuration passed to Pydantic to include only certain fields in the
  4151. response data.
  4152. Read more about it in the
  4153. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4154. """
  4155. ),
  4156. ] = None,
  4157. response_model_exclude: Annotated[
  4158. IncEx | None,
  4159. Doc(
  4160. """
  4161. Configuration passed to Pydantic to exclude certain fields in the
  4162. response data.
  4163. Read more about it in the
  4164. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4165. """
  4166. ),
  4167. ] = None,
  4168. response_model_by_alias: Annotated[
  4169. bool,
  4170. Doc(
  4171. """
  4172. Configuration passed to Pydantic to define if the response model
  4173. should be serialized by alias when an alias is used.
  4174. Read more about it in the
  4175. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4176. """
  4177. ),
  4178. ] = True,
  4179. response_model_exclude_unset: Annotated[
  4180. bool,
  4181. Doc(
  4182. """
  4183. Configuration passed to Pydantic to define if the response data
  4184. should have all the fields, including the ones that were not set and
  4185. have their default values. This is different from
  4186. `response_model_exclude_defaults` in that if the fields are set,
  4187. they will be included in the response, even if the value is the same
  4188. as the default.
  4189. When `True`, default values are omitted from the response.
  4190. Read more about it in the
  4191. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  4192. """
  4193. ),
  4194. ] = False,
  4195. response_model_exclude_defaults: Annotated[
  4196. bool,
  4197. Doc(
  4198. """
  4199. Configuration passed to Pydantic to define if the response data
  4200. should have all the fields, including the ones that have the same value
  4201. as the default. This is different from `response_model_exclude_unset`
  4202. in that if the fields are set but contain the same default values,
  4203. they will be excluded from the response.
  4204. When `True`, default values are omitted from the response.
  4205. Read more about it in the
  4206. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  4207. """
  4208. ),
  4209. ] = False,
  4210. response_model_exclude_none: Annotated[
  4211. bool,
  4212. Doc(
  4213. """
  4214. Configuration passed to Pydantic to define if the response data should
  4215. exclude fields set to `None`.
  4216. This is much simpler (less smart) than `response_model_exclude_unset`
  4217. and `response_model_exclude_defaults`. You probably want to use one of
  4218. those two instead of this one, as those allow returning `None` values
  4219. when it makes sense.
  4220. Read more about it in the
  4221. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  4222. """
  4223. ),
  4224. ] = False,
  4225. include_in_schema: Annotated[
  4226. bool,
  4227. Doc(
  4228. """
  4229. Include this *path operation* in the generated OpenAPI schema.
  4230. This affects the generated OpenAPI (e.g. visible at `/docs`).
  4231. Read more about it in the
  4232. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  4233. """
  4234. ),
  4235. ] = True,
  4236. response_class: Annotated[
  4237. type[Response],
  4238. Doc(
  4239. """
  4240. Response class to be used for this *path operation*.
  4241. This will not be used if you return a response directly.
  4242. Read more about it in the
  4243. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  4244. """
  4245. ),
  4246. ] = Default(JSONResponse),
  4247. name: Annotated[
  4248. str | None,
  4249. Doc(
  4250. """
  4251. Name for this *path operation*. Only used internally.
  4252. """
  4253. ),
  4254. ] = None,
  4255. callbacks: Annotated[
  4256. list[BaseRoute] | None,
  4257. Doc(
  4258. """
  4259. List of *path operations* that will be used as OpenAPI callbacks.
  4260. This is only for OpenAPI documentation, the callbacks won't be used
  4261. directly.
  4262. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4263. Read more about it in the
  4264. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  4265. """
  4266. ),
  4267. ] = None,
  4268. openapi_extra: Annotated[
  4269. dict[str, Any] | None,
  4270. Doc(
  4271. """
  4272. Extra metadata to be included in the OpenAPI schema for this *path
  4273. operation*.
  4274. Read more about it in the
  4275. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  4276. """
  4277. ),
  4278. ] = None,
  4279. generate_unique_id_function: Annotated[
  4280. Callable[[APIRoute], str],
  4281. Doc(
  4282. """
  4283. Customize the function used to generate unique IDs for the *path
  4284. operations* shown in the generated OpenAPI.
  4285. This is particularly useful when automatically generating clients or
  4286. SDKs for your API.
  4287. Read more about it in the
  4288. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  4289. """
  4290. ),
  4291. ] = Default(generate_unique_id),
  4292. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  4293. """
  4294. Add a *path operation* using an HTTP DELETE operation.
  4295. ## Example
  4296. ```python
  4297. from fastapi import APIRouter, FastAPI
  4298. app = FastAPI()
  4299. router = APIRouter()
  4300. @router.delete("/items/{item_id}")
  4301. def delete_item(item_id: str):
  4302. return {"message": "Item deleted"}
  4303. app.include_router(router)
  4304. ```
  4305. """
  4306. return self.api_route(
  4307. path=path,
  4308. response_model=response_model,
  4309. status_code=status_code,
  4310. tags=tags,
  4311. dependencies=dependencies,
  4312. summary=summary,
  4313. description=description,
  4314. response_description=response_description,
  4315. responses=responses,
  4316. deprecated=deprecated,
  4317. methods=["DELETE"],
  4318. operation_id=operation_id,
  4319. response_model_include=response_model_include,
  4320. response_model_exclude=response_model_exclude,
  4321. response_model_by_alias=response_model_by_alias,
  4322. response_model_exclude_unset=response_model_exclude_unset,
  4323. response_model_exclude_defaults=response_model_exclude_defaults,
  4324. response_model_exclude_none=response_model_exclude_none,
  4325. include_in_schema=include_in_schema,
  4326. response_class=response_class,
  4327. name=name,
  4328. callbacks=callbacks,
  4329. openapi_extra=openapi_extra,
  4330. generate_unique_id_function=generate_unique_id_function,
  4331. )
  4332. def options(
  4333. self,
  4334. path: Annotated[
  4335. str,
  4336. Doc(
  4337. """
  4338. The URL path to be used for this *path operation*.
  4339. For example, in `http://example.com/items`, the path is `/items`.
  4340. """
  4341. ),
  4342. ],
  4343. *,
  4344. response_model: Annotated[
  4345. Any,
  4346. Doc(
  4347. """
  4348. The type to use for the response.
  4349. It could be any valid Pydantic *field* type. So, it doesn't have to
  4350. be a Pydantic model, it could be other things, like a `list`, `dict`,
  4351. etc.
  4352. It will be used for:
  4353. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  4354. show it as the response (JSON Schema).
  4355. * Serialization: you could return an arbitrary object and the
  4356. `response_model` would be used to serialize that object into the
  4357. corresponding JSON.
  4358. * Filtering: the JSON sent to the client will only contain the data
  4359. (fields) defined in the `response_model`. If you returned an object
  4360. that contains an attribute `password` but the `response_model` does
  4361. not include that field, the JSON sent to the client would not have
  4362. that `password`.
  4363. * Validation: whatever you return will be serialized with the
  4364. `response_model`, converting any data as necessary to generate the
  4365. corresponding JSON. But if the data in the object returned is not
  4366. valid, that would mean a violation of the contract with the client,
  4367. so it's an error from the API developer. So, FastAPI will raise an
  4368. error and return a 500 error code (Internal Server Error).
  4369. Read more about it in the
  4370. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  4371. """
  4372. ),
  4373. ] = Default(None),
  4374. status_code: Annotated[
  4375. int | None,
  4376. Doc(
  4377. """
  4378. The default status code to be used for the response.
  4379. You could override the status code by returning a response directly.
  4380. Read more about it in the
  4381. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  4382. """
  4383. ),
  4384. ] = None,
  4385. tags: Annotated[
  4386. list[str | Enum] | None,
  4387. Doc(
  4388. """
  4389. A list of tags to be applied to the *path operation*.
  4390. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4391. Read more about it in the
  4392. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  4393. """
  4394. ),
  4395. ] = None,
  4396. dependencies: Annotated[
  4397. Sequence[params.Depends] | None,
  4398. Doc(
  4399. """
  4400. A list of dependencies (using `Depends()`) to be applied to the
  4401. *path operation*.
  4402. Read more about it in the
  4403. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  4404. """
  4405. ),
  4406. ] = None,
  4407. summary: Annotated[
  4408. str | None,
  4409. Doc(
  4410. """
  4411. A summary for the *path operation*.
  4412. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4413. Read more about it in the
  4414. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  4415. """
  4416. ),
  4417. ] = None,
  4418. description: Annotated[
  4419. str | None,
  4420. Doc(
  4421. """
  4422. A description for the *path operation*.
  4423. If not provided, it will be extracted automatically from the docstring
  4424. of the *path operation function*.
  4425. It can contain Markdown.
  4426. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4427. Read more about it in the
  4428. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  4429. """
  4430. ),
  4431. ] = None,
  4432. response_description: Annotated[
  4433. str,
  4434. Doc(
  4435. """
  4436. The description for the default response.
  4437. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4438. """
  4439. ),
  4440. ] = "Successful Response",
  4441. responses: Annotated[
  4442. dict[int | str, dict[str, Any]] | None,
  4443. Doc(
  4444. """
  4445. Additional responses that could be returned by this *path operation*.
  4446. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4447. """
  4448. ),
  4449. ] = None,
  4450. deprecated: Annotated[
  4451. bool | None,
  4452. Doc(
  4453. """
  4454. Mark this *path operation* as deprecated.
  4455. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4456. """
  4457. ),
  4458. ] = None,
  4459. operation_id: Annotated[
  4460. str | None,
  4461. Doc(
  4462. """
  4463. Custom operation ID to be used by this *path operation*.
  4464. By default, it is generated automatically.
  4465. If you provide a custom operation ID, you need to make sure it is
  4466. unique for the whole API.
  4467. You can customize the
  4468. operation ID generation with the parameter
  4469. `generate_unique_id_function` in the `FastAPI` class.
  4470. Read more about it in the
  4471. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  4472. """
  4473. ),
  4474. ] = None,
  4475. response_model_include: Annotated[
  4476. IncEx | None,
  4477. Doc(
  4478. """
  4479. Configuration passed to Pydantic to include only certain fields in the
  4480. response data.
  4481. Read more about it in the
  4482. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4483. """
  4484. ),
  4485. ] = None,
  4486. response_model_exclude: Annotated[
  4487. IncEx | None,
  4488. Doc(
  4489. """
  4490. Configuration passed to Pydantic to exclude certain fields in the
  4491. response data.
  4492. Read more about it in the
  4493. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4494. """
  4495. ),
  4496. ] = None,
  4497. response_model_by_alias: Annotated[
  4498. bool,
  4499. Doc(
  4500. """
  4501. Configuration passed to Pydantic to define if the response model
  4502. should be serialized by alias when an alias is used.
  4503. Read more about it in the
  4504. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4505. """
  4506. ),
  4507. ] = True,
  4508. response_model_exclude_unset: Annotated[
  4509. bool,
  4510. Doc(
  4511. """
  4512. Configuration passed to Pydantic to define if the response data
  4513. should have all the fields, including the ones that were not set and
  4514. have their default values. This is different from
  4515. `response_model_exclude_defaults` in that if the fields are set,
  4516. they will be included in the response, even if the value is the same
  4517. as the default.
  4518. When `True`, default values are omitted from the response.
  4519. Read more about it in the
  4520. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  4521. """
  4522. ),
  4523. ] = False,
  4524. response_model_exclude_defaults: Annotated[
  4525. bool,
  4526. Doc(
  4527. """
  4528. Configuration passed to Pydantic to define if the response data
  4529. should have all the fields, including the ones that have the same value
  4530. as the default. This is different from `response_model_exclude_unset`
  4531. in that if the fields are set but contain the same default values,
  4532. they will be excluded from the response.
  4533. When `True`, default values are omitted from the response.
  4534. Read more about it in the
  4535. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  4536. """
  4537. ),
  4538. ] = False,
  4539. response_model_exclude_none: Annotated[
  4540. bool,
  4541. Doc(
  4542. """
  4543. Configuration passed to Pydantic to define if the response data should
  4544. exclude fields set to `None`.
  4545. This is much simpler (less smart) than `response_model_exclude_unset`
  4546. and `response_model_exclude_defaults`. You probably want to use one of
  4547. those two instead of this one, as those allow returning `None` values
  4548. when it makes sense.
  4549. Read more about it in the
  4550. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  4551. """
  4552. ),
  4553. ] = False,
  4554. include_in_schema: Annotated[
  4555. bool,
  4556. Doc(
  4557. """
  4558. Include this *path operation* in the generated OpenAPI schema.
  4559. This affects the generated OpenAPI (e.g. visible at `/docs`).
  4560. Read more about it in the
  4561. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  4562. """
  4563. ),
  4564. ] = True,
  4565. response_class: Annotated[
  4566. type[Response],
  4567. Doc(
  4568. """
  4569. Response class to be used for this *path operation*.
  4570. This will not be used if you return a response directly.
  4571. Read more about it in the
  4572. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  4573. """
  4574. ),
  4575. ] = Default(JSONResponse),
  4576. name: Annotated[
  4577. str | None,
  4578. Doc(
  4579. """
  4580. Name for this *path operation*. Only used internally.
  4581. """
  4582. ),
  4583. ] = None,
  4584. callbacks: Annotated[
  4585. list[BaseRoute] | None,
  4586. Doc(
  4587. """
  4588. List of *path operations* that will be used as OpenAPI callbacks.
  4589. This is only for OpenAPI documentation, the callbacks won't be used
  4590. directly.
  4591. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4592. Read more about it in the
  4593. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  4594. """
  4595. ),
  4596. ] = None,
  4597. openapi_extra: Annotated[
  4598. dict[str, Any] | None,
  4599. Doc(
  4600. """
  4601. Extra metadata to be included in the OpenAPI schema for this *path
  4602. operation*.
  4603. Read more about it in the
  4604. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  4605. """
  4606. ),
  4607. ] = None,
  4608. generate_unique_id_function: Annotated[
  4609. Callable[[APIRoute], str],
  4610. Doc(
  4611. """
  4612. Customize the function used to generate unique IDs for the *path
  4613. operations* shown in the generated OpenAPI.
  4614. This is particularly useful when automatically generating clients or
  4615. SDKs for your API.
  4616. Read more about it in the
  4617. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  4618. """
  4619. ),
  4620. ] = Default(generate_unique_id),
  4621. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  4622. """
  4623. Add a *path operation* using an HTTP OPTIONS operation.
  4624. ## Example
  4625. ```python
  4626. from fastapi import APIRouter, FastAPI
  4627. app = FastAPI()
  4628. router = APIRouter()
  4629. @router.options("/items/")
  4630. def get_item_options():
  4631. return {"additions": ["Aji", "Guacamole"]}
  4632. app.include_router(router)
  4633. ```
  4634. """
  4635. return self.api_route(
  4636. path=path,
  4637. response_model=response_model,
  4638. status_code=status_code,
  4639. tags=tags,
  4640. dependencies=dependencies,
  4641. summary=summary,
  4642. description=description,
  4643. response_description=response_description,
  4644. responses=responses,
  4645. deprecated=deprecated,
  4646. methods=["OPTIONS"],
  4647. operation_id=operation_id,
  4648. response_model_include=response_model_include,
  4649. response_model_exclude=response_model_exclude,
  4650. response_model_by_alias=response_model_by_alias,
  4651. response_model_exclude_unset=response_model_exclude_unset,
  4652. response_model_exclude_defaults=response_model_exclude_defaults,
  4653. response_model_exclude_none=response_model_exclude_none,
  4654. include_in_schema=include_in_schema,
  4655. response_class=response_class,
  4656. name=name,
  4657. callbacks=callbacks,
  4658. openapi_extra=openapi_extra,
  4659. generate_unique_id_function=generate_unique_id_function,
  4660. )
  4661. def head(
  4662. self,
  4663. path: Annotated[
  4664. str,
  4665. Doc(
  4666. """
  4667. The URL path to be used for this *path operation*.
  4668. For example, in `http://example.com/items`, the path is `/items`.
  4669. """
  4670. ),
  4671. ],
  4672. *,
  4673. response_model: Annotated[
  4674. Any,
  4675. Doc(
  4676. """
  4677. The type to use for the response.
  4678. It could be any valid Pydantic *field* type. So, it doesn't have to
  4679. be a Pydantic model, it could be other things, like a `list`, `dict`,
  4680. etc.
  4681. It will be used for:
  4682. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  4683. show it as the response (JSON Schema).
  4684. * Serialization: you could return an arbitrary object and the
  4685. `response_model` would be used to serialize that object into the
  4686. corresponding JSON.
  4687. * Filtering: the JSON sent to the client will only contain the data
  4688. (fields) defined in the `response_model`. If you returned an object
  4689. that contains an attribute `password` but the `response_model` does
  4690. not include that field, the JSON sent to the client would not have
  4691. that `password`.
  4692. * Validation: whatever you return will be serialized with the
  4693. `response_model`, converting any data as necessary to generate the
  4694. corresponding JSON. But if the data in the object returned is not
  4695. valid, that would mean a violation of the contract with the client,
  4696. so it's an error from the API developer. So, FastAPI will raise an
  4697. error and return a 500 error code (Internal Server Error).
  4698. Read more about it in the
  4699. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  4700. """
  4701. ),
  4702. ] = Default(None),
  4703. status_code: Annotated[
  4704. int | None,
  4705. Doc(
  4706. """
  4707. The default status code to be used for the response.
  4708. You could override the status code by returning a response directly.
  4709. Read more about it in the
  4710. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  4711. """
  4712. ),
  4713. ] = None,
  4714. tags: Annotated[
  4715. list[str | Enum] | None,
  4716. Doc(
  4717. """
  4718. A list of tags to be applied to the *path operation*.
  4719. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4720. Read more about it in the
  4721. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  4722. """
  4723. ),
  4724. ] = None,
  4725. dependencies: Annotated[
  4726. Sequence[params.Depends] | None,
  4727. Doc(
  4728. """
  4729. A list of dependencies (using `Depends()`) to be applied to the
  4730. *path operation*.
  4731. Read more about it in the
  4732. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  4733. """
  4734. ),
  4735. ] = None,
  4736. summary: Annotated[
  4737. str | None,
  4738. Doc(
  4739. """
  4740. A summary for the *path operation*.
  4741. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4742. Read more about it in the
  4743. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  4744. """
  4745. ),
  4746. ] = None,
  4747. description: Annotated[
  4748. str | None,
  4749. Doc(
  4750. """
  4751. A description for the *path operation*.
  4752. If not provided, it will be extracted automatically from the docstring
  4753. of the *path operation function*.
  4754. It can contain Markdown.
  4755. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4756. Read more about it in the
  4757. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  4758. """
  4759. ),
  4760. ] = None,
  4761. response_description: Annotated[
  4762. str,
  4763. Doc(
  4764. """
  4765. The description for the default response.
  4766. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4767. """
  4768. ),
  4769. ] = "Successful Response",
  4770. responses: Annotated[
  4771. dict[int | str, dict[str, Any]] | None,
  4772. Doc(
  4773. """
  4774. Additional responses that could be returned by this *path operation*.
  4775. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4776. """
  4777. ),
  4778. ] = None,
  4779. deprecated: Annotated[
  4780. bool | None,
  4781. Doc(
  4782. """
  4783. Mark this *path operation* as deprecated.
  4784. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4785. """
  4786. ),
  4787. ] = None,
  4788. operation_id: Annotated[
  4789. str | None,
  4790. Doc(
  4791. """
  4792. Custom operation ID to be used by this *path operation*.
  4793. By default, it is generated automatically.
  4794. If you provide a custom operation ID, you need to make sure it is
  4795. unique for the whole API.
  4796. You can customize the
  4797. operation ID generation with the parameter
  4798. `generate_unique_id_function` in the `FastAPI` class.
  4799. Read more about it in the
  4800. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  4801. """
  4802. ),
  4803. ] = None,
  4804. response_model_include: Annotated[
  4805. IncEx | None,
  4806. Doc(
  4807. """
  4808. Configuration passed to Pydantic to include only certain fields in the
  4809. response data.
  4810. Read more about it in the
  4811. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4812. """
  4813. ),
  4814. ] = None,
  4815. response_model_exclude: Annotated[
  4816. IncEx | None,
  4817. Doc(
  4818. """
  4819. Configuration passed to Pydantic to exclude certain fields in the
  4820. response data.
  4821. Read more about it in the
  4822. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4823. """
  4824. ),
  4825. ] = None,
  4826. response_model_by_alias: Annotated[
  4827. bool,
  4828. Doc(
  4829. """
  4830. Configuration passed to Pydantic to define if the response model
  4831. should be serialized by alias when an alias is used.
  4832. Read more about it in the
  4833. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  4834. """
  4835. ),
  4836. ] = True,
  4837. response_model_exclude_unset: Annotated[
  4838. bool,
  4839. Doc(
  4840. """
  4841. Configuration passed to Pydantic to define if the response data
  4842. should have all the fields, including the ones that were not set and
  4843. have their default values. This is different from
  4844. `response_model_exclude_defaults` in that if the fields are set,
  4845. they will be included in the response, even if the value is the same
  4846. as the default.
  4847. When `True`, default values are omitted from the response.
  4848. Read more about it in the
  4849. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  4850. """
  4851. ),
  4852. ] = False,
  4853. response_model_exclude_defaults: Annotated[
  4854. bool,
  4855. Doc(
  4856. """
  4857. Configuration passed to Pydantic to define if the response data
  4858. should have all the fields, including the ones that have the same value
  4859. as the default. This is different from `response_model_exclude_unset`
  4860. in that if the fields are set but contain the same default values,
  4861. they will be excluded from the response.
  4862. When `True`, default values are omitted from the response.
  4863. Read more about it in the
  4864. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  4865. """
  4866. ),
  4867. ] = False,
  4868. response_model_exclude_none: Annotated[
  4869. bool,
  4870. Doc(
  4871. """
  4872. Configuration passed to Pydantic to define if the response data should
  4873. exclude fields set to `None`.
  4874. This is much simpler (less smart) than `response_model_exclude_unset`
  4875. and `response_model_exclude_defaults`. You probably want to use one of
  4876. those two instead of this one, as those allow returning `None` values
  4877. when it makes sense.
  4878. Read more about it in the
  4879. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  4880. """
  4881. ),
  4882. ] = False,
  4883. include_in_schema: Annotated[
  4884. bool,
  4885. Doc(
  4886. """
  4887. Include this *path operation* in the generated OpenAPI schema.
  4888. This affects the generated OpenAPI (e.g. visible at `/docs`).
  4889. Read more about it in the
  4890. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  4891. """
  4892. ),
  4893. ] = True,
  4894. response_class: Annotated[
  4895. type[Response],
  4896. Doc(
  4897. """
  4898. Response class to be used for this *path operation*.
  4899. This will not be used if you return a response directly.
  4900. Read more about it in the
  4901. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  4902. """
  4903. ),
  4904. ] = Default(JSONResponse),
  4905. name: Annotated[
  4906. str | None,
  4907. Doc(
  4908. """
  4909. Name for this *path operation*. Only used internally.
  4910. """
  4911. ),
  4912. ] = None,
  4913. callbacks: Annotated[
  4914. list[BaseRoute] | None,
  4915. Doc(
  4916. """
  4917. List of *path operations* that will be used as OpenAPI callbacks.
  4918. This is only for OpenAPI documentation, the callbacks won't be used
  4919. directly.
  4920. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  4921. Read more about it in the
  4922. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  4923. """
  4924. ),
  4925. ] = None,
  4926. openapi_extra: Annotated[
  4927. dict[str, Any] | None,
  4928. Doc(
  4929. """
  4930. Extra metadata to be included in the OpenAPI schema for this *path
  4931. operation*.
  4932. Read more about it in the
  4933. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  4934. """
  4935. ),
  4936. ] = None,
  4937. generate_unique_id_function: Annotated[
  4938. Callable[[APIRoute], str],
  4939. Doc(
  4940. """
  4941. Customize the function used to generate unique IDs for the *path
  4942. operations* shown in the generated OpenAPI.
  4943. This is particularly useful when automatically generating clients or
  4944. SDKs for your API.
  4945. Read more about it in the
  4946. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  4947. """
  4948. ),
  4949. ] = Default(generate_unique_id),
  4950. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  4951. """
  4952. Add a *path operation* using an HTTP HEAD operation.
  4953. ## Example
  4954. ```python
  4955. from fastapi import APIRouter, FastAPI
  4956. from pydantic import BaseModel
  4957. class Item(BaseModel):
  4958. name: str
  4959. description: str | None = None
  4960. app = FastAPI()
  4961. router = APIRouter()
  4962. @router.head("/items/", status_code=204)
  4963. def get_items_headers(response: Response):
  4964. response.headers["X-Cat-Dog"] = "Alone in the world"
  4965. app.include_router(router)
  4966. ```
  4967. """
  4968. return self.api_route(
  4969. path=path,
  4970. response_model=response_model,
  4971. status_code=status_code,
  4972. tags=tags,
  4973. dependencies=dependencies,
  4974. summary=summary,
  4975. description=description,
  4976. response_description=response_description,
  4977. responses=responses,
  4978. deprecated=deprecated,
  4979. methods=["HEAD"],
  4980. operation_id=operation_id,
  4981. response_model_include=response_model_include,
  4982. response_model_exclude=response_model_exclude,
  4983. response_model_by_alias=response_model_by_alias,
  4984. response_model_exclude_unset=response_model_exclude_unset,
  4985. response_model_exclude_defaults=response_model_exclude_defaults,
  4986. response_model_exclude_none=response_model_exclude_none,
  4987. include_in_schema=include_in_schema,
  4988. response_class=response_class,
  4989. name=name,
  4990. callbacks=callbacks,
  4991. openapi_extra=openapi_extra,
  4992. generate_unique_id_function=generate_unique_id_function,
  4993. )
  4994. def patch(
  4995. self,
  4996. path: Annotated[
  4997. str,
  4998. Doc(
  4999. """
  5000. The URL path to be used for this *path operation*.
  5001. For example, in `http://example.com/items`, the path is `/items`.
  5002. """
  5003. ),
  5004. ],
  5005. *,
  5006. response_model: Annotated[
  5007. Any,
  5008. Doc(
  5009. """
  5010. The type to use for the response.
  5011. It could be any valid Pydantic *field* type. So, it doesn't have to
  5012. be a Pydantic model, it could be other things, like a `list`, `dict`,
  5013. etc.
  5014. It will be used for:
  5015. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  5016. show it as the response (JSON Schema).
  5017. * Serialization: you could return an arbitrary object and the
  5018. `response_model` would be used to serialize that object into the
  5019. corresponding JSON.
  5020. * Filtering: the JSON sent to the client will only contain the data
  5021. (fields) defined in the `response_model`. If you returned an object
  5022. that contains an attribute `password` but the `response_model` does
  5023. not include that field, the JSON sent to the client would not have
  5024. that `password`.
  5025. * Validation: whatever you return will be serialized with the
  5026. `response_model`, converting any data as necessary to generate the
  5027. corresponding JSON. But if the data in the object returned is not
  5028. valid, that would mean a violation of the contract with the client,
  5029. so it's an error from the API developer. So, FastAPI will raise an
  5030. error and return a 500 error code (Internal Server Error).
  5031. Read more about it in the
  5032. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  5033. """
  5034. ),
  5035. ] = Default(None),
  5036. status_code: Annotated[
  5037. int | None,
  5038. Doc(
  5039. """
  5040. The default status code to be used for the response.
  5041. You could override the status code by returning a response directly.
  5042. Read more about it in the
  5043. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  5044. """
  5045. ),
  5046. ] = None,
  5047. tags: Annotated[
  5048. list[str | Enum] | None,
  5049. Doc(
  5050. """
  5051. A list of tags to be applied to the *path operation*.
  5052. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5053. Read more about it in the
  5054. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  5055. """
  5056. ),
  5057. ] = None,
  5058. dependencies: Annotated[
  5059. Sequence[params.Depends] | None,
  5060. Doc(
  5061. """
  5062. A list of dependencies (using `Depends()`) to be applied to the
  5063. *path operation*.
  5064. Read more about it in the
  5065. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  5066. """
  5067. ),
  5068. ] = None,
  5069. summary: Annotated[
  5070. str | None,
  5071. Doc(
  5072. """
  5073. A summary for the *path operation*.
  5074. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5075. Read more about it in the
  5076. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  5077. """
  5078. ),
  5079. ] = None,
  5080. description: Annotated[
  5081. str | None,
  5082. Doc(
  5083. """
  5084. A description for the *path operation*.
  5085. If not provided, it will be extracted automatically from the docstring
  5086. of the *path operation function*.
  5087. It can contain Markdown.
  5088. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5089. Read more about it in the
  5090. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  5091. """
  5092. ),
  5093. ] = None,
  5094. response_description: Annotated[
  5095. str,
  5096. Doc(
  5097. """
  5098. The description for the default response.
  5099. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5100. """
  5101. ),
  5102. ] = "Successful Response",
  5103. responses: Annotated[
  5104. dict[int | str, dict[str, Any]] | None,
  5105. Doc(
  5106. """
  5107. Additional responses that could be returned by this *path operation*.
  5108. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5109. """
  5110. ),
  5111. ] = None,
  5112. deprecated: Annotated[
  5113. bool | None,
  5114. Doc(
  5115. """
  5116. Mark this *path operation* as deprecated.
  5117. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5118. """
  5119. ),
  5120. ] = None,
  5121. operation_id: Annotated[
  5122. str | None,
  5123. Doc(
  5124. """
  5125. Custom operation ID to be used by this *path operation*.
  5126. By default, it is generated automatically.
  5127. If you provide a custom operation ID, you need to make sure it is
  5128. unique for the whole API.
  5129. You can customize the
  5130. operation ID generation with the parameter
  5131. `generate_unique_id_function` in the `FastAPI` class.
  5132. Read more about it in the
  5133. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  5134. """
  5135. ),
  5136. ] = None,
  5137. response_model_include: Annotated[
  5138. IncEx | None,
  5139. Doc(
  5140. """
  5141. Configuration passed to Pydantic to include only certain fields in the
  5142. response data.
  5143. Read more about it in the
  5144. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  5145. """
  5146. ),
  5147. ] = None,
  5148. response_model_exclude: Annotated[
  5149. IncEx | None,
  5150. Doc(
  5151. """
  5152. Configuration passed to Pydantic to exclude certain fields in the
  5153. response data.
  5154. Read more about it in the
  5155. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  5156. """
  5157. ),
  5158. ] = None,
  5159. response_model_by_alias: Annotated[
  5160. bool,
  5161. Doc(
  5162. """
  5163. Configuration passed to Pydantic to define if the response model
  5164. should be serialized by alias when an alias is used.
  5165. Read more about it in the
  5166. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  5167. """
  5168. ),
  5169. ] = True,
  5170. response_model_exclude_unset: Annotated[
  5171. bool,
  5172. Doc(
  5173. """
  5174. Configuration passed to Pydantic to define if the response data
  5175. should have all the fields, including the ones that were not set and
  5176. have their default values. This is different from
  5177. `response_model_exclude_defaults` in that if the fields are set,
  5178. they will be included in the response, even if the value is the same
  5179. as the default.
  5180. When `True`, default values are omitted from the response.
  5181. Read more about it in the
  5182. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  5183. """
  5184. ),
  5185. ] = False,
  5186. response_model_exclude_defaults: Annotated[
  5187. bool,
  5188. Doc(
  5189. """
  5190. Configuration passed to Pydantic to define if the response data
  5191. should have all the fields, including the ones that have the same value
  5192. as the default. This is different from `response_model_exclude_unset`
  5193. in that if the fields are set but contain the same default values,
  5194. they will be excluded from the response.
  5195. When `True`, default values are omitted from the response.
  5196. Read more about it in the
  5197. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  5198. """
  5199. ),
  5200. ] = False,
  5201. response_model_exclude_none: Annotated[
  5202. bool,
  5203. Doc(
  5204. """
  5205. Configuration passed to Pydantic to define if the response data should
  5206. exclude fields set to `None`.
  5207. This is much simpler (less smart) than `response_model_exclude_unset`
  5208. and `response_model_exclude_defaults`. You probably want to use one of
  5209. those two instead of this one, as those allow returning `None` values
  5210. when it makes sense.
  5211. Read more about it in the
  5212. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  5213. """
  5214. ),
  5215. ] = False,
  5216. include_in_schema: Annotated[
  5217. bool,
  5218. Doc(
  5219. """
  5220. Include this *path operation* in the generated OpenAPI schema.
  5221. This affects the generated OpenAPI (e.g. visible at `/docs`).
  5222. Read more about it in the
  5223. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  5224. """
  5225. ),
  5226. ] = True,
  5227. response_class: Annotated[
  5228. type[Response],
  5229. Doc(
  5230. """
  5231. Response class to be used for this *path operation*.
  5232. This will not be used if you return a response directly.
  5233. Read more about it in the
  5234. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  5235. """
  5236. ),
  5237. ] = Default(JSONResponse),
  5238. name: Annotated[
  5239. str | None,
  5240. Doc(
  5241. """
  5242. Name for this *path operation*. Only used internally.
  5243. """
  5244. ),
  5245. ] = None,
  5246. callbacks: Annotated[
  5247. list[BaseRoute] | None,
  5248. Doc(
  5249. """
  5250. List of *path operations* that will be used as OpenAPI callbacks.
  5251. This is only for OpenAPI documentation, the callbacks won't be used
  5252. directly.
  5253. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5254. Read more about it in the
  5255. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  5256. """
  5257. ),
  5258. ] = None,
  5259. openapi_extra: Annotated[
  5260. dict[str, Any] | None,
  5261. Doc(
  5262. """
  5263. Extra metadata to be included in the OpenAPI schema for this *path
  5264. operation*.
  5265. Read more about it in the
  5266. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  5267. """
  5268. ),
  5269. ] = None,
  5270. generate_unique_id_function: Annotated[
  5271. Callable[[APIRoute], str],
  5272. Doc(
  5273. """
  5274. Customize the function used to generate unique IDs for the *path
  5275. operations* shown in the generated OpenAPI.
  5276. This is particularly useful when automatically generating clients or
  5277. SDKs for your API.
  5278. Read more about it in the
  5279. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  5280. """
  5281. ),
  5282. ] = Default(generate_unique_id),
  5283. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  5284. """
  5285. Add a *path operation* using an HTTP PATCH operation.
  5286. ## Example
  5287. ```python
  5288. from fastapi import APIRouter, FastAPI
  5289. from pydantic import BaseModel
  5290. class Item(BaseModel):
  5291. name: str
  5292. description: str | None = None
  5293. app = FastAPI()
  5294. router = APIRouter()
  5295. @router.patch("/items/")
  5296. def update_item(item: Item):
  5297. return {"message": "Item updated in place"}
  5298. app.include_router(router)
  5299. ```
  5300. """
  5301. return self.api_route(
  5302. path=path,
  5303. response_model=response_model,
  5304. status_code=status_code,
  5305. tags=tags,
  5306. dependencies=dependencies,
  5307. summary=summary,
  5308. description=description,
  5309. response_description=response_description,
  5310. responses=responses,
  5311. deprecated=deprecated,
  5312. methods=["PATCH"],
  5313. operation_id=operation_id,
  5314. response_model_include=response_model_include,
  5315. response_model_exclude=response_model_exclude,
  5316. response_model_by_alias=response_model_by_alias,
  5317. response_model_exclude_unset=response_model_exclude_unset,
  5318. response_model_exclude_defaults=response_model_exclude_defaults,
  5319. response_model_exclude_none=response_model_exclude_none,
  5320. include_in_schema=include_in_schema,
  5321. response_class=response_class,
  5322. name=name,
  5323. callbacks=callbacks,
  5324. openapi_extra=openapi_extra,
  5325. generate_unique_id_function=generate_unique_id_function,
  5326. )
  5327. def trace(
  5328. self,
  5329. path: Annotated[
  5330. str,
  5331. Doc(
  5332. """
  5333. The URL path to be used for this *path operation*.
  5334. For example, in `http://example.com/items`, the path is `/items`.
  5335. """
  5336. ),
  5337. ],
  5338. *,
  5339. response_model: Annotated[
  5340. Any,
  5341. Doc(
  5342. """
  5343. The type to use for the response.
  5344. It could be any valid Pydantic *field* type. So, it doesn't have to
  5345. be a Pydantic model, it could be other things, like a `list`, `dict`,
  5346. etc.
  5347. It will be used for:
  5348. * Documentation: the generated OpenAPI (and the UI at `/docs`) will
  5349. show it as the response (JSON Schema).
  5350. * Serialization: you could return an arbitrary object and the
  5351. `response_model` would be used to serialize that object into the
  5352. corresponding JSON.
  5353. * Filtering: the JSON sent to the client will only contain the data
  5354. (fields) defined in the `response_model`. If you returned an object
  5355. that contains an attribute `password` but the `response_model` does
  5356. not include that field, the JSON sent to the client would not have
  5357. that `password`.
  5358. * Validation: whatever you return will be serialized with the
  5359. `response_model`, converting any data as necessary to generate the
  5360. corresponding JSON. But if the data in the object returned is not
  5361. valid, that would mean a violation of the contract with the client,
  5362. so it's an error from the API developer. So, FastAPI will raise an
  5363. error and return a 500 error code (Internal Server Error).
  5364. Read more about it in the
  5365. [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
  5366. """
  5367. ),
  5368. ] = Default(None),
  5369. status_code: Annotated[
  5370. int | None,
  5371. Doc(
  5372. """
  5373. The default status code to be used for the response.
  5374. You could override the status code by returning a response directly.
  5375. Read more about it in the
  5376. [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
  5377. """
  5378. ),
  5379. ] = None,
  5380. tags: Annotated[
  5381. list[str | Enum] | None,
  5382. Doc(
  5383. """
  5384. A list of tags to be applied to the *path operation*.
  5385. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5386. Read more about it in the
  5387. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
  5388. """
  5389. ),
  5390. ] = None,
  5391. dependencies: Annotated[
  5392. Sequence[params.Depends] | None,
  5393. Doc(
  5394. """
  5395. A list of dependencies (using `Depends()`) to be applied to the
  5396. *path operation*.
  5397. Read more about it in the
  5398. [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
  5399. """
  5400. ),
  5401. ] = None,
  5402. summary: Annotated[
  5403. str | None,
  5404. Doc(
  5405. """
  5406. A summary for the *path operation*.
  5407. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5408. Read more about it in the
  5409. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  5410. """
  5411. ),
  5412. ] = None,
  5413. description: Annotated[
  5414. str | None,
  5415. Doc(
  5416. """
  5417. A description for the *path operation*.
  5418. If not provided, it will be extracted automatically from the docstring
  5419. of the *path operation function*.
  5420. It can contain Markdown.
  5421. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5422. Read more about it in the
  5423. [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
  5424. """
  5425. ),
  5426. ] = None,
  5427. response_description: Annotated[
  5428. str,
  5429. Doc(
  5430. """
  5431. The description for the default response.
  5432. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5433. """
  5434. ),
  5435. ] = "Successful Response",
  5436. responses: Annotated[
  5437. dict[int | str, dict[str, Any]] | None,
  5438. Doc(
  5439. """
  5440. Additional responses that could be returned by this *path operation*.
  5441. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5442. """
  5443. ),
  5444. ] = None,
  5445. deprecated: Annotated[
  5446. bool | None,
  5447. Doc(
  5448. """
  5449. Mark this *path operation* as deprecated.
  5450. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5451. """
  5452. ),
  5453. ] = None,
  5454. operation_id: Annotated[
  5455. str | None,
  5456. Doc(
  5457. """
  5458. Custom operation ID to be used by this *path operation*.
  5459. By default, it is generated automatically.
  5460. If you provide a custom operation ID, you need to make sure it is
  5461. unique for the whole API.
  5462. You can customize the
  5463. operation ID generation with the parameter
  5464. `generate_unique_id_function` in the `FastAPI` class.
  5465. Read more about it in the
  5466. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  5467. """
  5468. ),
  5469. ] = None,
  5470. response_model_include: Annotated[
  5471. IncEx | None,
  5472. Doc(
  5473. """
  5474. Configuration passed to Pydantic to include only certain fields in the
  5475. response data.
  5476. Read more about it in the
  5477. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  5478. """
  5479. ),
  5480. ] = None,
  5481. response_model_exclude: Annotated[
  5482. IncEx | None,
  5483. Doc(
  5484. """
  5485. Configuration passed to Pydantic to exclude certain fields in the
  5486. response data.
  5487. Read more about it in the
  5488. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  5489. """
  5490. ),
  5491. ] = None,
  5492. response_model_by_alias: Annotated[
  5493. bool,
  5494. Doc(
  5495. """
  5496. Configuration passed to Pydantic to define if the response model
  5497. should be serialized by alias when an alias is used.
  5498. Read more about it in the
  5499. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
  5500. """
  5501. ),
  5502. ] = True,
  5503. response_model_exclude_unset: Annotated[
  5504. bool,
  5505. Doc(
  5506. """
  5507. Configuration passed to Pydantic to define if the response data
  5508. should have all the fields, including the ones that were not set and
  5509. have their default values. This is different from
  5510. `response_model_exclude_defaults` in that if the fields are set,
  5511. they will be included in the response, even if the value is the same
  5512. as the default.
  5513. When `True`, default values are omitted from the response.
  5514. Read more about it in the
  5515. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  5516. """
  5517. ),
  5518. ] = False,
  5519. response_model_exclude_defaults: Annotated[
  5520. bool,
  5521. Doc(
  5522. """
  5523. Configuration passed to Pydantic to define if the response data
  5524. should have all the fields, including the ones that have the same value
  5525. as the default. This is different from `response_model_exclude_unset`
  5526. in that if the fields are set but contain the same default values,
  5527. they will be excluded from the response.
  5528. When `True`, default values are omitted from the response.
  5529. Read more about it in the
  5530. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
  5531. """
  5532. ),
  5533. ] = False,
  5534. response_model_exclude_none: Annotated[
  5535. bool,
  5536. Doc(
  5537. """
  5538. Configuration passed to Pydantic to define if the response data should
  5539. exclude fields set to `None`.
  5540. This is much simpler (less smart) than `response_model_exclude_unset`
  5541. and `response_model_exclude_defaults`. You probably want to use one of
  5542. those two instead of this one, as those allow returning `None` values
  5543. when it makes sense.
  5544. Read more about it in the
  5545. [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
  5546. """
  5547. ),
  5548. ] = False,
  5549. include_in_schema: Annotated[
  5550. bool,
  5551. Doc(
  5552. """
  5553. Include this *path operation* in the generated OpenAPI schema.
  5554. This affects the generated OpenAPI (e.g. visible at `/docs`).
  5555. Read more about it in the
  5556. [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
  5557. """
  5558. ),
  5559. ] = True,
  5560. response_class: Annotated[
  5561. type[Response],
  5562. Doc(
  5563. """
  5564. Response class to be used for this *path operation*.
  5565. This will not be used if you return a response directly.
  5566. Read more about it in the
  5567. [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
  5568. """
  5569. ),
  5570. ] = Default(JSONResponse),
  5571. name: Annotated[
  5572. str | None,
  5573. Doc(
  5574. """
  5575. Name for this *path operation*. Only used internally.
  5576. """
  5577. ),
  5578. ] = None,
  5579. callbacks: Annotated[
  5580. list[BaseRoute] | None,
  5581. Doc(
  5582. """
  5583. List of *path operations* that will be used as OpenAPI callbacks.
  5584. This is only for OpenAPI documentation, the callbacks won't be used
  5585. directly.
  5586. It will be added to the generated OpenAPI (e.g. visible at `/docs`).
  5587. Read more about it in the
  5588. [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
  5589. """
  5590. ),
  5591. ] = None,
  5592. openapi_extra: Annotated[
  5593. dict[str, Any] | None,
  5594. Doc(
  5595. """
  5596. Extra metadata to be included in the OpenAPI schema for this *path
  5597. operation*.
  5598. Read more about it in the
  5599. [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
  5600. """
  5601. ),
  5602. ] = None,
  5603. generate_unique_id_function: Annotated[
  5604. Callable[[APIRoute], str],
  5605. Doc(
  5606. """
  5607. Customize the function used to generate unique IDs for the *path
  5608. operations* shown in the generated OpenAPI.
  5609. This is particularly useful when automatically generating clients or
  5610. SDKs for your API.
  5611. Read more about it in the
  5612. [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
  5613. """
  5614. ),
  5615. ] = Default(generate_unique_id),
  5616. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  5617. """
  5618. Add a *path operation* using an HTTP TRACE operation.
  5619. ## Example
  5620. ```python
  5621. from fastapi import APIRouter, FastAPI
  5622. from pydantic import BaseModel
  5623. class Item(BaseModel):
  5624. name: str
  5625. description: str | None = None
  5626. app = FastAPI()
  5627. router = APIRouter()
  5628. @router.trace("/items/{item_id}")
  5629. def trace_item(item_id: str):
  5630. return None
  5631. app.include_router(router)
  5632. ```
  5633. """
  5634. return self.api_route(
  5635. path=path,
  5636. response_model=response_model,
  5637. status_code=status_code,
  5638. tags=tags,
  5639. dependencies=dependencies,
  5640. summary=summary,
  5641. description=description,
  5642. response_description=response_description,
  5643. responses=responses,
  5644. deprecated=deprecated,
  5645. methods=["TRACE"],
  5646. operation_id=operation_id,
  5647. response_model_include=response_model_include,
  5648. response_model_exclude=response_model_exclude,
  5649. response_model_by_alias=response_model_by_alias,
  5650. response_model_exclude_unset=response_model_exclude_unset,
  5651. response_model_exclude_defaults=response_model_exclude_defaults,
  5652. response_model_exclude_none=response_model_exclude_none,
  5653. include_in_schema=include_in_schema,
  5654. response_class=response_class,
  5655. name=name,
  5656. callbacks=callbacks,
  5657. openapi_extra=openapi_extra,
  5658. generate_unique_id_function=generate_unique_id_function,
  5659. )
  5660. # TODO: remove this once the lifespan (or alternative) interface is improved
  5661. async def _startup(self) -> None:
  5662. """
  5663. Run any `.on_startup` event handlers.
  5664. This method is kept for backward compatibility after Starlette removed
  5665. support for on_startup/on_shutdown handlers.
  5666. Ref: https://github.com/Kludex/starlette/pull/3117
  5667. """
  5668. for handler in self.on_startup:
  5669. if is_async_callable(handler):
  5670. await handler()
  5671. else:
  5672. handler()
  5673. # TODO: remove this once the lifespan (or alternative) interface is improved
  5674. async def _shutdown(self) -> None:
  5675. """
  5676. Run any `.on_shutdown` event handlers.
  5677. This method is kept for backward compatibility after Starlette removed
  5678. support for on_startup/on_shutdown handlers.
  5679. Ref: https://github.com/Kludex/starlette/pull/3117
  5680. """
  5681. for handler in self.on_shutdown:
  5682. if is_async_callable(handler):
  5683. await handler()
  5684. else:
  5685. handler()
  5686. # TODO: remove this once the lifespan (or alternative) interface is improved
  5687. def add_event_handler(
  5688. self,
  5689. event_type: str,
  5690. func: Callable[[], Any],
  5691. ) -> None:
  5692. """
  5693. Add an event handler function for startup or shutdown.
  5694. This method is kept for backward compatibility after Starlette removed
  5695. support for on_startup/on_shutdown handlers.
  5696. Ref: https://github.com/Kludex/starlette/pull/3117
  5697. """
  5698. assert event_type in ("startup", "shutdown")
  5699. if event_type == "startup":
  5700. self.on_startup.append(func)
  5701. else:
  5702. self.on_shutdown.append(func)
  5703. @deprecated(
  5704. """
  5705. on_event is deprecated, use lifespan event handlers instead.
  5706. Read more about it in the
  5707. [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/).
  5708. """
  5709. )
  5710. def on_event(
  5711. self,
  5712. event_type: Annotated[
  5713. str,
  5714. Doc(
  5715. """
  5716. The type of event. `startup` or `shutdown`.
  5717. """
  5718. ),
  5719. ],
  5720. ) -> Callable[[DecoratedCallable], DecoratedCallable]:
  5721. """
  5722. Add an event handler for the router.
  5723. `on_event` is deprecated, use `lifespan` event handlers instead.
  5724. Read more about it in the
  5725. [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/#alternative-events-deprecated).
  5726. """
  5727. def decorator(func: DecoratedCallable) -> DecoratedCallable:
  5728. self.add_event_handler(event_type, func)
  5729. return func
  5730. return decorator