| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707 |
- # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
- # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
- # Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
- """Module for some node classes. More nodes in scoped_nodes.py"""
- from __future__ import annotations
- import abc
- import ast
- import itertools
- import operator
- import sys
- import typing
- import warnings
- from collections.abc import Callable, Generator, Iterable, Iterator, Mapping
- from functools import cached_property
- from typing import TYPE_CHECKING, Any, ClassVar, Literal, Union
- from astroid import decorators, protocols, util
- from astroid.bases import Instance, _infer_stmts
- from astroid.const import _EMPTY_OBJECT_MARKER, PY314_PLUS, Context
- from astroid.context import CallContext, InferenceContext, copy_context
- from astroid.exceptions import (
- AstroidBuildingError,
- AstroidError,
- AstroidIndexError,
- AstroidTypeError,
- AstroidValueError,
- AttributeInferenceError,
- InferenceError,
- NameInferenceError,
- NoDefault,
- ParentMissingError,
- _NonDeducibleTypeHierarchy,
- )
- from astroid.interpreter import dunder_lookup
- from astroid.manager import AstroidManager
- from astroid.nodes import _base_nodes
- from astroid.nodes.const import OP_PRECEDENCE
- from astroid.nodes.node_ng import NodeNG
- from astroid.nodes.scoped_nodes import SYNTHETIC_ROOT
- from astroid.typing import (
- ConstFactoryResult,
- InferenceErrorInfo,
- InferenceResult,
- SuccessfulInferenceResult,
- )
- if sys.version_info >= (3, 11):
- from typing import Self
- else:
- from typing_extensions import Self
- if TYPE_CHECKING:
- from astroid import nodes
- from astroid.nodes import LocalsDictNodeNG
- def _is_const(value) -> bool:
- return isinstance(value, tuple(CONST_CLS))
- _NodesT = typing.TypeVar("_NodesT", bound=NodeNG)
- _BadOpMessageT = typing.TypeVar("_BadOpMessageT", bound=util.BadOperationMessage)
- # pylint: disable-next=consider-alternative-union-syntax
- AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None]
- AssignedStmtsCall = Callable[
- [
- _NodesT,
- AssignedStmtsPossibleNode,
- InferenceContext | None,
- list[int] | None,
- ],
- Any,
- ]
- InferBinaryOperation = Callable[
- [_NodesT, InferenceContext | None],
- Generator[InferenceResult | _BadOpMessageT],
- ]
- InferLHS = Callable[
- [_NodesT, InferenceContext | None],
- Generator[InferenceResult, None, InferenceErrorInfo | None],
- ]
- InferUnaryOp = Callable[[_NodesT, str], ConstFactoryResult]
- @decorators.raise_if_nothing_inferred
- def unpack_infer(stmt, context: InferenceContext | None = None):
- """recursively generate nodes inferred by the given statement.
- If the inferred value is a list or a tuple, recurse on the elements
- """
- if isinstance(stmt, (List, Tuple)):
- for elt in stmt.elts:
- if elt is util.Uninferable:
- yield elt
- continue
- yield from unpack_infer(elt, context)
- return {"node": stmt, "context": context}
- # if inferred is a final node, return it and stop
- inferred = next(stmt.infer(context), util.Uninferable)
- if inferred is stmt:
- yield inferred
- return {"node": stmt, "context": context}
- # else, infer recursively, except Uninferable object that should be returned as is
- for inferred in stmt.infer(context):
- if isinstance(inferred, util.UninferableBase):
- yield inferred
- else:
- yield from unpack_infer(inferred, context)
- return {"node": stmt, "context": context}
- def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
- """return true if the two given statements are mutually exclusive
- `exceptions` may be a list of exception names. If specified, discard If
- branches and check one of the statement is in an exception handler catching
- one of the given exceptions.
- algorithm :
- 1) index stmt1's parents
- 2) climb among stmt2's parents until we find a common parent
- 3) if the common parent is a If or Try statement, look if nodes are
- in exclusive branches
- """
- # index stmt1's parents
- stmt1_parents = {}
- children = {}
- previous = stmt1
- for node in stmt1.node_ancestors():
- stmt1_parents[node] = 1
- children[node] = previous
- previous = node
- # climb among stmt2's parents until we find a common parent
- previous = stmt2
- for node in stmt2.node_ancestors():
- if node in stmt1_parents:
- # if the common parent is a If or Try statement, look if
- # nodes are in exclusive branches
- if isinstance(node, If) and exceptions is None:
- c2attr, c2node = node.locate_child(previous)
- c1attr, c1node = node.locate_child(children[node])
- if "test" in (c1attr, c2attr):
- # If any node is `If.test`, then it must be inclusive with
- # the other node (`If.body` and `If.orelse`)
- return False
- if c1attr != c2attr:
- # different `If` branches (`If.body` and `If.orelse`)
- return True
- elif isinstance(node, Try):
- c2attr, c2node = node.locate_child(previous)
- c1attr, c1node = node.locate_child(children[node])
- if c1node is not c2node:
- first_in_body_caught_by_handlers = (
- c2attr == "handlers"
- and c1attr == "body"
- and previous.catch(exceptions)
- )
- second_in_body_caught_by_handlers = (
- c2attr == "body"
- and c1attr == "handlers"
- and children[node].catch(exceptions)
- )
- first_in_else_other_in_handlers = (
- c2attr == "handlers" and c1attr == "orelse"
- )
- second_in_else_other_in_handlers = (
- c2attr == "orelse" and c1attr == "handlers"
- )
- if any(
- (
- first_in_body_caught_by_handlers,
- second_in_body_caught_by_handlers,
- first_in_else_other_in_handlers,
- second_in_else_other_in_handlers,
- )
- ):
- return True
- elif c2attr == "handlers" and c1attr == "handlers":
- return previous is not children[node]
- return False
- previous = node
- return False
- # getitem() helpers.
- _SLICE_SENTINEL = object()
- def _slice_value(index, context: InferenceContext | None = None):
- """Get the value of the given slice index."""
- if isinstance(index, Const):
- if isinstance(index.value, (int, type(None))):
- return index.value
- elif index is None:
- return None
- else:
- # Try to infer what the index actually is.
- # Since we can't return all the possible values,
- # we'll stop at the first possible value.
- try:
- inferred = next(index.infer(context=context))
- except (InferenceError, StopIteration):
- pass
- else:
- if isinstance(inferred, Const):
- if isinstance(inferred.value, (int, type(None))):
- return inferred.value
- # Use a sentinel, because None can be a valid
- # value that this function can return,
- # as it is the case for unspecified bounds.
- return _SLICE_SENTINEL
- def _infer_slice(node, context: InferenceContext | None = None):
- lower = _slice_value(node.lower, context)
- upper = _slice_value(node.upper, context)
- step = _slice_value(node.step, context)
- if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)):
- return slice(lower, upper, step)
- raise AstroidTypeError(
- message="Could not infer slice used in subscript",
- node=node,
- index=node.parent,
- context=context,
- )
- def _container_getitem(instance, elts, index, context: InferenceContext | None = None):
- """Get a slice or an item, using the given *index*, for the given sequence."""
- try:
- if isinstance(index, Slice):
- index_slice = _infer_slice(index, context=context)
- new_cls = instance.__class__()
- new_cls.elts = elts[index_slice]
- new_cls.parent = instance.parent
- return new_cls
- if isinstance(index, Const):
- return elts[index.value]
- except ValueError as exc:
- raise AstroidValueError(
- message="Slice {index!r} cannot index container",
- node=instance,
- index=index,
- context=context,
- ) from exc
- except IndexError as exc:
- raise AstroidIndexError(
- message="Index {index!s} out of range",
- node=instance,
- index=index,
- context=context,
- ) from exc
- except TypeError as exc:
- raise AstroidTypeError(
- message="Type error {error!r}", node=instance, index=index, context=context
- ) from exc
- raise AstroidTypeError(f"Could not use {index} as subscript index")
- class BaseContainer(_base_nodes.ParentAssignNode, Instance, metaclass=abc.ABCMeta):
- """Base class for Set, FrozenSet, Tuple and List."""
- _astroid_fields = ("elts",)
- def __init__(
- self,
- lineno: int | None,
- col_offset: int | None,
- parent: NodeNG | None,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.elts: list[SuccessfulInferenceResult] = []
- """The elements in the node."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, elts: list[SuccessfulInferenceResult]) -> None:
- self.elts = elts
- @classmethod
- def from_elements(cls, elts: Iterable[Any]) -> Self:
- """Create a node of this type from the given list of elements.
- :param elts: The list of elements that the node should contain.
- :returns: A new node containing the given elements.
- """
- node = cls(
- lineno=None,
- col_offset=None,
- parent=None,
- end_lineno=None,
- end_col_offset=None,
- )
- node.elts = [const_factory(e) if _is_const(e) else e for e in elts]
- return node
- def itered(self):
- """An iterator over the elements this node contains.
- :returns: The contents of this node.
- :rtype: iterable(NodeNG)
- """
- return self.elts
- def bool_value(self, context: InferenceContext | None = None) -> bool:
- """Determine the boolean value of this node.
- :returns: The boolean value of this node.
- """
- return bool(self.elts)
- @abc.abstractmethod
- def pytype(self) -> str:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- def get_children(self):
- yield from self.elts
- @decorators.raise_if_nothing_inferred
- def _infer(
- self,
- context: InferenceContext | None = None,
- **kwargs: Any,
- ) -> Iterator[Self]:
- has_starred_named_expr = any(
- isinstance(e, (Starred, NamedExpr)) for e in self.elts
- )
- if has_starred_named_expr:
- values = self._infer_sequence_helper(context)
- new_seq = type(self)(
- lineno=self.lineno,
- col_offset=self.col_offset,
- parent=self.parent,
- end_lineno=self.end_lineno,
- end_col_offset=self.end_col_offset,
- )
- new_seq.postinit(values)
- yield new_seq
- else:
- yield self
- def _infer_sequence_helper(
- self, context: InferenceContext | None = None
- ) -> list[SuccessfulInferenceResult]:
- """Infer all values based on BaseContainer.elts."""
- values = []
- for elt in self.elts:
- if isinstance(elt, Starred):
- starred = util.safe_infer(elt.value, context)
- if not starred:
- raise InferenceError(node=self, context=context)
- if not hasattr(starred, "elts"):
- raise InferenceError(node=self, context=context)
- # TODO: fresh context?
- values.extend(starred._infer_sequence_helper(context))
- elif isinstance(elt, NamedExpr):
- value = util.safe_infer(elt.value, context)
- if not value:
- raise InferenceError(node=self, context=context)
- values.append(value)
- else:
- values.append(elt)
- return values
- # Name classes
- class AssignName(
- _base_nodes.NoChildrenNode,
- _base_nodes.LookupMixIn,
- _base_nodes.ParentAssignNode,
- ):
- """Variation of :class:`ast.Assign` representing assignment to a name.
- An :class:`AssignName` is the name of something that is assigned to.
- This includes variables defined in a function signature or in a loop.
- >>> import astroid
- >>> node = astroid.extract_node('variable = range(10)')
- >>> node
- <Assign l.1 at 0x7effe1db8550>
- >>> list(node.get_children())
- [<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>]
- >>> list(node.get_children())[0].as_string()
- 'variable'
- """
- _other_fields = ("name",)
- def __init__(
- self,
- name: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.name = name
- """The name that is assigned to."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- assigned_stmts = protocols.assend_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- """Infer an AssignName: need to inspect the RHS part of the
- assign node.
- """
- if isinstance(self.parent, AugAssign):
- return self.parent.infer(context)
- stmts = list(self.assigned_stmts(context=context))
- return _infer_stmts(stmts, context)
- @decorators.raise_if_nothing_inferred
- def infer_lhs(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- """Infer a Name: use name lookup rules.
- Same implementation as Name._infer."""
- # pylint: disable=import-outside-toplevel
- from astroid.constraint import get_constraints
- from astroid.helpers import _higher_function_scope
- frame, stmts = self.lookup(self.name)
- if not stmts:
- # Try to see if the name is enclosed in a nested function
- # and use the higher (first function) scope for searching.
- parent_function = _higher_function_scope(self.scope())
- if parent_function:
- _, stmts = parent_function.lookup(self.name)
- if not stmts:
- raise NameInferenceError(
- name=self.name, scope=self.scope(), context=context
- )
- context = copy_context(context)
- context.lookupname = self.name
- context.constraints[self.name] = get_constraints(self, frame)
- return _infer_stmts(stmts, context, frame)
- class DelName(
- _base_nodes.NoChildrenNode, _base_nodes.LookupMixIn, _base_nodes.ParentAssignNode
- ):
- """Variation of :class:`ast.Delete` representing deletion of a name.
- A :class:`DelName` is the name of something that is deleted.
- >>> import astroid
- >>> node = astroid.extract_node("del variable #@")
- >>> list(node.get_children())
- [<DelName.variable l.1 at 0x7effe1da4d30>]
- >>> list(node.get_children())[0].as_string()
- 'variable'
- """
- _other_fields = ("name",)
- def __init__(
- self,
- name: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.name = name
- """The name that is being deleted."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- class Name(_base_nodes.LookupMixIn, _base_nodes.NoChildrenNode):
- """Class representing an :class:`ast.Name` node.
- A :class:`Name` node is something that is named, but not covered by
- :class:`AssignName` or :class:`DelName`.
- >>> import astroid
- >>> node = astroid.extract_node('range(10)')
- >>> node
- <Call l.1 at 0x7effe1db8710>
- >>> list(node.get_children())
- [<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>]
- >>> list(node.get_children())[0].as_string()
- 'range'
- """
- _other_fields = ("name",)
- def __init__(
- self,
- name: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.name = name
- """The name that this node refers to."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def _get_name_nodes(self):
- yield self
- for child_node in self.get_children():
- yield from child_node._get_name_nodes()
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- """Infer a Name: use name lookup rules
- Same implementation as AssignName._infer_lhs."""
- # pylint: disable=import-outside-toplevel
- from astroid.constraint import get_constraints
- from astroid.helpers import _higher_function_scope
- frame, stmts = self.lookup(self.name)
- if not stmts:
- # Try to see if the name is enclosed in a nested function
- # and use the higher (first function) scope for searching.
- parent_function = _higher_function_scope(self.scope())
- if parent_function:
- _, stmts = parent_function.lookup(self.name)
- if not stmts:
- raise NameInferenceError(
- name=self.name, scope=self.scope(), context=context
- )
- context = copy_context(context)
- context.lookupname = self.name
- context.constraints[self.name] = get_constraints(self, frame)
- return _infer_stmts(stmts, context, frame)
- DEPRECATED_ARGUMENT_DEFAULT = "DEPRECATED_ARGUMENT_DEFAULT"
- class Arguments(
- _base_nodes.AssignTypeNode
- ): # pylint: disable=too-many-instance-attributes
- """Class representing an :class:`ast.arguments` node.
- An :class:`Arguments` node represents that arguments in a
- function definition.
- >>> import astroid
- >>> node = astroid.extract_node('def foo(bar): pass')
- >>> node
- <FunctionDef.foo l.1 at 0x7effe1db8198>
- >>> node.args
- <Arguments l.1 at 0x7effe1db82e8>
- """
- # Python 3.4+ uses a different approach regarding annotations,
- # each argument is a new class, _ast.arg, which exposes an
- # 'annotation' attribute. In astroid though, arguments are exposed
- # as is in the Arguments node and the only way to expose annotations
- # is by using something similar with Python 3.3:
- # - we expose 'varargannotation' and 'kwargannotation' of annotations
- # of varargs and kwargs.
- # - we expose 'annotation', a list with annotations for
- # for each normal argument. If an argument doesn't have an
- # annotation, its value will be None.
- _astroid_fields = (
- "args",
- "defaults",
- "kwonlyargs",
- "posonlyargs",
- "posonlyargs_annotations",
- "kw_defaults",
- "annotations",
- "varargannotation",
- "kwargannotation",
- "kwonlyargs_annotations",
- "type_comment_args",
- "type_comment_kwonlyargs",
- "type_comment_posonlyargs",
- )
- _other_fields = ("vararg", "kwarg")
- args: list[AssignName] | None
- """The names of the required arguments.
- Can be None if the associated function does not have a retrievable
- signature and the arguments are therefore unknown.
- This can happen with (builtin) functions implemented in C that have
- incomplete signature information.
- """
- defaults: list[NodeNG] | None
- """The default values for arguments that can be passed positionally."""
- kwonlyargs: list[AssignName]
- """The keyword arguments that cannot be passed positionally."""
- posonlyargs: list[AssignName]
- """The arguments that can only be passed positionally."""
- kw_defaults: list[NodeNG | None] | None
- """The default values for keyword arguments that cannot be passed positionally."""
- annotations: list[NodeNG | None]
- """The type annotations of arguments that can be passed positionally."""
- posonlyargs_annotations: list[NodeNG | None]
- """The type annotations of arguments that can only be passed positionally."""
- kwonlyargs_annotations: list[NodeNG | None]
- """The type annotations of arguments that cannot be passed positionally."""
- type_comment_args: list[NodeNG | None]
- """The type annotation, passed by a type comment, of each argument.
- If an argument does not have a type comment,
- the value for that argument will be None.
- """
- type_comment_kwonlyargs: list[NodeNG | None]
- """The type annotation, passed by a type comment, of each keyword only argument.
- If an argument does not have a type comment,
- the value for that argument will be None.
- """
- type_comment_posonlyargs: list[NodeNG | None]
- """The type annotation, passed by a type comment, of each positional argument.
- If an argument does not have a type comment,
- the value for that argument will be None.
- """
- varargannotation: NodeNG | None
- """The type annotation for the variable length arguments."""
- kwargannotation: NodeNG | None
- """The type annotation for the variable length keyword arguments."""
- vararg_node: AssignName | None
- """The node for variable length arguments"""
- kwarg_node: AssignName | None
- """The node for variable keyword arguments"""
- def __init__(
- self,
- vararg: str | None,
- kwarg: str | None,
- parent: NodeNG,
- vararg_node: AssignName | None = None,
- kwarg_node: AssignName | None = None,
- ) -> None:
- """Almost all attributes can be None for living objects where introspection failed."""
- super().__init__(
- parent=parent,
- lineno=None,
- col_offset=None,
- end_lineno=None,
- end_col_offset=None,
- )
- self.vararg = vararg
- """The name of the variable length arguments."""
- self.kwarg = kwarg
- """The name of the variable length keyword arguments."""
- self.vararg_node = vararg_node
- self.kwarg_node = kwarg_node
- # pylint: disable=too-many-arguments, too-many-positional-arguments
- def postinit(
- self,
- args: list[AssignName] | None,
- defaults: list[NodeNG] | None,
- kwonlyargs: list[AssignName],
- kw_defaults: list[NodeNG | None] | None,
- annotations: list[NodeNG | None],
- posonlyargs: list[AssignName],
- kwonlyargs_annotations: list[NodeNG | None],
- posonlyargs_annotations: list[NodeNG | None],
- varargannotation: NodeNG | None = None,
- kwargannotation: NodeNG | None = None,
- type_comment_args: list[NodeNG | None] | None = None,
- type_comment_kwonlyargs: list[NodeNG | None] | None = None,
- type_comment_posonlyargs: list[NodeNG | None] | None = None,
- ) -> None:
- self.args = args
- self.defaults = defaults
- self.kwonlyargs = kwonlyargs
- self.posonlyargs = posonlyargs
- self.kw_defaults = kw_defaults
- self.annotations = annotations
- self.kwonlyargs_annotations = kwonlyargs_annotations
- self.posonlyargs_annotations = posonlyargs_annotations
- # Parameters that got added later and need a default
- self.varargannotation = varargannotation
- self.kwargannotation = kwargannotation
- if type_comment_args is None:
- type_comment_args = []
- self.type_comment_args = type_comment_args
- if type_comment_kwonlyargs is None:
- type_comment_kwonlyargs = []
- self.type_comment_kwonlyargs = type_comment_kwonlyargs
- if type_comment_posonlyargs is None:
- type_comment_posonlyargs = []
- self.type_comment_posonlyargs = type_comment_posonlyargs
- assigned_stmts = protocols.arguments_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def _infer_name(self, frame, name):
- if self.parent is frame:
- return name
- return None
- @cached_property
- def fromlineno(self) -> int:
- """The first line that this node appears on in the source code.
- Can also return 0 if the line can not be determined.
- """
- lineno = super().fromlineno
- return max(lineno, self.parent.fromlineno or 0)
- @cached_property
- def arguments(self):
- """Get all the arguments for this node. This includes:
- * Positional only arguments
- * Positional arguments
- * Keyword arguments
- * Variable arguments (.e.g *args)
- * Variable keyword arguments (e.g **kwargs)
- """
- retval = list(itertools.chain((self.posonlyargs or ()), (self.args or ())))
- if self.vararg_node:
- retval.append(self.vararg_node)
- retval += self.kwonlyargs or ()
- if self.kwarg_node:
- retval.append(self.kwarg_node)
- return retval
- def format_args(self, *, skippable_names: set[str] | None = None) -> str:
- """Get the arguments formatted as string.
- :returns: The formatted arguments.
- :rtype: str
- """
- result = []
- positional_only_defaults = []
- positional_or_keyword_defaults = self.defaults
- if self.defaults:
- args = self.args or []
- positional_or_keyword_defaults = self.defaults[-len(args) :]
- positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
- if self.posonlyargs:
- result.append(
- _format_args(
- self.posonlyargs,
- positional_only_defaults,
- self.posonlyargs_annotations,
- skippable_names=skippable_names,
- )
- )
- result.append("/")
- if self.args:
- result.append(
- _format_args(
- self.args,
- positional_or_keyword_defaults,
- getattr(self, "annotations", None),
- skippable_names=skippable_names,
- )
- )
- if self.vararg:
- result.append(f"*{self.vararg}")
- if self.kwonlyargs:
- if not self.vararg:
- result.append("*")
- result.append(
- _format_args(
- self.kwonlyargs,
- self.kw_defaults,
- self.kwonlyargs_annotations,
- skippable_names=skippable_names,
- )
- )
- if self.kwarg:
- result.append(f"**{self.kwarg}")
- return ", ".join(result)
- def _get_arguments_data(
- self,
- ) -> tuple[
- dict[str, tuple[str | None, str | None]],
- dict[str, tuple[str | None, str | None]],
- ]:
- """Get the arguments as dictionary with information about typing and defaults.
- The return tuple contains a dictionary for positional and keyword arguments with their typing
- and their default value, if any.
- The method follows a similar order as format_args but instead of formatting into a string it
- returns the data that is used to do so.
- """
- pos_only: dict[str, tuple[str | None, str | None]] = {}
- kw_only: dict[str, tuple[str | None, str | None]] = {}
- # Setup and match defaults with arguments
- positional_only_defaults = []
- positional_or_keyword_defaults = self.defaults
- if self.defaults:
- args = self.args or []
- positional_or_keyword_defaults = self.defaults[-len(args) :]
- positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
- for index, posonly in enumerate(self.posonlyargs):
- annotation, default = self.posonlyargs_annotations[index], None
- if annotation is not None:
- annotation = annotation.as_string()
- if positional_only_defaults:
- default = positional_only_defaults[index].as_string()
- pos_only[posonly.name] = (annotation, default)
- for index, arg in enumerate(self.args):
- annotation, default = self.annotations[index], None
- if annotation is not None:
- annotation = annotation.as_string()
- if positional_or_keyword_defaults:
- defaults_offset = len(self.args) - len(positional_or_keyword_defaults)
- default_index = index - defaults_offset
- if (
- default_index > -1
- and positional_or_keyword_defaults[default_index] is not None
- ):
- default = positional_or_keyword_defaults[default_index].as_string()
- pos_only[arg.name] = (annotation, default)
- if self.vararg:
- annotation = self.varargannotation
- if annotation is not None:
- annotation = annotation.as_string()
- pos_only[self.vararg] = (annotation, None)
- for index, kwarg in enumerate(self.kwonlyargs):
- annotation = self.kwonlyargs_annotations[index]
- if annotation is not None:
- annotation = annotation.as_string()
- default = self.kw_defaults[index]
- if default is not None:
- default = default.as_string()
- kw_only[kwarg.name] = (annotation, default)
- if self.kwarg:
- annotation = self.kwargannotation
- if annotation is not None:
- annotation = annotation.as_string()
- kw_only[self.kwarg] = (annotation, None)
- return pos_only, kw_only
- def default_value(self, argname):
- """Get the default value for an argument.
- :param argname: The name of the argument to get the default value for.
- :type argname: str
- :raises NoDefault: If there is no default value defined for the
- given argument.
- """
- args = [
- arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg]
- ]
- index = _find_arg(argname, self.kwonlyargs)[0]
- if (index is not None) and (len(self.kw_defaults) > index):
- if self.kw_defaults[index] is not None:
- return self.kw_defaults[index]
- raise NoDefault(func=self.parent, name=argname)
- index = _find_arg(argname, args)[0]
- if index is not None:
- idx = index - (len(args) - len(self.defaults) - len(self.kw_defaults))
- if idx >= 0:
- return self.defaults[idx]
- raise NoDefault(func=self.parent, name=argname)
- def is_argument(self, name) -> bool:
- """Check if the given name is defined in the arguments.
- :param name: The name to check for.
- :type name: str
- :returns: Whether the given name is defined in the arguments,
- """
- if name == self.vararg:
- return True
- if name == self.kwarg:
- return True
- return self.find_argname(name)[1] is not None
- def find_argname(self, argname, rec=DEPRECATED_ARGUMENT_DEFAULT):
- """Get the index and :class:`AssignName` node for given name.
- :param argname: The name of the argument to search for.
- :type argname: str
- :returns: The index and node for the argument.
- :rtype: tuple(str or None, AssignName or None)
- """
- if rec != DEPRECATED_ARGUMENT_DEFAULT: # pragma: no cover
- warnings.warn(
- "The rec argument will be removed in astroid 3.1.",
- DeprecationWarning,
- stacklevel=2,
- )
- if self.arguments:
- index, argument = _find_arg(argname, self.arguments)
- if argument:
- return index, argument
- return None, None
- def get_children(self):
- yield from self.posonlyargs or ()
- for elt in self.posonlyargs_annotations:
- if elt is not None:
- yield elt
- yield from self.args or ()
- if self.defaults is not None:
- yield from self.defaults
- yield from self.kwonlyargs
- for elt in self.kw_defaults or ():
- if elt is not None:
- yield elt
- for elt in self.annotations:
- if elt is not None:
- yield elt
- if self.varargannotation is not None:
- yield self.varargannotation
- if self.kwargannotation is not None:
- yield self.kwargannotation
- for elt in self.kwonlyargs_annotations:
- if elt is not None:
- yield elt
- @decorators.raise_if_nothing_inferred
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- # pylint: disable-next=import-outside-toplevel
- from astroid.protocols import _arguments_infer_argname
- if context is None or context.lookupname is None:
- raise InferenceError(node=self, context=context)
- return _arguments_infer_argname(self, context.lookupname, context)
- def _find_arg(argname, args):
- for i, arg in enumerate(args):
- if arg.name == argname:
- return i, arg
- return None, None
- def _format_args(
- args, defaults=None, annotations=None, skippable_names: set[str] | None = None
- ) -> str:
- if skippable_names is None:
- skippable_names = set()
- values = []
- if args is None:
- return ""
- if annotations is None:
- annotations = []
- if defaults is not None:
- default_offset = len(args) - len(defaults)
- else:
- default_offset = None
- packed = itertools.zip_longest(args, annotations)
- for i, (arg, annotation) in enumerate(packed):
- if arg.name in skippable_names:
- continue
- if isinstance(arg, Tuple):
- values.append(f"({_format_args(arg.elts)})")
- else:
- argname = arg.name
- default_sep = "="
- if annotation is not None:
- argname += ": " + annotation.as_string()
- default_sep = " = "
- values.append(argname)
- if default_offset is not None and i >= default_offset:
- if defaults[i - default_offset] is not None:
- values[-1] += default_sep + defaults[i - default_offset].as_string()
- return ", ".join(values)
- def _infer_attribute(
- node: nodes.AssignAttr | nodes.Attribute,
- context: InferenceContext | None = None,
- **kwargs: Any,
- ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
- """Infer an AssignAttr/Attribute node by using getattr on the associated object."""
- # pylint: disable=import-outside-toplevel
- from astroid.constraint import get_constraints
- from astroid.nodes import ClassDef
- for owner in node.expr.infer(context):
- if isinstance(owner, util.UninferableBase):
- yield owner
- continue
- context = copy_context(context)
- old_boundnode = context.boundnode
- try:
- context.boundnode = owner
- if isinstance(owner, (ClassDef, Instance)):
- frame = owner if isinstance(owner, ClassDef) else owner._proxied
- context.constraints[node.attrname] = get_constraints(node, frame=frame)
- if node.attrname == "argv" and owner.name == "sys":
- # sys.argv will never be inferable during static analysis
- # It's value would be the args passed to the linter itself
- yield util.Uninferable
- else:
- yield from owner.igetattr(node.attrname, context)
- except (
- AttributeInferenceError,
- InferenceError,
- AttributeError,
- ):
- pass
- finally:
- context.boundnode = old_boundnode
- return InferenceErrorInfo(node=node, context=context)
- class AssignAttr(_base_nodes.LookupMixIn, _base_nodes.ParentAssignNode):
- """Variation of :class:`ast.Assign` representing assignment to an attribute.
- >>> import astroid
- >>> node = astroid.extract_node('self.attribute = range(10)')
- >>> node
- <Assign l.1 at 0x7effe1d521d0>
- >>> list(node.get_children())
- [<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>]
- >>> list(node.get_children())[0].as_string()
- 'self.attribute'
- """
- expr: NodeNG
- _astroid_fields = ("expr",)
- _other_fields = ("attrname",)
- def __init__(
- self,
- attrname: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.attrname = attrname
- """The name of the attribute being assigned to."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, expr: NodeNG) -> None:
- self.expr = expr
- assigned_stmts = protocols.assend_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def get_children(self):
- yield self.expr
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- """Infer an AssignAttr: need to inspect the RHS part of the
- assign node.
- """
- if isinstance(self.parent, AugAssign):
- return self.parent.infer(context)
- stmts = list(self.assigned_stmts(context=context))
- return _infer_stmts(stmts, context)
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def infer_lhs(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- return _infer_attribute(self, context, **kwargs)
- class Assert(_base_nodes.Statement):
- """Class representing an :class:`ast.Assert` node.
- An :class:`Assert` node represents an assert statement.
- >>> import astroid
- >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"')
- >>> node
- <Assert l.1 at 0x7effe1d527b8>
- """
- _astroid_fields = ("test", "fail")
- test: NodeNG
- """The test that passes or fails the assertion."""
- fail: NodeNG | None
- """The message shown when the assertion fails."""
- def postinit(self, test: NodeNG, fail: NodeNG | None) -> None:
- self.fail = fail
- self.test = test
- def get_children(self):
- yield self.test
- if self.fail is not None:
- yield self.fail
- class Assign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Assign` node.
- An :class:`Assign` is a statement where something is explicitly
- asssigned to.
- >>> import astroid
- >>> node = astroid.extract_node('variable = range(10)')
- >>> node
- <Assign l.1 at 0x7effe1db8550>
- """
- targets: list[NodeNG]
- """What is being assigned to."""
- value: NodeNG
- """The value being assigned to the variables."""
- type_annotation: NodeNG | None
- """If present, this will contain the type annotation passed by a type comment"""
- _astroid_fields = ("targets", "value")
- _other_other_fields = ("type_annotation",)
- def postinit(
- self,
- targets: list[NodeNG],
- value: NodeNG,
- type_annotation: NodeNG | None,
- ) -> None:
- self.targets = targets
- self.value = value
- self.type_annotation = type_annotation
- assigned_stmts = protocols.assign_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def get_children(self):
- yield from self.targets
- yield self.value
- @cached_property
- def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
- return [self, *self.value._assign_nodes_in_scope]
- def _get_yield_nodes_skip_functions(self):
- yield from self.value._get_yield_nodes_skip_functions()
- def _get_yield_nodes_skip_lambdas(self):
- yield from self.value._get_yield_nodes_skip_lambdas()
- class AnnAssign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
- """Class representing an :class:`ast.AnnAssign` node.
- An :class:`AnnAssign` is an assignment with a type annotation.
- >>> import astroid
- >>> node = astroid.extract_node('variable: List[int] = range(10)')
- >>> node
- <AnnAssign l.1 at 0x7effe1d4c630>
- """
- _astroid_fields = ("target", "annotation", "value")
- _other_fields = ("simple",)
- target: Name | Attribute | Subscript
- """What is being assigned to."""
- annotation: NodeNG
- """The type annotation of what is being assigned to."""
- value: NodeNG | None
- """The value being assigned to the variables."""
- simple: int
- """Whether :attr:`target` is a pure name or a complex statement."""
- def postinit(
- self,
- target: Name | Attribute | Subscript,
- annotation: NodeNG,
- simple: int,
- value: NodeNG | None,
- ) -> None:
- self.target = target
- self.annotation = annotation
- self.value = value
- self.simple = simple
- assigned_stmts = protocols.assign_annassigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def get_children(self):
- yield self.target
- yield self.annotation
- if self.value is not None:
- yield self.value
- class AugAssign(
- _base_nodes.AssignTypeNode, _base_nodes.OperatorNode, _base_nodes.Statement
- ):
- """Class representing an :class:`ast.AugAssign` node.
- An :class:`AugAssign` is an assignment paired with an operator.
- >>> import astroid
- >>> node = astroid.extract_node('variable += 1')
- >>> node
- <AugAssign l.1 at 0x7effe1db4d68>
- """
- _astroid_fields = ("target", "value")
- _other_fields = ("op",)
- target: Name | Attribute | Subscript
- """What is being assigned to."""
- value: NodeNG
- """The value being assigned to the variable."""
- def __init__(
- self,
- op: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.op = op
- """The operator that is being combined with the assignment.
- This includes the equals sign.
- """
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, target: Name | Attribute | Subscript, value: NodeNG) -> None:
- self.target = target
- self.value = value
- assigned_stmts = protocols.assign_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def type_errors(
- self, context: InferenceContext | None = None
- ) -> list[util.BadBinaryOperationMessage]:
- """Get a list of type errors which can occur during inference.
- Each TypeError is represented by a :class:`BadBinaryOperationMessage` ,
- which holds the original exception.
- If any inferred result is uninferable, an empty list is returned.
- """
- bad = []
- try:
- for result in self._infer_augassign(context=context):
- if result is util.Uninferable:
- raise InferenceError
- if isinstance(result, util.BadBinaryOperationMessage):
- bad.append(result)
- except InferenceError:
- return []
- return bad
- def get_children(self):
- yield self.target
- yield self.value
- def _get_yield_nodes_skip_functions(self):
- """An AugAssign node can contain a Yield node in the value"""
- yield from self.value._get_yield_nodes_skip_functions()
- yield from super()._get_yield_nodes_skip_functions()
- def _get_yield_nodes_skip_lambdas(self):
- """An AugAssign node can contain a Yield node in the value"""
- yield from self.value._get_yield_nodes_skip_lambdas()
- yield from super()._get_yield_nodes_skip_lambdas()
- def _infer_augassign(
- self, context: InferenceContext | None = None
- ) -> Generator[InferenceResult | util.BadBinaryOperationMessage]:
- """Inference logic for augmented binary operations."""
- context = context or InferenceContext()
- rhs_context = context.clone()
- lhs_iter = self.target.infer_lhs(context=context)
- rhs_iter = self.value.infer(context=rhs_context)
- for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
- if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
- # Don't know how to process this.
- yield util.Uninferable
- return
- try:
- yield from self._infer_binary_operation(
- left=lhs,
- right=rhs,
- binary_opnode=self,
- context=context,
- flow_factory=self._get_aug_flow,
- )
- except _NonDeducibleTypeHierarchy:
- yield util.Uninferable
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- return self._filter_operation_errors(
- self._infer_augassign, context, util.BadBinaryOperationMessage
- )
- class BinOp(_base_nodes.OperatorNode):
- """Class representing an :class:`ast.BinOp` node.
- A :class:`BinOp` node is an application of a binary operator.
- >>> import astroid
- >>> node = astroid.extract_node('a + b')
- >>> node
- <BinOp l.1 at 0x7f23b2e8cfd0>
- """
- _astroid_fields = ("left", "right")
- _other_fields = ("op",)
- left: NodeNG
- """What is being applied to the operator on the left side."""
- right: NodeNG
- """What is being applied to the operator on the right side."""
- def __init__(
- self,
- op: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.op = op
- """The operator."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, left: NodeNG, right: NodeNG) -> None:
- self.left = left
- self.right = right
- def type_errors(
- self, context: InferenceContext | None = None
- ) -> list[util.BadBinaryOperationMessage]:
- """Get a list of type errors which can occur during inference.
- Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
- which holds the original exception.
- If any inferred result is uninferable, an empty list is returned.
- """
- bad = []
- try:
- for result in self._infer_binop(context=context):
- if result is util.Uninferable:
- raise InferenceError
- if isinstance(result, util.BadBinaryOperationMessage):
- bad.append(result)
- except InferenceError:
- return []
- return bad
- def get_children(self):
- yield self.left
- yield self.right
- def op_precedence(self) -> int:
- return OP_PRECEDENCE[self.op]
- def op_left_associative(self) -> bool:
- # 2**3**4 == 2**(3**4)
- return self.op != "**"
- def _infer_binop(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- """Binary operation inference logic."""
- left = self.left
- right = self.right
- # we use two separate contexts for evaluating lhs and rhs because
- # 1. evaluating lhs may leave some undesired entries in context.path
- # which may not let us infer right value of rhs
- context = context or InferenceContext()
- lhs_context = copy_context(context)
- rhs_context = copy_context(context)
- lhs_iter = left.infer(context=lhs_context)
- rhs_iter = right.infer(context=rhs_context)
- for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
- if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
- # Don't know how to process this.
- yield util.Uninferable
- return
- try:
- yield from self._infer_binary_operation(
- lhs, rhs, self, context, self._get_binop_flow
- )
- except _NonDeducibleTypeHierarchy:
- yield util.Uninferable
- @decorators.yes_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- return self._filter_operation_errors(
- self._infer_binop, context, util.BadBinaryOperationMessage
- )
- class BoolOp(NodeNG):
- """Class representing an :class:`ast.BoolOp` node.
- A :class:`BoolOp` is an application of a boolean operator.
- >>> import astroid
- >>> node = astroid.extract_node('a and b')
- >>> node
- <BinOp l.1 at 0x7f23b2e71c50>
- """
- _astroid_fields = ("values",)
- _other_fields = ("op",)
- def __init__(
- self,
- op: str,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param op: The operator.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.op: str = op
- """The operator."""
- self.values: list[NodeNG] = []
- """The values being applied to the operator."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, values: list[NodeNG] | None = None) -> None:
- """Do some setup after initialisation.
- :param values: The values being applied to the operator.
- """
- if values is not None:
- self.values = values
- def get_children(self):
- yield from self.values
- def op_precedence(self) -> int:
- return OP_PRECEDENCE[self.op]
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- """Infer a boolean operation (and / or / not).
- The function will calculate the boolean operation
- for all pairs generated through inference for each component
- node.
- """
- values = self.values
- if self.op == "or":
- predicate = operator.truth
- else:
- predicate = operator.not_
- try:
- inferred_values = [value.infer(context=context) for value in values]
- except InferenceError:
- yield util.Uninferable
- return None
- for pair in itertools.product(*inferred_values):
- if any(isinstance(item, util.UninferableBase) for item in pair):
- # Can't infer the final result, just yield Uninferable.
- yield util.Uninferable
- continue
- bool_values = [item.bool_value() for item in pair]
- if any(isinstance(item, util.UninferableBase) for item in bool_values):
- # Can't infer the final result, just yield Uninferable.
- yield util.Uninferable
- continue
- # Since the boolean operations are short circuited operations,
- # this code yields the first value for which the predicate is True
- # and if no value respected the predicate, then the last value will
- # be returned (or Uninferable if there was no last value).
- # This is conforming to the semantics of `and` and `or`:
- # 1 and 0 -> 1
- # 0 and 1 -> 0
- # 1 or 0 -> 1
- # 0 or 1 -> 1
- value = util.Uninferable
- for value, bool_value in zip(pair, bool_values):
- if predicate(bool_value):
- yield value
- break
- else:
- yield value
- return InferenceErrorInfo(node=self, context=context)
- class Break(_base_nodes.NoChildrenNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Break` node.
- >>> import astroid
- >>> node = astroid.extract_node('break')
- >>> node
- <Break l.1 at 0x7f23b2e9e5c0>
- """
- class Call(NodeNG):
- """Class representing an :class:`ast.Call` node.
- A :class:`Call` node is a call to a function, method, etc.
- >>> import astroid
- >>> node = astroid.extract_node('function()')
- >>> node
- <Call l.1 at 0x7f23b2e71eb8>
- """
- _astroid_fields = ("func", "args", "keywords")
- func: NodeNG
- """What is being called."""
- args: list[NodeNG]
- """The positional arguments being given to the call."""
- keywords: list[Keyword]
- """The keyword arguments being given to the call."""
- def postinit(
- self, func: NodeNG, args: list[NodeNG], keywords: list[Keyword]
- ) -> None:
- self.func = func
- self.args = args
- self.keywords = keywords
- @property
- def starargs(self) -> list[Starred]:
- """The positional arguments that unpack something."""
- return [arg for arg in self.args if isinstance(arg, Starred)]
- @property
- def kwargs(self) -> list[Keyword]:
- """The keyword arguments that unpack something."""
- return [keyword for keyword in self.keywords if keyword.arg is None]
- def get_children(self):
- yield self.func
- yield from self.args
- yield from self.keywords
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
- """Infer a Call node by trying to guess what the function returns."""
- callcontext = copy_context(context)
- callcontext.boundnode = None
- if context is not None:
- callcontext.extra_context = self._populate_context_lookup(context.clone())
- for callee in self.func.infer(context):
- if isinstance(callee, util.UninferableBase):
- yield callee
- continue
- try:
- if hasattr(callee, "infer_call_result"):
- callcontext.callcontext = CallContext(
- args=self.args, keywords=self.keywords, callee=callee
- )
- yield from callee.infer_call_result(
- caller=self, context=callcontext
- )
- except InferenceError:
- continue
- return InferenceErrorInfo(node=self, context=context)
- def _populate_context_lookup(self, context: InferenceContext | None):
- """Allows context to be saved for later for inference inside a function."""
- context_lookup: dict[InferenceResult, InferenceContext] = {}
- if context is None:
- return context_lookup
- for arg in self.args:
- if isinstance(arg, Starred):
- context_lookup[arg.value] = context
- else:
- context_lookup[arg] = context
- keywords = self.keywords if self.keywords is not None else []
- for keyword in keywords:
- context_lookup[keyword.value] = context
- return context_lookup
- COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = {
- "==": operator.eq,
- "!=": operator.ne,
- "<": operator.lt,
- "<=": operator.le,
- ">": operator.gt,
- ">=": operator.ge,
- "in": lambda a, b: a in b,
- "not in": lambda a, b: a not in b,
- }
- UNINFERABLE_OPS = {
- "is",
- "is not",
- }
- class Compare(NodeNG):
- """Class representing an :class:`ast.Compare` node.
- A :class:`Compare` node indicates a comparison.
- >>> import astroid
- >>> node = astroid.extract_node('a <= b <= c')
- >>> node
- <Compare l.1 at 0x7f23b2e9e6d8>
- >>> node.ops
- [('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)]
- """
- _astroid_fields = ("left", "ops")
- left: NodeNG
- """The value at the left being applied to a comparison operator."""
- ops: list[tuple[str, NodeNG]]
- """The remainder of the operators and their relevant right hand value."""
- def postinit(self, left: NodeNG, ops: list[tuple[str, NodeNG]]) -> None:
- self.left = left
- self.ops = ops
- def get_children(self):
- """Get the child nodes below this node.
- Overridden to handle the tuple fields and skip returning the operator
- strings.
- :returns: The children.
- :rtype: iterable(NodeNG)
- """
- yield self.left
- for _, comparator in self.ops:
- yield comparator # we don't want the 'op'
- def last_child(self):
- """An optimized version of list(get_children())[-1]
- :returns: The last child.
- :rtype: NodeNG
- """
- # XXX maybe if self.ops:
- return self.ops[-1][1]
- # return self.left
- # TODO: move to util?
- @staticmethod
- def _to_literal(node: SuccessfulInferenceResult) -> Any:
- # Can raise SyntaxError, ValueError, or TypeError from ast.literal_eval
- # Can raise AttributeError from node.as_string() as not all nodes have a visitor
- # Is this the stupidest idea or the simplest idea?
- return ast.literal_eval(node.as_string())
- def _do_compare(
- self,
- left_iter: Iterable[InferenceResult],
- op: str,
- right_iter: Iterable[InferenceResult],
- ) -> bool | util.UninferableBase:
- """
- If all possible combinations are either True or False, return that:
- >>> _do_compare([1, 2], '<=', [3, 4])
- True
- >>> _do_compare([1, 2], '==', [3, 4])
- False
- If any item is uninferable, or if some combinations are True and some
- are False, return Uninferable:
- >>> _do_compare([1, 3], '<=', [2, 4])
- util.Uninferable
- """
- retval: bool | None = None
- if op in UNINFERABLE_OPS:
- return util.Uninferable
- op_func = COMPARE_OPS[op]
- for left, right in itertools.product(left_iter, right_iter):
- if isinstance(left, util.UninferableBase) or isinstance(
- right, util.UninferableBase
- ):
- return util.Uninferable
- try:
- left, right = self._to_literal(left), self._to_literal(right)
- except (SyntaxError, ValueError, AttributeError, TypeError):
- return util.Uninferable
- try:
- expr = op_func(left, right)
- except TypeError as exc:
- raise AstroidTypeError from exc
- if retval is None:
- retval = expr
- elif retval != expr:
- return util.Uninferable
- # (or both, but "True | False" is basically the same)
- assert retval is not None
- return retval # it was all the same value
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[nodes.Const | util.UninferableBase]:
- """Chained comparison inference logic."""
- retval: bool | util.UninferableBase = True
- ops = self.ops
- left_node = self.left
- lhs = list(left_node.infer(context=context))
- # should we break early if first element is uninferable?
- for op, right_node in ops:
- # eagerly evaluate rhs so that values can be re-used as lhs
- rhs = list(right_node.infer(context=context))
- try:
- retval = self._do_compare(lhs, op, rhs)
- except AstroidTypeError:
- retval = util.Uninferable
- break
- if retval is not True:
- break # short-circuit
- lhs = rhs # continue
- if retval is util.Uninferable:
- yield retval # type: ignore[misc]
- else:
- yield Const(retval)
- class Comprehension(NodeNG):
- """Class representing an :class:`ast.comprehension` node.
- A :class:`Comprehension` indicates the loop inside any type of
- comprehension including generator expressions.
- >>> import astroid
- >>> node = astroid.extract_node('[x for x in some_values]')
- >>> list(node.get_children())
- [<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>]
- >>> list(node.get_children())[1].as_string()
- 'for x in some_values'
- """
- _astroid_fields = ("target", "iter", "ifs")
- _other_fields = ("is_async",)
- optional_assign = True
- """Whether this node optionally assigns a variable."""
- target: NodeNG
- """What is assigned to by the comprehension."""
- iter: NodeNG
- """What is iterated over by the comprehension."""
- ifs: list[NodeNG]
- """The contents of any if statements that filter the comprehension."""
- is_async: bool
- """Whether this is an asynchronous comprehension or not."""
- def postinit(
- self,
- target: NodeNG,
- iter: NodeNG, # pylint: disable = redefined-builtin
- ifs: list[NodeNG],
- is_async: bool,
- ) -> None:
- self.target = target
- self.iter = iter
- self.ifs = ifs
- self.is_async = is_async
- assigned_stmts = protocols.for_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def assign_type(self):
- """The type of assignment that this node performs.
- :returns: The assignment type.
- :rtype: NodeNG
- """
- return self
- def _get_filtered_stmts(
- self, lookup_node, node, stmts, mystmt: _base_nodes.Statement | None
- ):
- """method used in filter_stmts"""
- if self is mystmt:
- if isinstance(lookup_node, (Const, Name)):
- return [lookup_node], True
- elif self.statement() is mystmt:
- # original node's statement is the assignment, only keeps
- # current node (gen exp, list comp)
- return [node], True
- return stmts, False
- def get_children(self):
- yield self.target
- yield self.iter
- yield from self.ifs
- class Const(_base_nodes.NoChildrenNode, Instance):
- """Class representing any constant including num, str, bool, None, bytes.
- >>> import astroid
- >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")')
- >>> node
- <Tuple.tuple l.1 at 0x7f23b2e358d0>
- >>> list(node.get_children())
- [<Const.int l.1 at 0x7f23b2e35940>,
- <Const.str l.1 at 0x7f23b2e35978>,
- <Const.bool l.1 at 0x7f23b2e359b0>,
- <Const.NoneType l.1 at 0x7f23b2e359e8>,
- <Const.bytes l.1 at 0x7f23b2e35a20>]
- """
- _other_fields = ("value", "kind")
- def __init__(
- self,
- value: Any,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG = SYNTHETIC_ROOT,
- kind: str | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param value: The value that the constant represents.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- if getattr(value, "__name__", None) == "__doc__":
- warnings.warn( # pragma: no cover
- "You have most likely called a __doc__ field of some object "
- "and it didn't return a string. "
- "That happens to some symbols from the standard library. "
- "Check for isinstance(<X>.__doc__, str).",
- RuntimeWarning,
- stacklevel=0,
- )
- self.value = value
- """The value that the constant represents."""
- self.kind: str | None = kind # can be None
- """"The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- Instance.__init__(self, None)
- infer_unary_op = protocols.const_infer_unary_op
- infer_binary_op = protocols.const_infer_binary_op
- def __getattr__(self, name):
- # This is needed because of Proxy's __getattr__ method.
- # Calling object.__new__ on this class without calling
- # __init__ would result in an infinite loop otherwise
- # since __getattr__ is called when an attribute doesn't
- # exist and self._proxied indirectly calls self.value
- # and Proxy __getattr__ calls self.value
- if name == "value":
- raise AttributeError
- return super().__getattr__(name)
- def getitem(self, index, context: InferenceContext | None = None):
- """Get an item from this node if subscriptable.
- :param index: The node to use as a subscript index.
- :type index: Const or Slice
- :raises AstroidTypeError: When the given index cannot be used as a
- subscript index, or if this node is not subscriptable.
- """
- if isinstance(index, Const):
- index_value = index.value
- elif isinstance(index, Slice):
- index_value = _infer_slice(index, context=context)
- else:
- raise AstroidTypeError(
- f"Could not use type {type(index)} as subscript index"
- )
- try:
- if isinstance(self.value, (str, bytes)):
- return Const(self.value[index_value])
- except ValueError as exc:
- raise AstroidValueError(
- f"Could not index {self.value!r} with {index_value!r}"
- ) from exc
- except IndexError as exc:
- raise AstroidIndexError(
- message="Index {index!r} out of range",
- node=self,
- index=index,
- context=context,
- ) from exc
- except TypeError as exc:
- raise AstroidTypeError(
- message="Type error {error!r}", node=self, index=index, context=context
- ) from exc
- raise AstroidTypeError(f"{self!r} (value={self.value})")
- def has_dynamic_getattr(self) -> bool:
- """Check if the node has a custom __getattr__ or __getattribute__.
- :returns: Whether the class has a custom __getattr__ or __getattribute__.
- For a :class:`Const` this is always ``False``.
- """
- return False
- def itered(self):
- """An iterator over the elements this node contains.
- :returns: The contents of this node.
- :rtype: iterable(Const)
- :raises TypeError: If this node does not represent something that is iterable.
- """
- if isinstance(self.value, str):
- return [const_factory(elem) for elem in self.value]
- raise TypeError(f"Cannot iterate over type {type(self.value)!r}")
- def pytype(self) -> str:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- return self._proxied.qname()
- def bool_value(self, context: InferenceContext | None = None):
- """Determine the boolean value of this node.
- :returns: The boolean value of this node.
- :rtype: bool or Uninferable
- """
- # bool(NotImplemented) is deprecated; it raises TypeError starting from Python 3.14
- # and returns True for versions under 3.14
- if self.value is NotImplemented:
- return util.Uninferable if PY314_PLUS else True
- return bool(self.value)
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[Const]:
- yield self
- class Continue(_base_nodes.NoChildrenNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Continue` node.
- >>> import astroid
- >>> node = astroid.extract_node('continue')
- >>> node
- <Continue l.1 at 0x7f23b2e35588>
- """
- class Decorators(NodeNG):
- """A node representing a list of decorators.
- A :class:`Decorators` is the decorators that are applied to
- a method or function.
- >>> import astroid
- >>> node = astroid.extract_node('''
- @property
- def my_property(self):
- return 3
- ''')
- >>> node
- <FunctionDef.my_property l.2 at 0x7f23b2e35d30>
- >>> list(node.get_children())[0]
- <Decorators l.1 at 0x7f23b2e35d68>
- """
- _astroid_fields = ("nodes",)
- nodes: list[NodeNG]
- """The decorators that this node contains."""
- def postinit(self, nodes: list[NodeNG]) -> None:
- self.nodes = nodes
- def scope(self) -> LocalsDictNodeNG:
- """The first parent node defining a new scope.
- These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
- :returns: The first parent scope node.
- """
- # skip the function node to go directly to the upper level scope
- if not self.parent:
- raise ParentMissingError(target=self)
- if not self.parent.parent:
- raise ParentMissingError(target=self.parent)
- return self.parent.parent.scope()
- def get_children(self):
- yield from self.nodes
- class DelAttr(_base_nodes.ParentAssignNode):
- """Variation of :class:`ast.Delete` representing deletion of an attribute.
- >>> import astroid
- >>> node = astroid.extract_node('del self.attr')
- >>> node
- <Delete l.1 at 0x7f23b2e35f60>
- >>> list(node.get_children())[0]
- <DelAttr.attr l.1 at 0x7f23b2e411d0>
- """
- _astroid_fields = ("expr",)
- _other_fields = ("attrname",)
- expr: NodeNG
- """The name that this node represents."""
- def __init__(
- self,
- attrname: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.attrname = attrname
- """The name of the attribute that is being deleted."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, expr: NodeNG) -> None:
- self.expr = expr
- def get_children(self):
- yield self.expr
- class Delete(_base_nodes.AssignTypeNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Delete` node.
- A :class:`Delete` is a ``del`` statement this is deleting something.
- >>> import astroid
- >>> node = astroid.extract_node('del self.attr')
- >>> node
- <Delete l.1 at 0x7f23b2e35f60>
- """
- _astroid_fields = ("targets",)
- def __init__(
- self,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.targets: list[NodeNG] = []
- """What is being deleted."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, targets: list[NodeNG]) -> None:
- self.targets = targets
- def get_children(self):
- yield from self.targets
- class Dict(NodeNG, Instance):
- """Class representing an :class:`ast.Dict` node.
- A :class:`Dict` is a dictionary that is created with ``{}`` syntax.
- >>> import astroid
- >>> node = astroid.extract_node('{1: "1"}')
- >>> node
- <Dict.dict l.1 at 0x7f23b2e35cc0>
- """
- _astroid_fields = ("items",)
- def __init__(
- self,
- lineno: int | None,
- col_offset: int | None,
- parent: NodeNG | None,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.items: list[tuple[InferenceResult, InferenceResult]] = []
- """The key-value pairs contained in the dictionary."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, items: list[tuple[InferenceResult, InferenceResult]]) -> None:
- """Do some setup after initialisation.
- :param items: The key-value pairs contained in the dictionary.
- """
- self.items = items
- infer_unary_op = protocols.dict_infer_unary_op
- def pytype(self) -> Literal["builtins.dict"]:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- return "builtins.dict"
- def get_children(self):
- """Get the key and value nodes below this node.
- Children are returned in the order that they are defined in the source
- code, key first then the value.
- :returns: The children.
- :rtype: iterable(NodeNG)
- """
- for key, value in self.items:
- yield key
- yield value
- def last_child(self):
- """An optimized version of list(get_children())[-1]
- :returns: The last child, or None if no children exist.
- :rtype: NodeNG or None
- """
- if self.items:
- return self.items[-1][1]
- return None
- def itered(self):
- """An iterator over the keys this node contains.
- :returns: The keys of this node.
- :rtype: iterable(NodeNG)
- """
- return [key for (key, _) in self.items]
- def getitem(
- self, index: Const | Slice, context: InferenceContext | None = None
- ) -> NodeNG:
- """Get an item from this node.
- :param index: The node to use as a subscript index.
- :raises AstroidTypeError: When the given index cannot be used as a
- subscript index, or if this node is not subscriptable.
- :raises AstroidIndexError: If the given index does not exist in the
- dictionary.
- """
- for key, value in self.items:
- # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}.
- if isinstance(key, DictUnpack):
- inferred_value = util.safe_infer(value, context)
- if not isinstance(inferred_value, Dict):
- continue
- try:
- return inferred_value.getitem(index, context)
- except (AstroidTypeError, AstroidIndexError):
- continue
- for inferredkey in key.infer(context):
- if isinstance(inferredkey, util.UninferableBase):
- continue
- if isinstance(inferredkey, Const) and isinstance(index, Const):
- if inferredkey.value == index.value:
- return value
- raise AstroidIndexError(index)
- def bool_value(self, context: InferenceContext | None = None):
- """Determine the boolean value of this node.
- :returns: The boolean value of this node.
- :rtype: bool
- """
- return bool(self.items)
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[nodes.Dict]:
- if not any(isinstance(k, DictUnpack) for k, _ in self.items):
- yield self
- else:
- items = self._infer_map(context)
- new_seq = type(self)(
- lineno=self.lineno,
- col_offset=self.col_offset,
- parent=self.parent,
- end_lineno=self.end_lineno,
- end_col_offset=self.end_col_offset,
- )
- new_seq.postinit(list(items.items()))
- yield new_seq
- @staticmethod
- def _update_with_replacement(
- lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
- rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
- ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
- """Delete nodes that equate to duplicate keys.
- Since an astroid node doesn't 'equal' another node with the same value,
- this function uses the as_string method to make sure duplicate keys
- don't get through
- Note that both the key and the value are astroid nodes
- Fixes issue with DictUnpack causing duplicate keys
- in inferred Dict items
- :param lhs_dict: Dictionary to 'merge' nodes into
- :param rhs_dict: Dictionary with nodes to pull from
- :return : merged dictionary of nodes
- """
- combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
- # Overwrite keys which have the same string values
- string_map = {key.as_string(): (key, value) for key, value in combined_dict}
- # Return to dictionary
- return dict(string_map.values())
- def _infer_map(
- self, context: InferenceContext | None
- ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
- """Infer all values based on Dict.items."""
- values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {}
- for name, value in self.items:
- if isinstance(name, DictUnpack):
- double_starred = util.safe_infer(value, context)
- if not double_starred:
- raise InferenceError
- if not isinstance(double_starred, Dict):
- raise InferenceError(node=self, context=context)
- unpack_items = double_starred._infer_map(context)
- values = self._update_with_replacement(values, unpack_items)
- else:
- key = util.safe_infer(name, context=context)
- safe_value = util.safe_infer(value, context=context)
- if any(not elem for elem in (key, safe_value)):
- raise InferenceError(node=self, context=context)
- # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False
- values = self._update_with_replacement(values, {key: safe_value})
- return values
- class Expr(_base_nodes.Statement):
- """Class representing an :class:`ast.Expr` node.
- An :class:`Expr` is any expression that does not have its value used or
- stored.
- >>> import astroid
- >>> node = astroid.extract_node('method()')
- >>> node
- <Call l.1 at 0x7f23b2e352b0>
- >>> node.parent
- <Expr l.1 at 0x7f23b2e35278>
- """
- _astroid_fields = ("value",)
- value: NodeNG
- """What the expression does."""
- def postinit(self, value: NodeNG) -> None:
- self.value = value
- def get_children(self):
- yield self.value
- def _get_yield_nodes_skip_functions(self):
- if not self.value.is_function:
- yield from self.value._get_yield_nodes_skip_functions()
- def _get_yield_nodes_skip_lambdas(self):
- if not self.value.is_lambda:
- yield from self.value._get_yield_nodes_skip_lambdas()
- class EmptyNode(_base_nodes.NoChildrenNode):
- """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""
- object = None
- def __init__(
- self,
- lineno: None = None,
- col_offset: None = None,
- parent: NodeNG = SYNTHETIC_ROOT,
- *,
- end_lineno: None = None,
- end_col_offset: None = None,
- ) -> None:
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def has_underlying_object(self) -> bool:
- return self.object is not None and self.object is not _EMPTY_OBJECT_MARKER
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- if not self.has_underlying_object():
- yield util.Uninferable
- else:
- try:
- yield from AstroidManager().infer_ast_from_something(
- self.object, context=context
- )
- except AstroidError:
- yield util.Uninferable
- class ExceptHandler(
- _base_nodes.MultiLineBlockNode, _base_nodes.AssignTypeNode, _base_nodes.Statement
- ):
- """Class representing an :class:`ast.ExceptHandler`. node.
- An :class:`ExceptHandler` is an ``except`` block on a try-except.
- >>> import astroid
- >>> node = astroid.extract_node('''
- try:
- do_something()
- except Exception as error:
- print("Error!")
- ''')
- >>> node
- <Try l.2 at 0x7f23b2e9d908>
- >>> node.handlers
- [<ExceptHandler l.4 at 0x7f23b2e9e860>]
- """
- _astroid_fields = ("type", "name", "body")
- _multi_line_block_fields = ("body",)
- type: NodeNG | None
- """The types that the block handles."""
- name: AssignName | None
- """The name that the caught exception is assigned to."""
- body: list[NodeNG]
- """The contents of the block."""
- assigned_stmts = protocols.excepthandler_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def postinit(
- self,
- type: NodeNG | None, # pylint: disable = redefined-builtin
- name: AssignName | None,
- body: list[NodeNG],
- ) -> None:
- self.type = type
- self.name = name
- self.body = body
- def get_children(self):
- if self.type is not None:
- yield self.type
- if self.name is not None:
- yield self.name
- yield from self.body
- @cached_property
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
- :type: int
- """
- if self.name:
- return self.name.tolineno
- if self.type:
- return self.type.tolineno
- return self.lineno
- def catch(self, exceptions: list[str] | None) -> bool:
- """Check if this node handles any of the given
- :param exceptions: The names of the exceptions to check for.
- """
- if self.type is None or exceptions is None:
- return True
- return any(node.name in exceptions for node in self.type._get_name_nodes())
- class For(
- _base_nodes.MultiLineWithElseBlockNode,
- _base_nodes.AssignTypeNode,
- _base_nodes.Statement,
- ):
- """Class representing an :class:`ast.For` node.
- >>> import astroid
- >>> node = astroid.extract_node('for thing in things: print(thing)')
- >>> node
- <For l.1 at 0x7f23b2e8cf28>
- """
- _astroid_fields = ("target", "iter", "body", "orelse")
- _other_other_fields = ("type_annotation",)
- _multi_line_block_fields = ("body", "orelse")
- optional_assign = True
- """Whether this node optionally assigns a variable.
- This is always ``True`` for :class:`For` nodes.
- """
- target: NodeNG
- """What the loop assigns to."""
- iter: NodeNG
- """What the loop iterates over."""
- body: list[NodeNG]
- """The contents of the body of the loop."""
- orelse: list[NodeNG]
- """The contents of the ``else`` block of the loop."""
- type_annotation: NodeNG | None
- """If present, this will contain the type annotation passed by a type comment"""
- def postinit(
- self,
- target: NodeNG,
- iter: NodeNG, # pylint: disable = redefined-builtin
- body: list[NodeNG],
- orelse: list[NodeNG],
- type_annotation: NodeNG | None,
- ) -> None:
- self.target = target
- self.iter = iter
- self.body = body
- self.orelse = orelse
- self.type_annotation = type_annotation
- assigned_stmts = protocols.for_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- @cached_property
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
- :type: int
- """
- return self.iter.tolineno
- def get_children(self):
- yield self.target
- yield self.iter
- yield from self.body
- yield from self.orelse
- class AsyncFor(For):
- """Class representing an :class:`ast.AsyncFor` node.
- An :class:`AsyncFor` is an asynchronous :class:`For` built with
- the ``async`` keyword.
- >>> import astroid
- >>> node = astroid.extract_node('''
- async def func(things):
- async for thing in things:
- print(thing)
- ''')
- >>> node
- <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
- >>> node.body[0]
- <AsyncFor l.3 at 0x7f23b2e417b8>
- """
- class Await(NodeNG):
- """Class representing an :class:`ast.Await` node.
- An :class:`Await` is the ``await`` keyword.
- >>> import astroid
- >>> node = astroid.extract_node('''
- async def func(things):
- await other_func()
- ''')
- >>> node
- <AsyncFunctionDef.func l.2 at 0x7f23b2e41748>
- >>> node.body[0]
- <Expr l.3 at 0x7f23b2e419e8>
- >>> list(node.body[0].get_children())[0]
- <Await l.3 at 0x7f23b2e41a20>
- """
- _astroid_fields = ("value",)
- value: NodeNG
- """What to wait for."""
- def postinit(self, value: NodeNG) -> None:
- self.value = value
- def get_children(self):
- yield self.value
- class ImportFrom(_base_nodes.ImportNode):
- """Class representing an :class:`ast.ImportFrom` node.
- >>> import astroid
- >>> node = astroid.extract_node('from my_package import my_module')
- >>> node
- <ImportFrom l.1 at 0x7f23b2e415c0>
- """
- _other_fields = ("modname", "names", "level")
- def __init__(
- self,
- fromname: str | None,
- names: list[tuple[str, str | None]],
- level: int | None = 0,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param fromname: The module that is being imported from.
- :param names: What is being imported from the module.
- :param level: The level of relative import.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.modname: str | None = fromname # can be None
- """The module that is being imported from.
- This is ``None`` for relative imports.
- """
- self.names: list[tuple[str, str | None]] = names
- """What is being imported from the module.
- Each entry is a :class:`tuple` of the name being imported,
- and the alias that the name is assigned to (if any).
- """
- # TODO When is 'level' None?
- self.level: int | None = level # can be None
- """The level of relative import.
- Essentially this is the number of dots in the import.
- This is always 0 for absolute imports.
- """
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self,
- context: InferenceContext | None = None,
- asname: bool = True,
- **kwargs: Any,
- ) -> Generator[InferenceResult]:
- """Infer a ImportFrom node: return the imported module/object."""
- context = context or InferenceContext()
- name = context.lookupname
- if name is None:
- raise InferenceError(node=self, context=context)
- if asname:
- try:
- name = self.real_name(name)
- except AttributeInferenceError as exc:
- # See https://github.com/pylint-dev/pylint/issues/4692
- raise InferenceError(node=self, context=context) from exc
- try:
- module = self.do_import_module()
- except AstroidBuildingError as exc:
- raise InferenceError(node=self, context=context) from exc
- try:
- context = copy_context(context)
- context.lookupname = name
- stmts = module.getattr(name, ignore_locals=module is self.root())
- return _infer_stmts(stmts, context)
- except AttributeInferenceError as error:
- raise InferenceError(
- str(error), target=self, attribute=name, context=context
- ) from error
- class Attribute(NodeNG):
- """Class representing an :class:`ast.Attribute` node."""
- expr: NodeNG
- _astroid_fields = ("expr",)
- _other_fields = ("attrname",)
- def __init__(
- self,
- attrname: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.attrname = attrname
- """The name of the attribute."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, expr: NodeNG) -> None:
- self.expr = expr
- def get_children(self):
- yield self.expr
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
- return _infer_attribute(self, context, **kwargs)
- class Global(_base_nodes.NoChildrenNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Global` node.
- >>> import astroid
- >>> node = astroid.extract_node('global a_global')
- >>> node
- <Global l.1 at 0x7f23b2e9de10>
- """
- _other_fields = ("names",)
- def __init__(
- self,
- names: list[str],
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param names: The names being declared as global.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.names: list[str] = names
- """The names being declared as global."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def _infer_name(self, frame, name):
- return name
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- if context is None or context.lookupname is None:
- raise InferenceError(node=self, context=context)
- try:
- # pylint: disable-next=no-member
- return _infer_stmts(self.root().getattr(context.lookupname), context)
- except AttributeInferenceError as error:
- raise InferenceError(
- str(error), target=self, attribute=context.lookupname, context=context
- ) from error
- class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
- """Class representing an :class:`ast.If` node.
- >>> import astroid
- >>> node = astroid.extract_node('if condition: print(True)')
- >>> node
- <If l.1 at 0x7f23b2e9dd30>
- """
- _astroid_fields = ("test", "body", "orelse")
- _multi_line_block_fields = ("body", "orelse")
- test: NodeNG
- """The condition that the statement tests."""
- body: list[NodeNG]
- """The contents of the block."""
- orelse: list[NodeNG]
- """The contents of the ``else`` block."""
- def postinit(self, test: NodeNG, body: list[NodeNG], orelse: list[NodeNG]) -> None:
- self.test = test
- self.body = body
- self.orelse = orelse
- @cached_property
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
- :type: int
- """
- return self.test.tolineno
- def block_range(self, lineno: int) -> tuple[int, int]:
- """Get a range from the given line number to where this node ends.
- :param lineno: The line number to start the range at.
- :returns: The range of line numbers that this node belongs to,
- starting at the given line number.
- """
- if lineno == self.body[0].fromlineno:
- return lineno, lineno
- if lineno <= self.body[-1].tolineno:
- return lineno, self.body[-1].tolineno
- return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1)
- def get_children(self):
- yield self.test
- yield from self.body
- yield from self.orelse
- def has_elif_block(self) -> bool:
- return len(self.orelse) == 1 and isinstance(self.orelse[0], If)
- def _get_yield_nodes_skip_functions(self):
- """An If node can contain a Yield node in the test"""
- yield from self.test._get_yield_nodes_skip_functions()
- yield from super()._get_yield_nodes_skip_functions()
- def _get_yield_nodes_skip_lambdas(self):
- """An If node can contain a Yield node in the test"""
- yield from self.test._get_yield_nodes_skip_lambdas()
- yield from super()._get_yield_nodes_skip_lambdas()
- class IfExp(NodeNG):
- """Class representing an :class:`ast.IfExp` node.
- >>> import astroid
- >>> node = astroid.extract_node('value if condition else other')
- >>> node
- <IfExp l.1 at 0x7f23b2e9dbe0>
- """
- _astroid_fields = ("test", "body", "orelse")
- test: NodeNG
- """The condition that the statement tests."""
- body: NodeNG
- """The contents of the block."""
- orelse: NodeNG
- """The contents of the ``else`` block."""
- def postinit(self, test: NodeNG, body: NodeNG, orelse: NodeNG) -> None:
- self.test = test
- self.body = body
- self.orelse = orelse
- def get_children(self):
- yield self.test
- yield self.body
- yield self.orelse
- def op_left_associative(self) -> Literal[False]:
- # `1 if True else 2 if False else 3` is parsed as
- # `1 if True else (2 if False else 3)`
- return False
- @decorators.raise_if_nothing_inferred
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult]:
- """Support IfExp inference.
- If we can't infer the truthiness of the condition, we default
- to inferring both branches. Otherwise, we infer either branch
- depending on the condition.
- """
- # We use two separate contexts for evaluating lhs and rhs because
- # evaluating lhs may leave some undesired entries in context.path
- # which may not let us infer right value of rhs.
- context = context or InferenceContext()
- lhs_context = copy_context(context)
- rhs_context = copy_context(context)
- # Infer bool condition. Stop inferring if in doubt and fallback to
- # evaluating both branches.
- condition: bool | None = None
- try:
- for test in self.test.infer(context=context.clone()):
- if isinstance(test, util.UninferableBase):
- condition = None
- break
- test_bool_value = test.bool_value()
- if isinstance(test_bool_value, util.UninferableBase):
- condition = None
- break
- if condition is None:
- condition = test_bool_value
- elif test_bool_value != condition:
- condition = None
- break
- except InferenceError:
- condition = None
- if condition is True or condition is None:
- yield from self.body.infer(context=lhs_context)
- if condition is False or condition is None:
- yield from self.orelse.infer(context=rhs_context)
- class Import(_base_nodes.ImportNode):
- """Class representing an :class:`ast.Import` node.
- >>> import astroid
- >>> node = astroid.extract_node('import astroid')
- >>> node
- <Import l.1 at 0x7f23b2e4e5c0>
- """
- _other_fields = ("names",)
- def __init__(
- self,
- names: list[tuple[str, str | None]],
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param names: The names being imported.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.names: list[tuple[str, str | None]] = names
- """The names being imported.
- Each entry is a :class:`tuple` of the name being imported,
- and the alias that the name is assigned to (if any).
- """
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self,
- context: InferenceContext | None = None,
- asname: bool = True,
- **kwargs: Any,
- ) -> Generator[nodes.Module]:
- """Infer an Import node: return the imported module/object."""
- context = context or InferenceContext()
- name = context.lookupname
- if name is None:
- raise InferenceError(node=self, context=context)
- try:
- if asname:
- yield self.do_import_module(self.real_name(name))
- else:
- yield self.do_import_module(name)
- except AstroidBuildingError as exc:
- raise InferenceError(node=self, context=context) from exc
- class Keyword(NodeNG):
- """Class representing an :class:`ast.keyword` node.
- >>> import astroid
- >>> node = astroid.extract_node('function(a_kwarg=True)')
- >>> node
- <Call l.1 at 0x7f23b2e9e320>
- >>> node.keywords
- [<Keyword l.1 at 0x7f23b2e9e9b0>]
- """
- _astroid_fields = ("value",)
- _other_fields = ("arg",)
- value: NodeNG
- """The value being assigned to the keyword argument."""
- def __init__(
- self,
- arg: str | None,
- lineno: int | None,
- col_offset: int | None,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.arg = arg
- """The argument being assigned to."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, value: NodeNG) -> None:
- self.value = value
- def get_children(self):
- yield self.value
- class List(BaseContainer):
- """Class representing an :class:`ast.List` node.
- >>> import astroid
- >>> node = astroid.extract_node('[1, 2, 3]')
- >>> node
- <List.list l.1 at 0x7f23b2e9e128>
- """
- _other_fields = ("ctx",)
- def __init__(
- self,
- ctx: Context | None = None,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param ctx: Whether the list is assigned to or loaded from.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.ctx: Context | None = ctx
- """Whether the list is assigned to or loaded from."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- assigned_stmts = protocols.sequence_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- infer_unary_op = protocols.list_infer_unary_op
- infer_binary_op = protocols.tl_infer_binary_op
- def pytype(self) -> Literal["builtins.list"]:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- return "builtins.list"
- def getitem(self, index, context: InferenceContext | None = None):
- """Get an item from this node.
- :param index: The node to use as a subscript index.
- :type index: Const or Slice
- """
- return _container_getitem(self, self.elts, index, context=context)
- class Nonlocal(_base_nodes.NoChildrenNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Nonlocal` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- def function():
- nonlocal var
- ''')
- >>> node
- <FunctionDef.function l.2 at 0x7f23b2e9e208>
- >>> node.body[0]
- <Nonlocal l.3 at 0x7f23b2e9e908>
- """
- _other_fields = ("names",)
- def __init__(
- self,
- names: list[str],
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param names: The names being declared as not local.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.names: list[str] = names
- """The names being declared as not local."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def _infer_name(self, frame, name):
- return name
- class ParamSpec(_base_nodes.AssignTypeNode):
- """Class representing a :class:`ast.ParamSpec` node.
- >>> import astroid
- >>> node = astroid.extract_node('type Alias[**P] = Callable[P, int]')
- >>> node.type_params[0]
- <ParamSpec l.1 at 0x7f23b2e4e198>
- """
- _astroid_fields = ("name", "default_value")
- name: AssignName
- default_value: NodeNG | None
- def __init__(
- self,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int,
- end_col_offset: int,
- ) -> None:
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, *, name: AssignName, default_value: NodeNG | None) -> None:
- self.name = name
- self.default_value = default_value
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[ParamSpec]:
- yield self
- assigned_stmts = protocols.generic_type_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement):
- """Class representing an :class:`ast.Pass` node.
- >>> import astroid
- >>> node = astroid.extract_node('pass')
- >>> node
- <Pass l.1 at 0x7f23b2e9e748>
- """
- class Raise(_base_nodes.Statement):
- """Class representing an :class:`ast.Raise` node.
- >>> import astroid
- >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")')
- >>> node
- <Raise l.1 at 0x7f23b2e9e828>
- """
- _astroid_fields = ("exc", "cause")
- exc: NodeNG | None
- """What is being raised."""
- cause: NodeNG | None
- """The exception being used to raise this one."""
- def postinit(
- self,
- exc: NodeNG | None,
- cause: NodeNG | None,
- ) -> None:
- self.exc = exc
- self.cause = cause
- def raises_not_implemented(self) -> bool:
- """Check if this node raises a :class:`NotImplementedError`.
- :returns: Whether this node raises a :class:`NotImplementedError`.
- """
- if not self.exc:
- return False
- return any(
- name.name == "NotImplementedError" for name in self.exc._get_name_nodes()
- )
- def get_children(self):
- if self.exc is not None:
- yield self.exc
- if self.cause is not None:
- yield self.cause
- class Return(_base_nodes.Statement):
- """Class representing an :class:`ast.Return` node.
- >>> import astroid
- >>> node = astroid.extract_node('return True')
- >>> node
- <Return l.1 at 0x7f23b8211908>
- """
- _astroid_fields = ("value",)
- value: NodeNG | None
- """The value being returned."""
- def postinit(self, value: NodeNG | None) -> None:
- self.value = value
- def get_children(self):
- if self.value is not None:
- yield self.value
- def is_tuple_return(self) -> bool:
- return isinstance(self.value, Tuple)
- def _get_return_nodes_skip_functions(self):
- yield self
- class Set(BaseContainer):
- """Class representing an :class:`ast.Set` node.
- >>> import astroid
- >>> node = astroid.extract_node('{1, 2, 3}')
- >>> node
- <Set.set l.1 at 0x7f23b2e71d68>
- """
- infer_unary_op = protocols.set_infer_unary_op
- def pytype(self) -> Literal["builtins.set"]:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- return "builtins.set"
- class Slice(NodeNG):
- """Class representing an :class:`ast.Slice` node.
- >>> import astroid
- >>> node = astroid.extract_node('things[1:3]')
- >>> node
- <Subscript l.1 at 0x7f23b2e71f60>
- >>> node.slice
- <Slice l.1 at 0x7f23b2e71e80>
- """
- _astroid_fields = ("lower", "upper", "step")
- lower: NodeNG | None
- """The lower index in the slice."""
- upper: NodeNG | None
- """The upper index in the slice."""
- step: NodeNG | None
- """The step to take between indexes."""
- def postinit(
- self,
- lower: NodeNG | None,
- upper: NodeNG | None,
- step: NodeNG | None,
- ) -> None:
- self.lower = lower
- self.upper = upper
- self.step = step
- def _wrap_attribute(self, attr):
- """Wrap the empty attributes of the Slice in a Const node."""
- if not attr:
- const = const_factory(attr)
- const.parent = self
- return const
- return attr
- @cached_property
- def _proxied(self) -> nodes.ClassDef:
- builtins = AstroidManager().builtins_module
- return builtins.getattr("slice")[0]
- def pytype(self) -> Literal["builtins.slice"]:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- return "builtins.slice"
- def display_type(self) -> Literal["Slice"]:
- """A human readable type of this node.
- :returns: The type of this node.
- """
- return "Slice"
- def igetattr(
- self, attrname: str, context: InferenceContext | None = None
- ) -> Iterator[SuccessfulInferenceResult]:
- """Infer the possible values of the given attribute on the slice.
- :param attrname: The name of the attribute to infer.
- :returns: The inferred possible values.
- """
- if attrname == "start":
- yield self._wrap_attribute(self.lower)
- elif attrname == "stop":
- yield self._wrap_attribute(self.upper)
- elif attrname == "step":
- yield self._wrap_attribute(self.step)
- else:
- yield from self.getattr(attrname, context=context)
- def getattr(self, attrname, context: InferenceContext | None = None):
- return self._proxied.getattr(attrname, context)
- def get_children(self):
- if self.lower is not None:
- yield self.lower
- if self.upper is not None:
- yield self.upper
- if self.step is not None:
- yield self.step
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[Slice]:
- yield self
- class Starred(_base_nodes.ParentAssignNode):
- """Class representing an :class:`ast.Starred` node.
- >>> import astroid
- >>> node = astroid.extract_node('*args')
- >>> node
- <Starred l.1 at 0x7f23b2e41978>
- """
- _astroid_fields = ("value",)
- _other_fields = ("ctx",)
- value: NodeNG
- """What is being unpacked."""
- def __init__(
- self,
- ctx: Context,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.ctx = ctx
- """Whether the starred item is assigned to or loaded from."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, value: NodeNG) -> None:
- self.value = value
- assigned_stmts = protocols.starred_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def get_children(self):
- yield self.value
- class Subscript(NodeNG):
- """Class representing an :class:`ast.Subscript` node.
- >>> import astroid
- >>> node = astroid.extract_node('things[1:3]')
- >>> node
- <Subscript l.1 at 0x7f23b2e71f60>
- """
- _SUBSCRIPT_SENTINEL = object()
- _astroid_fields = ("value", "slice")
- _other_fields = ("ctx",)
- value: NodeNG
- """What is being indexed."""
- slice: NodeNG
- """The slice being used to lookup."""
- def __init__(
- self,
- ctx: Context,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.ctx = ctx
- """Whether the subscripted item is assigned to or loaded from."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
- def postinit(self, value: NodeNG, slice: NodeNG) -> None:
- self.value = value
- self.slice = slice
- def get_children(self):
- yield self.value
- yield self.slice
- def _infer_subscript(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- """Inference for subscripts.
- We're understanding if the index is a Const
- or a slice, passing the result of inference
- to the value's `getitem` method, which should
- handle each supported index type accordingly.
- """
- from astroid import helpers # pylint: disable=import-outside-toplevel
- found_one = False
- for value in self.value.infer(context):
- if isinstance(value, util.UninferableBase):
- yield util.Uninferable
- return None
- for index in self.slice.infer(context):
- if isinstance(index, util.UninferableBase):
- yield util.Uninferable
- return None
- # Try to deduce the index value.
- index_value = self._SUBSCRIPT_SENTINEL
- if value.__class__ == Instance:
- index_value = index
- elif index.__class__ == Instance:
- instance_as_index = helpers.class_instance_as_index(index)
- if instance_as_index:
- index_value = instance_as_index
- else:
- index_value = index
- if index_value is self._SUBSCRIPT_SENTINEL:
- raise InferenceError(node=self, context=context)
- try:
- assigned = value.getitem(index_value, context)
- except (
- AstroidTypeError,
- AstroidIndexError,
- AstroidValueError,
- AttributeInferenceError,
- AttributeError,
- ) as exc:
- raise InferenceError(node=self, context=context) from exc
- # Prevent inferring if the inferred subscript
- # is the same as the original subscripted object.
- if self is assigned or isinstance(assigned, util.UninferableBase):
- yield util.Uninferable
- return None
- yield from assigned.infer(context)
- found_one = True
- if found_one:
- return InferenceErrorInfo(node=self, context=context)
- return None
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
- return self._infer_subscript(context, **kwargs)
- @decorators.raise_if_nothing_inferred
- def infer_lhs(self, context: InferenceContext | None = None, **kwargs: Any):
- return self._infer_subscript(context, **kwargs)
- class Try(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
- """Class representing a :class:`ast.Try` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- try:
- do_something()
- except Exception as error:
- print("Error!")
- finally:
- print("Cleanup!")
- ''')
- >>> node
- <Try l.2 at 0x7f23b2e41d68>
- """
- _astroid_fields = ("body", "handlers", "orelse", "finalbody")
- _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
- def __init__(
- self,
- *,
- lineno: int,
- col_offset: int,
- end_lineno: int,
- end_col_offset: int,
- parent: NodeNG,
- ) -> None:
- """
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.body: list[NodeNG] = []
- """The contents of the block to catch exceptions from."""
- self.handlers: list[ExceptHandler] = []
- """The exception handlers."""
- self.orelse: list[NodeNG] = []
- """The contents of the ``else`` block."""
- self.finalbody: list[NodeNG] = []
- """The contents of the ``finally`` block."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- body: list[NodeNG],
- handlers: list[ExceptHandler],
- orelse: list[NodeNG],
- finalbody: list[NodeNG],
- ) -> None:
- """Do some setup after initialisation.
- :param body: The contents of the block to catch exceptions from.
- :param handlers: The exception handlers.
- :param orelse: The contents of the ``else`` block.
- :param finalbody: The contents of the ``finally`` block.
- """
- self.body = body
- self.handlers = handlers
- self.orelse = orelse
- self.finalbody = finalbody
- def _infer_name(self, frame, name):
- return name
- def block_range(self, lineno: int) -> tuple[int, int]:
- """Get a range from a given line number to where this node ends."""
- if lineno == self.fromlineno:
- return lineno, lineno
- if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
- # Inside try body - return from lineno till end of try body
- return lineno, self.body[-1].tolineno
- for exhandler in self.handlers:
- if exhandler.type and lineno == exhandler.type.fromlineno:
- return lineno, lineno
- if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
- return lineno, exhandler.body[-1].tolineno
- if self.orelse:
- if self.orelse[0].fromlineno - 1 == lineno:
- return lineno, lineno
- if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
- return lineno, self.orelse[-1].tolineno
- if self.finalbody:
- if self.finalbody[0].fromlineno - 1 == lineno:
- return lineno, lineno
- if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
- return lineno, self.finalbody[-1].tolineno
- return lineno, self.tolineno
- def get_children(self):
- yield from self.body
- yield from self.handlers
- yield from self.orelse
- yield from self.finalbody
- class TryStar(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
- """Class representing an :class:`ast.TryStar` node."""
- _astroid_fields = ("body", "handlers", "orelse", "finalbody")
- _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
- def __init__(
- self,
- *,
- lineno: int | None = None,
- col_offset: int | None = None,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- parent: NodeNG | None = None,
- ) -> None:
- """
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.body: list[NodeNG] = []
- """The contents of the block to catch exceptions from."""
- self.handlers: list[ExceptHandler] = []
- """The exception handlers."""
- self.orelse: list[NodeNG] = []
- """The contents of the ``else`` block."""
- self.finalbody: list[NodeNG] = []
- """The contents of the ``finally`` block."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- body: list[NodeNG] | None = None,
- handlers: list[ExceptHandler] | None = None,
- orelse: list[NodeNG] | None = None,
- finalbody: list[NodeNG] | None = None,
- ) -> None:
- """Do some setup after initialisation.
- :param body: The contents of the block to catch exceptions from.
- :param handlers: The exception handlers.
- :param orelse: The contents of the ``else`` block.
- :param finalbody: The contents of the ``finally`` block.
- """
- if body:
- self.body = body
- if handlers:
- self.handlers = handlers
- if orelse:
- self.orelse = orelse
- if finalbody:
- self.finalbody = finalbody
- def _infer_name(self, frame, name):
- return name
- def block_range(self, lineno: int) -> tuple[int, int]:
- """Get a range from a given line number to where this node ends."""
- if lineno == self.fromlineno:
- return lineno, lineno
- if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
- # Inside try body - return from lineno till end of try body
- return lineno, self.body[-1].tolineno
- for exhandler in self.handlers:
- if exhandler.type and lineno == exhandler.type.fromlineno:
- return lineno, lineno
- if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
- return lineno, exhandler.body[-1].tolineno
- if self.orelse:
- if self.orelse[0].fromlineno - 1 == lineno:
- return lineno, lineno
- if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
- return lineno, self.orelse[-1].tolineno
- if self.finalbody:
- if self.finalbody[0].fromlineno - 1 == lineno:
- return lineno, lineno
- if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
- return lineno, self.finalbody[-1].tolineno
- return lineno, self.tolineno
- def get_children(self):
- yield from self.body
- yield from self.handlers
- yield from self.orelse
- yield from self.finalbody
- class Tuple(BaseContainer):
- """Class representing an :class:`ast.Tuple` node.
- >>> import astroid
- >>> node = astroid.extract_node('(1, 2, 3)')
- >>> node
- <Tuple.tuple l.1 at 0x7f23b2e41780>
- """
- _other_fields = ("ctx",)
- def __init__(
- self,
- ctx: Context | None = None,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param ctx: Whether the tuple is assigned to or loaded from.
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.ctx: Context | None = ctx
- """Whether the tuple is assigned to or loaded from."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- assigned_stmts = protocols.sequence_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- infer_unary_op = protocols.tuple_infer_unary_op
- infer_binary_op = protocols.tl_infer_binary_op
- def pytype(self) -> Literal["builtins.tuple"]:
- """Get the name of the type that this node represents.
- :returns: The name of the type.
- """
- return "builtins.tuple"
- def getitem(self, index, context: InferenceContext | None = None):
- """Get an item from this node.
- :param index: The node to use as a subscript index.
- :type index: Const or Slice
- """
- return _container_getitem(self, self.elts, index, context=context)
- class TypeAlias(_base_nodes.AssignTypeNode, _base_nodes.Statement):
- """Class representing a :class:`ast.TypeAlias` node.
- >>> import astroid
- >>> node = astroid.extract_node('type Point = tuple[float, float]')
- >>> node
- <TypeAlias l.1 at 0x7f23b2e4e198>
- """
- _astroid_fields = ("name", "type_params", "value")
- name: AssignName
- type_params: list[TypeVar | ParamSpec | TypeVarTuple]
- value: NodeNG
- def __init__(
- self,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int,
- end_col_offset: int,
- ) -> None:
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- name: AssignName,
- type_params: list[TypeVar | ParamSpec | TypeVarTuple],
- value: NodeNG,
- ) -> None:
- self.name = name
- self.type_params = type_params
- self.value = value
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[TypeAlias]:
- yield self
- assigned_stmts: ClassVar[
- Callable[
- [
- TypeAlias,
- AssignName,
- InferenceContext | None,
- None,
- ],
- Generator[NodeNG],
- ]
- ] = protocols.assign_assigned_stmts
- class TypeVar(_base_nodes.AssignTypeNode):
- """Class representing a :class:`ast.TypeVar` node.
- >>> import astroid
- >>> node = astroid.extract_node('type Point[T] = tuple[float, float]')
- >>> node.type_params[0]
- <TypeVar l.1 at 0x7f23b2e4e198>
- """
- _astroid_fields = ("name", "bound", "default_value")
- name: AssignName
- bound: NodeNG | None
- default_value: NodeNG | None
- def __init__(
- self,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int,
- end_col_offset: int,
- ) -> None:
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- name: AssignName,
- bound: NodeNG | None,
- default_value: NodeNG | None = None,
- ) -> None:
- self.name = name
- self.bound = bound
- self.default_value = default_value
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[TypeVar]:
- yield self
- assigned_stmts = protocols.generic_type_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- class TypeVarTuple(_base_nodes.AssignTypeNode):
- """Class representing a :class:`ast.TypeVarTuple` node.
- >>> import astroid
- >>> node = astroid.extract_node('type Alias[*Ts] = tuple[*Ts]')
- >>> node.type_params[0]
- <TypeVarTuple l.1 at 0x7f23b2e4e198>
- """
- _astroid_fields = ("name", "default_value")
- name: AssignName
- default_value: NodeNG | None
- def __init__(
- self,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int,
- end_col_offset: int,
- ) -> None:
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self, *, name: AssignName, default_value: NodeNG | None = None
- ) -> None:
- self.name = name
- self.default_value = default_value
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Iterator[TypeVarTuple]:
- yield self
- assigned_stmts = protocols.generic_type_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- UNARY_OP_METHOD = {
- "+": "__pos__",
- "-": "__neg__",
- "~": "__invert__",
- "not": None, # XXX not '__nonzero__'
- }
- class UnaryOp(_base_nodes.OperatorNode):
- """Class representing an :class:`ast.UnaryOp` node.
- >>> import astroid
- >>> node = astroid.extract_node('-5')
- >>> node
- <UnaryOp l.1 at 0x7f23b2e4e198>
- """
- _astroid_fields = ("operand",)
- _other_fields = ("op",)
- operand: NodeNG
- """What the unary operator is applied to."""
- def __init__(
- self,
- op: str,
- lineno: int,
- col_offset: int,
- parent: NodeNG,
- *,
- end_lineno: int | None,
- end_col_offset: int | None,
- ) -> None:
- self.op = op
- """The operator."""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, operand: NodeNG) -> None:
- self.operand = operand
- def type_errors(
- self, context: InferenceContext | None = None
- ) -> list[util.BadUnaryOperationMessage]:
- """Get a list of type errors which can occur during inference.
- Each TypeError is represented by a :class:`BadUnaryOperationMessage`,
- which holds the original exception.
- If any inferred result is uninferable, an empty list is returned.
- """
- bad = []
- try:
- for result in self._infer_unaryop(context=context):
- if result is util.Uninferable:
- raise InferenceError
- if isinstance(result, util.BadUnaryOperationMessage):
- bad.append(result)
- except InferenceError:
- return []
- return bad
- def get_children(self):
- yield self.operand
- def op_precedence(self) -> int:
- if self.op == "not":
- return OP_PRECEDENCE[self.op]
- return super().op_precedence()
- def _infer_unaryop(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[
- InferenceResult | util.BadUnaryOperationMessage, None, InferenceErrorInfo
- ]:
- """Infer what an UnaryOp should return when evaluated."""
- from astroid.nodes import ClassDef # pylint: disable=import-outside-toplevel
- for operand in self.operand.infer(context):
- try:
- yield operand.infer_unary_op(self.op)
- except TypeError as exc:
- # The operand doesn't support this operation.
- yield util.BadUnaryOperationMessage(operand, self.op, exc)
- except AttributeError as exc:
- meth = UNARY_OP_METHOD[self.op]
- if meth is None:
- # `not node`. Determine node's boolean
- # value and negate its result, unless it is
- # Uninferable, which will be returned as is.
- bool_value = operand.bool_value()
- if not isinstance(bool_value, util.UninferableBase):
- yield const_factory(not bool_value)
- else:
- yield util.Uninferable
- else:
- if not isinstance(operand, (Instance, ClassDef)):
- # The operation was used on something which
- # doesn't support it.
- yield util.BadUnaryOperationMessage(operand, self.op, exc)
- continue
- try:
- try:
- methods = dunder_lookup.lookup(operand, meth)
- except AttributeInferenceError:
- yield util.BadUnaryOperationMessage(operand, self.op, exc)
- continue
- meth = methods[0]
- inferred = next(meth.infer(context=context), None)
- if (
- isinstance(inferred, util.UninferableBase)
- or not inferred.callable()
- ):
- continue
- context = copy_context(context)
- context.boundnode = operand
- context.callcontext = CallContext(args=[], callee=inferred)
- call_results = inferred.infer_call_result(self, context=context)
- result = next(call_results, None)
- if result is None:
- # Failed to infer, return the same type.
- yield operand
- else:
- yield result
- except AttributeInferenceError as inner_exc:
- # The unary operation special method was not found.
- yield util.BadUnaryOperationMessage(operand, self.op, inner_exc)
- except InferenceError:
- yield util.Uninferable
- @decorators.raise_if_nothing_inferred
- @decorators.path_wrapper
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
- """Infer what an UnaryOp should return when evaluated."""
- yield from self._filter_operation_errors(
- self._infer_unaryop, context, util.BadUnaryOperationMessage
- )
- return InferenceErrorInfo(node=self, context=context)
- class While(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
- """Class representing an :class:`ast.While` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- while condition():
- print("True")
- ''')
- >>> node
- <While l.2 at 0x7f23b2e4e390>
- """
- _astroid_fields = ("test", "body", "orelse")
- _multi_line_block_fields = ("body", "orelse")
- test: NodeNG
- """The condition that the loop tests."""
- body: list[NodeNG]
- """The contents of the loop."""
- orelse: list[NodeNG]
- """The contents of the ``else`` block."""
- def postinit(
- self,
- test: NodeNG,
- body: list[NodeNG],
- orelse: list[NodeNG],
- ) -> None:
- self.test = test
- self.body = body
- self.orelse = orelse
- @cached_property
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
- :type: int
- """
- return self.test.tolineno
- def block_range(self, lineno: int) -> tuple[int, int]:
- """Get a range from the given line number to where this node ends.
- :param lineno: The line number to start the range at.
- :returns: The range of line numbers that this node belongs to,
- starting at the given line number.
- """
- return self._elsed_block_range(lineno, self.orelse)
- def get_children(self):
- yield self.test
- yield from self.body
- yield from self.orelse
- def _get_yield_nodes_skip_functions(self):
- """A While node can contain a Yield node in the test"""
- yield from self.test._get_yield_nodes_skip_functions()
- yield from super()._get_yield_nodes_skip_functions()
- def _get_yield_nodes_skip_lambdas(self):
- """A While node can contain a Yield node in the test"""
- yield from self.test._get_yield_nodes_skip_lambdas()
- yield from super()._get_yield_nodes_skip_lambdas()
- class With(
- _base_nodes.MultiLineWithElseBlockNode,
- _base_nodes.AssignTypeNode,
- _base_nodes.Statement,
- ):
- """Class representing an :class:`ast.With` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- with open(file_path) as file_:
- print(file_.read())
- ''')
- >>> node
- <With l.2 at 0x7f23b2e4e710>
- """
- _astroid_fields = ("items", "body")
- _other_other_fields = ("type_annotation",)
- _multi_line_block_fields = ("body",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.items: list[tuple[NodeNG, NodeNG | None]] = []
- """The pairs of context managers and the names they are assigned to."""
- self.body: list[NodeNG] = []
- """The contents of the ``with`` block."""
- self.type_annotation: NodeNG | None = None # can be None
- """If present, this will contain the type annotation passed by a type comment"""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- items: list[tuple[NodeNG, NodeNG | None]] | None = None,
- body: list[NodeNG] | None = None,
- type_annotation: NodeNG | None = None,
- ) -> None:
- """Do some setup after initialisation.
- :param items: The pairs of context managers and the names
- they are assigned to.
- :param body: The contents of the ``with`` block.
- """
- if items is not None:
- self.items = items
- if body is not None:
- self.body = body
- self.type_annotation = type_annotation
- assigned_stmts = protocols.with_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- @cached_property
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
- :type: int
- """
- return self.items[-1][0].tolineno
- def get_children(self):
- """Get the child nodes below this node.
- :returns: The children.
- :rtype: iterable(NodeNG)
- """
- for expr, var in self.items:
- yield expr
- if var:
- yield var
- yield from self.body
- class AsyncWith(With):
- """Asynchronous ``with`` built with the ``async`` keyword."""
- class Yield(NodeNG):
- """Class representing an :class:`ast.Yield` node.
- >>> import astroid
- >>> node = astroid.extract_node('yield True')
- >>> node
- <Yield l.1 at 0x7f23b2e4e5f8>
- """
- _astroid_fields = ("value",)
- value: NodeNG | None
- """The value to yield."""
- def postinit(self, value: NodeNG | None) -> None:
- self.value = value
- def get_children(self):
- if self.value is not None:
- yield self.value
- def _get_yield_nodes_skip_functions(self):
- yield self
- def _get_yield_nodes_skip_lambdas(self):
- yield self
- class YieldFrom(Yield): # TODO value is required, not optional
- """Class representing an :class:`ast.YieldFrom` node."""
- class DictUnpack(_base_nodes.NoChildrenNode):
- """Represents the unpacking of dicts into dicts using :pep:`448`."""
- class FormattedValue(NodeNG):
- """Class representing an :class:`ast.FormattedValue` node.
- Represents a :pep:`498` format string.
- >>> import astroid
- >>> node = astroid.extract_node('f"Format {type_}"')
- >>> node
- <JoinedStr l.1 at 0x7f23b2e4ed30>
- >>> node.values
- [<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>]
- """
- _astroid_fields = ("value", "format_spec")
- _other_fields = ("conversion",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.value: NodeNG
- """The value to be formatted into the string."""
- self.conversion: int
- """The type of formatting to be applied to the value.
- .. seealso::
- :class:`ast.FormattedValue`
- """
- self.format_spec: JoinedStr | None = None
- """The formatting to be applied to the value.
- .. seealso::
- :class:`ast.FormattedValue`
- """
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- value: NodeNG,
- conversion: int,
- format_spec: JoinedStr | None = None,
- ) -> None:
- """Do some setup after initialisation.
- :param value: The value to be formatted into the string.
- :param conversion: The type of formatting to be applied to the value.
- :param format_spec: The formatting to be applied to the value.
- :type format_spec: JoinedStr or None
- """
- self.value = value
- self.conversion = conversion
- self.format_spec = format_spec
- def get_children(self):
- yield self.value
- if self.format_spec is not None:
- yield self.format_spec
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- format_specs = Const("") if self.format_spec is None else self.format_spec
- uninferable_already_generated = False
- for format_spec in format_specs.infer(context, **kwargs):
- if not isinstance(format_spec, Const):
- if not uninferable_already_generated:
- yield util.Uninferable
- uninferable_already_generated = True
- continue
- for value in self.value.infer(context, **kwargs):
- value_to_format = value
- if isinstance(value, Const):
- value_to_format = value.value
- try:
- formatted = format(value_to_format, format_spec.value)
- yield Const(
- formatted,
- lineno=self.lineno,
- col_offset=self.col_offset,
- end_lineno=self.end_lineno,
- end_col_offset=self.end_col_offset,
- )
- continue
- except (ValueError, TypeError):
- # happens when format_spec.value is invalid
- yield util.Uninferable
- uninferable_already_generated = True
- continue
- UNINFERABLE_VALUE = "{Uninferable}"
- class JoinedStr(NodeNG):
- """Represents a list of string expressions to be joined.
- >>> import astroid
- >>> node = astroid.extract_node('f"Format {type_}"')
- >>> node
- <JoinedStr l.1 at 0x7f23b2e4ed30>
- """
- _astroid_fields = ("values",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.values: list[NodeNG] = []
- """The string expressions to be joined.
- :type: list(FormattedValue or Const)
- """
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, values: list[NodeNG] | None = None) -> None:
- """Do some setup after initialisation.
- :param value: The string expressions to be joined.
- :type: list(FormattedValue or Const)
- """
- if values is not None:
- self.values = values
- def get_children(self):
- yield from self.values
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- if self.values:
- yield from self._infer_with_values(context)
- else:
- yield Const("")
- def _infer_with_values(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- uninferable_already_generated = False
- for inferred in self._infer_from_values(self.values, context):
- failed = inferred is util.Uninferable or (
- isinstance(inferred, Const) and UNINFERABLE_VALUE in inferred.value
- )
- if failed:
- if not uninferable_already_generated:
- uninferable_already_generated = True
- yield util.Uninferable
- continue
- yield inferred
- @classmethod
- def _infer_from_values(
- cls, nodes: list[NodeNG], context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- if not nodes:
- return
- if len(nodes) == 1:
- for node in cls._safe_infer_from_node(nodes[0], context, **kwargs):
- if isinstance(node, Const):
- yield node
- continue
- yield Const(UNINFERABLE_VALUE)
- return
- for prefix in cls._safe_infer_from_node(nodes[0], context, **kwargs):
- for suffix in cls._infer_from_values(nodes[1:], context, **kwargs):
- result = ""
- for node in (prefix, suffix):
- if isinstance(node, Const):
- result += str(node.value)
- continue
- result += UNINFERABLE_VALUE
- yield Const(result)
- @classmethod
- def _safe_infer_from_node(
- cls, node: NodeNG, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
- try:
- yield from node._infer(context, **kwargs)
- except InferenceError:
- yield util.Uninferable
- class NamedExpr(_base_nodes.AssignTypeNode):
- """Represents the assignment from the assignment expression
- >>> import astroid
- >>> module = astroid.parse('if a := 1: pass')
- >>> module.body[0].test
- <NamedExpr l.1 at 0x7f23b2e4ed30>
- """
- _astroid_fields = ("target", "value")
- optional_assign = True
- """Whether this node optionally assigns a variable.
- Since NamedExpr are not always called they do not always assign."""
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- """
- :param lineno: The line that this node appears on in the source code.
- :param col_offset: The column that this node appears on in the
- source code.
- :param parent: The parent node in the syntax tree.
- :param end_lineno: The last line this node appears on in the source code.
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- """
- self.target: NodeNG
- """The assignment target
- :type: Name
- """
- self.value: NodeNG
- """The value that gets assigned in the expression"""
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, target: NodeNG, value: NodeNG) -> None:
- self.target = target
- self.value = value
- assigned_stmts = protocols.named_expr_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- def frame(self) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
- """The first parent frame node.
- A frame node is a :class:`Module`, :class:`FunctionDef`,
- or :class:`ClassDef`.
- :returns: The first parent frame node.
- """
- if not self.parent:
- raise ParentMissingError(target=self)
- # For certain parents NamedExpr evaluate to the scope of the parent
- if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
- if not self.parent.parent:
- raise ParentMissingError(target=self.parent)
- if not self.parent.parent.parent:
- raise ParentMissingError(target=self.parent.parent)
- return self.parent.parent.parent.frame()
- return self.parent.frame()
- def scope(self) -> LocalsDictNodeNG:
- """The first parent node defining a new scope.
- These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
- :returns: The first parent scope node.
- """
- if not self.parent:
- raise ParentMissingError(target=self)
- # For certain parents NamedExpr evaluate to the scope of the parent
- if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
- if not self.parent.parent:
- raise ParentMissingError(target=self.parent)
- if not self.parent.parent.parent:
- raise ParentMissingError(target=self.parent.parent)
- return self.parent.parent.parent.scope()
- return self.parent.scope()
- def set_local(self, name: str, stmt: NodeNG) -> None:
- """Define that the given name is declared in the given statement node.
- NamedExpr's in Arguments, Keyword or Comprehension are evaluated in their
- parent's parent scope. So we add to their frame's locals.
- .. seealso:: :meth:`scope`
- :param name: The name that is being defined.
- :param stmt: The statement that defines the given name.
- """
- self.frame().set_local(name, stmt)
- class Unknown(_base_nodes.AssignTypeNode):
- """This node represents a node in a constructed AST where
- introspection is not possible. At the moment, it's only used in
- the args attribute of FunctionDef nodes where function signature
- introspection failed.
- """
- name = "Unknown"
- def __init__(
- self,
- parent: NodeNG,
- lineno: None = None,
- col_offset: None = None,
- *,
- end_lineno: None = None,
- end_col_offset: None = None,
- ) -> None:
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def qname(self) -> Literal["Unknown"]:
- return "Unknown"
- def _infer(self, context: InferenceContext | None = None, **kwargs):
- """Inference on an Unknown node immediately terminates."""
- yield util.Uninferable
- UNATTACHED_UNKNOWN = Unknown(parent=SYNTHETIC_ROOT)
- class EvaluatedObject(NodeNG):
- """Contains an object that has already been inferred
- This class is useful to pre-evaluate a particular node,
- with the resulting class acting as the non-evaluated node.
- """
- name = "EvaluatedObject"
- _astroid_fields = ("original",)
- _other_fields = ("value",)
- def __init__(
- self, original: SuccessfulInferenceResult, value: InferenceResult
- ) -> None:
- self.original: SuccessfulInferenceResult = original
- """The original node that has already been evaluated"""
- self.value: InferenceResult = value
- """The inferred value"""
- super().__init__(
- lineno=self.original.lineno,
- col_offset=self.original.col_offset,
- parent=self.original.parent,
- end_lineno=self.original.end_lineno,
- end_col_offset=self.original.end_col_offset,
- )
- def _infer(
- self, context: InferenceContext | None = None, **kwargs: Any
- ) -> Generator[NodeNG | util.UninferableBase]:
- yield self.value
- # Pattern matching #######################################################
- class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode):
- """Class representing a :class:`ast.Match` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case 200:
- ...
- case _:
- ...
- ''')
- >>> node
- <Match l.2 at 0x10c24e170>
- """
- _astroid_fields = ("subject", "cases")
- _multi_line_block_fields = ("cases",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.subject: NodeNG
- self.cases: list[MatchCase]
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- subject: NodeNG,
- cases: list[MatchCase],
- ) -> None:
- self.subject = subject
- self.cases = cases
- class Pattern(NodeNG):
- """Base class for all Pattern nodes."""
- class MatchCase(_base_nodes.MultiLineBlockNode):
- """Class representing a :class:`ast.match_case` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case 200:
- ...
- ''')
- >>> node.cases[0]
- <MatchCase l.3 at 0x10c24e590>
- """
- _astroid_fields = ("pattern", "guard", "body")
- _multi_line_block_fields = ("body",)
- lineno: None
- col_offset: None
- end_lineno: None
- end_col_offset: None
- def __init__(self, *, parent: NodeNG | None = None) -> None:
- self.pattern: Pattern
- self.guard: NodeNG | None
- self.body: list[NodeNG]
- super().__init__(
- parent=parent,
- lineno=None,
- col_offset=None,
- end_lineno=None,
- end_col_offset=None,
- )
- def postinit(
- self,
- *,
- pattern: Pattern,
- guard: NodeNG | None,
- body: list[NodeNG],
- ) -> None:
- self.pattern = pattern
- self.guard = guard
- self.body = body
- class MatchValue(Pattern):
- """Class representing a :class:`ast.MatchValue` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case 200:
- ...
- ''')
- >>> node.cases[0].pattern
- <MatchValue l.3 at 0x10c24e200>
- """
- _astroid_fields = ("value",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.value: NodeNG
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, *, value: NodeNG) -> None:
- self.value = value
- class MatchSingleton(Pattern):
- """Class representing a :class:`ast.MatchSingleton` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case True:
- ...
- case False:
- ...
- case None:
- ...
- ''')
- >>> node.cases[0].pattern
- <MatchSingleton l.3 at 0x10c2282e0>
- >>> node.cases[1].pattern
- <MatchSingleton l.5 at 0x10c228af0>
- >>> node.cases[2].pattern
- <MatchSingleton l.7 at 0x10c229f90>
- """
- _other_fields = ("value",)
- def __init__(
- self,
- *,
- value: Literal[True, False, None],
- lineno: int | None = None,
- col_offset: int | None = None,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- parent: NodeNG | None = None,
- ) -> None:
- self.value = value
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- class MatchSequence(Pattern):
- """Class representing a :class:`ast.MatchSequence` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case [1, 2]:
- ...
- case (1, 2, *_):
- ...
- ''')
- >>> node.cases[0].pattern
- <MatchSequence l.3 at 0x10ca80d00>
- >>> node.cases[1].pattern
- <MatchSequence l.5 at 0x10ca80b20>
- """
- _astroid_fields = ("patterns",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.patterns: list[Pattern]
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, *, patterns: list[Pattern]) -> None:
- self.patterns = patterns
- class MatchMapping(_base_nodes.AssignTypeNode, Pattern):
- """Class representing a :class:`ast.MatchMapping` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case {1: "Hello", 2: "World", 3: _, **rest}:
- ...
- ''')
- >>> node.cases[0].pattern
- <MatchMapping l.3 at 0x10c8a8850>
- """
- _astroid_fields = ("keys", "patterns", "rest")
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.keys: list[NodeNG]
- self.patterns: list[Pattern]
- self.rest: AssignName | None
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- keys: list[NodeNG],
- patterns: list[Pattern],
- rest: AssignName | None,
- ) -> None:
- self.keys = keys
- self.patterns = patterns
- self.rest = rest
- assigned_stmts = protocols.match_mapping_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- class MatchClass(Pattern):
- """Class representing a :class:`ast.MatchClass` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case Point2D(0, 0):
- ...
- case Point3D(x=0, y=0, z=0):
- ...
- ''')
- >>> node.cases[0].pattern
- <MatchClass l.3 at 0x10ca83940>
- >>> node.cases[1].pattern
- <MatchClass l.5 at 0x10ca80880>
- """
- _astroid_fields = ("cls", "patterns", "kwd_patterns")
- _other_fields = ("kwd_attrs",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.cls: NodeNG
- self.patterns: list[Pattern]
- self.kwd_attrs: list[str]
- self.kwd_patterns: list[Pattern]
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- cls: NodeNG,
- patterns: list[Pattern],
- kwd_attrs: list[str],
- kwd_patterns: list[Pattern],
- ) -> None:
- self.cls = cls
- self.patterns = patterns
- self.kwd_attrs = kwd_attrs
- self.kwd_patterns = kwd_patterns
- class MatchStar(_base_nodes.AssignTypeNode, Pattern):
- """Class representing a :class:`ast.MatchStar` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case [1, *_]:
- ...
- ''')
- >>> node.cases[0].pattern.patterns[1]
- <MatchStar l.3 at 0x10ca809a0>
- """
- _astroid_fields = ("name",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.name: AssignName | None
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, *, name: AssignName | None) -> None:
- self.name = name
- assigned_stmts = protocols.match_star_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- class MatchAs(_base_nodes.AssignTypeNode, Pattern):
- """Class representing a :class:`ast.MatchAs` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case [1, a]:
- ...
- case {'key': b}:
- ...
- case Point2D(0, 0) as c:
- ...
- case d:
- ...
- ''')
- >>> node.cases[0].pattern.patterns[1]
- <MatchAs l.3 at 0x10d0b2da0>
- >>> node.cases[1].pattern.patterns[0]
- <MatchAs l.5 at 0x10d0b2920>
- >>> node.cases[2].pattern
- <MatchAs l.7 at 0x10d0b06a0>
- >>> node.cases[3].pattern
- <MatchAs l.9 at 0x10d09b880>
- """
- _astroid_fields = ("pattern", "name")
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.pattern: Pattern | None
- self.name: AssignName | None
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- pattern: Pattern | None,
- name: AssignName | None,
- ) -> None:
- self.pattern = pattern
- self.name = name
- assigned_stmts = protocols.match_as_assigned_stmts
- """Returns the assigned statement (non inferred) according to the assignment type.
- See astroid/protocols.py for actual implementation.
- """
- class MatchOr(Pattern):
- """Class representing a :class:`ast.MatchOr` node.
- >>> import astroid
- >>> node = astroid.extract_node('''
- match x:
- case 400 | 401 | 402:
- ...
- ''')
- >>> node.cases[0].pattern
- <MatchOr l.3 at 0x10d0b0b50>
- """
- _astroid_fields = ("patterns",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.patterns: list[Pattern]
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, *, patterns: list[Pattern]) -> None:
- self.patterns = patterns
- class TemplateStr(NodeNG):
- """Class representing an :class:`ast.TemplateStr` node.
- >>> import astroid
- >>> node = astroid.extract_node('t"{name} finished {place!s}"')
- >>> node
- <TemplateStr l.1 at 0x103b7aa50>
- """
- _astroid_fields = ("values",)
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.values: list[NodeNG]
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(self, *, values: list[NodeNG]) -> None:
- self.values = values
- def get_children(self) -> Iterator[NodeNG]:
- yield from self.values
- class Interpolation(NodeNG):
- """Class representing an :class:`ast.Interpolation` node.
- >>> import astroid
- >>> node = astroid.extract_node('t"{name} finished {place!s}"')
- >>> node
- <TemplateStr l.1 at 0x103b7aa50>
- >>> node.values[0]
- <Interpolation l.1 at 0x103b7acf0>
- >>> node.values[2]
- <Interpolation l.1 at 0x10411e5d0>
- """
- _astroid_fields = ("value", "format_spec")
- _other_fields = ("str", "conversion")
- def __init__(
- self,
- lineno: int | None = None,
- col_offset: int | None = None,
- parent: NodeNG | None = None,
- *,
- end_lineno: int | None = None,
- end_col_offset: int | None = None,
- ) -> None:
- self.value: NodeNG
- """Any expression node."""
- self.str: str
- """Text of the interpolation expression."""
- self.conversion: int
- """The type of formatting to be applied to the value.
- .. seealso::
- :class:`ast.Interpolation`
- """
- self.format_spec: JoinedStr | None = None
- """The formatting to be applied to the value.
- .. seealso::
- :class:`ast.Interpolation`
- """
- super().__init__(
- lineno=lineno,
- col_offset=col_offset,
- end_lineno=end_lineno,
- end_col_offset=end_col_offset,
- parent=parent,
- )
- def postinit(
- self,
- *,
- value: NodeNG,
- str: str, # pylint: disable=redefined-builtin
- conversion: int = -1,
- format_spec: JoinedStr | None = None,
- ) -> None:
- self.value = value
- self.str = str
- self.conversion = conversion
- self.format_spec = format_spec
- def get_children(self) -> Iterator[NodeNG]:
- yield self.value
- if self.format_spec:
- yield self.format_spec
- # constants ##############################################################
- # The _proxied attribute of all container types (List, Tuple, etc.)
- # are set during bootstrapping by _astroid_bootstrapping().
- CONST_CLS: dict[type, type[NodeNG]] = {
- list: List,
- tuple: Tuple,
- dict: Dict,
- set: Set,
- type(None): Const,
- type(NotImplemented): Const,
- type(...): Const,
- bool: Const,
- int: Const,
- float: Const,
- complex: Const,
- str: Const,
- bytes: Const,
- }
- def _create_basic_elements(
- value: Iterable[Any], node: List | Set | Tuple
- ) -> list[NodeNG]:
- """Create a list of nodes to function as the elements of a new node."""
- elements: list[NodeNG] = []
- for element in value:
- # NOTE: avoid accessing any attributes of element in the loop.
- element_node = const_factory(element)
- element_node.parent = node
- elements.append(element_node)
- return elements
- def _create_dict_items(
- values: Mapping[Any, Any], node: Dict
- ) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]:
- """Create a list of node pairs to function as the items of a new dict node."""
- elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = []
- for key, value in values.items():
- # NOTE: avoid accessing any attributes of both key and value in the loop.
- key_node = const_factory(key)
- key_node.parent = node
- value_node = const_factory(value)
- value_node.parent = node
- elements.append((key_node, value_node))
- return elements
- def const_factory(value: Any) -> ConstFactoryResult:
- """Return an astroid node for a python value."""
- # NOTE: avoid accessing any attributes of value until it is known that value
- # is of a const type, to avoid possibly triggering code for a live object.
- # Accesses include value.__class__ and isinstance(value, ...), but not type(value).
- # See: https://github.com/pylint-dev/astroid/issues/2686
- value_type = type(value)
- assert not issubclass(value_type, NodeNG)
- # This only handles instances of the CONST types. Any
- # subclasses get inferred as EmptyNode.
- # TODO: See if we should revisit these with the normal builder.
- if value_type not in CONST_CLS:
- node = EmptyNode()
- node.object = value
- return node
- instance: List | Set | Tuple | Dict
- initializer_cls = CONST_CLS[value_type]
- if issubclass(initializer_cls, (List, Set, Tuple)):
- instance = initializer_cls(
- lineno=None,
- col_offset=None,
- parent=SYNTHETIC_ROOT,
- end_lineno=None,
- end_col_offset=None,
- )
- instance.postinit(_create_basic_elements(value, instance))
- return instance
- if issubclass(initializer_cls, Dict):
- instance = initializer_cls(
- lineno=None,
- col_offset=None,
- parent=SYNTHETIC_ROOT,
- end_lineno=None,
- end_col_offset=None,
- )
- instance.postinit(_create_dict_items(value, instance))
- return instance
- return Const(value)
|