node_classes.py 173 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707
  1. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  2. # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
  4. """Module for some node classes. More nodes in scoped_nodes.py"""
  5. from __future__ import annotations
  6. import abc
  7. import ast
  8. import itertools
  9. import operator
  10. import sys
  11. import typing
  12. import warnings
  13. from collections.abc import Callable, Generator, Iterable, Iterator, Mapping
  14. from functools import cached_property
  15. from typing import TYPE_CHECKING, Any, ClassVar, Literal, Union
  16. from astroid import decorators, protocols, util
  17. from astroid.bases import Instance, _infer_stmts
  18. from astroid.const import _EMPTY_OBJECT_MARKER, PY314_PLUS, Context
  19. from astroid.context import CallContext, InferenceContext, copy_context
  20. from astroid.exceptions import (
  21. AstroidBuildingError,
  22. AstroidError,
  23. AstroidIndexError,
  24. AstroidTypeError,
  25. AstroidValueError,
  26. AttributeInferenceError,
  27. InferenceError,
  28. NameInferenceError,
  29. NoDefault,
  30. ParentMissingError,
  31. _NonDeducibleTypeHierarchy,
  32. )
  33. from astroid.interpreter import dunder_lookup
  34. from astroid.manager import AstroidManager
  35. from astroid.nodes import _base_nodes
  36. from astroid.nodes.const import OP_PRECEDENCE
  37. from astroid.nodes.node_ng import NodeNG
  38. from astroid.nodes.scoped_nodes import SYNTHETIC_ROOT
  39. from astroid.typing import (
  40. ConstFactoryResult,
  41. InferenceErrorInfo,
  42. InferenceResult,
  43. SuccessfulInferenceResult,
  44. )
  45. if sys.version_info >= (3, 11):
  46. from typing import Self
  47. else:
  48. from typing_extensions import Self
  49. if TYPE_CHECKING:
  50. from astroid import nodes
  51. from astroid.nodes import LocalsDictNodeNG
  52. def _is_const(value) -> bool:
  53. return isinstance(value, tuple(CONST_CLS))
  54. _NodesT = typing.TypeVar("_NodesT", bound=NodeNG)
  55. _BadOpMessageT = typing.TypeVar("_BadOpMessageT", bound=util.BadOperationMessage)
  56. # pylint: disable-next=consider-alternative-union-syntax
  57. AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None]
  58. AssignedStmtsCall = Callable[
  59. [
  60. _NodesT,
  61. AssignedStmtsPossibleNode,
  62. InferenceContext | None,
  63. list[int] | None,
  64. ],
  65. Any,
  66. ]
  67. InferBinaryOperation = Callable[
  68. [_NodesT, InferenceContext | None],
  69. Generator[InferenceResult | _BadOpMessageT],
  70. ]
  71. InferLHS = Callable[
  72. [_NodesT, InferenceContext | None],
  73. Generator[InferenceResult, None, InferenceErrorInfo | None],
  74. ]
  75. InferUnaryOp = Callable[[_NodesT, str], ConstFactoryResult]
  76. @decorators.raise_if_nothing_inferred
  77. def unpack_infer(stmt, context: InferenceContext | None = None):
  78. """recursively generate nodes inferred by the given statement.
  79. If the inferred value is a list or a tuple, recurse on the elements
  80. """
  81. if isinstance(stmt, (List, Tuple)):
  82. for elt in stmt.elts:
  83. if elt is util.Uninferable:
  84. yield elt
  85. continue
  86. yield from unpack_infer(elt, context)
  87. return {"node": stmt, "context": context}
  88. # if inferred is a final node, return it and stop
  89. inferred = next(stmt.infer(context), util.Uninferable)
  90. if inferred is stmt:
  91. yield inferred
  92. return {"node": stmt, "context": context}
  93. # else, infer recursively, except Uninferable object that should be returned as is
  94. for inferred in stmt.infer(context):
  95. if isinstance(inferred, util.UninferableBase):
  96. yield inferred
  97. else:
  98. yield from unpack_infer(inferred, context)
  99. return {"node": stmt, "context": context}
  100. def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
  101. """return true if the two given statements are mutually exclusive
  102. `exceptions` may be a list of exception names. If specified, discard If
  103. branches and check one of the statement is in an exception handler catching
  104. one of the given exceptions.
  105. algorithm :
  106. 1) index stmt1's parents
  107. 2) climb among stmt2's parents until we find a common parent
  108. 3) if the common parent is a If or Try statement, look if nodes are
  109. in exclusive branches
  110. """
  111. # index stmt1's parents
  112. stmt1_parents = {}
  113. children = {}
  114. previous = stmt1
  115. for node in stmt1.node_ancestors():
  116. stmt1_parents[node] = 1
  117. children[node] = previous
  118. previous = node
  119. # climb among stmt2's parents until we find a common parent
  120. previous = stmt2
  121. for node in stmt2.node_ancestors():
  122. if node in stmt1_parents:
  123. # if the common parent is a If or Try statement, look if
  124. # nodes are in exclusive branches
  125. if isinstance(node, If) and exceptions is None:
  126. c2attr, c2node = node.locate_child(previous)
  127. c1attr, c1node = node.locate_child(children[node])
  128. if "test" in (c1attr, c2attr):
  129. # If any node is `If.test`, then it must be inclusive with
  130. # the other node (`If.body` and `If.orelse`)
  131. return False
  132. if c1attr != c2attr:
  133. # different `If` branches (`If.body` and `If.orelse`)
  134. return True
  135. elif isinstance(node, Try):
  136. c2attr, c2node = node.locate_child(previous)
  137. c1attr, c1node = node.locate_child(children[node])
  138. if c1node is not c2node:
  139. first_in_body_caught_by_handlers = (
  140. c2attr == "handlers"
  141. and c1attr == "body"
  142. and previous.catch(exceptions)
  143. )
  144. second_in_body_caught_by_handlers = (
  145. c2attr == "body"
  146. and c1attr == "handlers"
  147. and children[node].catch(exceptions)
  148. )
  149. first_in_else_other_in_handlers = (
  150. c2attr == "handlers" and c1attr == "orelse"
  151. )
  152. second_in_else_other_in_handlers = (
  153. c2attr == "orelse" and c1attr == "handlers"
  154. )
  155. if any(
  156. (
  157. first_in_body_caught_by_handlers,
  158. second_in_body_caught_by_handlers,
  159. first_in_else_other_in_handlers,
  160. second_in_else_other_in_handlers,
  161. )
  162. ):
  163. return True
  164. elif c2attr == "handlers" and c1attr == "handlers":
  165. return previous is not children[node]
  166. return False
  167. previous = node
  168. return False
  169. # getitem() helpers.
  170. _SLICE_SENTINEL = object()
  171. def _slice_value(index, context: InferenceContext | None = None):
  172. """Get the value of the given slice index."""
  173. if isinstance(index, Const):
  174. if isinstance(index.value, (int, type(None))):
  175. return index.value
  176. elif index is None:
  177. return None
  178. else:
  179. # Try to infer what the index actually is.
  180. # Since we can't return all the possible values,
  181. # we'll stop at the first possible value.
  182. try:
  183. inferred = next(index.infer(context=context))
  184. except (InferenceError, StopIteration):
  185. pass
  186. else:
  187. if isinstance(inferred, Const):
  188. if isinstance(inferred.value, (int, type(None))):
  189. return inferred.value
  190. # Use a sentinel, because None can be a valid
  191. # value that this function can return,
  192. # as it is the case for unspecified bounds.
  193. return _SLICE_SENTINEL
  194. def _infer_slice(node, context: InferenceContext | None = None):
  195. lower = _slice_value(node.lower, context)
  196. upper = _slice_value(node.upper, context)
  197. step = _slice_value(node.step, context)
  198. if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)):
  199. return slice(lower, upper, step)
  200. raise AstroidTypeError(
  201. message="Could not infer slice used in subscript",
  202. node=node,
  203. index=node.parent,
  204. context=context,
  205. )
  206. def _container_getitem(instance, elts, index, context: InferenceContext | None = None):
  207. """Get a slice or an item, using the given *index*, for the given sequence."""
  208. try:
  209. if isinstance(index, Slice):
  210. index_slice = _infer_slice(index, context=context)
  211. new_cls = instance.__class__()
  212. new_cls.elts = elts[index_slice]
  213. new_cls.parent = instance.parent
  214. return new_cls
  215. if isinstance(index, Const):
  216. return elts[index.value]
  217. except ValueError as exc:
  218. raise AstroidValueError(
  219. message="Slice {index!r} cannot index container",
  220. node=instance,
  221. index=index,
  222. context=context,
  223. ) from exc
  224. except IndexError as exc:
  225. raise AstroidIndexError(
  226. message="Index {index!s} out of range",
  227. node=instance,
  228. index=index,
  229. context=context,
  230. ) from exc
  231. except TypeError as exc:
  232. raise AstroidTypeError(
  233. message="Type error {error!r}", node=instance, index=index, context=context
  234. ) from exc
  235. raise AstroidTypeError(f"Could not use {index} as subscript index")
  236. class BaseContainer(_base_nodes.ParentAssignNode, Instance, metaclass=abc.ABCMeta):
  237. """Base class for Set, FrozenSet, Tuple and List."""
  238. _astroid_fields = ("elts",)
  239. def __init__(
  240. self,
  241. lineno: int | None,
  242. col_offset: int | None,
  243. parent: NodeNG | None,
  244. *,
  245. end_lineno: int | None,
  246. end_col_offset: int | None,
  247. ) -> None:
  248. self.elts: list[SuccessfulInferenceResult] = []
  249. """The elements in the node."""
  250. super().__init__(
  251. lineno=lineno,
  252. col_offset=col_offset,
  253. end_lineno=end_lineno,
  254. end_col_offset=end_col_offset,
  255. parent=parent,
  256. )
  257. def postinit(self, elts: list[SuccessfulInferenceResult]) -> None:
  258. self.elts = elts
  259. @classmethod
  260. def from_elements(cls, elts: Iterable[Any]) -> Self:
  261. """Create a node of this type from the given list of elements.
  262. :param elts: The list of elements that the node should contain.
  263. :returns: A new node containing the given elements.
  264. """
  265. node = cls(
  266. lineno=None,
  267. col_offset=None,
  268. parent=None,
  269. end_lineno=None,
  270. end_col_offset=None,
  271. )
  272. node.elts = [const_factory(e) if _is_const(e) else e for e in elts]
  273. return node
  274. def itered(self):
  275. """An iterator over the elements this node contains.
  276. :returns: The contents of this node.
  277. :rtype: iterable(NodeNG)
  278. """
  279. return self.elts
  280. def bool_value(self, context: InferenceContext | None = None) -> bool:
  281. """Determine the boolean value of this node.
  282. :returns: The boolean value of this node.
  283. """
  284. return bool(self.elts)
  285. @abc.abstractmethod
  286. def pytype(self) -> str:
  287. """Get the name of the type that this node represents.
  288. :returns: The name of the type.
  289. """
  290. def get_children(self):
  291. yield from self.elts
  292. @decorators.raise_if_nothing_inferred
  293. def _infer(
  294. self,
  295. context: InferenceContext | None = None,
  296. **kwargs: Any,
  297. ) -> Iterator[Self]:
  298. has_starred_named_expr = any(
  299. isinstance(e, (Starred, NamedExpr)) for e in self.elts
  300. )
  301. if has_starred_named_expr:
  302. values = self._infer_sequence_helper(context)
  303. new_seq = type(self)(
  304. lineno=self.lineno,
  305. col_offset=self.col_offset,
  306. parent=self.parent,
  307. end_lineno=self.end_lineno,
  308. end_col_offset=self.end_col_offset,
  309. )
  310. new_seq.postinit(values)
  311. yield new_seq
  312. else:
  313. yield self
  314. def _infer_sequence_helper(
  315. self, context: InferenceContext | None = None
  316. ) -> list[SuccessfulInferenceResult]:
  317. """Infer all values based on BaseContainer.elts."""
  318. values = []
  319. for elt in self.elts:
  320. if isinstance(elt, Starred):
  321. starred = util.safe_infer(elt.value, context)
  322. if not starred:
  323. raise InferenceError(node=self, context=context)
  324. if not hasattr(starred, "elts"):
  325. raise InferenceError(node=self, context=context)
  326. # TODO: fresh context?
  327. values.extend(starred._infer_sequence_helper(context))
  328. elif isinstance(elt, NamedExpr):
  329. value = util.safe_infer(elt.value, context)
  330. if not value:
  331. raise InferenceError(node=self, context=context)
  332. values.append(value)
  333. else:
  334. values.append(elt)
  335. return values
  336. # Name classes
  337. class AssignName(
  338. _base_nodes.NoChildrenNode,
  339. _base_nodes.LookupMixIn,
  340. _base_nodes.ParentAssignNode,
  341. ):
  342. """Variation of :class:`ast.Assign` representing assignment to a name.
  343. An :class:`AssignName` is the name of something that is assigned to.
  344. This includes variables defined in a function signature or in a loop.
  345. >>> import astroid
  346. >>> node = astroid.extract_node('variable = range(10)')
  347. >>> node
  348. <Assign l.1 at 0x7effe1db8550>
  349. >>> list(node.get_children())
  350. [<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>]
  351. >>> list(node.get_children())[0].as_string()
  352. 'variable'
  353. """
  354. _other_fields = ("name",)
  355. def __init__(
  356. self,
  357. name: str,
  358. lineno: int,
  359. col_offset: int,
  360. parent: NodeNG,
  361. *,
  362. end_lineno: int | None,
  363. end_col_offset: int | None,
  364. ) -> None:
  365. self.name = name
  366. """The name that is assigned to."""
  367. super().__init__(
  368. lineno=lineno,
  369. col_offset=col_offset,
  370. end_lineno=end_lineno,
  371. end_col_offset=end_col_offset,
  372. parent=parent,
  373. )
  374. assigned_stmts = protocols.assend_assigned_stmts
  375. """Returns the assigned statement (non inferred) according to the assignment type.
  376. See astroid/protocols.py for actual implementation.
  377. """
  378. @decorators.raise_if_nothing_inferred
  379. @decorators.path_wrapper
  380. def _infer(
  381. self, context: InferenceContext | None = None, **kwargs: Any
  382. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  383. """Infer an AssignName: need to inspect the RHS part of the
  384. assign node.
  385. """
  386. if isinstance(self.parent, AugAssign):
  387. return self.parent.infer(context)
  388. stmts = list(self.assigned_stmts(context=context))
  389. return _infer_stmts(stmts, context)
  390. @decorators.raise_if_nothing_inferred
  391. def infer_lhs(
  392. self, context: InferenceContext | None = None, **kwargs: Any
  393. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  394. """Infer a Name: use name lookup rules.
  395. Same implementation as Name._infer."""
  396. # pylint: disable=import-outside-toplevel
  397. from astroid.constraint import get_constraints
  398. from astroid.helpers import _higher_function_scope
  399. frame, stmts = self.lookup(self.name)
  400. if not stmts:
  401. # Try to see if the name is enclosed in a nested function
  402. # and use the higher (first function) scope for searching.
  403. parent_function = _higher_function_scope(self.scope())
  404. if parent_function:
  405. _, stmts = parent_function.lookup(self.name)
  406. if not stmts:
  407. raise NameInferenceError(
  408. name=self.name, scope=self.scope(), context=context
  409. )
  410. context = copy_context(context)
  411. context.lookupname = self.name
  412. context.constraints[self.name] = get_constraints(self, frame)
  413. return _infer_stmts(stmts, context, frame)
  414. class DelName(
  415. _base_nodes.NoChildrenNode, _base_nodes.LookupMixIn, _base_nodes.ParentAssignNode
  416. ):
  417. """Variation of :class:`ast.Delete` representing deletion of a name.
  418. A :class:`DelName` is the name of something that is deleted.
  419. >>> import astroid
  420. >>> node = astroid.extract_node("del variable #@")
  421. >>> list(node.get_children())
  422. [<DelName.variable l.1 at 0x7effe1da4d30>]
  423. >>> list(node.get_children())[0].as_string()
  424. 'variable'
  425. """
  426. _other_fields = ("name",)
  427. def __init__(
  428. self,
  429. name: str,
  430. lineno: int,
  431. col_offset: int,
  432. parent: NodeNG,
  433. *,
  434. end_lineno: int | None,
  435. end_col_offset: int | None,
  436. ) -> None:
  437. self.name = name
  438. """The name that is being deleted."""
  439. super().__init__(
  440. lineno=lineno,
  441. col_offset=col_offset,
  442. end_lineno=end_lineno,
  443. end_col_offset=end_col_offset,
  444. parent=parent,
  445. )
  446. class Name(_base_nodes.LookupMixIn, _base_nodes.NoChildrenNode):
  447. """Class representing an :class:`ast.Name` node.
  448. A :class:`Name` node is something that is named, but not covered by
  449. :class:`AssignName` or :class:`DelName`.
  450. >>> import astroid
  451. >>> node = astroid.extract_node('range(10)')
  452. >>> node
  453. <Call l.1 at 0x7effe1db8710>
  454. >>> list(node.get_children())
  455. [<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>]
  456. >>> list(node.get_children())[0].as_string()
  457. 'range'
  458. """
  459. _other_fields = ("name",)
  460. def __init__(
  461. self,
  462. name: str,
  463. lineno: int,
  464. col_offset: int,
  465. parent: NodeNG,
  466. *,
  467. end_lineno: int | None,
  468. end_col_offset: int | None,
  469. ) -> None:
  470. self.name = name
  471. """The name that this node refers to."""
  472. super().__init__(
  473. lineno=lineno,
  474. col_offset=col_offset,
  475. end_lineno=end_lineno,
  476. end_col_offset=end_col_offset,
  477. parent=parent,
  478. )
  479. def _get_name_nodes(self):
  480. yield self
  481. for child_node in self.get_children():
  482. yield from child_node._get_name_nodes()
  483. @decorators.raise_if_nothing_inferred
  484. @decorators.path_wrapper
  485. def _infer(
  486. self, context: InferenceContext | None = None, **kwargs: Any
  487. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  488. """Infer a Name: use name lookup rules
  489. Same implementation as AssignName._infer_lhs."""
  490. # pylint: disable=import-outside-toplevel
  491. from astroid.constraint import get_constraints
  492. from astroid.helpers import _higher_function_scope
  493. frame, stmts = self.lookup(self.name)
  494. if not stmts:
  495. # Try to see if the name is enclosed in a nested function
  496. # and use the higher (first function) scope for searching.
  497. parent_function = _higher_function_scope(self.scope())
  498. if parent_function:
  499. _, stmts = parent_function.lookup(self.name)
  500. if not stmts:
  501. raise NameInferenceError(
  502. name=self.name, scope=self.scope(), context=context
  503. )
  504. context = copy_context(context)
  505. context.lookupname = self.name
  506. context.constraints[self.name] = get_constraints(self, frame)
  507. return _infer_stmts(stmts, context, frame)
  508. DEPRECATED_ARGUMENT_DEFAULT = "DEPRECATED_ARGUMENT_DEFAULT"
  509. class Arguments(
  510. _base_nodes.AssignTypeNode
  511. ): # pylint: disable=too-many-instance-attributes
  512. """Class representing an :class:`ast.arguments` node.
  513. An :class:`Arguments` node represents that arguments in a
  514. function definition.
  515. >>> import astroid
  516. >>> node = astroid.extract_node('def foo(bar): pass')
  517. >>> node
  518. <FunctionDef.foo l.1 at 0x7effe1db8198>
  519. >>> node.args
  520. <Arguments l.1 at 0x7effe1db82e8>
  521. """
  522. # Python 3.4+ uses a different approach regarding annotations,
  523. # each argument is a new class, _ast.arg, which exposes an
  524. # 'annotation' attribute. In astroid though, arguments are exposed
  525. # as is in the Arguments node and the only way to expose annotations
  526. # is by using something similar with Python 3.3:
  527. # - we expose 'varargannotation' and 'kwargannotation' of annotations
  528. # of varargs and kwargs.
  529. # - we expose 'annotation', a list with annotations for
  530. # for each normal argument. If an argument doesn't have an
  531. # annotation, its value will be None.
  532. _astroid_fields = (
  533. "args",
  534. "defaults",
  535. "kwonlyargs",
  536. "posonlyargs",
  537. "posonlyargs_annotations",
  538. "kw_defaults",
  539. "annotations",
  540. "varargannotation",
  541. "kwargannotation",
  542. "kwonlyargs_annotations",
  543. "type_comment_args",
  544. "type_comment_kwonlyargs",
  545. "type_comment_posonlyargs",
  546. )
  547. _other_fields = ("vararg", "kwarg")
  548. args: list[AssignName] | None
  549. """The names of the required arguments.
  550. Can be None if the associated function does not have a retrievable
  551. signature and the arguments are therefore unknown.
  552. This can happen with (builtin) functions implemented in C that have
  553. incomplete signature information.
  554. """
  555. defaults: list[NodeNG] | None
  556. """The default values for arguments that can be passed positionally."""
  557. kwonlyargs: list[AssignName]
  558. """The keyword arguments that cannot be passed positionally."""
  559. posonlyargs: list[AssignName]
  560. """The arguments that can only be passed positionally."""
  561. kw_defaults: list[NodeNG | None] | None
  562. """The default values for keyword arguments that cannot be passed positionally."""
  563. annotations: list[NodeNG | None]
  564. """The type annotations of arguments that can be passed positionally."""
  565. posonlyargs_annotations: list[NodeNG | None]
  566. """The type annotations of arguments that can only be passed positionally."""
  567. kwonlyargs_annotations: list[NodeNG | None]
  568. """The type annotations of arguments that cannot be passed positionally."""
  569. type_comment_args: list[NodeNG | None]
  570. """The type annotation, passed by a type comment, of each argument.
  571. If an argument does not have a type comment,
  572. the value for that argument will be None.
  573. """
  574. type_comment_kwonlyargs: list[NodeNG | None]
  575. """The type annotation, passed by a type comment, of each keyword only argument.
  576. If an argument does not have a type comment,
  577. the value for that argument will be None.
  578. """
  579. type_comment_posonlyargs: list[NodeNG | None]
  580. """The type annotation, passed by a type comment, of each positional argument.
  581. If an argument does not have a type comment,
  582. the value for that argument will be None.
  583. """
  584. varargannotation: NodeNG | None
  585. """The type annotation for the variable length arguments."""
  586. kwargannotation: NodeNG | None
  587. """The type annotation for the variable length keyword arguments."""
  588. vararg_node: AssignName | None
  589. """The node for variable length arguments"""
  590. kwarg_node: AssignName | None
  591. """The node for variable keyword arguments"""
  592. def __init__(
  593. self,
  594. vararg: str | None,
  595. kwarg: str | None,
  596. parent: NodeNG,
  597. vararg_node: AssignName | None = None,
  598. kwarg_node: AssignName | None = None,
  599. ) -> None:
  600. """Almost all attributes can be None for living objects where introspection failed."""
  601. super().__init__(
  602. parent=parent,
  603. lineno=None,
  604. col_offset=None,
  605. end_lineno=None,
  606. end_col_offset=None,
  607. )
  608. self.vararg = vararg
  609. """The name of the variable length arguments."""
  610. self.kwarg = kwarg
  611. """The name of the variable length keyword arguments."""
  612. self.vararg_node = vararg_node
  613. self.kwarg_node = kwarg_node
  614. # pylint: disable=too-many-arguments, too-many-positional-arguments
  615. def postinit(
  616. self,
  617. args: list[AssignName] | None,
  618. defaults: list[NodeNG] | None,
  619. kwonlyargs: list[AssignName],
  620. kw_defaults: list[NodeNG | None] | None,
  621. annotations: list[NodeNG | None],
  622. posonlyargs: list[AssignName],
  623. kwonlyargs_annotations: list[NodeNG | None],
  624. posonlyargs_annotations: list[NodeNG | None],
  625. varargannotation: NodeNG | None = None,
  626. kwargannotation: NodeNG | None = None,
  627. type_comment_args: list[NodeNG | None] | None = None,
  628. type_comment_kwonlyargs: list[NodeNG | None] | None = None,
  629. type_comment_posonlyargs: list[NodeNG | None] | None = None,
  630. ) -> None:
  631. self.args = args
  632. self.defaults = defaults
  633. self.kwonlyargs = kwonlyargs
  634. self.posonlyargs = posonlyargs
  635. self.kw_defaults = kw_defaults
  636. self.annotations = annotations
  637. self.kwonlyargs_annotations = kwonlyargs_annotations
  638. self.posonlyargs_annotations = posonlyargs_annotations
  639. # Parameters that got added later and need a default
  640. self.varargannotation = varargannotation
  641. self.kwargannotation = kwargannotation
  642. if type_comment_args is None:
  643. type_comment_args = []
  644. self.type_comment_args = type_comment_args
  645. if type_comment_kwonlyargs is None:
  646. type_comment_kwonlyargs = []
  647. self.type_comment_kwonlyargs = type_comment_kwonlyargs
  648. if type_comment_posonlyargs is None:
  649. type_comment_posonlyargs = []
  650. self.type_comment_posonlyargs = type_comment_posonlyargs
  651. assigned_stmts = protocols.arguments_assigned_stmts
  652. """Returns the assigned statement (non inferred) according to the assignment type.
  653. See astroid/protocols.py for actual implementation.
  654. """
  655. def _infer_name(self, frame, name):
  656. if self.parent is frame:
  657. return name
  658. return None
  659. @cached_property
  660. def fromlineno(self) -> int:
  661. """The first line that this node appears on in the source code.
  662. Can also return 0 if the line can not be determined.
  663. """
  664. lineno = super().fromlineno
  665. return max(lineno, self.parent.fromlineno or 0)
  666. @cached_property
  667. def arguments(self):
  668. """Get all the arguments for this node. This includes:
  669. * Positional only arguments
  670. * Positional arguments
  671. * Keyword arguments
  672. * Variable arguments (.e.g *args)
  673. * Variable keyword arguments (e.g **kwargs)
  674. """
  675. retval = list(itertools.chain((self.posonlyargs or ()), (self.args or ())))
  676. if self.vararg_node:
  677. retval.append(self.vararg_node)
  678. retval += self.kwonlyargs or ()
  679. if self.kwarg_node:
  680. retval.append(self.kwarg_node)
  681. return retval
  682. def format_args(self, *, skippable_names: set[str] | None = None) -> str:
  683. """Get the arguments formatted as string.
  684. :returns: The formatted arguments.
  685. :rtype: str
  686. """
  687. result = []
  688. positional_only_defaults = []
  689. positional_or_keyword_defaults = self.defaults
  690. if self.defaults:
  691. args = self.args or []
  692. positional_or_keyword_defaults = self.defaults[-len(args) :]
  693. positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
  694. if self.posonlyargs:
  695. result.append(
  696. _format_args(
  697. self.posonlyargs,
  698. positional_only_defaults,
  699. self.posonlyargs_annotations,
  700. skippable_names=skippable_names,
  701. )
  702. )
  703. result.append("/")
  704. if self.args:
  705. result.append(
  706. _format_args(
  707. self.args,
  708. positional_or_keyword_defaults,
  709. getattr(self, "annotations", None),
  710. skippable_names=skippable_names,
  711. )
  712. )
  713. if self.vararg:
  714. result.append(f"*{self.vararg}")
  715. if self.kwonlyargs:
  716. if not self.vararg:
  717. result.append("*")
  718. result.append(
  719. _format_args(
  720. self.kwonlyargs,
  721. self.kw_defaults,
  722. self.kwonlyargs_annotations,
  723. skippable_names=skippable_names,
  724. )
  725. )
  726. if self.kwarg:
  727. result.append(f"**{self.kwarg}")
  728. return ", ".join(result)
  729. def _get_arguments_data(
  730. self,
  731. ) -> tuple[
  732. dict[str, tuple[str | None, str | None]],
  733. dict[str, tuple[str | None, str | None]],
  734. ]:
  735. """Get the arguments as dictionary with information about typing and defaults.
  736. The return tuple contains a dictionary for positional and keyword arguments with their typing
  737. and their default value, if any.
  738. The method follows a similar order as format_args but instead of formatting into a string it
  739. returns the data that is used to do so.
  740. """
  741. pos_only: dict[str, tuple[str | None, str | None]] = {}
  742. kw_only: dict[str, tuple[str | None, str | None]] = {}
  743. # Setup and match defaults with arguments
  744. positional_only_defaults = []
  745. positional_or_keyword_defaults = self.defaults
  746. if self.defaults:
  747. args = self.args or []
  748. positional_or_keyword_defaults = self.defaults[-len(args) :]
  749. positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
  750. for index, posonly in enumerate(self.posonlyargs):
  751. annotation, default = self.posonlyargs_annotations[index], None
  752. if annotation is not None:
  753. annotation = annotation.as_string()
  754. if positional_only_defaults:
  755. default = positional_only_defaults[index].as_string()
  756. pos_only[posonly.name] = (annotation, default)
  757. for index, arg in enumerate(self.args):
  758. annotation, default = self.annotations[index], None
  759. if annotation is not None:
  760. annotation = annotation.as_string()
  761. if positional_or_keyword_defaults:
  762. defaults_offset = len(self.args) - len(positional_or_keyword_defaults)
  763. default_index = index - defaults_offset
  764. if (
  765. default_index > -1
  766. and positional_or_keyword_defaults[default_index] is not None
  767. ):
  768. default = positional_or_keyword_defaults[default_index].as_string()
  769. pos_only[arg.name] = (annotation, default)
  770. if self.vararg:
  771. annotation = self.varargannotation
  772. if annotation is not None:
  773. annotation = annotation.as_string()
  774. pos_only[self.vararg] = (annotation, None)
  775. for index, kwarg in enumerate(self.kwonlyargs):
  776. annotation = self.kwonlyargs_annotations[index]
  777. if annotation is not None:
  778. annotation = annotation.as_string()
  779. default = self.kw_defaults[index]
  780. if default is not None:
  781. default = default.as_string()
  782. kw_only[kwarg.name] = (annotation, default)
  783. if self.kwarg:
  784. annotation = self.kwargannotation
  785. if annotation is not None:
  786. annotation = annotation.as_string()
  787. kw_only[self.kwarg] = (annotation, None)
  788. return pos_only, kw_only
  789. def default_value(self, argname):
  790. """Get the default value for an argument.
  791. :param argname: The name of the argument to get the default value for.
  792. :type argname: str
  793. :raises NoDefault: If there is no default value defined for the
  794. given argument.
  795. """
  796. args = [
  797. arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg]
  798. ]
  799. index = _find_arg(argname, self.kwonlyargs)[0]
  800. if (index is not None) and (len(self.kw_defaults) > index):
  801. if self.kw_defaults[index] is not None:
  802. return self.kw_defaults[index]
  803. raise NoDefault(func=self.parent, name=argname)
  804. index = _find_arg(argname, args)[0]
  805. if index is not None:
  806. idx = index - (len(args) - len(self.defaults) - len(self.kw_defaults))
  807. if idx >= 0:
  808. return self.defaults[idx]
  809. raise NoDefault(func=self.parent, name=argname)
  810. def is_argument(self, name) -> bool:
  811. """Check if the given name is defined in the arguments.
  812. :param name: The name to check for.
  813. :type name: str
  814. :returns: Whether the given name is defined in the arguments,
  815. """
  816. if name == self.vararg:
  817. return True
  818. if name == self.kwarg:
  819. return True
  820. return self.find_argname(name)[1] is not None
  821. def find_argname(self, argname, rec=DEPRECATED_ARGUMENT_DEFAULT):
  822. """Get the index and :class:`AssignName` node for given name.
  823. :param argname: The name of the argument to search for.
  824. :type argname: str
  825. :returns: The index and node for the argument.
  826. :rtype: tuple(str or None, AssignName or None)
  827. """
  828. if rec != DEPRECATED_ARGUMENT_DEFAULT: # pragma: no cover
  829. warnings.warn(
  830. "The rec argument will be removed in astroid 3.1.",
  831. DeprecationWarning,
  832. stacklevel=2,
  833. )
  834. if self.arguments:
  835. index, argument = _find_arg(argname, self.arguments)
  836. if argument:
  837. return index, argument
  838. return None, None
  839. def get_children(self):
  840. yield from self.posonlyargs or ()
  841. for elt in self.posonlyargs_annotations:
  842. if elt is not None:
  843. yield elt
  844. yield from self.args or ()
  845. if self.defaults is not None:
  846. yield from self.defaults
  847. yield from self.kwonlyargs
  848. for elt in self.kw_defaults or ():
  849. if elt is not None:
  850. yield elt
  851. for elt in self.annotations:
  852. if elt is not None:
  853. yield elt
  854. if self.varargannotation is not None:
  855. yield self.varargannotation
  856. if self.kwargannotation is not None:
  857. yield self.kwargannotation
  858. for elt in self.kwonlyargs_annotations:
  859. if elt is not None:
  860. yield elt
  861. @decorators.raise_if_nothing_inferred
  862. def _infer(
  863. self, context: InferenceContext | None = None, **kwargs: Any
  864. ) -> Generator[InferenceResult]:
  865. # pylint: disable-next=import-outside-toplevel
  866. from astroid.protocols import _arguments_infer_argname
  867. if context is None or context.lookupname is None:
  868. raise InferenceError(node=self, context=context)
  869. return _arguments_infer_argname(self, context.lookupname, context)
  870. def _find_arg(argname, args):
  871. for i, arg in enumerate(args):
  872. if arg.name == argname:
  873. return i, arg
  874. return None, None
  875. def _format_args(
  876. args, defaults=None, annotations=None, skippable_names: set[str] | None = None
  877. ) -> str:
  878. if skippable_names is None:
  879. skippable_names = set()
  880. values = []
  881. if args is None:
  882. return ""
  883. if annotations is None:
  884. annotations = []
  885. if defaults is not None:
  886. default_offset = len(args) - len(defaults)
  887. else:
  888. default_offset = None
  889. packed = itertools.zip_longest(args, annotations)
  890. for i, (arg, annotation) in enumerate(packed):
  891. if arg.name in skippable_names:
  892. continue
  893. if isinstance(arg, Tuple):
  894. values.append(f"({_format_args(arg.elts)})")
  895. else:
  896. argname = arg.name
  897. default_sep = "="
  898. if annotation is not None:
  899. argname += ": " + annotation.as_string()
  900. default_sep = " = "
  901. values.append(argname)
  902. if default_offset is not None and i >= default_offset:
  903. if defaults[i - default_offset] is not None:
  904. values[-1] += default_sep + defaults[i - default_offset].as_string()
  905. return ", ".join(values)
  906. def _infer_attribute(
  907. node: nodes.AssignAttr | nodes.Attribute,
  908. context: InferenceContext | None = None,
  909. **kwargs: Any,
  910. ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
  911. """Infer an AssignAttr/Attribute node by using getattr on the associated object."""
  912. # pylint: disable=import-outside-toplevel
  913. from astroid.constraint import get_constraints
  914. from astroid.nodes import ClassDef
  915. for owner in node.expr.infer(context):
  916. if isinstance(owner, util.UninferableBase):
  917. yield owner
  918. continue
  919. context = copy_context(context)
  920. old_boundnode = context.boundnode
  921. try:
  922. context.boundnode = owner
  923. if isinstance(owner, (ClassDef, Instance)):
  924. frame = owner if isinstance(owner, ClassDef) else owner._proxied
  925. context.constraints[node.attrname] = get_constraints(node, frame=frame)
  926. if node.attrname == "argv" and owner.name == "sys":
  927. # sys.argv will never be inferable during static analysis
  928. # It's value would be the args passed to the linter itself
  929. yield util.Uninferable
  930. else:
  931. yield from owner.igetattr(node.attrname, context)
  932. except (
  933. AttributeInferenceError,
  934. InferenceError,
  935. AttributeError,
  936. ):
  937. pass
  938. finally:
  939. context.boundnode = old_boundnode
  940. return InferenceErrorInfo(node=node, context=context)
  941. class AssignAttr(_base_nodes.LookupMixIn, _base_nodes.ParentAssignNode):
  942. """Variation of :class:`ast.Assign` representing assignment to an attribute.
  943. >>> import astroid
  944. >>> node = astroid.extract_node('self.attribute = range(10)')
  945. >>> node
  946. <Assign l.1 at 0x7effe1d521d0>
  947. >>> list(node.get_children())
  948. [<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>]
  949. >>> list(node.get_children())[0].as_string()
  950. 'self.attribute'
  951. """
  952. expr: NodeNG
  953. _astroid_fields = ("expr",)
  954. _other_fields = ("attrname",)
  955. def __init__(
  956. self,
  957. attrname: str,
  958. lineno: int,
  959. col_offset: int,
  960. parent: NodeNG,
  961. *,
  962. end_lineno: int | None,
  963. end_col_offset: int | None,
  964. ) -> None:
  965. self.attrname = attrname
  966. """The name of the attribute being assigned to."""
  967. super().__init__(
  968. lineno=lineno,
  969. col_offset=col_offset,
  970. end_lineno=end_lineno,
  971. end_col_offset=end_col_offset,
  972. parent=parent,
  973. )
  974. def postinit(self, expr: NodeNG) -> None:
  975. self.expr = expr
  976. assigned_stmts = protocols.assend_assigned_stmts
  977. """Returns the assigned statement (non inferred) according to the assignment type.
  978. See astroid/protocols.py for actual implementation.
  979. """
  980. def get_children(self):
  981. yield self.expr
  982. @decorators.raise_if_nothing_inferred
  983. @decorators.path_wrapper
  984. def _infer(
  985. self, context: InferenceContext | None = None, **kwargs: Any
  986. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  987. """Infer an AssignAttr: need to inspect the RHS part of the
  988. assign node.
  989. """
  990. if isinstance(self.parent, AugAssign):
  991. return self.parent.infer(context)
  992. stmts = list(self.assigned_stmts(context=context))
  993. return _infer_stmts(stmts, context)
  994. @decorators.raise_if_nothing_inferred
  995. @decorators.path_wrapper
  996. def infer_lhs(
  997. self, context: InferenceContext | None = None, **kwargs: Any
  998. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  999. return _infer_attribute(self, context, **kwargs)
  1000. class Assert(_base_nodes.Statement):
  1001. """Class representing an :class:`ast.Assert` node.
  1002. An :class:`Assert` node represents an assert statement.
  1003. >>> import astroid
  1004. >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"')
  1005. >>> node
  1006. <Assert l.1 at 0x7effe1d527b8>
  1007. """
  1008. _astroid_fields = ("test", "fail")
  1009. test: NodeNG
  1010. """The test that passes or fails the assertion."""
  1011. fail: NodeNG | None
  1012. """The message shown when the assertion fails."""
  1013. def postinit(self, test: NodeNG, fail: NodeNG | None) -> None:
  1014. self.fail = fail
  1015. self.test = test
  1016. def get_children(self):
  1017. yield self.test
  1018. if self.fail is not None:
  1019. yield self.fail
  1020. class Assign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
  1021. """Class representing an :class:`ast.Assign` node.
  1022. An :class:`Assign` is a statement where something is explicitly
  1023. asssigned to.
  1024. >>> import astroid
  1025. >>> node = astroid.extract_node('variable = range(10)')
  1026. >>> node
  1027. <Assign l.1 at 0x7effe1db8550>
  1028. """
  1029. targets: list[NodeNG]
  1030. """What is being assigned to."""
  1031. value: NodeNG
  1032. """The value being assigned to the variables."""
  1033. type_annotation: NodeNG | None
  1034. """If present, this will contain the type annotation passed by a type comment"""
  1035. _astroid_fields = ("targets", "value")
  1036. _other_other_fields = ("type_annotation",)
  1037. def postinit(
  1038. self,
  1039. targets: list[NodeNG],
  1040. value: NodeNG,
  1041. type_annotation: NodeNG | None,
  1042. ) -> None:
  1043. self.targets = targets
  1044. self.value = value
  1045. self.type_annotation = type_annotation
  1046. assigned_stmts = protocols.assign_assigned_stmts
  1047. """Returns the assigned statement (non inferred) according to the assignment type.
  1048. See astroid/protocols.py for actual implementation.
  1049. """
  1050. def get_children(self):
  1051. yield from self.targets
  1052. yield self.value
  1053. @cached_property
  1054. def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
  1055. return [self, *self.value._assign_nodes_in_scope]
  1056. def _get_yield_nodes_skip_functions(self):
  1057. yield from self.value._get_yield_nodes_skip_functions()
  1058. def _get_yield_nodes_skip_lambdas(self):
  1059. yield from self.value._get_yield_nodes_skip_lambdas()
  1060. class AnnAssign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
  1061. """Class representing an :class:`ast.AnnAssign` node.
  1062. An :class:`AnnAssign` is an assignment with a type annotation.
  1063. >>> import astroid
  1064. >>> node = astroid.extract_node('variable: List[int] = range(10)')
  1065. >>> node
  1066. <AnnAssign l.1 at 0x7effe1d4c630>
  1067. """
  1068. _astroid_fields = ("target", "annotation", "value")
  1069. _other_fields = ("simple",)
  1070. target: Name | Attribute | Subscript
  1071. """What is being assigned to."""
  1072. annotation: NodeNG
  1073. """The type annotation of what is being assigned to."""
  1074. value: NodeNG | None
  1075. """The value being assigned to the variables."""
  1076. simple: int
  1077. """Whether :attr:`target` is a pure name or a complex statement."""
  1078. def postinit(
  1079. self,
  1080. target: Name | Attribute | Subscript,
  1081. annotation: NodeNG,
  1082. simple: int,
  1083. value: NodeNG | None,
  1084. ) -> None:
  1085. self.target = target
  1086. self.annotation = annotation
  1087. self.value = value
  1088. self.simple = simple
  1089. assigned_stmts = protocols.assign_annassigned_stmts
  1090. """Returns the assigned statement (non inferred) according to the assignment type.
  1091. See astroid/protocols.py for actual implementation.
  1092. """
  1093. def get_children(self):
  1094. yield self.target
  1095. yield self.annotation
  1096. if self.value is not None:
  1097. yield self.value
  1098. class AugAssign(
  1099. _base_nodes.AssignTypeNode, _base_nodes.OperatorNode, _base_nodes.Statement
  1100. ):
  1101. """Class representing an :class:`ast.AugAssign` node.
  1102. An :class:`AugAssign` is an assignment paired with an operator.
  1103. >>> import astroid
  1104. >>> node = astroid.extract_node('variable += 1')
  1105. >>> node
  1106. <AugAssign l.1 at 0x7effe1db4d68>
  1107. """
  1108. _astroid_fields = ("target", "value")
  1109. _other_fields = ("op",)
  1110. target: Name | Attribute | Subscript
  1111. """What is being assigned to."""
  1112. value: NodeNG
  1113. """The value being assigned to the variable."""
  1114. def __init__(
  1115. self,
  1116. op: str,
  1117. lineno: int,
  1118. col_offset: int,
  1119. parent: NodeNG,
  1120. *,
  1121. end_lineno: int | None,
  1122. end_col_offset: int | None,
  1123. ) -> None:
  1124. self.op = op
  1125. """The operator that is being combined with the assignment.
  1126. This includes the equals sign.
  1127. """
  1128. super().__init__(
  1129. lineno=lineno,
  1130. col_offset=col_offset,
  1131. end_lineno=end_lineno,
  1132. end_col_offset=end_col_offset,
  1133. parent=parent,
  1134. )
  1135. def postinit(self, target: Name | Attribute | Subscript, value: NodeNG) -> None:
  1136. self.target = target
  1137. self.value = value
  1138. assigned_stmts = protocols.assign_assigned_stmts
  1139. """Returns the assigned statement (non inferred) according to the assignment type.
  1140. See astroid/protocols.py for actual implementation.
  1141. """
  1142. def type_errors(
  1143. self, context: InferenceContext | None = None
  1144. ) -> list[util.BadBinaryOperationMessage]:
  1145. """Get a list of type errors which can occur during inference.
  1146. Each TypeError is represented by a :class:`BadBinaryOperationMessage` ,
  1147. which holds the original exception.
  1148. If any inferred result is uninferable, an empty list is returned.
  1149. """
  1150. bad = []
  1151. try:
  1152. for result in self._infer_augassign(context=context):
  1153. if result is util.Uninferable:
  1154. raise InferenceError
  1155. if isinstance(result, util.BadBinaryOperationMessage):
  1156. bad.append(result)
  1157. except InferenceError:
  1158. return []
  1159. return bad
  1160. def get_children(self):
  1161. yield self.target
  1162. yield self.value
  1163. def _get_yield_nodes_skip_functions(self):
  1164. """An AugAssign node can contain a Yield node in the value"""
  1165. yield from self.value._get_yield_nodes_skip_functions()
  1166. yield from super()._get_yield_nodes_skip_functions()
  1167. def _get_yield_nodes_skip_lambdas(self):
  1168. """An AugAssign node can contain a Yield node in the value"""
  1169. yield from self.value._get_yield_nodes_skip_lambdas()
  1170. yield from super()._get_yield_nodes_skip_lambdas()
  1171. def _infer_augassign(
  1172. self, context: InferenceContext | None = None
  1173. ) -> Generator[InferenceResult | util.BadBinaryOperationMessage]:
  1174. """Inference logic for augmented binary operations."""
  1175. context = context or InferenceContext()
  1176. rhs_context = context.clone()
  1177. lhs_iter = self.target.infer_lhs(context=context)
  1178. rhs_iter = self.value.infer(context=rhs_context)
  1179. for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
  1180. if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
  1181. # Don't know how to process this.
  1182. yield util.Uninferable
  1183. return
  1184. try:
  1185. yield from self._infer_binary_operation(
  1186. left=lhs,
  1187. right=rhs,
  1188. binary_opnode=self,
  1189. context=context,
  1190. flow_factory=self._get_aug_flow,
  1191. )
  1192. except _NonDeducibleTypeHierarchy:
  1193. yield util.Uninferable
  1194. @decorators.raise_if_nothing_inferred
  1195. @decorators.path_wrapper
  1196. def _infer(
  1197. self, context: InferenceContext | None = None, **kwargs: Any
  1198. ) -> Generator[InferenceResult]:
  1199. return self._filter_operation_errors(
  1200. self._infer_augassign, context, util.BadBinaryOperationMessage
  1201. )
  1202. class BinOp(_base_nodes.OperatorNode):
  1203. """Class representing an :class:`ast.BinOp` node.
  1204. A :class:`BinOp` node is an application of a binary operator.
  1205. >>> import astroid
  1206. >>> node = astroid.extract_node('a + b')
  1207. >>> node
  1208. <BinOp l.1 at 0x7f23b2e8cfd0>
  1209. """
  1210. _astroid_fields = ("left", "right")
  1211. _other_fields = ("op",)
  1212. left: NodeNG
  1213. """What is being applied to the operator on the left side."""
  1214. right: NodeNG
  1215. """What is being applied to the operator on the right side."""
  1216. def __init__(
  1217. self,
  1218. op: str,
  1219. lineno: int,
  1220. col_offset: int,
  1221. parent: NodeNG,
  1222. *,
  1223. end_lineno: int | None,
  1224. end_col_offset: int | None,
  1225. ) -> None:
  1226. self.op = op
  1227. """The operator."""
  1228. super().__init__(
  1229. lineno=lineno,
  1230. col_offset=col_offset,
  1231. end_lineno=end_lineno,
  1232. end_col_offset=end_col_offset,
  1233. parent=parent,
  1234. )
  1235. def postinit(self, left: NodeNG, right: NodeNG) -> None:
  1236. self.left = left
  1237. self.right = right
  1238. def type_errors(
  1239. self, context: InferenceContext | None = None
  1240. ) -> list[util.BadBinaryOperationMessage]:
  1241. """Get a list of type errors which can occur during inference.
  1242. Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
  1243. which holds the original exception.
  1244. If any inferred result is uninferable, an empty list is returned.
  1245. """
  1246. bad = []
  1247. try:
  1248. for result in self._infer_binop(context=context):
  1249. if result is util.Uninferable:
  1250. raise InferenceError
  1251. if isinstance(result, util.BadBinaryOperationMessage):
  1252. bad.append(result)
  1253. except InferenceError:
  1254. return []
  1255. return bad
  1256. def get_children(self):
  1257. yield self.left
  1258. yield self.right
  1259. def op_precedence(self) -> int:
  1260. return OP_PRECEDENCE[self.op]
  1261. def op_left_associative(self) -> bool:
  1262. # 2**3**4 == 2**(3**4)
  1263. return self.op != "**"
  1264. def _infer_binop(
  1265. self, context: InferenceContext | None = None, **kwargs: Any
  1266. ) -> Generator[InferenceResult]:
  1267. """Binary operation inference logic."""
  1268. left = self.left
  1269. right = self.right
  1270. # we use two separate contexts for evaluating lhs and rhs because
  1271. # 1. evaluating lhs may leave some undesired entries in context.path
  1272. # which may not let us infer right value of rhs
  1273. context = context or InferenceContext()
  1274. lhs_context = copy_context(context)
  1275. rhs_context = copy_context(context)
  1276. lhs_iter = left.infer(context=lhs_context)
  1277. rhs_iter = right.infer(context=rhs_context)
  1278. for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
  1279. if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
  1280. # Don't know how to process this.
  1281. yield util.Uninferable
  1282. return
  1283. try:
  1284. yield from self._infer_binary_operation(
  1285. lhs, rhs, self, context, self._get_binop_flow
  1286. )
  1287. except _NonDeducibleTypeHierarchy:
  1288. yield util.Uninferable
  1289. @decorators.yes_if_nothing_inferred
  1290. @decorators.path_wrapper
  1291. def _infer(
  1292. self, context: InferenceContext | None = None, **kwargs: Any
  1293. ) -> Generator[InferenceResult]:
  1294. return self._filter_operation_errors(
  1295. self._infer_binop, context, util.BadBinaryOperationMessage
  1296. )
  1297. class BoolOp(NodeNG):
  1298. """Class representing an :class:`ast.BoolOp` node.
  1299. A :class:`BoolOp` is an application of a boolean operator.
  1300. >>> import astroid
  1301. >>> node = astroid.extract_node('a and b')
  1302. >>> node
  1303. <BinOp l.1 at 0x7f23b2e71c50>
  1304. """
  1305. _astroid_fields = ("values",)
  1306. _other_fields = ("op",)
  1307. def __init__(
  1308. self,
  1309. op: str,
  1310. lineno: int | None = None,
  1311. col_offset: int | None = None,
  1312. parent: NodeNG | None = None,
  1313. *,
  1314. end_lineno: int | None = None,
  1315. end_col_offset: int | None = None,
  1316. ) -> None:
  1317. """
  1318. :param op: The operator.
  1319. :param lineno: The line that this node appears on in the source code.
  1320. :param col_offset: The column that this node appears on in the
  1321. source code.
  1322. :param parent: The parent node in the syntax tree.
  1323. :param end_lineno: The last line this node appears on in the source code.
  1324. :param end_col_offset: The end column this node appears on in the
  1325. source code. Note: This is after the last symbol.
  1326. """
  1327. self.op: str = op
  1328. """The operator."""
  1329. self.values: list[NodeNG] = []
  1330. """The values being applied to the operator."""
  1331. super().__init__(
  1332. lineno=lineno,
  1333. col_offset=col_offset,
  1334. end_lineno=end_lineno,
  1335. end_col_offset=end_col_offset,
  1336. parent=parent,
  1337. )
  1338. def postinit(self, values: list[NodeNG] | None = None) -> None:
  1339. """Do some setup after initialisation.
  1340. :param values: The values being applied to the operator.
  1341. """
  1342. if values is not None:
  1343. self.values = values
  1344. def get_children(self):
  1345. yield from self.values
  1346. def op_precedence(self) -> int:
  1347. return OP_PRECEDENCE[self.op]
  1348. @decorators.raise_if_nothing_inferred
  1349. @decorators.path_wrapper
  1350. def _infer(
  1351. self, context: InferenceContext | None = None, **kwargs: Any
  1352. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  1353. """Infer a boolean operation (and / or / not).
  1354. The function will calculate the boolean operation
  1355. for all pairs generated through inference for each component
  1356. node.
  1357. """
  1358. values = self.values
  1359. if self.op == "or":
  1360. predicate = operator.truth
  1361. else:
  1362. predicate = operator.not_
  1363. try:
  1364. inferred_values = [value.infer(context=context) for value in values]
  1365. except InferenceError:
  1366. yield util.Uninferable
  1367. return None
  1368. for pair in itertools.product(*inferred_values):
  1369. if any(isinstance(item, util.UninferableBase) for item in pair):
  1370. # Can't infer the final result, just yield Uninferable.
  1371. yield util.Uninferable
  1372. continue
  1373. bool_values = [item.bool_value() for item in pair]
  1374. if any(isinstance(item, util.UninferableBase) for item in bool_values):
  1375. # Can't infer the final result, just yield Uninferable.
  1376. yield util.Uninferable
  1377. continue
  1378. # Since the boolean operations are short circuited operations,
  1379. # this code yields the first value for which the predicate is True
  1380. # and if no value respected the predicate, then the last value will
  1381. # be returned (or Uninferable if there was no last value).
  1382. # This is conforming to the semantics of `and` and `or`:
  1383. # 1 and 0 -> 1
  1384. # 0 and 1 -> 0
  1385. # 1 or 0 -> 1
  1386. # 0 or 1 -> 1
  1387. value = util.Uninferable
  1388. for value, bool_value in zip(pair, bool_values):
  1389. if predicate(bool_value):
  1390. yield value
  1391. break
  1392. else:
  1393. yield value
  1394. return InferenceErrorInfo(node=self, context=context)
  1395. class Break(_base_nodes.NoChildrenNode, _base_nodes.Statement):
  1396. """Class representing an :class:`ast.Break` node.
  1397. >>> import astroid
  1398. >>> node = astroid.extract_node('break')
  1399. >>> node
  1400. <Break l.1 at 0x7f23b2e9e5c0>
  1401. """
  1402. class Call(NodeNG):
  1403. """Class representing an :class:`ast.Call` node.
  1404. A :class:`Call` node is a call to a function, method, etc.
  1405. >>> import astroid
  1406. >>> node = astroid.extract_node('function()')
  1407. >>> node
  1408. <Call l.1 at 0x7f23b2e71eb8>
  1409. """
  1410. _astroid_fields = ("func", "args", "keywords")
  1411. func: NodeNG
  1412. """What is being called."""
  1413. args: list[NodeNG]
  1414. """The positional arguments being given to the call."""
  1415. keywords: list[Keyword]
  1416. """The keyword arguments being given to the call."""
  1417. def postinit(
  1418. self, func: NodeNG, args: list[NodeNG], keywords: list[Keyword]
  1419. ) -> None:
  1420. self.func = func
  1421. self.args = args
  1422. self.keywords = keywords
  1423. @property
  1424. def starargs(self) -> list[Starred]:
  1425. """The positional arguments that unpack something."""
  1426. return [arg for arg in self.args if isinstance(arg, Starred)]
  1427. @property
  1428. def kwargs(self) -> list[Keyword]:
  1429. """The keyword arguments that unpack something."""
  1430. return [keyword for keyword in self.keywords if keyword.arg is None]
  1431. def get_children(self):
  1432. yield self.func
  1433. yield from self.args
  1434. yield from self.keywords
  1435. @decorators.raise_if_nothing_inferred
  1436. @decorators.path_wrapper
  1437. def _infer(
  1438. self, context: InferenceContext | None = None, **kwargs: Any
  1439. ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
  1440. """Infer a Call node by trying to guess what the function returns."""
  1441. callcontext = copy_context(context)
  1442. callcontext.boundnode = None
  1443. if context is not None:
  1444. callcontext.extra_context = self._populate_context_lookup(context.clone())
  1445. for callee in self.func.infer(context):
  1446. if isinstance(callee, util.UninferableBase):
  1447. yield callee
  1448. continue
  1449. try:
  1450. if hasattr(callee, "infer_call_result"):
  1451. callcontext.callcontext = CallContext(
  1452. args=self.args, keywords=self.keywords, callee=callee
  1453. )
  1454. yield from callee.infer_call_result(
  1455. caller=self, context=callcontext
  1456. )
  1457. except InferenceError:
  1458. continue
  1459. return InferenceErrorInfo(node=self, context=context)
  1460. def _populate_context_lookup(self, context: InferenceContext | None):
  1461. """Allows context to be saved for later for inference inside a function."""
  1462. context_lookup: dict[InferenceResult, InferenceContext] = {}
  1463. if context is None:
  1464. return context_lookup
  1465. for arg in self.args:
  1466. if isinstance(arg, Starred):
  1467. context_lookup[arg.value] = context
  1468. else:
  1469. context_lookup[arg] = context
  1470. keywords = self.keywords if self.keywords is not None else []
  1471. for keyword in keywords:
  1472. context_lookup[keyword.value] = context
  1473. return context_lookup
  1474. COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = {
  1475. "==": operator.eq,
  1476. "!=": operator.ne,
  1477. "<": operator.lt,
  1478. "<=": operator.le,
  1479. ">": operator.gt,
  1480. ">=": operator.ge,
  1481. "in": lambda a, b: a in b,
  1482. "not in": lambda a, b: a not in b,
  1483. }
  1484. UNINFERABLE_OPS = {
  1485. "is",
  1486. "is not",
  1487. }
  1488. class Compare(NodeNG):
  1489. """Class representing an :class:`ast.Compare` node.
  1490. A :class:`Compare` node indicates a comparison.
  1491. >>> import astroid
  1492. >>> node = astroid.extract_node('a <= b <= c')
  1493. >>> node
  1494. <Compare l.1 at 0x7f23b2e9e6d8>
  1495. >>> node.ops
  1496. [('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)]
  1497. """
  1498. _astroid_fields = ("left", "ops")
  1499. left: NodeNG
  1500. """The value at the left being applied to a comparison operator."""
  1501. ops: list[tuple[str, NodeNG]]
  1502. """The remainder of the operators and their relevant right hand value."""
  1503. def postinit(self, left: NodeNG, ops: list[tuple[str, NodeNG]]) -> None:
  1504. self.left = left
  1505. self.ops = ops
  1506. def get_children(self):
  1507. """Get the child nodes below this node.
  1508. Overridden to handle the tuple fields and skip returning the operator
  1509. strings.
  1510. :returns: The children.
  1511. :rtype: iterable(NodeNG)
  1512. """
  1513. yield self.left
  1514. for _, comparator in self.ops:
  1515. yield comparator # we don't want the 'op'
  1516. def last_child(self):
  1517. """An optimized version of list(get_children())[-1]
  1518. :returns: The last child.
  1519. :rtype: NodeNG
  1520. """
  1521. # XXX maybe if self.ops:
  1522. return self.ops[-1][1]
  1523. # return self.left
  1524. # TODO: move to util?
  1525. @staticmethod
  1526. def _to_literal(node: SuccessfulInferenceResult) -> Any:
  1527. # Can raise SyntaxError, ValueError, or TypeError from ast.literal_eval
  1528. # Can raise AttributeError from node.as_string() as not all nodes have a visitor
  1529. # Is this the stupidest idea or the simplest idea?
  1530. return ast.literal_eval(node.as_string())
  1531. def _do_compare(
  1532. self,
  1533. left_iter: Iterable[InferenceResult],
  1534. op: str,
  1535. right_iter: Iterable[InferenceResult],
  1536. ) -> bool | util.UninferableBase:
  1537. """
  1538. If all possible combinations are either True or False, return that:
  1539. >>> _do_compare([1, 2], '<=', [3, 4])
  1540. True
  1541. >>> _do_compare([1, 2], '==', [3, 4])
  1542. False
  1543. If any item is uninferable, or if some combinations are True and some
  1544. are False, return Uninferable:
  1545. >>> _do_compare([1, 3], '<=', [2, 4])
  1546. util.Uninferable
  1547. """
  1548. retval: bool | None = None
  1549. if op in UNINFERABLE_OPS:
  1550. return util.Uninferable
  1551. op_func = COMPARE_OPS[op]
  1552. for left, right in itertools.product(left_iter, right_iter):
  1553. if isinstance(left, util.UninferableBase) or isinstance(
  1554. right, util.UninferableBase
  1555. ):
  1556. return util.Uninferable
  1557. try:
  1558. left, right = self._to_literal(left), self._to_literal(right)
  1559. except (SyntaxError, ValueError, AttributeError, TypeError):
  1560. return util.Uninferable
  1561. try:
  1562. expr = op_func(left, right)
  1563. except TypeError as exc:
  1564. raise AstroidTypeError from exc
  1565. if retval is None:
  1566. retval = expr
  1567. elif retval != expr:
  1568. return util.Uninferable
  1569. # (or both, but "True | False" is basically the same)
  1570. assert retval is not None
  1571. return retval # it was all the same value
  1572. def _infer(
  1573. self, context: InferenceContext | None = None, **kwargs: Any
  1574. ) -> Generator[nodes.Const | util.UninferableBase]:
  1575. """Chained comparison inference logic."""
  1576. retval: bool | util.UninferableBase = True
  1577. ops = self.ops
  1578. left_node = self.left
  1579. lhs = list(left_node.infer(context=context))
  1580. # should we break early if first element is uninferable?
  1581. for op, right_node in ops:
  1582. # eagerly evaluate rhs so that values can be re-used as lhs
  1583. rhs = list(right_node.infer(context=context))
  1584. try:
  1585. retval = self._do_compare(lhs, op, rhs)
  1586. except AstroidTypeError:
  1587. retval = util.Uninferable
  1588. break
  1589. if retval is not True:
  1590. break # short-circuit
  1591. lhs = rhs # continue
  1592. if retval is util.Uninferable:
  1593. yield retval # type: ignore[misc]
  1594. else:
  1595. yield Const(retval)
  1596. class Comprehension(NodeNG):
  1597. """Class representing an :class:`ast.comprehension` node.
  1598. A :class:`Comprehension` indicates the loop inside any type of
  1599. comprehension including generator expressions.
  1600. >>> import astroid
  1601. >>> node = astroid.extract_node('[x for x in some_values]')
  1602. >>> list(node.get_children())
  1603. [<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>]
  1604. >>> list(node.get_children())[1].as_string()
  1605. 'for x in some_values'
  1606. """
  1607. _astroid_fields = ("target", "iter", "ifs")
  1608. _other_fields = ("is_async",)
  1609. optional_assign = True
  1610. """Whether this node optionally assigns a variable."""
  1611. target: NodeNG
  1612. """What is assigned to by the comprehension."""
  1613. iter: NodeNG
  1614. """What is iterated over by the comprehension."""
  1615. ifs: list[NodeNG]
  1616. """The contents of any if statements that filter the comprehension."""
  1617. is_async: bool
  1618. """Whether this is an asynchronous comprehension or not."""
  1619. def postinit(
  1620. self,
  1621. target: NodeNG,
  1622. iter: NodeNG, # pylint: disable = redefined-builtin
  1623. ifs: list[NodeNG],
  1624. is_async: bool,
  1625. ) -> None:
  1626. self.target = target
  1627. self.iter = iter
  1628. self.ifs = ifs
  1629. self.is_async = is_async
  1630. assigned_stmts = protocols.for_assigned_stmts
  1631. """Returns the assigned statement (non inferred) according to the assignment type.
  1632. See astroid/protocols.py for actual implementation.
  1633. """
  1634. def assign_type(self):
  1635. """The type of assignment that this node performs.
  1636. :returns: The assignment type.
  1637. :rtype: NodeNG
  1638. """
  1639. return self
  1640. def _get_filtered_stmts(
  1641. self, lookup_node, node, stmts, mystmt: _base_nodes.Statement | None
  1642. ):
  1643. """method used in filter_stmts"""
  1644. if self is mystmt:
  1645. if isinstance(lookup_node, (Const, Name)):
  1646. return [lookup_node], True
  1647. elif self.statement() is mystmt:
  1648. # original node's statement is the assignment, only keeps
  1649. # current node (gen exp, list comp)
  1650. return [node], True
  1651. return stmts, False
  1652. def get_children(self):
  1653. yield self.target
  1654. yield self.iter
  1655. yield from self.ifs
  1656. class Const(_base_nodes.NoChildrenNode, Instance):
  1657. """Class representing any constant including num, str, bool, None, bytes.
  1658. >>> import astroid
  1659. >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")')
  1660. >>> node
  1661. <Tuple.tuple l.1 at 0x7f23b2e358d0>
  1662. >>> list(node.get_children())
  1663. [<Const.int l.1 at 0x7f23b2e35940>,
  1664. <Const.str l.1 at 0x7f23b2e35978>,
  1665. <Const.bool l.1 at 0x7f23b2e359b0>,
  1666. <Const.NoneType l.1 at 0x7f23b2e359e8>,
  1667. <Const.bytes l.1 at 0x7f23b2e35a20>]
  1668. """
  1669. _other_fields = ("value", "kind")
  1670. def __init__(
  1671. self,
  1672. value: Any,
  1673. lineno: int | None = None,
  1674. col_offset: int | None = None,
  1675. parent: NodeNG = SYNTHETIC_ROOT,
  1676. kind: str | None = None,
  1677. *,
  1678. end_lineno: int | None = None,
  1679. end_col_offset: int | None = None,
  1680. ) -> None:
  1681. """
  1682. :param value: The value that the constant represents.
  1683. :param lineno: The line that this node appears on in the source code.
  1684. :param col_offset: The column that this node appears on in the
  1685. source code.
  1686. :param parent: The parent node in the syntax tree.
  1687. :param kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.
  1688. :param end_lineno: The last line this node appears on in the source code.
  1689. :param end_col_offset: The end column this node appears on in the
  1690. source code. Note: This is after the last symbol.
  1691. """
  1692. if getattr(value, "__name__", None) == "__doc__":
  1693. warnings.warn( # pragma: no cover
  1694. "You have most likely called a __doc__ field of some object "
  1695. "and it didn't return a string. "
  1696. "That happens to some symbols from the standard library. "
  1697. "Check for isinstance(<X>.__doc__, str).",
  1698. RuntimeWarning,
  1699. stacklevel=0,
  1700. )
  1701. self.value = value
  1702. """The value that the constant represents."""
  1703. self.kind: str | None = kind # can be None
  1704. """"The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only."""
  1705. super().__init__(
  1706. lineno=lineno,
  1707. col_offset=col_offset,
  1708. end_lineno=end_lineno,
  1709. end_col_offset=end_col_offset,
  1710. parent=parent,
  1711. )
  1712. Instance.__init__(self, None)
  1713. infer_unary_op = protocols.const_infer_unary_op
  1714. infer_binary_op = protocols.const_infer_binary_op
  1715. def __getattr__(self, name):
  1716. # This is needed because of Proxy's __getattr__ method.
  1717. # Calling object.__new__ on this class without calling
  1718. # __init__ would result in an infinite loop otherwise
  1719. # since __getattr__ is called when an attribute doesn't
  1720. # exist and self._proxied indirectly calls self.value
  1721. # and Proxy __getattr__ calls self.value
  1722. if name == "value":
  1723. raise AttributeError
  1724. return super().__getattr__(name)
  1725. def getitem(self, index, context: InferenceContext | None = None):
  1726. """Get an item from this node if subscriptable.
  1727. :param index: The node to use as a subscript index.
  1728. :type index: Const or Slice
  1729. :raises AstroidTypeError: When the given index cannot be used as a
  1730. subscript index, or if this node is not subscriptable.
  1731. """
  1732. if isinstance(index, Const):
  1733. index_value = index.value
  1734. elif isinstance(index, Slice):
  1735. index_value = _infer_slice(index, context=context)
  1736. else:
  1737. raise AstroidTypeError(
  1738. f"Could not use type {type(index)} as subscript index"
  1739. )
  1740. try:
  1741. if isinstance(self.value, (str, bytes)):
  1742. return Const(self.value[index_value])
  1743. except ValueError as exc:
  1744. raise AstroidValueError(
  1745. f"Could not index {self.value!r} with {index_value!r}"
  1746. ) from exc
  1747. except IndexError as exc:
  1748. raise AstroidIndexError(
  1749. message="Index {index!r} out of range",
  1750. node=self,
  1751. index=index,
  1752. context=context,
  1753. ) from exc
  1754. except TypeError as exc:
  1755. raise AstroidTypeError(
  1756. message="Type error {error!r}", node=self, index=index, context=context
  1757. ) from exc
  1758. raise AstroidTypeError(f"{self!r} (value={self.value})")
  1759. def has_dynamic_getattr(self) -> bool:
  1760. """Check if the node has a custom __getattr__ or __getattribute__.
  1761. :returns: Whether the class has a custom __getattr__ or __getattribute__.
  1762. For a :class:`Const` this is always ``False``.
  1763. """
  1764. return False
  1765. def itered(self):
  1766. """An iterator over the elements this node contains.
  1767. :returns: The contents of this node.
  1768. :rtype: iterable(Const)
  1769. :raises TypeError: If this node does not represent something that is iterable.
  1770. """
  1771. if isinstance(self.value, str):
  1772. return [const_factory(elem) for elem in self.value]
  1773. raise TypeError(f"Cannot iterate over type {type(self.value)!r}")
  1774. def pytype(self) -> str:
  1775. """Get the name of the type that this node represents.
  1776. :returns: The name of the type.
  1777. """
  1778. return self._proxied.qname()
  1779. def bool_value(self, context: InferenceContext | None = None):
  1780. """Determine the boolean value of this node.
  1781. :returns: The boolean value of this node.
  1782. :rtype: bool or Uninferable
  1783. """
  1784. # bool(NotImplemented) is deprecated; it raises TypeError starting from Python 3.14
  1785. # and returns True for versions under 3.14
  1786. if self.value is NotImplemented:
  1787. return util.Uninferable if PY314_PLUS else True
  1788. return bool(self.value)
  1789. def _infer(
  1790. self, context: InferenceContext | None = None, **kwargs: Any
  1791. ) -> Iterator[Const]:
  1792. yield self
  1793. class Continue(_base_nodes.NoChildrenNode, _base_nodes.Statement):
  1794. """Class representing an :class:`ast.Continue` node.
  1795. >>> import astroid
  1796. >>> node = astroid.extract_node('continue')
  1797. >>> node
  1798. <Continue l.1 at 0x7f23b2e35588>
  1799. """
  1800. class Decorators(NodeNG):
  1801. """A node representing a list of decorators.
  1802. A :class:`Decorators` is the decorators that are applied to
  1803. a method or function.
  1804. >>> import astroid
  1805. >>> node = astroid.extract_node('''
  1806. @property
  1807. def my_property(self):
  1808. return 3
  1809. ''')
  1810. >>> node
  1811. <FunctionDef.my_property l.2 at 0x7f23b2e35d30>
  1812. >>> list(node.get_children())[0]
  1813. <Decorators l.1 at 0x7f23b2e35d68>
  1814. """
  1815. _astroid_fields = ("nodes",)
  1816. nodes: list[NodeNG]
  1817. """The decorators that this node contains."""
  1818. def postinit(self, nodes: list[NodeNG]) -> None:
  1819. self.nodes = nodes
  1820. def scope(self) -> LocalsDictNodeNG:
  1821. """The first parent node defining a new scope.
  1822. These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
  1823. :returns: The first parent scope node.
  1824. """
  1825. # skip the function node to go directly to the upper level scope
  1826. if not self.parent:
  1827. raise ParentMissingError(target=self)
  1828. if not self.parent.parent:
  1829. raise ParentMissingError(target=self.parent)
  1830. return self.parent.parent.scope()
  1831. def get_children(self):
  1832. yield from self.nodes
  1833. class DelAttr(_base_nodes.ParentAssignNode):
  1834. """Variation of :class:`ast.Delete` representing deletion of an attribute.
  1835. >>> import astroid
  1836. >>> node = astroid.extract_node('del self.attr')
  1837. >>> node
  1838. <Delete l.1 at 0x7f23b2e35f60>
  1839. >>> list(node.get_children())[0]
  1840. <DelAttr.attr l.1 at 0x7f23b2e411d0>
  1841. """
  1842. _astroid_fields = ("expr",)
  1843. _other_fields = ("attrname",)
  1844. expr: NodeNG
  1845. """The name that this node represents."""
  1846. def __init__(
  1847. self,
  1848. attrname: str,
  1849. lineno: int,
  1850. col_offset: int,
  1851. parent: NodeNG,
  1852. *,
  1853. end_lineno: int | None,
  1854. end_col_offset: int | None,
  1855. ) -> None:
  1856. self.attrname = attrname
  1857. """The name of the attribute that is being deleted."""
  1858. super().__init__(
  1859. lineno=lineno,
  1860. col_offset=col_offset,
  1861. end_lineno=end_lineno,
  1862. end_col_offset=end_col_offset,
  1863. parent=parent,
  1864. )
  1865. def postinit(self, expr: NodeNG) -> None:
  1866. self.expr = expr
  1867. def get_children(self):
  1868. yield self.expr
  1869. class Delete(_base_nodes.AssignTypeNode, _base_nodes.Statement):
  1870. """Class representing an :class:`ast.Delete` node.
  1871. A :class:`Delete` is a ``del`` statement this is deleting something.
  1872. >>> import astroid
  1873. >>> node = astroid.extract_node('del self.attr')
  1874. >>> node
  1875. <Delete l.1 at 0x7f23b2e35f60>
  1876. """
  1877. _astroid_fields = ("targets",)
  1878. def __init__(
  1879. self,
  1880. lineno: int,
  1881. col_offset: int,
  1882. parent: NodeNG,
  1883. *,
  1884. end_lineno: int | None,
  1885. end_col_offset: int | None,
  1886. ) -> None:
  1887. self.targets: list[NodeNG] = []
  1888. """What is being deleted."""
  1889. super().__init__(
  1890. lineno=lineno,
  1891. col_offset=col_offset,
  1892. end_lineno=end_lineno,
  1893. end_col_offset=end_col_offset,
  1894. parent=parent,
  1895. )
  1896. def postinit(self, targets: list[NodeNG]) -> None:
  1897. self.targets = targets
  1898. def get_children(self):
  1899. yield from self.targets
  1900. class Dict(NodeNG, Instance):
  1901. """Class representing an :class:`ast.Dict` node.
  1902. A :class:`Dict` is a dictionary that is created with ``{}`` syntax.
  1903. >>> import astroid
  1904. >>> node = astroid.extract_node('{1: "1"}')
  1905. >>> node
  1906. <Dict.dict l.1 at 0x7f23b2e35cc0>
  1907. """
  1908. _astroid_fields = ("items",)
  1909. def __init__(
  1910. self,
  1911. lineno: int | None,
  1912. col_offset: int | None,
  1913. parent: NodeNG | None,
  1914. *,
  1915. end_lineno: int | None,
  1916. end_col_offset: int | None,
  1917. ) -> None:
  1918. self.items: list[tuple[InferenceResult, InferenceResult]] = []
  1919. """The key-value pairs contained in the dictionary."""
  1920. super().__init__(
  1921. lineno=lineno,
  1922. col_offset=col_offset,
  1923. end_lineno=end_lineno,
  1924. end_col_offset=end_col_offset,
  1925. parent=parent,
  1926. )
  1927. def postinit(self, items: list[tuple[InferenceResult, InferenceResult]]) -> None:
  1928. """Do some setup after initialisation.
  1929. :param items: The key-value pairs contained in the dictionary.
  1930. """
  1931. self.items = items
  1932. infer_unary_op = protocols.dict_infer_unary_op
  1933. def pytype(self) -> Literal["builtins.dict"]:
  1934. """Get the name of the type that this node represents.
  1935. :returns: The name of the type.
  1936. """
  1937. return "builtins.dict"
  1938. def get_children(self):
  1939. """Get the key and value nodes below this node.
  1940. Children are returned in the order that they are defined in the source
  1941. code, key first then the value.
  1942. :returns: The children.
  1943. :rtype: iterable(NodeNG)
  1944. """
  1945. for key, value in self.items:
  1946. yield key
  1947. yield value
  1948. def last_child(self):
  1949. """An optimized version of list(get_children())[-1]
  1950. :returns: The last child, or None if no children exist.
  1951. :rtype: NodeNG or None
  1952. """
  1953. if self.items:
  1954. return self.items[-1][1]
  1955. return None
  1956. def itered(self):
  1957. """An iterator over the keys this node contains.
  1958. :returns: The keys of this node.
  1959. :rtype: iterable(NodeNG)
  1960. """
  1961. return [key for (key, _) in self.items]
  1962. def getitem(
  1963. self, index: Const | Slice, context: InferenceContext | None = None
  1964. ) -> NodeNG:
  1965. """Get an item from this node.
  1966. :param index: The node to use as a subscript index.
  1967. :raises AstroidTypeError: When the given index cannot be used as a
  1968. subscript index, or if this node is not subscriptable.
  1969. :raises AstroidIndexError: If the given index does not exist in the
  1970. dictionary.
  1971. """
  1972. for key, value in self.items:
  1973. # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}.
  1974. if isinstance(key, DictUnpack):
  1975. inferred_value = util.safe_infer(value, context)
  1976. if not isinstance(inferred_value, Dict):
  1977. continue
  1978. try:
  1979. return inferred_value.getitem(index, context)
  1980. except (AstroidTypeError, AstroidIndexError):
  1981. continue
  1982. for inferredkey in key.infer(context):
  1983. if isinstance(inferredkey, util.UninferableBase):
  1984. continue
  1985. if isinstance(inferredkey, Const) and isinstance(index, Const):
  1986. if inferredkey.value == index.value:
  1987. return value
  1988. raise AstroidIndexError(index)
  1989. def bool_value(self, context: InferenceContext | None = None):
  1990. """Determine the boolean value of this node.
  1991. :returns: The boolean value of this node.
  1992. :rtype: bool
  1993. """
  1994. return bool(self.items)
  1995. def _infer(
  1996. self, context: InferenceContext | None = None, **kwargs: Any
  1997. ) -> Iterator[nodes.Dict]:
  1998. if not any(isinstance(k, DictUnpack) for k, _ in self.items):
  1999. yield self
  2000. else:
  2001. items = self._infer_map(context)
  2002. new_seq = type(self)(
  2003. lineno=self.lineno,
  2004. col_offset=self.col_offset,
  2005. parent=self.parent,
  2006. end_lineno=self.end_lineno,
  2007. end_col_offset=self.end_col_offset,
  2008. )
  2009. new_seq.postinit(list(items.items()))
  2010. yield new_seq
  2011. @staticmethod
  2012. def _update_with_replacement(
  2013. lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
  2014. rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
  2015. ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
  2016. """Delete nodes that equate to duplicate keys.
  2017. Since an astroid node doesn't 'equal' another node with the same value,
  2018. this function uses the as_string method to make sure duplicate keys
  2019. don't get through
  2020. Note that both the key and the value are astroid nodes
  2021. Fixes issue with DictUnpack causing duplicate keys
  2022. in inferred Dict items
  2023. :param lhs_dict: Dictionary to 'merge' nodes into
  2024. :param rhs_dict: Dictionary with nodes to pull from
  2025. :return : merged dictionary of nodes
  2026. """
  2027. combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
  2028. # Overwrite keys which have the same string values
  2029. string_map = {key.as_string(): (key, value) for key, value in combined_dict}
  2030. # Return to dictionary
  2031. return dict(string_map.values())
  2032. def _infer_map(
  2033. self, context: InferenceContext | None
  2034. ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
  2035. """Infer all values based on Dict.items."""
  2036. values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {}
  2037. for name, value in self.items:
  2038. if isinstance(name, DictUnpack):
  2039. double_starred = util.safe_infer(value, context)
  2040. if not double_starred:
  2041. raise InferenceError
  2042. if not isinstance(double_starred, Dict):
  2043. raise InferenceError(node=self, context=context)
  2044. unpack_items = double_starred._infer_map(context)
  2045. values = self._update_with_replacement(values, unpack_items)
  2046. else:
  2047. key = util.safe_infer(name, context=context)
  2048. safe_value = util.safe_infer(value, context=context)
  2049. if any(not elem for elem in (key, safe_value)):
  2050. raise InferenceError(node=self, context=context)
  2051. # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False
  2052. values = self._update_with_replacement(values, {key: safe_value})
  2053. return values
  2054. class Expr(_base_nodes.Statement):
  2055. """Class representing an :class:`ast.Expr` node.
  2056. An :class:`Expr` is any expression that does not have its value used or
  2057. stored.
  2058. >>> import astroid
  2059. >>> node = astroid.extract_node('method()')
  2060. >>> node
  2061. <Call l.1 at 0x7f23b2e352b0>
  2062. >>> node.parent
  2063. <Expr l.1 at 0x7f23b2e35278>
  2064. """
  2065. _astroid_fields = ("value",)
  2066. value: NodeNG
  2067. """What the expression does."""
  2068. def postinit(self, value: NodeNG) -> None:
  2069. self.value = value
  2070. def get_children(self):
  2071. yield self.value
  2072. def _get_yield_nodes_skip_functions(self):
  2073. if not self.value.is_function:
  2074. yield from self.value._get_yield_nodes_skip_functions()
  2075. def _get_yield_nodes_skip_lambdas(self):
  2076. if not self.value.is_lambda:
  2077. yield from self.value._get_yield_nodes_skip_lambdas()
  2078. class EmptyNode(_base_nodes.NoChildrenNode):
  2079. """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""
  2080. object = None
  2081. def __init__(
  2082. self,
  2083. lineno: None = None,
  2084. col_offset: None = None,
  2085. parent: NodeNG = SYNTHETIC_ROOT,
  2086. *,
  2087. end_lineno: None = None,
  2088. end_col_offset: None = None,
  2089. ) -> None:
  2090. super().__init__(
  2091. lineno=lineno,
  2092. col_offset=col_offset,
  2093. end_lineno=end_lineno,
  2094. end_col_offset=end_col_offset,
  2095. parent=parent,
  2096. )
  2097. def has_underlying_object(self) -> bool:
  2098. return self.object is not None and self.object is not _EMPTY_OBJECT_MARKER
  2099. @decorators.raise_if_nothing_inferred
  2100. @decorators.path_wrapper
  2101. def _infer(
  2102. self, context: InferenceContext | None = None, **kwargs: Any
  2103. ) -> Generator[InferenceResult]:
  2104. if not self.has_underlying_object():
  2105. yield util.Uninferable
  2106. else:
  2107. try:
  2108. yield from AstroidManager().infer_ast_from_something(
  2109. self.object, context=context
  2110. )
  2111. except AstroidError:
  2112. yield util.Uninferable
  2113. class ExceptHandler(
  2114. _base_nodes.MultiLineBlockNode, _base_nodes.AssignTypeNode, _base_nodes.Statement
  2115. ):
  2116. """Class representing an :class:`ast.ExceptHandler`. node.
  2117. An :class:`ExceptHandler` is an ``except`` block on a try-except.
  2118. >>> import astroid
  2119. >>> node = astroid.extract_node('''
  2120. try:
  2121. do_something()
  2122. except Exception as error:
  2123. print("Error!")
  2124. ''')
  2125. >>> node
  2126. <Try l.2 at 0x7f23b2e9d908>
  2127. >>> node.handlers
  2128. [<ExceptHandler l.4 at 0x7f23b2e9e860>]
  2129. """
  2130. _astroid_fields = ("type", "name", "body")
  2131. _multi_line_block_fields = ("body",)
  2132. type: NodeNG | None
  2133. """The types that the block handles."""
  2134. name: AssignName | None
  2135. """The name that the caught exception is assigned to."""
  2136. body: list[NodeNG]
  2137. """The contents of the block."""
  2138. assigned_stmts = protocols.excepthandler_assigned_stmts
  2139. """Returns the assigned statement (non inferred) according to the assignment type.
  2140. See astroid/protocols.py for actual implementation.
  2141. """
  2142. def postinit(
  2143. self,
  2144. type: NodeNG | None, # pylint: disable = redefined-builtin
  2145. name: AssignName | None,
  2146. body: list[NodeNG],
  2147. ) -> None:
  2148. self.type = type
  2149. self.name = name
  2150. self.body = body
  2151. def get_children(self):
  2152. if self.type is not None:
  2153. yield self.type
  2154. if self.name is not None:
  2155. yield self.name
  2156. yield from self.body
  2157. @cached_property
  2158. def blockstart_tolineno(self):
  2159. """The line on which the beginning of this block ends.
  2160. :type: int
  2161. """
  2162. if self.name:
  2163. return self.name.tolineno
  2164. if self.type:
  2165. return self.type.tolineno
  2166. return self.lineno
  2167. def catch(self, exceptions: list[str] | None) -> bool:
  2168. """Check if this node handles any of the given
  2169. :param exceptions: The names of the exceptions to check for.
  2170. """
  2171. if self.type is None or exceptions is None:
  2172. return True
  2173. return any(node.name in exceptions for node in self.type._get_name_nodes())
  2174. class For(
  2175. _base_nodes.MultiLineWithElseBlockNode,
  2176. _base_nodes.AssignTypeNode,
  2177. _base_nodes.Statement,
  2178. ):
  2179. """Class representing an :class:`ast.For` node.
  2180. >>> import astroid
  2181. >>> node = astroid.extract_node('for thing in things: print(thing)')
  2182. >>> node
  2183. <For l.1 at 0x7f23b2e8cf28>
  2184. """
  2185. _astroid_fields = ("target", "iter", "body", "orelse")
  2186. _other_other_fields = ("type_annotation",)
  2187. _multi_line_block_fields = ("body", "orelse")
  2188. optional_assign = True
  2189. """Whether this node optionally assigns a variable.
  2190. This is always ``True`` for :class:`For` nodes.
  2191. """
  2192. target: NodeNG
  2193. """What the loop assigns to."""
  2194. iter: NodeNG
  2195. """What the loop iterates over."""
  2196. body: list[NodeNG]
  2197. """The contents of the body of the loop."""
  2198. orelse: list[NodeNG]
  2199. """The contents of the ``else`` block of the loop."""
  2200. type_annotation: NodeNG | None
  2201. """If present, this will contain the type annotation passed by a type comment"""
  2202. def postinit(
  2203. self,
  2204. target: NodeNG,
  2205. iter: NodeNG, # pylint: disable = redefined-builtin
  2206. body: list[NodeNG],
  2207. orelse: list[NodeNG],
  2208. type_annotation: NodeNG | None,
  2209. ) -> None:
  2210. self.target = target
  2211. self.iter = iter
  2212. self.body = body
  2213. self.orelse = orelse
  2214. self.type_annotation = type_annotation
  2215. assigned_stmts = protocols.for_assigned_stmts
  2216. """Returns the assigned statement (non inferred) according to the assignment type.
  2217. See astroid/protocols.py for actual implementation.
  2218. """
  2219. @cached_property
  2220. def blockstart_tolineno(self):
  2221. """The line on which the beginning of this block ends.
  2222. :type: int
  2223. """
  2224. return self.iter.tolineno
  2225. def get_children(self):
  2226. yield self.target
  2227. yield self.iter
  2228. yield from self.body
  2229. yield from self.orelse
  2230. class AsyncFor(For):
  2231. """Class representing an :class:`ast.AsyncFor` node.
  2232. An :class:`AsyncFor` is an asynchronous :class:`For` built with
  2233. the ``async`` keyword.
  2234. >>> import astroid
  2235. >>> node = astroid.extract_node('''
  2236. async def func(things):
  2237. async for thing in things:
  2238. print(thing)
  2239. ''')
  2240. >>> node
  2241. <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
  2242. >>> node.body[0]
  2243. <AsyncFor l.3 at 0x7f23b2e417b8>
  2244. """
  2245. class Await(NodeNG):
  2246. """Class representing an :class:`ast.Await` node.
  2247. An :class:`Await` is the ``await`` keyword.
  2248. >>> import astroid
  2249. >>> node = astroid.extract_node('''
  2250. async def func(things):
  2251. await other_func()
  2252. ''')
  2253. >>> node
  2254. <AsyncFunctionDef.func l.2 at 0x7f23b2e41748>
  2255. >>> node.body[0]
  2256. <Expr l.3 at 0x7f23b2e419e8>
  2257. >>> list(node.body[0].get_children())[0]
  2258. <Await l.3 at 0x7f23b2e41a20>
  2259. """
  2260. _astroid_fields = ("value",)
  2261. value: NodeNG
  2262. """What to wait for."""
  2263. def postinit(self, value: NodeNG) -> None:
  2264. self.value = value
  2265. def get_children(self):
  2266. yield self.value
  2267. class ImportFrom(_base_nodes.ImportNode):
  2268. """Class representing an :class:`ast.ImportFrom` node.
  2269. >>> import astroid
  2270. >>> node = astroid.extract_node('from my_package import my_module')
  2271. >>> node
  2272. <ImportFrom l.1 at 0x7f23b2e415c0>
  2273. """
  2274. _other_fields = ("modname", "names", "level")
  2275. def __init__(
  2276. self,
  2277. fromname: str | None,
  2278. names: list[tuple[str, str | None]],
  2279. level: int | None = 0,
  2280. lineno: int | None = None,
  2281. col_offset: int | None = None,
  2282. parent: NodeNG | None = None,
  2283. *,
  2284. end_lineno: int | None = None,
  2285. end_col_offset: int | None = None,
  2286. ) -> None:
  2287. """
  2288. :param fromname: The module that is being imported from.
  2289. :param names: What is being imported from the module.
  2290. :param level: The level of relative import.
  2291. :param lineno: The line that this node appears on in the source code.
  2292. :param col_offset: The column that this node appears on in the
  2293. source code.
  2294. :param parent: The parent node in the syntax tree.
  2295. :param end_lineno: The last line this node appears on in the source code.
  2296. :param end_col_offset: The end column this node appears on in the
  2297. source code. Note: This is after the last symbol.
  2298. """
  2299. self.modname: str | None = fromname # can be None
  2300. """The module that is being imported from.
  2301. This is ``None`` for relative imports.
  2302. """
  2303. self.names: list[tuple[str, str | None]] = names
  2304. """What is being imported from the module.
  2305. Each entry is a :class:`tuple` of the name being imported,
  2306. and the alias that the name is assigned to (if any).
  2307. """
  2308. # TODO When is 'level' None?
  2309. self.level: int | None = level # can be None
  2310. """The level of relative import.
  2311. Essentially this is the number of dots in the import.
  2312. This is always 0 for absolute imports.
  2313. """
  2314. super().__init__(
  2315. lineno=lineno,
  2316. col_offset=col_offset,
  2317. end_lineno=end_lineno,
  2318. end_col_offset=end_col_offset,
  2319. parent=parent,
  2320. )
  2321. @decorators.raise_if_nothing_inferred
  2322. @decorators.path_wrapper
  2323. def _infer(
  2324. self,
  2325. context: InferenceContext | None = None,
  2326. asname: bool = True,
  2327. **kwargs: Any,
  2328. ) -> Generator[InferenceResult]:
  2329. """Infer a ImportFrom node: return the imported module/object."""
  2330. context = context or InferenceContext()
  2331. name = context.lookupname
  2332. if name is None:
  2333. raise InferenceError(node=self, context=context)
  2334. if asname:
  2335. try:
  2336. name = self.real_name(name)
  2337. except AttributeInferenceError as exc:
  2338. # See https://github.com/pylint-dev/pylint/issues/4692
  2339. raise InferenceError(node=self, context=context) from exc
  2340. try:
  2341. module = self.do_import_module()
  2342. except AstroidBuildingError as exc:
  2343. raise InferenceError(node=self, context=context) from exc
  2344. try:
  2345. context = copy_context(context)
  2346. context.lookupname = name
  2347. stmts = module.getattr(name, ignore_locals=module is self.root())
  2348. return _infer_stmts(stmts, context)
  2349. except AttributeInferenceError as error:
  2350. raise InferenceError(
  2351. str(error), target=self, attribute=name, context=context
  2352. ) from error
  2353. class Attribute(NodeNG):
  2354. """Class representing an :class:`ast.Attribute` node."""
  2355. expr: NodeNG
  2356. _astroid_fields = ("expr",)
  2357. _other_fields = ("attrname",)
  2358. def __init__(
  2359. self,
  2360. attrname: str,
  2361. lineno: int,
  2362. col_offset: int,
  2363. parent: NodeNG,
  2364. *,
  2365. end_lineno: int | None,
  2366. end_col_offset: int | None,
  2367. ) -> None:
  2368. self.attrname = attrname
  2369. """The name of the attribute."""
  2370. super().__init__(
  2371. lineno=lineno,
  2372. col_offset=col_offset,
  2373. end_lineno=end_lineno,
  2374. end_col_offset=end_col_offset,
  2375. parent=parent,
  2376. )
  2377. def postinit(self, expr: NodeNG) -> None:
  2378. self.expr = expr
  2379. def get_children(self):
  2380. yield self.expr
  2381. @decorators.raise_if_nothing_inferred
  2382. @decorators.path_wrapper
  2383. def _infer(
  2384. self, context: InferenceContext | None = None, **kwargs: Any
  2385. ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
  2386. return _infer_attribute(self, context, **kwargs)
  2387. class Global(_base_nodes.NoChildrenNode, _base_nodes.Statement):
  2388. """Class representing an :class:`ast.Global` node.
  2389. >>> import astroid
  2390. >>> node = astroid.extract_node('global a_global')
  2391. >>> node
  2392. <Global l.1 at 0x7f23b2e9de10>
  2393. """
  2394. _other_fields = ("names",)
  2395. def __init__(
  2396. self,
  2397. names: list[str],
  2398. lineno: int | None = None,
  2399. col_offset: int | None = None,
  2400. parent: NodeNG | None = None,
  2401. *,
  2402. end_lineno: int | None = None,
  2403. end_col_offset: int | None = None,
  2404. ) -> None:
  2405. """
  2406. :param names: The names being declared as global.
  2407. :param lineno: The line that this node appears on in the source code.
  2408. :param col_offset: The column that this node appears on in the
  2409. source code.
  2410. :param parent: The parent node in the syntax tree.
  2411. :param end_lineno: The last line this node appears on in the source code.
  2412. :param end_col_offset: The end column this node appears on in the
  2413. source code. Note: This is after the last symbol.
  2414. """
  2415. self.names: list[str] = names
  2416. """The names being declared as global."""
  2417. super().__init__(
  2418. lineno=lineno,
  2419. col_offset=col_offset,
  2420. end_lineno=end_lineno,
  2421. end_col_offset=end_col_offset,
  2422. parent=parent,
  2423. )
  2424. def _infer_name(self, frame, name):
  2425. return name
  2426. @decorators.raise_if_nothing_inferred
  2427. @decorators.path_wrapper
  2428. def _infer(
  2429. self, context: InferenceContext | None = None, **kwargs: Any
  2430. ) -> Generator[InferenceResult]:
  2431. if context is None or context.lookupname is None:
  2432. raise InferenceError(node=self, context=context)
  2433. try:
  2434. # pylint: disable-next=no-member
  2435. return _infer_stmts(self.root().getattr(context.lookupname), context)
  2436. except AttributeInferenceError as error:
  2437. raise InferenceError(
  2438. str(error), target=self, attribute=context.lookupname, context=context
  2439. ) from error
  2440. class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
  2441. """Class representing an :class:`ast.If` node.
  2442. >>> import astroid
  2443. >>> node = astroid.extract_node('if condition: print(True)')
  2444. >>> node
  2445. <If l.1 at 0x7f23b2e9dd30>
  2446. """
  2447. _astroid_fields = ("test", "body", "orelse")
  2448. _multi_line_block_fields = ("body", "orelse")
  2449. test: NodeNG
  2450. """The condition that the statement tests."""
  2451. body: list[NodeNG]
  2452. """The contents of the block."""
  2453. orelse: list[NodeNG]
  2454. """The contents of the ``else`` block."""
  2455. def postinit(self, test: NodeNG, body: list[NodeNG], orelse: list[NodeNG]) -> None:
  2456. self.test = test
  2457. self.body = body
  2458. self.orelse = orelse
  2459. @cached_property
  2460. def blockstart_tolineno(self):
  2461. """The line on which the beginning of this block ends.
  2462. :type: int
  2463. """
  2464. return self.test.tolineno
  2465. def block_range(self, lineno: int) -> tuple[int, int]:
  2466. """Get a range from the given line number to where this node ends.
  2467. :param lineno: The line number to start the range at.
  2468. :returns: The range of line numbers that this node belongs to,
  2469. starting at the given line number.
  2470. """
  2471. if lineno == self.body[0].fromlineno:
  2472. return lineno, lineno
  2473. if lineno <= self.body[-1].tolineno:
  2474. return lineno, self.body[-1].tolineno
  2475. return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1)
  2476. def get_children(self):
  2477. yield self.test
  2478. yield from self.body
  2479. yield from self.orelse
  2480. def has_elif_block(self) -> bool:
  2481. return len(self.orelse) == 1 and isinstance(self.orelse[0], If)
  2482. def _get_yield_nodes_skip_functions(self):
  2483. """An If node can contain a Yield node in the test"""
  2484. yield from self.test._get_yield_nodes_skip_functions()
  2485. yield from super()._get_yield_nodes_skip_functions()
  2486. def _get_yield_nodes_skip_lambdas(self):
  2487. """An If node can contain a Yield node in the test"""
  2488. yield from self.test._get_yield_nodes_skip_lambdas()
  2489. yield from super()._get_yield_nodes_skip_lambdas()
  2490. class IfExp(NodeNG):
  2491. """Class representing an :class:`ast.IfExp` node.
  2492. >>> import astroid
  2493. >>> node = astroid.extract_node('value if condition else other')
  2494. >>> node
  2495. <IfExp l.1 at 0x7f23b2e9dbe0>
  2496. """
  2497. _astroid_fields = ("test", "body", "orelse")
  2498. test: NodeNG
  2499. """The condition that the statement tests."""
  2500. body: NodeNG
  2501. """The contents of the block."""
  2502. orelse: NodeNG
  2503. """The contents of the ``else`` block."""
  2504. def postinit(self, test: NodeNG, body: NodeNG, orelse: NodeNG) -> None:
  2505. self.test = test
  2506. self.body = body
  2507. self.orelse = orelse
  2508. def get_children(self):
  2509. yield self.test
  2510. yield self.body
  2511. yield self.orelse
  2512. def op_left_associative(self) -> Literal[False]:
  2513. # `1 if True else 2 if False else 3` is parsed as
  2514. # `1 if True else (2 if False else 3)`
  2515. return False
  2516. @decorators.raise_if_nothing_inferred
  2517. def _infer(
  2518. self, context: InferenceContext | None = None, **kwargs: Any
  2519. ) -> Generator[InferenceResult]:
  2520. """Support IfExp inference.
  2521. If we can't infer the truthiness of the condition, we default
  2522. to inferring both branches. Otherwise, we infer either branch
  2523. depending on the condition.
  2524. """
  2525. # We use two separate contexts for evaluating lhs and rhs because
  2526. # evaluating lhs may leave some undesired entries in context.path
  2527. # which may not let us infer right value of rhs.
  2528. context = context or InferenceContext()
  2529. lhs_context = copy_context(context)
  2530. rhs_context = copy_context(context)
  2531. # Infer bool condition. Stop inferring if in doubt and fallback to
  2532. # evaluating both branches.
  2533. condition: bool | None = None
  2534. try:
  2535. for test in self.test.infer(context=context.clone()):
  2536. if isinstance(test, util.UninferableBase):
  2537. condition = None
  2538. break
  2539. test_bool_value = test.bool_value()
  2540. if isinstance(test_bool_value, util.UninferableBase):
  2541. condition = None
  2542. break
  2543. if condition is None:
  2544. condition = test_bool_value
  2545. elif test_bool_value != condition:
  2546. condition = None
  2547. break
  2548. except InferenceError:
  2549. condition = None
  2550. if condition is True or condition is None:
  2551. yield from self.body.infer(context=lhs_context)
  2552. if condition is False or condition is None:
  2553. yield from self.orelse.infer(context=rhs_context)
  2554. class Import(_base_nodes.ImportNode):
  2555. """Class representing an :class:`ast.Import` node.
  2556. >>> import astroid
  2557. >>> node = astroid.extract_node('import astroid')
  2558. >>> node
  2559. <Import l.1 at 0x7f23b2e4e5c0>
  2560. """
  2561. _other_fields = ("names",)
  2562. def __init__(
  2563. self,
  2564. names: list[tuple[str, str | None]],
  2565. lineno: int | None = None,
  2566. col_offset: int | None = None,
  2567. parent: NodeNG | None = None,
  2568. *,
  2569. end_lineno: int | None = None,
  2570. end_col_offset: int | None = None,
  2571. ) -> None:
  2572. """
  2573. :param names: The names being imported.
  2574. :param lineno: The line that this node appears on in the source code.
  2575. :param col_offset: The column that this node appears on in the
  2576. source code.
  2577. :param parent: The parent node in the syntax tree.
  2578. :param end_lineno: The last line this node appears on in the source code.
  2579. :param end_col_offset: The end column this node appears on in the
  2580. source code. Note: This is after the last symbol.
  2581. """
  2582. self.names: list[tuple[str, str | None]] = names
  2583. """The names being imported.
  2584. Each entry is a :class:`tuple` of the name being imported,
  2585. and the alias that the name is assigned to (if any).
  2586. """
  2587. super().__init__(
  2588. lineno=lineno,
  2589. col_offset=col_offset,
  2590. end_lineno=end_lineno,
  2591. end_col_offset=end_col_offset,
  2592. parent=parent,
  2593. )
  2594. @decorators.raise_if_nothing_inferred
  2595. @decorators.path_wrapper
  2596. def _infer(
  2597. self,
  2598. context: InferenceContext | None = None,
  2599. asname: bool = True,
  2600. **kwargs: Any,
  2601. ) -> Generator[nodes.Module]:
  2602. """Infer an Import node: return the imported module/object."""
  2603. context = context or InferenceContext()
  2604. name = context.lookupname
  2605. if name is None:
  2606. raise InferenceError(node=self, context=context)
  2607. try:
  2608. if asname:
  2609. yield self.do_import_module(self.real_name(name))
  2610. else:
  2611. yield self.do_import_module(name)
  2612. except AstroidBuildingError as exc:
  2613. raise InferenceError(node=self, context=context) from exc
  2614. class Keyword(NodeNG):
  2615. """Class representing an :class:`ast.keyword` node.
  2616. >>> import astroid
  2617. >>> node = astroid.extract_node('function(a_kwarg=True)')
  2618. >>> node
  2619. <Call l.1 at 0x7f23b2e9e320>
  2620. >>> node.keywords
  2621. [<Keyword l.1 at 0x7f23b2e9e9b0>]
  2622. """
  2623. _astroid_fields = ("value",)
  2624. _other_fields = ("arg",)
  2625. value: NodeNG
  2626. """The value being assigned to the keyword argument."""
  2627. def __init__(
  2628. self,
  2629. arg: str | None,
  2630. lineno: int | None,
  2631. col_offset: int | None,
  2632. parent: NodeNG,
  2633. *,
  2634. end_lineno: int | None,
  2635. end_col_offset: int | None,
  2636. ) -> None:
  2637. self.arg = arg
  2638. """The argument being assigned to."""
  2639. super().__init__(
  2640. lineno=lineno,
  2641. col_offset=col_offset,
  2642. end_lineno=end_lineno,
  2643. end_col_offset=end_col_offset,
  2644. parent=parent,
  2645. )
  2646. def postinit(self, value: NodeNG) -> None:
  2647. self.value = value
  2648. def get_children(self):
  2649. yield self.value
  2650. class List(BaseContainer):
  2651. """Class representing an :class:`ast.List` node.
  2652. >>> import astroid
  2653. >>> node = astroid.extract_node('[1, 2, 3]')
  2654. >>> node
  2655. <List.list l.1 at 0x7f23b2e9e128>
  2656. """
  2657. _other_fields = ("ctx",)
  2658. def __init__(
  2659. self,
  2660. ctx: Context | None = None,
  2661. lineno: int | None = None,
  2662. col_offset: int | None = None,
  2663. parent: NodeNG | None = None,
  2664. *,
  2665. end_lineno: int | None = None,
  2666. end_col_offset: int | None = None,
  2667. ) -> None:
  2668. """
  2669. :param ctx: Whether the list is assigned to or loaded from.
  2670. :param lineno: The line that this node appears on in the source code.
  2671. :param col_offset: The column that this node appears on in the
  2672. source code.
  2673. :param parent: The parent node in the syntax tree.
  2674. :param end_lineno: The last line this node appears on in the source code.
  2675. :param end_col_offset: The end column this node appears on in the
  2676. source code. Note: This is after the last symbol.
  2677. """
  2678. self.ctx: Context | None = ctx
  2679. """Whether the list is assigned to or loaded from."""
  2680. super().__init__(
  2681. lineno=lineno,
  2682. col_offset=col_offset,
  2683. end_lineno=end_lineno,
  2684. end_col_offset=end_col_offset,
  2685. parent=parent,
  2686. )
  2687. assigned_stmts = protocols.sequence_assigned_stmts
  2688. """Returns the assigned statement (non inferred) according to the assignment type.
  2689. See astroid/protocols.py for actual implementation.
  2690. """
  2691. infer_unary_op = protocols.list_infer_unary_op
  2692. infer_binary_op = protocols.tl_infer_binary_op
  2693. def pytype(self) -> Literal["builtins.list"]:
  2694. """Get the name of the type that this node represents.
  2695. :returns: The name of the type.
  2696. """
  2697. return "builtins.list"
  2698. def getitem(self, index, context: InferenceContext | None = None):
  2699. """Get an item from this node.
  2700. :param index: The node to use as a subscript index.
  2701. :type index: Const or Slice
  2702. """
  2703. return _container_getitem(self, self.elts, index, context=context)
  2704. class Nonlocal(_base_nodes.NoChildrenNode, _base_nodes.Statement):
  2705. """Class representing an :class:`ast.Nonlocal` node.
  2706. >>> import astroid
  2707. >>> node = astroid.extract_node('''
  2708. def function():
  2709. nonlocal var
  2710. ''')
  2711. >>> node
  2712. <FunctionDef.function l.2 at 0x7f23b2e9e208>
  2713. >>> node.body[0]
  2714. <Nonlocal l.3 at 0x7f23b2e9e908>
  2715. """
  2716. _other_fields = ("names",)
  2717. def __init__(
  2718. self,
  2719. names: list[str],
  2720. lineno: int | None = None,
  2721. col_offset: int | None = None,
  2722. parent: NodeNG | None = None,
  2723. *,
  2724. end_lineno: int | None = None,
  2725. end_col_offset: int | None = None,
  2726. ) -> None:
  2727. """
  2728. :param names: The names being declared as not local.
  2729. :param lineno: The line that this node appears on in the source code.
  2730. :param col_offset: The column that this node appears on in the
  2731. source code.
  2732. :param parent: The parent node in the syntax tree.
  2733. :param end_lineno: The last line this node appears on in the source code.
  2734. :param end_col_offset: The end column this node appears on in the
  2735. source code. Note: This is after the last symbol.
  2736. """
  2737. self.names: list[str] = names
  2738. """The names being declared as not local."""
  2739. super().__init__(
  2740. lineno=lineno,
  2741. col_offset=col_offset,
  2742. end_lineno=end_lineno,
  2743. end_col_offset=end_col_offset,
  2744. parent=parent,
  2745. )
  2746. def _infer_name(self, frame, name):
  2747. return name
  2748. class ParamSpec(_base_nodes.AssignTypeNode):
  2749. """Class representing a :class:`ast.ParamSpec` node.
  2750. >>> import astroid
  2751. >>> node = astroid.extract_node('type Alias[**P] = Callable[P, int]')
  2752. >>> node.type_params[0]
  2753. <ParamSpec l.1 at 0x7f23b2e4e198>
  2754. """
  2755. _astroid_fields = ("name", "default_value")
  2756. name: AssignName
  2757. default_value: NodeNG | None
  2758. def __init__(
  2759. self,
  2760. lineno: int,
  2761. col_offset: int,
  2762. parent: NodeNG,
  2763. *,
  2764. end_lineno: int,
  2765. end_col_offset: int,
  2766. ) -> None:
  2767. super().__init__(
  2768. lineno=lineno,
  2769. col_offset=col_offset,
  2770. end_lineno=end_lineno,
  2771. end_col_offset=end_col_offset,
  2772. parent=parent,
  2773. )
  2774. def postinit(self, *, name: AssignName, default_value: NodeNG | None) -> None:
  2775. self.name = name
  2776. self.default_value = default_value
  2777. def _infer(
  2778. self, context: InferenceContext | None = None, **kwargs: Any
  2779. ) -> Iterator[ParamSpec]:
  2780. yield self
  2781. assigned_stmts = protocols.generic_type_assigned_stmts
  2782. """Returns the assigned statement (non inferred) according to the assignment type.
  2783. See astroid/protocols.py for actual implementation.
  2784. """
  2785. class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement):
  2786. """Class representing an :class:`ast.Pass` node.
  2787. >>> import astroid
  2788. >>> node = astroid.extract_node('pass')
  2789. >>> node
  2790. <Pass l.1 at 0x7f23b2e9e748>
  2791. """
  2792. class Raise(_base_nodes.Statement):
  2793. """Class representing an :class:`ast.Raise` node.
  2794. >>> import astroid
  2795. >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")')
  2796. >>> node
  2797. <Raise l.1 at 0x7f23b2e9e828>
  2798. """
  2799. _astroid_fields = ("exc", "cause")
  2800. exc: NodeNG | None
  2801. """What is being raised."""
  2802. cause: NodeNG | None
  2803. """The exception being used to raise this one."""
  2804. def postinit(
  2805. self,
  2806. exc: NodeNG | None,
  2807. cause: NodeNG | None,
  2808. ) -> None:
  2809. self.exc = exc
  2810. self.cause = cause
  2811. def raises_not_implemented(self) -> bool:
  2812. """Check if this node raises a :class:`NotImplementedError`.
  2813. :returns: Whether this node raises a :class:`NotImplementedError`.
  2814. """
  2815. if not self.exc:
  2816. return False
  2817. return any(
  2818. name.name == "NotImplementedError" for name in self.exc._get_name_nodes()
  2819. )
  2820. def get_children(self):
  2821. if self.exc is not None:
  2822. yield self.exc
  2823. if self.cause is not None:
  2824. yield self.cause
  2825. class Return(_base_nodes.Statement):
  2826. """Class representing an :class:`ast.Return` node.
  2827. >>> import astroid
  2828. >>> node = astroid.extract_node('return True')
  2829. >>> node
  2830. <Return l.1 at 0x7f23b8211908>
  2831. """
  2832. _astroid_fields = ("value",)
  2833. value: NodeNG | None
  2834. """The value being returned."""
  2835. def postinit(self, value: NodeNG | None) -> None:
  2836. self.value = value
  2837. def get_children(self):
  2838. if self.value is not None:
  2839. yield self.value
  2840. def is_tuple_return(self) -> bool:
  2841. return isinstance(self.value, Tuple)
  2842. def _get_return_nodes_skip_functions(self):
  2843. yield self
  2844. class Set(BaseContainer):
  2845. """Class representing an :class:`ast.Set` node.
  2846. >>> import astroid
  2847. >>> node = astroid.extract_node('{1, 2, 3}')
  2848. >>> node
  2849. <Set.set l.1 at 0x7f23b2e71d68>
  2850. """
  2851. infer_unary_op = protocols.set_infer_unary_op
  2852. def pytype(self) -> Literal["builtins.set"]:
  2853. """Get the name of the type that this node represents.
  2854. :returns: The name of the type.
  2855. """
  2856. return "builtins.set"
  2857. class Slice(NodeNG):
  2858. """Class representing an :class:`ast.Slice` node.
  2859. >>> import astroid
  2860. >>> node = astroid.extract_node('things[1:3]')
  2861. >>> node
  2862. <Subscript l.1 at 0x7f23b2e71f60>
  2863. >>> node.slice
  2864. <Slice l.1 at 0x7f23b2e71e80>
  2865. """
  2866. _astroid_fields = ("lower", "upper", "step")
  2867. lower: NodeNG | None
  2868. """The lower index in the slice."""
  2869. upper: NodeNG | None
  2870. """The upper index in the slice."""
  2871. step: NodeNG | None
  2872. """The step to take between indexes."""
  2873. def postinit(
  2874. self,
  2875. lower: NodeNG | None,
  2876. upper: NodeNG | None,
  2877. step: NodeNG | None,
  2878. ) -> None:
  2879. self.lower = lower
  2880. self.upper = upper
  2881. self.step = step
  2882. def _wrap_attribute(self, attr):
  2883. """Wrap the empty attributes of the Slice in a Const node."""
  2884. if not attr:
  2885. const = const_factory(attr)
  2886. const.parent = self
  2887. return const
  2888. return attr
  2889. @cached_property
  2890. def _proxied(self) -> nodes.ClassDef:
  2891. builtins = AstroidManager().builtins_module
  2892. return builtins.getattr("slice")[0]
  2893. def pytype(self) -> Literal["builtins.slice"]:
  2894. """Get the name of the type that this node represents.
  2895. :returns: The name of the type.
  2896. """
  2897. return "builtins.slice"
  2898. def display_type(self) -> Literal["Slice"]:
  2899. """A human readable type of this node.
  2900. :returns: The type of this node.
  2901. """
  2902. return "Slice"
  2903. def igetattr(
  2904. self, attrname: str, context: InferenceContext | None = None
  2905. ) -> Iterator[SuccessfulInferenceResult]:
  2906. """Infer the possible values of the given attribute on the slice.
  2907. :param attrname: The name of the attribute to infer.
  2908. :returns: The inferred possible values.
  2909. """
  2910. if attrname == "start":
  2911. yield self._wrap_attribute(self.lower)
  2912. elif attrname == "stop":
  2913. yield self._wrap_attribute(self.upper)
  2914. elif attrname == "step":
  2915. yield self._wrap_attribute(self.step)
  2916. else:
  2917. yield from self.getattr(attrname, context=context)
  2918. def getattr(self, attrname, context: InferenceContext | None = None):
  2919. return self._proxied.getattr(attrname, context)
  2920. def get_children(self):
  2921. if self.lower is not None:
  2922. yield self.lower
  2923. if self.upper is not None:
  2924. yield self.upper
  2925. if self.step is not None:
  2926. yield self.step
  2927. def _infer(
  2928. self, context: InferenceContext | None = None, **kwargs: Any
  2929. ) -> Iterator[Slice]:
  2930. yield self
  2931. class Starred(_base_nodes.ParentAssignNode):
  2932. """Class representing an :class:`ast.Starred` node.
  2933. >>> import astroid
  2934. >>> node = astroid.extract_node('*args')
  2935. >>> node
  2936. <Starred l.1 at 0x7f23b2e41978>
  2937. """
  2938. _astroid_fields = ("value",)
  2939. _other_fields = ("ctx",)
  2940. value: NodeNG
  2941. """What is being unpacked."""
  2942. def __init__(
  2943. self,
  2944. ctx: Context,
  2945. lineno: int,
  2946. col_offset: int,
  2947. parent: NodeNG,
  2948. *,
  2949. end_lineno: int | None,
  2950. end_col_offset: int | None,
  2951. ) -> None:
  2952. self.ctx = ctx
  2953. """Whether the starred item is assigned to or loaded from."""
  2954. super().__init__(
  2955. lineno=lineno,
  2956. col_offset=col_offset,
  2957. end_lineno=end_lineno,
  2958. end_col_offset=end_col_offset,
  2959. parent=parent,
  2960. )
  2961. def postinit(self, value: NodeNG) -> None:
  2962. self.value = value
  2963. assigned_stmts = protocols.starred_assigned_stmts
  2964. """Returns the assigned statement (non inferred) according to the assignment type.
  2965. See astroid/protocols.py for actual implementation.
  2966. """
  2967. def get_children(self):
  2968. yield self.value
  2969. class Subscript(NodeNG):
  2970. """Class representing an :class:`ast.Subscript` node.
  2971. >>> import astroid
  2972. >>> node = astroid.extract_node('things[1:3]')
  2973. >>> node
  2974. <Subscript l.1 at 0x7f23b2e71f60>
  2975. """
  2976. _SUBSCRIPT_SENTINEL = object()
  2977. _astroid_fields = ("value", "slice")
  2978. _other_fields = ("ctx",)
  2979. value: NodeNG
  2980. """What is being indexed."""
  2981. slice: NodeNG
  2982. """The slice being used to lookup."""
  2983. def __init__(
  2984. self,
  2985. ctx: Context,
  2986. lineno: int,
  2987. col_offset: int,
  2988. parent: NodeNG,
  2989. *,
  2990. end_lineno: int | None,
  2991. end_col_offset: int | None,
  2992. ) -> None:
  2993. self.ctx = ctx
  2994. """Whether the subscripted item is assigned to or loaded from."""
  2995. super().__init__(
  2996. lineno=lineno,
  2997. col_offset=col_offset,
  2998. end_lineno=end_lineno,
  2999. end_col_offset=end_col_offset,
  3000. parent=parent,
  3001. )
  3002. # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
  3003. def postinit(self, value: NodeNG, slice: NodeNG) -> None:
  3004. self.value = value
  3005. self.slice = slice
  3006. def get_children(self):
  3007. yield self.value
  3008. yield self.slice
  3009. def _infer_subscript(
  3010. self, context: InferenceContext | None = None, **kwargs: Any
  3011. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  3012. """Inference for subscripts.
  3013. We're understanding if the index is a Const
  3014. or a slice, passing the result of inference
  3015. to the value's `getitem` method, which should
  3016. handle each supported index type accordingly.
  3017. """
  3018. from astroid import helpers # pylint: disable=import-outside-toplevel
  3019. found_one = False
  3020. for value in self.value.infer(context):
  3021. if isinstance(value, util.UninferableBase):
  3022. yield util.Uninferable
  3023. return None
  3024. for index in self.slice.infer(context):
  3025. if isinstance(index, util.UninferableBase):
  3026. yield util.Uninferable
  3027. return None
  3028. # Try to deduce the index value.
  3029. index_value = self._SUBSCRIPT_SENTINEL
  3030. if value.__class__ == Instance:
  3031. index_value = index
  3032. elif index.__class__ == Instance:
  3033. instance_as_index = helpers.class_instance_as_index(index)
  3034. if instance_as_index:
  3035. index_value = instance_as_index
  3036. else:
  3037. index_value = index
  3038. if index_value is self._SUBSCRIPT_SENTINEL:
  3039. raise InferenceError(node=self, context=context)
  3040. try:
  3041. assigned = value.getitem(index_value, context)
  3042. except (
  3043. AstroidTypeError,
  3044. AstroidIndexError,
  3045. AstroidValueError,
  3046. AttributeInferenceError,
  3047. AttributeError,
  3048. ) as exc:
  3049. raise InferenceError(node=self, context=context) from exc
  3050. # Prevent inferring if the inferred subscript
  3051. # is the same as the original subscripted object.
  3052. if self is assigned or isinstance(assigned, util.UninferableBase):
  3053. yield util.Uninferable
  3054. return None
  3055. yield from assigned.infer(context)
  3056. found_one = True
  3057. if found_one:
  3058. return InferenceErrorInfo(node=self, context=context)
  3059. return None
  3060. @decorators.raise_if_nothing_inferred
  3061. @decorators.path_wrapper
  3062. def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
  3063. return self._infer_subscript(context, **kwargs)
  3064. @decorators.raise_if_nothing_inferred
  3065. def infer_lhs(self, context: InferenceContext | None = None, **kwargs: Any):
  3066. return self._infer_subscript(context, **kwargs)
  3067. class Try(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
  3068. """Class representing a :class:`ast.Try` node.
  3069. >>> import astroid
  3070. >>> node = astroid.extract_node('''
  3071. try:
  3072. do_something()
  3073. except Exception as error:
  3074. print("Error!")
  3075. finally:
  3076. print("Cleanup!")
  3077. ''')
  3078. >>> node
  3079. <Try l.2 at 0x7f23b2e41d68>
  3080. """
  3081. _astroid_fields = ("body", "handlers", "orelse", "finalbody")
  3082. _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
  3083. def __init__(
  3084. self,
  3085. *,
  3086. lineno: int,
  3087. col_offset: int,
  3088. end_lineno: int,
  3089. end_col_offset: int,
  3090. parent: NodeNG,
  3091. ) -> None:
  3092. """
  3093. :param lineno: The line that this node appears on in the source code.
  3094. :param col_offset: The column that this node appears on in the
  3095. source code.
  3096. :param parent: The parent node in the syntax tree.
  3097. :param end_lineno: The last line this node appears on in the source code.
  3098. :param end_col_offset: The end column this node appears on in the
  3099. source code. Note: This is after the last symbol.
  3100. """
  3101. self.body: list[NodeNG] = []
  3102. """The contents of the block to catch exceptions from."""
  3103. self.handlers: list[ExceptHandler] = []
  3104. """The exception handlers."""
  3105. self.orelse: list[NodeNG] = []
  3106. """The contents of the ``else`` block."""
  3107. self.finalbody: list[NodeNG] = []
  3108. """The contents of the ``finally`` block."""
  3109. super().__init__(
  3110. lineno=lineno,
  3111. col_offset=col_offset,
  3112. end_lineno=end_lineno,
  3113. end_col_offset=end_col_offset,
  3114. parent=parent,
  3115. )
  3116. def postinit(
  3117. self,
  3118. *,
  3119. body: list[NodeNG],
  3120. handlers: list[ExceptHandler],
  3121. orelse: list[NodeNG],
  3122. finalbody: list[NodeNG],
  3123. ) -> None:
  3124. """Do some setup after initialisation.
  3125. :param body: The contents of the block to catch exceptions from.
  3126. :param handlers: The exception handlers.
  3127. :param orelse: The contents of the ``else`` block.
  3128. :param finalbody: The contents of the ``finally`` block.
  3129. """
  3130. self.body = body
  3131. self.handlers = handlers
  3132. self.orelse = orelse
  3133. self.finalbody = finalbody
  3134. def _infer_name(self, frame, name):
  3135. return name
  3136. def block_range(self, lineno: int) -> tuple[int, int]:
  3137. """Get a range from a given line number to where this node ends."""
  3138. if lineno == self.fromlineno:
  3139. return lineno, lineno
  3140. if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
  3141. # Inside try body - return from lineno till end of try body
  3142. return lineno, self.body[-1].tolineno
  3143. for exhandler in self.handlers:
  3144. if exhandler.type and lineno == exhandler.type.fromlineno:
  3145. return lineno, lineno
  3146. if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
  3147. return lineno, exhandler.body[-1].tolineno
  3148. if self.orelse:
  3149. if self.orelse[0].fromlineno - 1 == lineno:
  3150. return lineno, lineno
  3151. if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
  3152. return lineno, self.orelse[-1].tolineno
  3153. if self.finalbody:
  3154. if self.finalbody[0].fromlineno - 1 == lineno:
  3155. return lineno, lineno
  3156. if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
  3157. return lineno, self.finalbody[-1].tolineno
  3158. return lineno, self.tolineno
  3159. def get_children(self):
  3160. yield from self.body
  3161. yield from self.handlers
  3162. yield from self.orelse
  3163. yield from self.finalbody
  3164. class TryStar(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
  3165. """Class representing an :class:`ast.TryStar` node."""
  3166. _astroid_fields = ("body", "handlers", "orelse", "finalbody")
  3167. _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
  3168. def __init__(
  3169. self,
  3170. *,
  3171. lineno: int | None = None,
  3172. col_offset: int | None = None,
  3173. end_lineno: int | None = None,
  3174. end_col_offset: int | None = None,
  3175. parent: NodeNG | None = None,
  3176. ) -> None:
  3177. """
  3178. :param lineno: The line that this node appears on in the source code.
  3179. :param col_offset: The column that this node appears on in the
  3180. source code.
  3181. :param parent: The parent node in the syntax tree.
  3182. :param end_lineno: The last line this node appears on in the source code.
  3183. :param end_col_offset: The end column this node appears on in the
  3184. source code. Note: This is after the last symbol.
  3185. """
  3186. self.body: list[NodeNG] = []
  3187. """The contents of the block to catch exceptions from."""
  3188. self.handlers: list[ExceptHandler] = []
  3189. """The exception handlers."""
  3190. self.orelse: list[NodeNG] = []
  3191. """The contents of the ``else`` block."""
  3192. self.finalbody: list[NodeNG] = []
  3193. """The contents of the ``finally`` block."""
  3194. super().__init__(
  3195. lineno=lineno,
  3196. col_offset=col_offset,
  3197. end_lineno=end_lineno,
  3198. end_col_offset=end_col_offset,
  3199. parent=parent,
  3200. )
  3201. def postinit(
  3202. self,
  3203. *,
  3204. body: list[NodeNG] | None = None,
  3205. handlers: list[ExceptHandler] | None = None,
  3206. orelse: list[NodeNG] | None = None,
  3207. finalbody: list[NodeNG] | None = None,
  3208. ) -> None:
  3209. """Do some setup after initialisation.
  3210. :param body: The contents of the block to catch exceptions from.
  3211. :param handlers: The exception handlers.
  3212. :param orelse: The contents of the ``else`` block.
  3213. :param finalbody: The contents of the ``finally`` block.
  3214. """
  3215. if body:
  3216. self.body = body
  3217. if handlers:
  3218. self.handlers = handlers
  3219. if orelse:
  3220. self.orelse = orelse
  3221. if finalbody:
  3222. self.finalbody = finalbody
  3223. def _infer_name(self, frame, name):
  3224. return name
  3225. def block_range(self, lineno: int) -> tuple[int, int]:
  3226. """Get a range from a given line number to where this node ends."""
  3227. if lineno == self.fromlineno:
  3228. return lineno, lineno
  3229. if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
  3230. # Inside try body - return from lineno till end of try body
  3231. return lineno, self.body[-1].tolineno
  3232. for exhandler in self.handlers:
  3233. if exhandler.type and lineno == exhandler.type.fromlineno:
  3234. return lineno, lineno
  3235. if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
  3236. return lineno, exhandler.body[-1].tolineno
  3237. if self.orelse:
  3238. if self.orelse[0].fromlineno - 1 == lineno:
  3239. return lineno, lineno
  3240. if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
  3241. return lineno, self.orelse[-1].tolineno
  3242. if self.finalbody:
  3243. if self.finalbody[0].fromlineno - 1 == lineno:
  3244. return lineno, lineno
  3245. if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
  3246. return lineno, self.finalbody[-1].tolineno
  3247. return lineno, self.tolineno
  3248. def get_children(self):
  3249. yield from self.body
  3250. yield from self.handlers
  3251. yield from self.orelse
  3252. yield from self.finalbody
  3253. class Tuple(BaseContainer):
  3254. """Class representing an :class:`ast.Tuple` node.
  3255. >>> import astroid
  3256. >>> node = astroid.extract_node('(1, 2, 3)')
  3257. >>> node
  3258. <Tuple.tuple l.1 at 0x7f23b2e41780>
  3259. """
  3260. _other_fields = ("ctx",)
  3261. def __init__(
  3262. self,
  3263. ctx: Context | None = None,
  3264. lineno: int | None = None,
  3265. col_offset: int | None = None,
  3266. parent: NodeNG | None = None,
  3267. *,
  3268. end_lineno: int | None = None,
  3269. end_col_offset: int | None = None,
  3270. ) -> None:
  3271. """
  3272. :param ctx: Whether the tuple is assigned to or loaded from.
  3273. :param lineno: The line that this node appears on in the source code.
  3274. :param col_offset: The column that this node appears on in the
  3275. source code.
  3276. :param parent: The parent node in the syntax tree.
  3277. :param end_lineno: The last line this node appears on in the source code.
  3278. :param end_col_offset: The end column this node appears on in the
  3279. source code. Note: This is after the last symbol.
  3280. """
  3281. self.ctx: Context | None = ctx
  3282. """Whether the tuple is assigned to or loaded from."""
  3283. super().__init__(
  3284. lineno=lineno,
  3285. col_offset=col_offset,
  3286. end_lineno=end_lineno,
  3287. end_col_offset=end_col_offset,
  3288. parent=parent,
  3289. )
  3290. assigned_stmts = protocols.sequence_assigned_stmts
  3291. """Returns the assigned statement (non inferred) according to the assignment type.
  3292. See astroid/protocols.py for actual implementation.
  3293. """
  3294. infer_unary_op = protocols.tuple_infer_unary_op
  3295. infer_binary_op = protocols.tl_infer_binary_op
  3296. def pytype(self) -> Literal["builtins.tuple"]:
  3297. """Get the name of the type that this node represents.
  3298. :returns: The name of the type.
  3299. """
  3300. return "builtins.tuple"
  3301. def getitem(self, index, context: InferenceContext | None = None):
  3302. """Get an item from this node.
  3303. :param index: The node to use as a subscript index.
  3304. :type index: Const or Slice
  3305. """
  3306. return _container_getitem(self, self.elts, index, context=context)
  3307. class TypeAlias(_base_nodes.AssignTypeNode, _base_nodes.Statement):
  3308. """Class representing a :class:`ast.TypeAlias` node.
  3309. >>> import astroid
  3310. >>> node = astroid.extract_node('type Point = tuple[float, float]')
  3311. >>> node
  3312. <TypeAlias l.1 at 0x7f23b2e4e198>
  3313. """
  3314. _astroid_fields = ("name", "type_params", "value")
  3315. name: AssignName
  3316. type_params: list[TypeVar | ParamSpec | TypeVarTuple]
  3317. value: NodeNG
  3318. def __init__(
  3319. self,
  3320. lineno: int,
  3321. col_offset: int,
  3322. parent: NodeNG,
  3323. *,
  3324. end_lineno: int,
  3325. end_col_offset: int,
  3326. ) -> None:
  3327. super().__init__(
  3328. lineno=lineno,
  3329. col_offset=col_offset,
  3330. end_lineno=end_lineno,
  3331. end_col_offset=end_col_offset,
  3332. parent=parent,
  3333. )
  3334. def postinit(
  3335. self,
  3336. *,
  3337. name: AssignName,
  3338. type_params: list[TypeVar | ParamSpec | TypeVarTuple],
  3339. value: NodeNG,
  3340. ) -> None:
  3341. self.name = name
  3342. self.type_params = type_params
  3343. self.value = value
  3344. def _infer(
  3345. self, context: InferenceContext | None = None, **kwargs: Any
  3346. ) -> Iterator[TypeAlias]:
  3347. yield self
  3348. assigned_stmts: ClassVar[
  3349. Callable[
  3350. [
  3351. TypeAlias,
  3352. AssignName,
  3353. InferenceContext | None,
  3354. None,
  3355. ],
  3356. Generator[NodeNG],
  3357. ]
  3358. ] = protocols.assign_assigned_stmts
  3359. class TypeVar(_base_nodes.AssignTypeNode):
  3360. """Class representing a :class:`ast.TypeVar` node.
  3361. >>> import astroid
  3362. >>> node = astroid.extract_node('type Point[T] = tuple[float, float]')
  3363. >>> node.type_params[0]
  3364. <TypeVar l.1 at 0x7f23b2e4e198>
  3365. """
  3366. _astroid_fields = ("name", "bound", "default_value")
  3367. name: AssignName
  3368. bound: NodeNG | None
  3369. default_value: NodeNG | None
  3370. def __init__(
  3371. self,
  3372. lineno: int,
  3373. col_offset: int,
  3374. parent: NodeNG,
  3375. *,
  3376. end_lineno: int,
  3377. end_col_offset: int,
  3378. ) -> None:
  3379. super().__init__(
  3380. lineno=lineno,
  3381. col_offset=col_offset,
  3382. end_lineno=end_lineno,
  3383. end_col_offset=end_col_offset,
  3384. parent=parent,
  3385. )
  3386. def postinit(
  3387. self,
  3388. *,
  3389. name: AssignName,
  3390. bound: NodeNG | None,
  3391. default_value: NodeNG | None = None,
  3392. ) -> None:
  3393. self.name = name
  3394. self.bound = bound
  3395. self.default_value = default_value
  3396. def _infer(
  3397. self, context: InferenceContext | None = None, **kwargs: Any
  3398. ) -> Iterator[TypeVar]:
  3399. yield self
  3400. assigned_stmts = protocols.generic_type_assigned_stmts
  3401. """Returns the assigned statement (non inferred) according to the assignment type.
  3402. See astroid/protocols.py for actual implementation.
  3403. """
  3404. class TypeVarTuple(_base_nodes.AssignTypeNode):
  3405. """Class representing a :class:`ast.TypeVarTuple` node.
  3406. >>> import astroid
  3407. >>> node = astroid.extract_node('type Alias[*Ts] = tuple[*Ts]')
  3408. >>> node.type_params[0]
  3409. <TypeVarTuple l.1 at 0x7f23b2e4e198>
  3410. """
  3411. _astroid_fields = ("name", "default_value")
  3412. name: AssignName
  3413. default_value: NodeNG | None
  3414. def __init__(
  3415. self,
  3416. lineno: int,
  3417. col_offset: int,
  3418. parent: NodeNG,
  3419. *,
  3420. end_lineno: int,
  3421. end_col_offset: int,
  3422. ) -> None:
  3423. super().__init__(
  3424. lineno=lineno,
  3425. col_offset=col_offset,
  3426. end_lineno=end_lineno,
  3427. end_col_offset=end_col_offset,
  3428. parent=parent,
  3429. )
  3430. def postinit(
  3431. self, *, name: AssignName, default_value: NodeNG | None = None
  3432. ) -> None:
  3433. self.name = name
  3434. self.default_value = default_value
  3435. def _infer(
  3436. self, context: InferenceContext | None = None, **kwargs: Any
  3437. ) -> Iterator[TypeVarTuple]:
  3438. yield self
  3439. assigned_stmts = protocols.generic_type_assigned_stmts
  3440. """Returns the assigned statement (non inferred) according to the assignment type.
  3441. See astroid/protocols.py for actual implementation.
  3442. """
  3443. UNARY_OP_METHOD = {
  3444. "+": "__pos__",
  3445. "-": "__neg__",
  3446. "~": "__invert__",
  3447. "not": None, # XXX not '__nonzero__'
  3448. }
  3449. class UnaryOp(_base_nodes.OperatorNode):
  3450. """Class representing an :class:`ast.UnaryOp` node.
  3451. >>> import astroid
  3452. >>> node = astroid.extract_node('-5')
  3453. >>> node
  3454. <UnaryOp l.1 at 0x7f23b2e4e198>
  3455. """
  3456. _astroid_fields = ("operand",)
  3457. _other_fields = ("op",)
  3458. operand: NodeNG
  3459. """What the unary operator is applied to."""
  3460. def __init__(
  3461. self,
  3462. op: str,
  3463. lineno: int,
  3464. col_offset: int,
  3465. parent: NodeNG,
  3466. *,
  3467. end_lineno: int | None,
  3468. end_col_offset: int | None,
  3469. ) -> None:
  3470. self.op = op
  3471. """The operator."""
  3472. super().__init__(
  3473. lineno=lineno,
  3474. col_offset=col_offset,
  3475. end_lineno=end_lineno,
  3476. end_col_offset=end_col_offset,
  3477. parent=parent,
  3478. )
  3479. def postinit(self, operand: NodeNG) -> None:
  3480. self.operand = operand
  3481. def type_errors(
  3482. self, context: InferenceContext | None = None
  3483. ) -> list[util.BadUnaryOperationMessage]:
  3484. """Get a list of type errors which can occur during inference.
  3485. Each TypeError is represented by a :class:`BadUnaryOperationMessage`,
  3486. which holds the original exception.
  3487. If any inferred result is uninferable, an empty list is returned.
  3488. """
  3489. bad = []
  3490. try:
  3491. for result in self._infer_unaryop(context=context):
  3492. if result is util.Uninferable:
  3493. raise InferenceError
  3494. if isinstance(result, util.BadUnaryOperationMessage):
  3495. bad.append(result)
  3496. except InferenceError:
  3497. return []
  3498. return bad
  3499. def get_children(self):
  3500. yield self.operand
  3501. def op_precedence(self) -> int:
  3502. if self.op == "not":
  3503. return OP_PRECEDENCE[self.op]
  3504. return super().op_precedence()
  3505. def _infer_unaryop(
  3506. self, context: InferenceContext | None = None, **kwargs: Any
  3507. ) -> Generator[
  3508. InferenceResult | util.BadUnaryOperationMessage, None, InferenceErrorInfo
  3509. ]:
  3510. """Infer what an UnaryOp should return when evaluated."""
  3511. from astroid.nodes import ClassDef # pylint: disable=import-outside-toplevel
  3512. for operand in self.operand.infer(context):
  3513. try:
  3514. yield operand.infer_unary_op(self.op)
  3515. except TypeError as exc:
  3516. # The operand doesn't support this operation.
  3517. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  3518. except AttributeError as exc:
  3519. meth = UNARY_OP_METHOD[self.op]
  3520. if meth is None:
  3521. # `not node`. Determine node's boolean
  3522. # value and negate its result, unless it is
  3523. # Uninferable, which will be returned as is.
  3524. bool_value = operand.bool_value()
  3525. if not isinstance(bool_value, util.UninferableBase):
  3526. yield const_factory(not bool_value)
  3527. else:
  3528. yield util.Uninferable
  3529. else:
  3530. if not isinstance(operand, (Instance, ClassDef)):
  3531. # The operation was used on something which
  3532. # doesn't support it.
  3533. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  3534. continue
  3535. try:
  3536. try:
  3537. methods = dunder_lookup.lookup(operand, meth)
  3538. except AttributeInferenceError:
  3539. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  3540. continue
  3541. meth = methods[0]
  3542. inferred = next(meth.infer(context=context), None)
  3543. if (
  3544. isinstance(inferred, util.UninferableBase)
  3545. or not inferred.callable()
  3546. ):
  3547. continue
  3548. context = copy_context(context)
  3549. context.boundnode = operand
  3550. context.callcontext = CallContext(args=[], callee=inferred)
  3551. call_results = inferred.infer_call_result(self, context=context)
  3552. result = next(call_results, None)
  3553. if result is None:
  3554. # Failed to infer, return the same type.
  3555. yield operand
  3556. else:
  3557. yield result
  3558. except AttributeInferenceError as inner_exc:
  3559. # The unary operation special method was not found.
  3560. yield util.BadUnaryOperationMessage(operand, self.op, inner_exc)
  3561. except InferenceError:
  3562. yield util.Uninferable
  3563. @decorators.raise_if_nothing_inferred
  3564. @decorators.path_wrapper
  3565. def _infer(
  3566. self, context: InferenceContext | None = None, **kwargs: Any
  3567. ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
  3568. """Infer what an UnaryOp should return when evaluated."""
  3569. yield from self._filter_operation_errors(
  3570. self._infer_unaryop, context, util.BadUnaryOperationMessage
  3571. )
  3572. return InferenceErrorInfo(node=self, context=context)
  3573. class While(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
  3574. """Class representing an :class:`ast.While` node.
  3575. >>> import astroid
  3576. >>> node = astroid.extract_node('''
  3577. while condition():
  3578. print("True")
  3579. ''')
  3580. >>> node
  3581. <While l.2 at 0x7f23b2e4e390>
  3582. """
  3583. _astroid_fields = ("test", "body", "orelse")
  3584. _multi_line_block_fields = ("body", "orelse")
  3585. test: NodeNG
  3586. """The condition that the loop tests."""
  3587. body: list[NodeNG]
  3588. """The contents of the loop."""
  3589. orelse: list[NodeNG]
  3590. """The contents of the ``else`` block."""
  3591. def postinit(
  3592. self,
  3593. test: NodeNG,
  3594. body: list[NodeNG],
  3595. orelse: list[NodeNG],
  3596. ) -> None:
  3597. self.test = test
  3598. self.body = body
  3599. self.orelse = orelse
  3600. @cached_property
  3601. def blockstart_tolineno(self):
  3602. """The line on which the beginning of this block ends.
  3603. :type: int
  3604. """
  3605. return self.test.tolineno
  3606. def block_range(self, lineno: int) -> tuple[int, int]:
  3607. """Get a range from the given line number to where this node ends.
  3608. :param lineno: The line number to start the range at.
  3609. :returns: The range of line numbers that this node belongs to,
  3610. starting at the given line number.
  3611. """
  3612. return self._elsed_block_range(lineno, self.orelse)
  3613. def get_children(self):
  3614. yield self.test
  3615. yield from self.body
  3616. yield from self.orelse
  3617. def _get_yield_nodes_skip_functions(self):
  3618. """A While node can contain a Yield node in the test"""
  3619. yield from self.test._get_yield_nodes_skip_functions()
  3620. yield from super()._get_yield_nodes_skip_functions()
  3621. def _get_yield_nodes_skip_lambdas(self):
  3622. """A While node can contain a Yield node in the test"""
  3623. yield from self.test._get_yield_nodes_skip_lambdas()
  3624. yield from super()._get_yield_nodes_skip_lambdas()
  3625. class With(
  3626. _base_nodes.MultiLineWithElseBlockNode,
  3627. _base_nodes.AssignTypeNode,
  3628. _base_nodes.Statement,
  3629. ):
  3630. """Class representing an :class:`ast.With` node.
  3631. >>> import astroid
  3632. >>> node = astroid.extract_node('''
  3633. with open(file_path) as file_:
  3634. print(file_.read())
  3635. ''')
  3636. >>> node
  3637. <With l.2 at 0x7f23b2e4e710>
  3638. """
  3639. _astroid_fields = ("items", "body")
  3640. _other_other_fields = ("type_annotation",)
  3641. _multi_line_block_fields = ("body",)
  3642. def __init__(
  3643. self,
  3644. lineno: int | None = None,
  3645. col_offset: int | None = None,
  3646. parent: NodeNG | None = None,
  3647. *,
  3648. end_lineno: int | None = None,
  3649. end_col_offset: int | None = None,
  3650. ) -> None:
  3651. """
  3652. :param lineno: The line that this node appears on in the source code.
  3653. :param col_offset: The column that this node appears on in the
  3654. source code.
  3655. :param parent: The parent node in the syntax tree.
  3656. :param end_lineno: The last line this node appears on in the source code.
  3657. :param end_col_offset: The end column this node appears on in the
  3658. source code. Note: This is after the last symbol.
  3659. """
  3660. self.items: list[tuple[NodeNG, NodeNG | None]] = []
  3661. """The pairs of context managers and the names they are assigned to."""
  3662. self.body: list[NodeNG] = []
  3663. """The contents of the ``with`` block."""
  3664. self.type_annotation: NodeNG | None = None # can be None
  3665. """If present, this will contain the type annotation passed by a type comment"""
  3666. super().__init__(
  3667. lineno=lineno,
  3668. col_offset=col_offset,
  3669. end_lineno=end_lineno,
  3670. end_col_offset=end_col_offset,
  3671. parent=parent,
  3672. )
  3673. def postinit(
  3674. self,
  3675. items: list[tuple[NodeNG, NodeNG | None]] | None = None,
  3676. body: list[NodeNG] | None = None,
  3677. type_annotation: NodeNG | None = None,
  3678. ) -> None:
  3679. """Do some setup after initialisation.
  3680. :param items: The pairs of context managers and the names
  3681. they are assigned to.
  3682. :param body: The contents of the ``with`` block.
  3683. """
  3684. if items is not None:
  3685. self.items = items
  3686. if body is not None:
  3687. self.body = body
  3688. self.type_annotation = type_annotation
  3689. assigned_stmts = protocols.with_assigned_stmts
  3690. """Returns the assigned statement (non inferred) according to the assignment type.
  3691. See astroid/protocols.py for actual implementation.
  3692. """
  3693. @cached_property
  3694. def blockstart_tolineno(self):
  3695. """The line on which the beginning of this block ends.
  3696. :type: int
  3697. """
  3698. return self.items[-1][0].tolineno
  3699. def get_children(self):
  3700. """Get the child nodes below this node.
  3701. :returns: The children.
  3702. :rtype: iterable(NodeNG)
  3703. """
  3704. for expr, var in self.items:
  3705. yield expr
  3706. if var:
  3707. yield var
  3708. yield from self.body
  3709. class AsyncWith(With):
  3710. """Asynchronous ``with`` built with the ``async`` keyword."""
  3711. class Yield(NodeNG):
  3712. """Class representing an :class:`ast.Yield` node.
  3713. >>> import astroid
  3714. >>> node = astroid.extract_node('yield True')
  3715. >>> node
  3716. <Yield l.1 at 0x7f23b2e4e5f8>
  3717. """
  3718. _astroid_fields = ("value",)
  3719. value: NodeNG | None
  3720. """The value to yield."""
  3721. def postinit(self, value: NodeNG | None) -> None:
  3722. self.value = value
  3723. def get_children(self):
  3724. if self.value is not None:
  3725. yield self.value
  3726. def _get_yield_nodes_skip_functions(self):
  3727. yield self
  3728. def _get_yield_nodes_skip_lambdas(self):
  3729. yield self
  3730. class YieldFrom(Yield): # TODO value is required, not optional
  3731. """Class representing an :class:`ast.YieldFrom` node."""
  3732. class DictUnpack(_base_nodes.NoChildrenNode):
  3733. """Represents the unpacking of dicts into dicts using :pep:`448`."""
  3734. class FormattedValue(NodeNG):
  3735. """Class representing an :class:`ast.FormattedValue` node.
  3736. Represents a :pep:`498` format string.
  3737. >>> import astroid
  3738. >>> node = astroid.extract_node('f"Format {type_}"')
  3739. >>> node
  3740. <JoinedStr l.1 at 0x7f23b2e4ed30>
  3741. >>> node.values
  3742. [<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>]
  3743. """
  3744. _astroid_fields = ("value", "format_spec")
  3745. _other_fields = ("conversion",)
  3746. def __init__(
  3747. self,
  3748. lineno: int | None = None,
  3749. col_offset: int | None = None,
  3750. parent: NodeNG | None = None,
  3751. *,
  3752. end_lineno: int | None = None,
  3753. end_col_offset: int | None = None,
  3754. ) -> None:
  3755. """
  3756. :param lineno: The line that this node appears on in the source code.
  3757. :param col_offset: The column that this node appears on in the
  3758. source code.
  3759. :param parent: The parent node in the syntax tree.
  3760. :param end_lineno: The last line this node appears on in the source code.
  3761. :param end_col_offset: The end column this node appears on in the
  3762. source code. Note: This is after the last symbol.
  3763. """
  3764. self.value: NodeNG
  3765. """The value to be formatted into the string."""
  3766. self.conversion: int
  3767. """The type of formatting to be applied to the value.
  3768. .. seealso::
  3769. :class:`ast.FormattedValue`
  3770. """
  3771. self.format_spec: JoinedStr | None = None
  3772. """The formatting to be applied to the value.
  3773. .. seealso::
  3774. :class:`ast.FormattedValue`
  3775. """
  3776. super().__init__(
  3777. lineno=lineno,
  3778. col_offset=col_offset,
  3779. end_lineno=end_lineno,
  3780. end_col_offset=end_col_offset,
  3781. parent=parent,
  3782. )
  3783. def postinit(
  3784. self,
  3785. *,
  3786. value: NodeNG,
  3787. conversion: int,
  3788. format_spec: JoinedStr | None = None,
  3789. ) -> None:
  3790. """Do some setup after initialisation.
  3791. :param value: The value to be formatted into the string.
  3792. :param conversion: The type of formatting to be applied to the value.
  3793. :param format_spec: The formatting to be applied to the value.
  3794. :type format_spec: JoinedStr or None
  3795. """
  3796. self.value = value
  3797. self.conversion = conversion
  3798. self.format_spec = format_spec
  3799. def get_children(self):
  3800. yield self.value
  3801. if self.format_spec is not None:
  3802. yield self.format_spec
  3803. def _infer(
  3804. self, context: InferenceContext | None = None, **kwargs: Any
  3805. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  3806. format_specs = Const("") if self.format_spec is None else self.format_spec
  3807. uninferable_already_generated = False
  3808. for format_spec in format_specs.infer(context, **kwargs):
  3809. if not isinstance(format_spec, Const):
  3810. if not uninferable_already_generated:
  3811. yield util.Uninferable
  3812. uninferable_already_generated = True
  3813. continue
  3814. for value in self.value.infer(context, **kwargs):
  3815. value_to_format = value
  3816. if isinstance(value, Const):
  3817. value_to_format = value.value
  3818. try:
  3819. formatted = format(value_to_format, format_spec.value)
  3820. yield Const(
  3821. formatted,
  3822. lineno=self.lineno,
  3823. col_offset=self.col_offset,
  3824. end_lineno=self.end_lineno,
  3825. end_col_offset=self.end_col_offset,
  3826. )
  3827. continue
  3828. except (ValueError, TypeError):
  3829. # happens when format_spec.value is invalid
  3830. yield util.Uninferable
  3831. uninferable_already_generated = True
  3832. continue
  3833. UNINFERABLE_VALUE = "{Uninferable}"
  3834. class JoinedStr(NodeNG):
  3835. """Represents a list of string expressions to be joined.
  3836. >>> import astroid
  3837. >>> node = astroid.extract_node('f"Format {type_}"')
  3838. >>> node
  3839. <JoinedStr l.1 at 0x7f23b2e4ed30>
  3840. """
  3841. _astroid_fields = ("values",)
  3842. def __init__(
  3843. self,
  3844. lineno: int | None = None,
  3845. col_offset: int | None = None,
  3846. parent: NodeNG | None = None,
  3847. *,
  3848. end_lineno: int | None = None,
  3849. end_col_offset: int | None = None,
  3850. ) -> None:
  3851. """
  3852. :param lineno: The line that this node appears on in the source code.
  3853. :param col_offset: The column that this node appears on in the
  3854. source code.
  3855. :param parent: The parent node in the syntax tree.
  3856. :param end_lineno: The last line this node appears on in the source code.
  3857. :param end_col_offset: The end column this node appears on in the
  3858. source code. Note: This is after the last symbol.
  3859. """
  3860. self.values: list[NodeNG] = []
  3861. """The string expressions to be joined.
  3862. :type: list(FormattedValue or Const)
  3863. """
  3864. super().__init__(
  3865. lineno=lineno,
  3866. col_offset=col_offset,
  3867. end_lineno=end_lineno,
  3868. end_col_offset=end_col_offset,
  3869. parent=parent,
  3870. )
  3871. def postinit(self, values: list[NodeNG] | None = None) -> None:
  3872. """Do some setup after initialisation.
  3873. :param value: The string expressions to be joined.
  3874. :type: list(FormattedValue or Const)
  3875. """
  3876. if values is not None:
  3877. self.values = values
  3878. def get_children(self):
  3879. yield from self.values
  3880. def _infer(
  3881. self, context: InferenceContext | None = None, **kwargs: Any
  3882. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  3883. if self.values:
  3884. yield from self._infer_with_values(context)
  3885. else:
  3886. yield Const("")
  3887. def _infer_with_values(
  3888. self, context: InferenceContext | None = None, **kwargs: Any
  3889. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  3890. uninferable_already_generated = False
  3891. for inferred in self._infer_from_values(self.values, context):
  3892. failed = inferred is util.Uninferable or (
  3893. isinstance(inferred, Const) and UNINFERABLE_VALUE in inferred.value
  3894. )
  3895. if failed:
  3896. if not uninferable_already_generated:
  3897. uninferable_already_generated = True
  3898. yield util.Uninferable
  3899. continue
  3900. yield inferred
  3901. @classmethod
  3902. def _infer_from_values(
  3903. cls, nodes: list[NodeNG], context: InferenceContext | None = None, **kwargs: Any
  3904. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  3905. if not nodes:
  3906. return
  3907. if len(nodes) == 1:
  3908. for node in cls._safe_infer_from_node(nodes[0], context, **kwargs):
  3909. if isinstance(node, Const):
  3910. yield node
  3911. continue
  3912. yield Const(UNINFERABLE_VALUE)
  3913. return
  3914. for prefix in cls._safe_infer_from_node(nodes[0], context, **kwargs):
  3915. for suffix in cls._infer_from_values(nodes[1:], context, **kwargs):
  3916. result = ""
  3917. for node in (prefix, suffix):
  3918. if isinstance(node, Const):
  3919. result += str(node.value)
  3920. continue
  3921. result += UNINFERABLE_VALUE
  3922. yield Const(result)
  3923. @classmethod
  3924. def _safe_infer_from_node(
  3925. cls, node: NodeNG, context: InferenceContext | None = None, **kwargs: Any
  3926. ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
  3927. try:
  3928. yield from node._infer(context, **kwargs)
  3929. except InferenceError:
  3930. yield util.Uninferable
  3931. class NamedExpr(_base_nodes.AssignTypeNode):
  3932. """Represents the assignment from the assignment expression
  3933. >>> import astroid
  3934. >>> module = astroid.parse('if a := 1: pass')
  3935. >>> module.body[0].test
  3936. <NamedExpr l.1 at 0x7f23b2e4ed30>
  3937. """
  3938. _astroid_fields = ("target", "value")
  3939. optional_assign = True
  3940. """Whether this node optionally assigns a variable.
  3941. Since NamedExpr are not always called they do not always assign."""
  3942. def __init__(
  3943. self,
  3944. lineno: int | None = None,
  3945. col_offset: int | None = None,
  3946. parent: NodeNG | None = None,
  3947. *,
  3948. end_lineno: int | None = None,
  3949. end_col_offset: int | None = None,
  3950. ) -> None:
  3951. """
  3952. :param lineno: The line that this node appears on in the source code.
  3953. :param col_offset: The column that this node appears on in the
  3954. source code.
  3955. :param parent: The parent node in the syntax tree.
  3956. :param end_lineno: The last line this node appears on in the source code.
  3957. :param end_col_offset: The end column this node appears on in the
  3958. source code. Note: This is after the last symbol.
  3959. """
  3960. self.target: NodeNG
  3961. """The assignment target
  3962. :type: Name
  3963. """
  3964. self.value: NodeNG
  3965. """The value that gets assigned in the expression"""
  3966. super().__init__(
  3967. lineno=lineno,
  3968. col_offset=col_offset,
  3969. end_lineno=end_lineno,
  3970. end_col_offset=end_col_offset,
  3971. parent=parent,
  3972. )
  3973. def postinit(self, target: NodeNG, value: NodeNG) -> None:
  3974. self.target = target
  3975. self.value = value
  3976. assigned_stmts = protocols.named_expr_assigned_stmts
  3977. """Returns the assigned statement (non inferred) according to the assignment type.
  3978. See astroid/protocols.py for actual implementation.
  3979. """
  3980. def frame(self) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
  3981. """The first parent frame node.
  3982. A frame node is a :class:`Module`, :class:`FunctionDef`,
  3983. or :class:`ClassDef`.
  3984. :returns: The first parent frame node.
  3985. """
  3986. if not self.parent:
  3987. raise ParentMissingError(target=self)
  3988. # For certain parents NamedExpr evaluate to the scope of the parent
  3989. if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
  3990. if not self.parent.parent:
  3991. raise ParentMissingError(target=self.parent)
  3992. if not self.parent.parent.parent:
  3993. raise ParentMissingError(target=self.parent.parent)
  3994. return self.parent.parent.parent.frame()
  3995. return self.parent.frame()
  3996. def scope(self) -> LocalsDictNodeNG:
  3997. """The first parent node defining a new scope.
  3998. These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
  3999. :returns: The first parent scope node.
  4000. """
  4001. if not self.parent:
  4002. raise ParentMissingError(target=self)
  4003. # For certain parents NamedExpr evaluate to the scope of the parent
  4004. if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
  4005. if not self.parent.parent:
  4006. raise ParentMissingError(target=self.parent)
  4007. if not self.parent.parent.parent:
  4008. raise ParentMissingError(target=self.parent.parent)
  4009. return self.parent.parent.parent.scope()
  4010. return self.parent.scope()
  4011. def set_local(self, name: str, stmt: NodeNG) -> None:
  4012. """Define that the given name is declared in the given statement node.
  4013. NamedExpr's in Arguments, Keyword or Comprehension are evaluated in their
  4014. parent's parent scope. So we add to their frame's locals.
  4015. .. seealso:: :meth:`scope`
  4016. :param name: The name that is being defined.
  4017. :param stmt: The statement that defines the given name.
  4018. """
  4019. self.frame().set_local(name, stmt)
  4020. class Unknown(_base_nodes.AssignTypeNode):
  4021. """This node represents a node in a constructed AST where
  4022. introspection is not possible. At the moment, it's only used in
  4023. the args attribute of FunctionDef nodes where function signature
  4024. introspection failed.
  4025. """
  4026. name = "Unknown"
  4027. def __init__(
  4028. self,
  4029. parent: NodeNG,
  4030. lineno: None = None,
  4031. col_offset: None = None,
  4032. *,
  4033. end_lineno: None = None,
  4034. end_col_offset: None = None,
  4035. ) -> None:
  4036. super().__init__(
  4037. lineno=lineno,
  4038. col_offset=col_offset,
  4039. end_lineno=end_lineno,
  4040. end_col_offset=end_col_offset,
  4041. parent=parent,
  4042. )
  4043. def qname(self) -> Literal["Unknown"]:
  4044. return "Unknown"
  4045. def _infer(self, context: InferenceContext | None = None, **kwargs):
  4046. """Inference on an Unknown node immediately terminates."""
  4047. yield util.Uninferable
  4048. UNATTACHED_UNKNOWN = Unknown(parent=SYNTHETIC_ROOT)
  4049. class EvaluatedObject(NodeNG):
  4050. """Contains an object that has already been inferred
  4051. This class is useful to pre-evaluate a particular node,
  4052. with the resulting class acting as the non-evaluated node.
  4053. """
  4054. name = "EvaluatedObject"
  4055. _astroid_fields = ("original",)
  4056. _other_fields = ("value",)
  4057. def __init__(
  4058. self, original: SuccessfulInferenceResult, value: InferenceResult
  4059. ) -> None:
  4060. self.original: SuccessfulInferenceResult = original
  4061. """The original node that has already been evaluated"""
  4062. self.value: InferenceResult = value
  4063. """The inferred value"""
  4064. super().__init__(
  4065. lineno=self.original.lineno,
  4066. col_offset=self.original.col_offset,
  4067. parent=self.original.parent,
  4068. end_lineno=self.original.end_lineno,
  4069. end_col_offset=self.original.end_col_offset,
  4070. )
  4071. def _infer(
  4072. self, context: InferenceContext | None = None, **kwargs: Any
  4073. ) -> Generator[NodeNG | util.UninferableBase]:
  4074. yield self.value
  4075. # Pattern matching #######################################################
  4076. class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode):
  4077. """Class representing a :class:`ast.Match` node.
  4078. >>> import astroid
  4079. >>> node = astroid.extract_node('''
  4080. match x:
  4081. case 200:
  4082. ...
  4083. case _:
  4084. ...
  4085. ''')
  4086. >>> node
  4087. <Match l.2 at 0x10c24e170>
  4088. """
  4089. _astroid_fields = ("subject", "cases")
  4090. _multi_line_block_fields = ("cases",)
  4091. def __init__(
  4092. self,
  4093. lineno: int | None = None,
  4094. col_offset: int | None = None,
  4095. parent: NodeNG | None = None,
  4096. *,
  4097. end_lineno: int | None = None,
  4098. end_col_offset: int | None = None,
  4099. ) -> None:
  4100. self.subject: NodeNG
  4101. self.cases: list[MatchCase]
  4102. super().__init__(
  4103. lineno=lineno,
  4104. col_offset=col_offset,
  4105. end_lineno=end_lineno,
  4106. end_col_offset=end_col_offset,
  4107. parent=parent,
  4108. )
  4109. def postinit(
  4110. self,
  4111. *,
  4112. subject: NodeNG,
  4113. cases: list[MatchCase],
  4114. ) -> None:
  4115. self.subject = subject
  4116. self.cases = cases
  4117. class Pattern(NodeNG):
  4118. """Base class for all Pattern nodes."""
  4119. class MatchCase(_base_nodes.MultiLineBlockNode):
  4120. """Class representing a :class:`ast.match_case` node.
  4121. >>> import astroid
  4122. >>> node = astroid.extract_node('''
  4123. match x:
  4124. case 200:
  4125. ...
  4126. ''')
  4127. >>> node.cases[0]
  4128. <MatchCase l.3 at 0x10c24e590>
  4129. """
  4130. _astroid_fields = ("pattern", "guard", "body")
  4131. _multi_line_block_fields = ("body",)
  4132. lineno: None
  4133. col_offset: None
  4134. end_lineno: None
  4135. end_col_offset: None
  4136. def __init__(self, *, parent: NodeNG | None = None) -> None:
  4137. self.pattern: Pattern
  4138. self.guard: NodeNG | None
  4139. self.body: list[NodeNG]
  4140. super().__init__(
  4141. parent=parent,
  4142. lineno=None,
  4143. col_offset=None,
  4144. end_lineno=None,
  4145. end_col_offset=None,
  4146. )
  4147. def postinit(
  4148. self,
  4149. *,
  4150. pattern: Pattern,
  4151. guard: NodeNG | None,
  4152. body: list[NodeNG],
  4153. ) -> None:
  4154. self.pattern = pattern
  4155. self.guard = guard
  4156. self.body = body
  4157. class MatchValue(Pattern):
  4158. """Class representing a :class:`ast.MatchValue` node.
  4159. >>> import astroid
  4160. >>> node = astroid.extract_node('''
  4161. match x:
  4162. case 200:
  4163. ...
  4164. ''')
  4165. >>> node.cases[0].pattern
  4166. <MatchValue l.3 at 0x10c24e200>
  4167. """
  4168. _astroid_fields = ("value",)
  4169. def __init__(
  4170. self,
  4171. lineno: int | None = None,
  4172. col_offset: int | None = None,
  4173. parent: NodeNG | None = None,
  4174. *,
  4175. end_lineno: int | None = None,
  4176. end_col_offset: int | None = None,
  4177. ) -> None:
  4178. self.value: NodeNG
  4179. super().__init__(
  4180. lineno=lineno,
  4181. col_offset=col_offset,
  4182. end_lineno=end_lineno,
  4183. end_col_offset=end_col_offset,
  4184. parent=parent,
  4185. )
  4186. def postinit(self, *, value: NodeNG) -> None:
  4187. self.value = value
  4188. class MatchSingleton(Pattern):
  4189. """Class representing a :class:`ast.MatchSingleton` node.
  4190. >>> import astroid
  4191. >>> node = astroid.extract_node('''
  4192. match x:
  4193. case True:
  4194. ...
  4195. case False:
  4196. ...
  4197. case None:
  4198. ...
  4199. ''')
  4200. >>> node.cases[0].pattern
  4201. <MatchSingleton l.3 at 0x10c2282e0>
  4202. >>> node.cases[1].pattern
  4203. <MatchSingleton l.5 at 0x10c228af0>
  4204. >>> node.cases[2].pattern
  4205. <MatchSingleton l.7 at 0x10c229f90>
  4206. """
  4207. _other_fields = ("value",)
  4208. def __init__(
  4209. self,
  4210. *,
  4211. value: Literal[True, False, None],
  4212. lineno: int | None = None,
  4213. col_offset: int | None = None,
  4214. end_lineno: int | None = None,
  4215. end_col_offset: int | None = None,
  4216. parent: NodeNG | None = None,
  4217. ) -> None:
  4218. self.value = value
  4219. super().__init__(
  4220. lineno=lineno,
  4221. col_offset=col_offset,
  4222. end_lineno=end_lineno,
  4223. end_col_offset=end_col_offset,
  4224. parent=parent,
  4225. )
  4226. class MatchSequence(Pattern):
  4227. """Class representing a :class:`ast.MatchSequence` node.
  4228. >>> import astroid
  4229. >>> node = astroid.extract_node('''
  4230. match x:
  4231. case [1, 2]:
  4232. ...
  4233. case (1, 2, *_):
  4234. ...
  4235. ''')
  4236. >>> node.cases[0].pattern
  4237. <MatchSequence l.3 at 0x10ca80d00>
  4238. >>> node.cases[1].pattern
  4239. <MatchSequence l.5 at 0x10ca80b20>
  4240. """
  4241. _astroid_fields = ("patterns",)
  4242. def __init__(
  4243. self,
  4244. lineno: int | None = None,
  4245. col_offset: int | None = None,
  4246. parent: NodeNG | None = None,
  4247. *,
  4248. end_lineno: int | None = None,
  4249. end_col_offset: int | None = None,
  4250. ) -> None:
  4251. self.patterns: list[Pattern]
  4252. super().__init__(
  4253. lineno=lineno,
  4254. col_offset=col_offset,
  4255. end_lineno=end_lineno,
  4256. end_col_offset=end_col_offset,
  4257. parent=parent,
  4258. )
  4259. def postinit(self, *, patterns: list[Pattern]) -> None:
  4260. self.patterns = patterns
  4261. class MatchMapping(_base_nodes.AssignTypeNode, Pattern):
  4262. """Class representing a :class:`ast.MatchMapping` node.
  4263. >>> import astroid
  4264. >>> node = astroid.extract_node('''
  4265. match x:
  4266. case {1: "Hello", 2: "World", 3: _, **rest}:
  4267. ...
  4268. ''')
  4269. >>> node.cases[0].pattern
  4270. <MatchMapping l.3 at 0x10c8a8850>
  4271. """
  4272. _astroid_fields = ("keys", "patterns", "rest")
  4273. def __init__(
  4274. self,
  4275. lineno: int | None = None,
  4276. col_offset: int | None = None,
  4277. parent: NodeNG | None = None,
  4278. *,
  4279. end_lineno: int | None = None,
  4280. end_col_offset: int | None = None,
  4281. ) -> None:
  4282. self.keys: list[NodeNG]
  4283. self.patterns: list[Pattern]
  4284. self.rest: AssignName | None
  4285. super().__init__(
  4286. lineno=lineno,
  4287. col_offset=col_offset,
  4288. end_lineno=end_lineno,
  4289. end_col_offset=end_col_offset,
  4290. parent=parent,
  4291. )
  4292. def postinit(
  4293. self,
  4294. *,
  4295. keys: list[NodeNG],
  4296. patterns: list[Pattern],
  4297. rest: AssignName | None,
  4298. ) -> None:
  4299. self.keys = keys
  4300. self.patterns = patterns
  4301. self.rest = rest
  4302. assigned_stmts = protocols.match_mapping_assigned_stmts
  4303. """Returns the assigned statement (non inferred) according to the assignment type.
  4304. See astroid/protocols.py for actual implementation.
  4305. """
  4306. class MatchClass(Pattern):
  4307. """Class representing a :class:`ast.MatchClass` node.
  4308. >>> import astroid
  4309. >>> node = astroid.extract_node('''
  4310. match x:
  4311. case Point2D(0, 0):
  4312. ...
  4313. case Point3D(x=0, y=0, z=0):
  4314. ...
  4315. ''')
  4316. >>> node.cases[0].pattern
  4317. <MatchClass l.3 at 0x10ca83940>
  4318. >>> node.cases[1].pattern
  4319. <MatchClass l.5 at 0x10ca80880>
  4320. """
  4321. _astroid_fields = ("cls", "patterns", "kwd_patterns")
  4322. _other_fields = ("kwd_attrs",)
  4323. def __init__(
  4324. self,
  4325. lineno: int | None = None,
  4326. col_offset: int | None = None,
  4327. parent: NodeNG | None = None,
  4328. *,
  4329. end_lineno: int | None = None,
  4330. end_col_offset: int | None = None,
  4331. ) -> None:
  4332. self.cls: NodeNG
  4333. self.patterns: list[Pattern]
  4334. self.kwd_attrs: list[str]
  4335. self.kwd_patterns: list[Pattern]
  4336. super().__init__(
  4337. lineno=lineno,
  4338. col_offset=col_offset,
  4339. end_lineno=end_lineno,
  4340. end_col_offset=end_col_offset,
  4341. parent=parent,
  4342. )
  4343. def postinit(
  4344. self,
  4345. *,
  4346. cls: NodeNG,
  4347. patterns: list[Pattern],
  4348. kwd_attrs: list[str],
  4349. kwd_patterns: list[Pattern],
  4350. ) -> None:
  4351. self.cls = cls
  4352. self.patterns = patterns
  4353. self.kwd_attrs = kwd_attrs
  4354. self.kwd_patterns = kwd_patterns
  4355. class MatchStar(_base_nodes.AssignTypeNode, Pattern):
  4356. """Class representing a :class:`ast.MatchStar` node.
  4357. >>> import astroid
  4358. >>> node = astroid.extract_node('''
  4359. match x:
  4360. case [1, *_]:
  4361. ...
  4362. ''')
  4363. >>> node.cases[0].pattern.patterns[1]
  4364. <MatchStar l.3 at 0x10ca809a0>
  4365. """
  4366. _astroid_fields = ("name",)
  4367. def __init__(
  4368. self,
  4369. lineno: int | None = None,
  4370. col_offset: int | None = None,
  4371. parent: NodeNG | None = None,
  4372. *,
  4373. end_lineno: int | None = None,
  4374. end_col_offset: int | None = None,
  4375. ) -> None:
  4376. self.name: AssignName | None
  4377. super().__init__(
  4378. lineno=lineno,
  4379. col_offset=col_offset,
  4380. end_lineno=end_lineno,
  4381. end_col_offset=end_col_offset,
  4382. parent=parent,
  4383. )
  4384. def postinit(self, *, name: AssignName | None) -> None:
  4385. self.name = name
  4386. assigned_stmts = protocols.match_star_assigned_stmts
  4387. """Returns the assigned statement (non inferred) according to the assignment type.
  4388. See astroid/protocols.py for actual implementation.
  4389. """
  4390. class MatchAs(_base_nodes.AssignTypeNode, Pattern):
  4391. """Class representing a :class:`ast.MatchAs` node.
  4392. >>> import astroid
  4393. >>> node = astroid.extract_node('''
  4394. match x:
  4395. case [1, a]:
  4396. ...
  4397. case {'key': b}:
  4398. ...
  4399. case Point2D(0, 0) as c:
  4400. ...
  4401. case d:
  4402. ...
  4403. ''')
  4404. >>> node.cases[0].pattern.patterns[1]
  4405. <MatchAs l.3 at 0x10d0b2da0>
  4406. >>> node.cases[1].pattern.patterns[0]
  4407. <MatchAs l.5 at 0x10d0b2920>
  4408. >>> node.cases[2].pattern
  4409. <MatchAs l.7 at 0x10d0b06a0>
  4410. >>> node.cases[3].pattern
  4411. <MatchAs l.9 at 0x10d09b880>
  4412. """
  4413. _astroid_fields = ("pattern", "name")
  4414. def __init__(
  4415. self,
  4416. lineno: int | None = None,
  4417. col_offset: int | None = None,
  4418. parent: NodeNG | None = None,
  4419. *,
  4420. end_lineno: int | None = None,
  4421. end_col_offset: int | None = None,
  4422. ) -> None:
  4423. self.pattern: Pattern | None
  4424. self.name: AssignName | None
  4425. super().__init__(
  4426. lineno=lineno,
  4427. col_offset=col_offset,
  4428. end_lineno=end_lineno,
  4429. end_col_offset=end_col_offset,
  4430. parent=parent,
  4431. )
  4432. def postinit(
  4433. self,
  4434. *,
  4435. pattern: Pattern | None,
  4436. name: AssignName | None,
  4437. ) -> None:
  4438. self.pattern = pattern
  4439. self.name = name
  4440. assigned_stmts = protocols.match_as_assigned_stmts
  4441. """Returns the assigned statement (non inferred) according to the assignment type.
  4442. See astroid/protocols.py for actual implementation.
  4443. """
  4444. class MatchOr(Pattern):
  4445. """Class representing a :class:`ast.MatchOr` node.
  4446. >>> import astroid
  4447. >>> node = astroid.extract_node('''
  4448. match x:
  4449. case 400 | 401 | 402:
  4450. ...
  4451. ''')
  4452. >>> node.cases[0].pattern
  4453. <MatchOr l.3 at 0x10d0b0b50>
  4454. """
  4455. _astroid_fields = ("patterns",)
  4456. def __init__(
  4457. self,
  4458. lineno: int | None = None,
  4459. col_offset: int | None = None,
  4460. parent: NodeNG | None = None,
  4461. *,
  4462. end_lineno: int | None = None,
  4463. end_col_offset: int | None = None,
  4464. ) -> None:
  4465. self.patterns: list[Pattern]
  4466. super().__init__(
  4467. lineno=lineno,
  4468. col_offset=col_offset,
  4469. end_lineno=end_lineno,
  4470. end_col_offset=end_col_offset,
  4471. parent=parent,
  4472. )
  4473. def postinit(self, *, patterns: list[Pattern]) -> None:
  4474. self.patterns = patterns
  4475. class TemplateStr(NodeNG):
  4476. """Class representing an :class:`ast.TemplateStr` node.
  4477. >>> import astroid
  4478. >>> node = astroid.extract_node('t"{name} finished {place!s}"')
  4479. >>> node
  4480. <TemplateStr l.1 at 0x103b7aa50>
  4481. """
  4482. _astroid_fields = ("values",)
  4483. def __init__(
  4484. self,
  4485. lineno: int | None = None,
  4486. col_offset: int | None = None,
  4487. parent: NodeNG | None = None,
  4488. *,
  4489. end_lineno: int | None = None,
  4490. end_col_offset: int | None = None,
  4491. ) -> None:
  4492. self.values: list[NodeNG]
  4493. super().__init__(
  4494. lineno=lineno,
  4495. col_offset=col_offset,
  4496. end_lineno=end_lineno,
  4497. end_col_offset=end_col_offset,
  4498. parent=parent,
  4499. )
  4500. def postinit(self, *, values: list[NodeNG]) -> None:
  4501. self.values = values
  4502. def get_children(self) -> Iterator[NodeNG]:
  4503. yield from self.values
  4504. class Interpolation(NodeNG):
  4505. """Class representing an :class:`ast.Interpolation` node.
  4506. >>> import astroid
  4507. >>> node = astroid.extract_node('t"{name} finished {place!s}"')
  4508. >>> node
  4509. <TemplateStr l.1 at 0x103b7aa50>
  4510. >>> node.values[0]
  4511. <Interpolation l.1 at 0x103b7acf0>
  4512. >>> node.values[2]
  4513. <Interpolation l.1 at 0x10411e5d0>
  4514. """
  4515. _astroid_fields = ("value", "format_spec")
  4516. _other_fields = ("str", "conversion")
  4517. def __init__(
  4518. self,
  4519. lineno: int | None = None,
  4520. col_offset: int | None = None,
  4521. parent: NodeNG | None = None,
  4522. *,
  4523. end_lineno: int | None = None,
  4524. end_col_offset: int | None = None,
  4525. ) -> None:
  4526. self.value: NodeNG
  4527. """Any expression node."""
  4528. self.str: str
  4529. """Text of the interpolation expression."""
  4530. self.conversion: int
  4531. """The type of formatting to be applied to the value.
  4532. .. seealso::
  4533. :class:`ast.Interpolation`
  4534. """
  4535. self.format_spec: JoinedStr | None = None
  4536. """The formatting to be applied to the value.
  4537. .. seealso::
  4538. :class:`ast.Interpolation`
  4539. """
  4540. super().__init__(
  4541. lineno=lineno,
  4542. col_offset=col_offset,
  4543. end_lineno=end_lineno,
  4544. end_col_offset=end_col_offset,
  4545. parent=parent,
  4546. )
  4547. def postinit(
  4548. self,
  4549. *,
  4550. value: NodeNG,
  4551. str: str, # pylint: disable=redefined-builtin
  4552. conversion: int = -1,
  4553. format_spec: JoinedStr | None = None,
  4554. ) -> None:
  4555. self.value = value
  4556. self.str = str
  4557. self.conversion = conversion
  4558. self.format_spec = format_spec
  4559. def get_children(self) -> Iterator[NodeNG]:
  4560. yield self.value
  4561. if self.format_spec:
  4562. yield self.format_spec
  4563. # constants ##############################################################
  4564. # The _proxied attribute of all container types (List, Tuple, etc.)
  4565. # are set during bootstrapping by _astroid_bootstrapping().
  4566. CONST_CLS: dict[type, type[NodeNG]] = {
  4567. list: List,
  4568. tuple: Tuple,
  4569. dict: Dict,
  4570. set: Set,
  4571. type(None): Const,
  4572. type(NotImplemented): Const,
  4573. type(...): Const,
  4574. bool: Const,
  4575. int: Const,
  4576. float: Const,
  4577. complex: Const,
  4578. str: Const,
  4579. bytes: Const,
  4580. }
  4581. def _create_basic_elements(
  4582. value: Iterable[Any], node: List | Set | Tuple
  4583. ) -> list[NodeNG]:
  4584. """Create a list of nodes to function as the elements of a new node."""
  4585. elements: list[NodeNG] = []
  4586. for element in value:
  4587. # NOTE: avoid accessing any attributes of element in the loop.
  4588. element_node = const_factory(element)
  4589. element_node.parent = node
  4590. elements.append(element_node)
  4591. return elements
  4592. def _create_dict_items(
  4593. values: Mapping[Any, Any], node: Dict
  4594. ) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]:
  4595. """Create a list of node pairs to function as the items of a new dict node."""
  4596. elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = []
  4597. for key, value in values.items():
  4598. # NOTE: avoid accessing any attributes of both key and value in the loop.
  4599. key_node = const_factory(key)
  4600. key_node.parent = node
  4601. value_node = const_factory(value)
  4602. value_node.parent = node
  4603. elements.append((key_node, value_node))
  4604. return elements
  4605. def const_factory(value: Any) -> ConstFactoryResult:
  4606. """Return an astroid node for a python value."""
  4607. # NOTE: avoid accessing any attributes of value until it is known that value
  4608. # is of a const type, to avoid possibly triggering code for a live object.
  4609. # Accesses include value.__class__ and isinstance(value, ...), but not type(value).
  4610. # See: https://github.com/pylint-dev/astroid/issues/2686
  4611. value_type = type(value)
  4612. assert not issubclass(value_type, NodeNG)
  4613. # This only handles instances of the CONST types. Any
  4614. # subclasses get inferred as EmptyNode.
  4615. # TODO: See if we should revisit these with the normal builder.
  4616. if value_type not in CONST_CLS:
  4617. node = EmptyNode()
  4618. node.object = value
  4619. return node
  4620. instance: List | Set | Tuple | Dict
  4621. initializer_cls = CONST_CLS[value_type]
  4622. if issubclass(initializer_cls, (List, Set, Tuple)):
  4623. instance = initializer_cls(
  4624. lineno=None,
  4625. col_offset=None,
  4626. parent=SYNTHETIC_ROOT,
  4627. end_lineno=None,
  4628. end_col_offset=None,
  4629. )
  4630. instance.postinit(_create_basic_elements(value, instance))
  4631. return instance
  4632. if issubclass(initializer_cls, Dict):
  4633. instance = initializer_cls(
  4634. lineno=None,
  4635. col_offset=None,
  4636. parent=SYNTHETIC_ROOT,
  4637. end_lineno=None,
  4638. end_col_offset=None,
  4639. )
  4640. instance.postinit(_create_dict_items(value, instance))
  4641. return instance
  4642. return Const(value)