123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719 |
- #
- # Parse tree nodes for expressions
- #
- from __future__ import absolute_import
- import cython
- cython.declare(error=object, warning=object, warn_once=object, InternalError=object,
- CompileError=object, UtilityCode=object, TempitaUtilityCode=object,
- StringEncoding=object, operator=object, local_errors=object, report_error=object,
- Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object,
- list_type=object, tuple_type=object, set_type=object, dict_type=object,
- unicode_type=object, str_type=object, bytes_type=object, type_type=object,
- Builtin=object, Symtab=object, Utils=object, find_coercion_error=object,
- debug_disposal_code=object, debug_temp_alloc=object, debug_coercion=object,
- bytearray_type=object, slice_type=object, _py_int_types=object,
- IS_PYTHON3=cython.bint)
- import re
- import sys
- import copy
- import os.path
- import operator
- from .Errors import (
- error, warning, InternalError, CompileError, report_error, local_errors)
- from .Code import UtilityCode, TempitaUtilityCode
- from . import StringEncoding
- from . import Naming
- from . import Nodes
- from .Nodes import Node, utility_code_for_imports, analyse_type_annotation
- from . import PyrexTypes
- from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \
- unspecified_type
- from . import TypeSlots
- from .Builtin import list_type, tuple_type, set_type, dict_type, type_type, \
- unicode_type, str_type, bytes_type, bytearray_type, basestring_type, slice_type
- from . import Builtin
- from . import Symtab
- from .. import Utils
- from .Annotate import AnnotationItem
- from . import Future
- from ..Debugging import print_call_chain
- from .DebugFlags import debug_disposal_code, debug_temp_alloc, \
- debug_coercion
- from .Pythran import (to_pythran, is_pythran_supported_type, is_pythran_supported_operation_type,
- is_pythran_expr, pythran_func_type, pythran_binop_type, pythran_unaryop_type, has_np_pythran,
- pythran_indexing_code, pythran_indexing_type, is_pythran_supported_node_or_none, pythran_type,
- pythran_is_numpy_func_supported, pythran_get_func_include_file, pythran_functor)
- from .PyrexTypes import PythranExpr
- try:
- from __builtin__ import basestring
- except ImportError:
- # Python 3
- basestring = str
- any_string_type = (bytes, str)
- else:
- # Python 2
- any_string_type = (bytes, unicode)
- if sys.version_info[0] >= 3:
- IS_PYTHON3 = True
- _py_int_types = int
- else:
- IS_PYTHON3 = False
- _py_int_types = (int, long)
- class NotConstant(object):
- _obj = None
- def __new__(cls):
- if NotConstant._obj is None:
- NotConstant._obj = super(NotConstant, cls).__new__(cls)
- return NotConstant._obj
- def __repr__(self):
- return "<NOT CONSTANT>"
- not_a_constant = NotConstant()
- constant_value_not_set = object()
- # error messages when coercing from key[0] to key[1]
- coercion_error_dict = {
- # string related errors
- (unicode_type, str_type): ("Cannot convert Unicode string to 'str' implicitly."
- " This is not portable and requires explicit encoding."),
- (unicode_type, bytes_type): "Cannot convert Unicode string to 'bytes' implicitly, encoding required.",
- (unicode_type, PyrexTypes.c_char_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (unicode_type, PyrexTypes.c_const_char_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (unicode_type, PyrexTypes.c_uchar_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (unicode_type, PyrexTypes.c_const_uchar_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (bytes_type, unicode_type): "Cannot convert 'bytes' object to unicode implicitly, decoding required",
- (bytes_type, str_type): "Cannot convert 'bytes' object to str implicitly. This is not portable to Py3.",
- (bytes_type, basestring_type): ("Cannot convert 'bytes' object to basestring implicitly."
- " This is not portable to Py3."),
- (bytes_type, PyrexTypes.c_py_unicode_ptr_type): "Cannot convert 'bytes' object to Py_UNICODE*, use 'unicode'.",
- (bytes_type, PyrexTypes.c_const_py_unicode_ptr_type): (
- "Cannot convert 'bytes' object to Py_UNICODE*, use 'unicode'."),
- (basestring_type, bytes_type): "Cannot convert 'basestring' object to bytes implicitly. This is not portable.",
- (str_type, unicode_type): ("str objects do not support coercion to unicode,"
- " use a unicode string literal instead (u'')"),
- (str_type, bytes_type): "Cannot convert 'str' to 'bytes' implicitly. This is not portable.",
- (str_type, PyrexTypes.c_char_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_const_char_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_uchar_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_const_uchar_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_py_unicode_ptr_type): "'str' objects do not support coercion to C types (use 'unicode'?).",
- (str_type, PyrexTypes.c_const_py_unicode_ptr_type): (
- "'str' objects do not support coercion to C types (use 'unicode'?)."),
- (PyrexTypes.c_char_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required",
- (PyrexTypes.c_const_char_ptr_type, unicode_type): (
- "Cannot convert 'char*' to unicode implicitly, decoding required"),
- (PyrexTypes.c_uchar_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required",
- (PyrexTypes.c_const_uchar_ptr_type, unicode_type): (
- "Cannot convert 'char*' to unicode implicitly, decoding required"),
- }
- def find_coercion_error(type_tuple, default, env):
- err = coercion_error_dict.get(type_tuple)
- if err is None:
- return default
- elif (env.directives['c_string_encoding'] and
- any(t in type_tuple for t in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_uchar_ptr_type,
- PyrexTypes.c_const_char_ptr_type, PyrexTypes.c_const_uchar_ptr_type))):
- if type_tuple[1].is_pyobject:
- return default
- elif env.directives['c_string_encoding'] in ('ascii', 'default'):
- return default
- else:
- return "'%s' objects do not support coercion to C types with non-ascii or non-default c_string_encoding" % type_tuple[0].name
- else:
- return err
- def default_str_type(env):
- return {
- 'bytes': bytes_type,
- 'bytearray': bytearray_type,
- 'str': str_type,
- 'unicode': unicode_type
- }.get(env.directives['c_string_type'])
- def check_negative_indices(*nodes):
- """
- Raise a warning on nodes that are known to have negative numeric values.
- Used to find (potential) bugs inside of "wraparound=False" sections.
- """
- for node in nodes:
- if node is None or (
- not isinstance(node.constant_result, _py_int_types) and
- not isinstance(node.constant_result, float)):
- continue
- if node.constant_result < 0:
- warning(node.pos,
- "the result of using negative indices inside of "
- "code sections marked as 'wraparound=False' is "
- "undefined", level=1)
- def infer_sequence_item_type(env, seq_node, index_node=None, seq_type=None):
- if not seq_node.is_sequence_constructor:
- if seq_type is None:
- seq_type = seq_node.infer_type(env)
- if seq_type is tuple_type:
- # tuples are immutable => we can safely follow assignments
- if seq_node.cf_state and len(seq_node.cf_state) == 1:
- try:
- seq_node = seq_node.cf_state[0].rhs
- except AttributeError:
- pass
- if seq_node is not None and seq_node.is_sequence_constructor:
- if index_node is not None and index_node.has_constant_result():
- try:
- item = seq_node.args[index_node.constant_result]
- except (ValueError, TypeError, IndexError):
- pass
- else:
- return item.infer_type(env)
- # if we're lucky, all items have the same type
- item_types = set([item.infer_type(env) for item in seq_node.args])
- if len(item_types) == 1:
- return item_types.pop()
- return None
- def make_dedup_key(outer_type, item_nodes):
- """
- Recursively generate a deduplication key from a sequence of values.
- Includes Cython node types to work around the fact that (1, 2.0) == (1.0, 2), for example.
- @param outer_type: The type of the outer container.
- @param item_nodes: A sequence of constant nodes that will be traversed recursively.
- @return: A tuple that can be used as a dict key for deduplication.
- """
- item_keys = [
- (py_object_type, None, type(None)) if node is None
- # For sequences and their "mult_factor", see TupleNode.
- else make_dedup_key(node.type, [node.mult_factor if node.is_literal else None] + node.args) if node.is_sequence_constructor
- else make_dedup_key(node.type, (node.start, node.stop, node.step)) if node.is_slice
- # For constants, look at the Python value type if we don't know the concrete Cython type.
- else (node.type, node.constant_result,
- type(node.constant_result) if node.type is py_object_type else None) if node.has_constant_result()
- else None # something we cannot handle => short-circuit below
- for node in item_nodes
- ]
- if None in item_keys:
- return None
- return outer_type, tuple(item_keys)
- # Returns a block of code to translate the exception,
- # plus a boolean indicating whether to check for Python exceptions.
- def get_exception_handler(exception_value):
- if exception_value is None:
- return "__Pyx_CppExn2PyErr();", False
- elif (exception_value.type == PyrexTypes.c_char_type
- and exception_value.value == '*'):
- return "__Pyx_CppExn2PyErr();", True
- elif exception_value.type.is_pyobject:
- return (
- 'try { throw; } catch(const std::exception& exn) {'
- 'PyErr_SetString(%s, exn.what());'
- '} catch(...) { PyErr_SetNone(%s); }' % (
- exception_value.entry.cname,
- exception_value.entry.cname),
- False)
- else:
- return (
- '%s(); if (!PyErr_Occurred())'
- 'PyErr_SetString(PyExc_RuntimeError, '
- '"Error converting c++ exception.");' % (
- exception_value.entry.cname),
- False)
- def maybe_check_py_error(code, check_py_exception, pos, nogil):
- if check_py_exception:
- if nogil:
- code.putln(code.error_goto_if("__Pyx_ErrOccurredWithGIL()", pos))
- else:
- code.putln(code.error_goto_if("PyErr_Occurred()", pos))
- def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil):
- raise_py_exception, check_py_exception = get_exception_handler(exception_value)
- code.putln("try {")
- code.putln("%s" % inside)
- if py_result:
- code.putln(code.error_goto_if_null(py_result, pos))
- maybe_check_py_error(code, check_py_exception, pos, nogil)
- code.putln("} catch(...) {")
- if nogil:
- code.put_ensure_gil(declare_gilstate=True)
- code.putln(raise_py_exception)
- if nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(pos))
- code.putln("}")
- # Used to handle the case where an lvalue expression and an overloaded assignment
- # both have an exception declaration.
- def translate_double_cpp_exception(code, pos, lhs_type, lhs_code, rhs_code,
- lhs_exc_val, assign_exc_val, nogil):
- handle_lhs_exc, lhc_check_py_exc = get_exception_handler(lhs_exc_val)
- handle_assignment_exc, assignment_check_py_exc = get_exception_handler(assign_exc_val)
- code.putln("try {")
- code.putln(lhs_type.declaration_code("__pyx_local_lvalue = %s;" % lhs_code))
- maybe_check_py_error(code, lhc_check_py_exc, pos, nogil)
- code.putln("try {")
- code.putln("__pyx_local_lvalue = %s;" % rhs_code)
- maybe_check_py_error(code, assignment_check_py_exc, pos, nogil)
- # Catch any exception from the overloaded assignment.
- code.putln("} catch(...) {")
- if nogil:
- code.put_ensure_gil(declare_gilstate=True)
- code.putln(handle_assignment_exc)
- if nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(pos))
- code.putln("}")
- # Catch any exception from evaluating lhs.
- code.putln("} catch(...) {")
- if nogil:
- code.put_ensure_gil(declare_gilstate=True)
- code.putln(handle_lhs_exc)
- if nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(pos))
- code.putln('}')
- class ExprNode(Node):
- # subexprs [string] Class var holding names of subexpr node attrs
- # type PyrexType Type of the result
- # result_code string Code fragment
- # result_ctype string C type of result_code if different from type
- # is_temp boolean Result is in a temporary variable
- # is_sequence_constructor
- # boolean Is a list or tuple constructor expression
- # is_starred boolean Is a starred expression (e.g. '*a')
- # saved_subexpr_nodes
- # [ExprNode or [ExprNode or None] or None]
- # Cached result of subexpr_nodes()
- # use_managed_ref boolean use ref-counted temps/assignments/etc.
- # result_is_used boolean indicates that the result will be dropped and the
- # result_code/temp_result can safely be set to None
- # is_numpy_attribute boolean Is a Numpy module attribute
- # annotation ExprNode or None PEP526 annotation for names or expressions
- result_ctype = None
- type = None
- annotation = None
- temp_code = None
- old_temp = None # error checker for multiple frees etc.
- use_managed_ref = True # can be set by optimisation transforms
- result_is_used = True
- is_numpy_attribute = False
- # The Analyse Expressions phase for expressions is split
- # into two sub-phases:
- #
- # Analyse Types
- # Determines the result type of the expression based
- # on the types of its sub-expressions, and inserts
- # coercion nodes into the expression tree where needed.
- # Marks nodes which will need to have temporary variables
- # allocated.
- #
- # Allocate Temps
- # Allocates temporary variables where needed, and fills
- # in the result_code field of each node.
- #
- # ExprNode provides some convenience routines which
- # perform both of the above phases. These should only
- # be called from statement nodes, and only when no
- # coercion nodes need to be added around the expression
- # being analysed. In that case, the above two phases
- # should be invoked separately.
- #
- # Framework code in ExprNode provides much of the common
- # processing for the various phases. It makes use of the
- # 'subexprs' class attribute of ExprNodes, which should
- # contain a list of the names of attributes which can
- # hold sub-nodes or sequences of sub-nodes.
- #
- # The framework makes use of a number of abstract methods.
- # Their responsibilities are as follows.
- #
- # Declaration Analysis phase
- #
- # analyse_target_declaration
- # Called during the Analyse Declarations phase to analyse
- # the LHS of an assignment or argument of a del statement.
- # Nodes which cannot be the LHS of an assignment need not
- # implement it.
- #
- # Expression Analysis phase
- #
- # analyse_types
- # - Call analyse_types on all sub-expressions.
- # - Check operand types, and wrap coercion nodes around
- # sub-expressions where needed.
- # - Set the type of this node.
- # - If a temporary variable will be required for the
- # result, set the is_temp flag of this node.
- #
- # analyse_target_types
- # Called during the Analyse Types phase to analyse
- # the LHS of an assignment or argument of a del
- # statement. Similar responsibilities to analyse_types.
- #
- # target_code
- # Called by the default implementation of allocate_target_temps.
- # Should return a C lvalue for assigning to the node. The default
- # implementation calls calculate_result_code.
- #
- # check_const
- # - Check that this node and its subnodes form a
- # legal constant expression. If so, do nothing,
- # otherwise call not_const.
- #
- # The default implementation of check_const
- # assumes that the expression is not constant.
- #
- # check_const_addr
- # - Same as check_const, except check that the
- # expression is a C lvalue whose address is
- # constant. Otherwise, call addr_not_const.
- #
- # The default implementation of calc_const_addr
- # assumes that the expression is not a constant
- # lvalue.
- #
- # Code Generation phase
- #
- # generate_evaluation_code
- # - Call generate_evaluation_code for sub-expressions.
- # - Perform the functions of generate_result_code
- # (see below).
- # - If result is temporary, call generate_disposal_code
- # on all sub-expressions.
- #
- # A default implementation of generate_evaluation_code
- # is provided which uses the following abstract methods:
- #
- # generate_result_code
- # - Generate any C statements necessary to calculate
- # the result of this node from the results of its
- # sub-expressions.
- #
- # calculate_result_code
- # - Should return a C code fragment evaluating to the
- # result. This is only called when the result is not
- # a temporary.
- #
- # generate_assignment_code
- # Called on the LHS of an assignment.
- # - Call generate_evaluation_code for sub-expressions.
- # - Generate code to perform the assignment.
- # - If the assignment absorbed a reference, call
- # generate_post_assignment_code on the RHS,
- # otherwise call generate_disposal_code on it.
- #
- # generate_deletion_code
- # Called on an argument of a del statement.
- # - Call generate_evaluation_code for sub-expressions.
- # - Generate code to perform the deletion.
- # - Call generate_disposal_code on all sub-expressions.
- #
- #
- is_sequence_constructor = False
- is_dict_literal = False
- is_set_literal = False
- is_string_literal = False
- is_attribute = False
- is_subscript = False
- is_slice = False
- is_buffer_access = False
- is_memview_index = False
- is_memview_slice = False
- is_memview_broadcast = False
- is_memview_copy_assignment = False
- saved_subexpr_nodes = None
- is_temp = False
- is_target = False
- is_starred = False
- constant_result = constant_value_not_set
- child_attrs = property(fget=operator.attrgetter('subexprs'))
- def not_implemented(self, method_name):
- print_call_chain(method_name, "not implemented") ###
- raise InternalError(
- "%s.%s not implemented" %
- (self.__class__.__name__, method_name))
- def is_lvalue(self):
- return 0
- def is_addressable(self):
- return self.is_lvalue() and not self.type.is_memoryviewslice
- def is_ephemeral(self):
- # An ephemeral node is one whose result is in
- # a Python temporary and we suspect there are no
- # other references to it. Certain operations are
- # disallowed on such values, since they are
- # likely to result in a dangling pointer.
- return self.type.is_pyobject and self.is_temp
- def subexpr_nodes(self):
- # Extract a list of subexpression nodes based
- # on the contents of the subexprs class attribute.
- nodes = []
- for name in self.subexprs:
- item = getattr(self, name)
- if item is not None:
- if type(item) is list:
- nodes.extend(item)
- else:
- nodes.append(item)
- return nodes
- def result(self):
- if self.is_temp:
- #if not self.temp_code:
- # pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
- # raise RuntimeError("temp result name not set in %s at %r" % (
- # self.__class__.__name__, pos))
- return self.temp_code
- else:
- return self.calculate_result_code()
- def pythran_result(self, type_=None):
- if is_pythran_supported_node_or_none(self):
- return to_pythran(self)
- assert(type_ is not None)
- return to_pythran(self, type_)
- def is_c_result_required(self):
- """
- Subtypes may return False here if result temp allocation can be skipped.
- """
- return True
- def result_as(self, type = None):
- # Return the result code cast to the specified C type.
- if (self.is_temp and self.type.is_pyobject and
- type != py_object_type):
- # Allocated temporaries are always PyObject *, which may not
- # reflect the actual type (e.g. an extension type)
- return typecast(type, py_object_type, self.result())
- return typecast(type, self.ctype(), self.result())
- def py_result(self):
- # Return the result code cast to PyObject *.
- return self.result_as(py_object_type)
- def ctype(self):
- # Return the native C type of the result (i.e. the
- # C type of the result_code expression).
- return self.result_ctype or self.type
- def get_constant_c_result_code(self):
- # Return the constant value of this node as a result code
- # string, or None if the node is not constant. This method
- # can be called when the constant result code is required
- # before the code generation phase.
- #
- # The return value is a string that can represent a simple C
- # value, a constant C name or a constant C expression. If the
- # node type depends on Python code, this must return None.
- return None
- def calculate_constant_result(self):
- # Calculate the constant compile time result value of this
- # expression and store it in ``self.constant_result``. Does
- # nothing by default, thus leaving ``self.constant_result``
- # unknown. If valid, the result can be an arbitrary Python
- # value.
- #
- # This must only be called when it is assured that all
- # sub-expressions have a valid constant_result value. The
- # ConstantFolding transform will do this.
- pass
- def has_constant_result(self):
- return self.constant_result is not constant_value_not_set and \
- self.constant_result is not not_a_constant
- def compile_time_value(self, denv):
- # Return value of compile-time expression, or report error.
- error(self.pos, "Invalid compile-time expression")
- def compile_time_value_error(self, e):
- error(self.pos, "Error in compile-time expression: %s: %s" % (
- e.__class__.__name__, e))
- # ------------- Declaration Analysis ----------------
- def analyse_target_declaration(self, env):
- error(self.pos, "Cannot assign to or delete this")
- # ------------- Expression Analysis ----------------
- def analyse_const_expression(self, env):
- # Called during the analyse_declarations phase of a
- # constant expression. Analyses the expression's type,
- # checks whether it is a legal const expression,
- # and determines its value.
- node = self.analyse_types(env)
- node.check_const()
- return node
- def analyse_expressions(self, env):
- # Convenience routine performing both the Type
- # Analysis and Temp Allocation phases for a whole
- # expression.
- return self.analyse_types(env)
- def analyse_target_expression(self, env, rhs):
- # Convenience routine performing both the Type
- # Analysis and Temp Allocation phases for the LHS of
- # an assignment.
- return self.analyse_target_types(env)
- def analyse_boolean_expression(self, env):
- # Analyse expression and coerce to a boolean.
- node = self.analyse_types(env)
- bool = node.coerce_to_boolean(env)
- return bool
- def analyse_temp_boolean_expression(self, env):
- # Analyse boolean expression and coerce result into
- # a temporary. This is used when a branch is to be
- # performed on the result and we won't have an
- # opportunity to ensure disposal code is executed
- # afterwards. By forcing the result into a temporary,
- # we ensure that all disposal has been done by the
- # time we get the result.
- node = self.analyse_types(env)
- return node.coerce_to_boolean(env).coerce_to_simple(env)
- # --------------- Type Inference -----------------
- def type_dependencies(self, env):
- # Returns the list of entries whose types must be determined
- # before the type of self can be inferred.
- if hasattr(self, 'type') and self.type is not None:
- return ()
- return sum([node.type_dependencies(env) for node in self.subexpr_nodes()], ())
- def infer_type(self, env):
- # Attempt to deduce the type of self.
- # Differs from analyse_types as it avoids unnecessary
- # analysis of subexpressions, but can assume everything
- # in self.type_dependencies() has been resolved.
- if hasattr(self, 'type') and self.type is not None:
- return self.type
- elif hasattr(self, 'entry') and self.entry is not None:
- return self.entry.type
- else:
- self.not_implemented("infer_type")
- def nonlocally_immutable(self):
- # Returns whether this variable is a safe reference, i.e.
- # can't be modified as part of globals or closures.
- return self.is_literal or self.is_temp or self.type.is_array or self.type.is_cfunction
- def inferable_item_node(self, index=0):
- """
- Return a node that represents the (type) result of an indexing operation,
- e.g. for tuple unpacking or iteration.
- """
- return IndexNode(self.pos, base=self, index=IntNode(
- self.pos, value=str(index), constant_result=index, type=PyrexTypes.c_py_ssize_t_type))
- # --------------- Type Analysis ------------------
- def analyse_as_module(self, env):
- # If this node can be interpreted as a reference to a
- # cimported module, return its scope, else None.
- return None
- def analyse_as_type(self, env):
- # If this node can be interpreted as a reference to a
- # type, return that type, else None.
- return None
- def analyse_as_extension_type(self, env):
- # If this node can be interpreted as a reference to an
- # extension type or builtin type, return its type, else None.
- return None
- def analyse_types(self, env):
- self.not_implemented("analyse_types")
- def analyse_target_types(self, env):
- return self.analyse_types(env)
- def nogil_check(self, env):
- # By default, any expression based on Python objects is
- # prevented in nogil environments. Subtypes must override
- # this if they can work without the GIL.
- if self.type and self.type.is_pyobject:
- self.gil_error()
- def gil_assignment_check(self, env):
- if env.nogil and self.type.is_pyobject:
- error(self.pos, "Assignment of Python object not allowed without gil")
- def check_const(self):
- self.not_const()
- return False
- def not_const(self):
- error(self.pos, "Not allowed in a constant expression")
- def check_const_addr(self):
- self.addr_not_const()
- return False
- def addr_not_const(self):
- error(self.pos, "Address is not constant")
- # ----------------- Result Allocation -----------------
- def result_in_temp(self):
- # Return true if result is in a temporary owned by
- # this node or one of its subexpressions. Overridden
- # by certain nodes which can share the result of
- # a subnode.
- return self.is_temp
- def target_code(self):
- # Return code fragment for use as LHS of a C assignment.
- return self.calculate_result_code()
- def calculate_result_code(self):
- self.not_implemented("calculate_result_code")
- # def release_target_temp(self, env):
- # # Release temporaries used by LHS of an assignment.
- # self.release_subexpr_temps(env)
- def allocate_temp_result(self, code):
- if self.temp_code:
- raise RuntimeError("Temp allocated multiple times in %r: %r" % (self.__class__.__name__, self.pos))
- type = self.type
- if not type.is_void:
- if type.is_pyobject:
- type = PyrexTypes.py_object_type
- elif not (self.result_is_used or type.is_memoryviewslice or self.is_c_result_required()):
- self.temp_code = None
- return
- self.temp_code = code.funcstate.allocate_temp(
- type, manage_ref=self.use_managed_ref)
- else:
- self.temp_code = None
- def release_temp_result(self, code):
- if not self.temp_code:
- if not self.result_is_used:
- # not used anyway, so ignore if not set up
- return
- pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
- if self.old_temp:
- raise RuntimeError("temp %s released multiple times in %s at %r" % (
- self.old_temp, self.__class__.__name__, pos))
- else:
- raise RuntimeError("no temp, but release requested in %s at %r" % (
- self.__class__.__name__, pos))
- code.funcstate.release_temp(self.temp_code)
- self.old_temp = self.temp_code
- self.temp_code = None
- # ---------------- Code Generation -----------------
- def make_owned_reference(self, code):
- """
- If result is a pyobject, make sure we own a reference to it.
- If the result is in a temp, it is already a new reference.
- """
- if self.type.is_pyobject and not self.result_in_temp():
- code.put_incref(self.result(), self.ctype())
- def make_owned_memoryviewslice(self, code):
- """
- Make sure we own the reference to this memoryview slice.
- """
- if not self.result_in_temp():
- code.put_incref_memoryviewslice(self.result(),
- have_gil=self.in_nogil_context)
- def generate_evaluation_code(self, code):
- # Generate code to evaluate this node and
- # its sub-expressions, and dispose of any
- # temporary results of its sub-expressions.
- self.generate_subexpr_evaluation_code(code)
- code.mark_pos(self.pos)
- if self.is_temp:
- self.allocate_temp_result(code)
- self.generate_result_code(code)
- if self.is_temp and not (self.type.is_string or self.type.is_pyunicode_ptr):
- # If we are temp we do not need to wait until this node is disposed
- # before disposing children.
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- def generate_subexpr_evaluation_code(self, code):
- for node in self.subexpr_nodes():
- node.generate_evaluation_code(code)
- def generate_result_code(self, code):
- self.not_implemented("generate_result_code")
- def generate_disposal_code(self, code):
- if self.is_temp:
- if self.type.is_string or self.type.is_pyunicode_ptr:
- # postponed from self.generate_evaluation_code()
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- if self.result():
- if self.type.is_pyobject:
- code.put_decref_clear(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=not self.in_nogil_context)
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- # Already done if self.is_temp
- self.generate_subexpr_disposal_code(code)
- def generate_subexpr_disposal_code(self, code):
- # Generate code to dispose of temporary results
- # of all sub-expressions.
- for node in self.subexpr_nodes():
- node.generate_disposal_code(code)
- def generate_post_assignment_code(self, code):
- if self.is_temp:
- if self.type.is_string or self.type.is_pyunicode_ptr:
- # postponed from self.generate_evaluation_code()
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- elif self.type.is_pyobject:
- code.putln("%s = 0;" % self.result())
- elif self.type.is_memoryviewslice:
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- self.generate_subexpr_disposal_code(code)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- # Stub method for nodes which are not legal as
- # the LHS of an assignment. An error will have
- # been reported earlier.
- pass
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- # Stub method for nodes that are not legal as
- # the argument of a del statement. An error
- # will have been reported earlier.
- pass
- def free_temps(self, code):
- if self.is_temp:
- if not self.type.is_void:
- self.release_temp_result(code)
- else:
- self.free_subexpr_temps(code)
- def free_subexpr_temps(self, code):
- for sub in self.subexpr_nodes():
- sub.free_temps(code)
- def generate_function_definitions(self, env, code):
- pass
- # ---------------- Annotation ---------------------
- def annotate(self, code):
- for node in self.subexpr_nodes():
- node.annotate(code)
- # ----------------- Coercion ----------------------
- def coerce_to(self, dst_type, env):
- # Coerce the result so that it can be assigned to
- # something of type dst_type. If processing is necessary,
- # wraps this node in a coercion node and returns that.
- # Otherwise, returns this node unchanged.
- #
- # This method is called during the analyse_expressions
- # phase of the src_node's processing.
- #
- # Note that subclasses that override this (especially
- # ConstNodes) must not (re-)set their own .type attribute
- # here. Since expression nodes may turn up in different
- # places in the tree (e.g. inside of CloneNodes in cascaded
- # assignments), this method must return a new node instance
- # if it changes the type.
- #
- src = self
- src_type = self.type
- if self.check_for_coercion_error(dst_type, env):
- return self
- used_as_reference = dst_type.is_reference
- if used_as_reference and not src_type.is_reference:
- dst_type = dst_type.ref_base_type
- if src_type.is_const:
- src_type = src_type.const_base_type
- if src_type.is_fused or dst_type.is_fused:
- # See if we are coercing a fused function to a pointer to a
- # specialized function
- if (src_type.is_cfunction and not dst_type.is_fused and
- dst_type.is_ptr and dst_type.base_type.is_cfunction):
- dst_type = dst_type.base_type
- for signature in src_type.get_all_specialized_function_types():
- if signature.same_as(dst_type):
- src.type = signature
- src.entry = src.type.entry
- src.entry.used = True
- return self
- if src_type.is_fused:
- error(self.pos, "Type is not specialized")
- elif src_type.is_null_ptr and dst_type.is_ptr:
- # NULL can be implicitly cast to any pointer type
- return self
- else:
- error(self.pos, "Cannot coerce to a type that is not specialized")
- self.type = error_type
- return self
- if self.coercion_type is not None:
- # This is purely for error checking purposes!
- node = NameNode(self.pos, name='', type=self.coercion_type)
- node.coerce_to(dst_type, env)
- if dst_type.is_memoryviewslice:
- from . import MemoryView
- if not src.type.is_memoryviewslice:
- if src.type.is_pyobject:
- src = CoerceToMemViewSliceNode(src, dst_type, env)
- elif src.type.is_array:
- src = CythonArrayNode.from_carray(src, env).coerce_to(dst_type, env)
- elif not src_type.is_error:
- error(self.pos,
- "Cannot convert '%s' to memoryviewslice" % (src_type,))
- else:
- if src.type.writable_needed:
- dst_type.writable_needed = True
- if not src.type.conforms_to(dst_type, broadcast=self.is_memview_broadcast,
- copying=self.is_memview_copy_assignment):
- if src.type.dtype.same_as(dst_type.dtype):
- msg = "Memoryview '%s' not conformable to memoryview '%s'."
- tup = src.type, dst_type
- else:
- msg = "Different base types for memoryviews (%s, %s)"
- tup = src.type.dtype, dst_type.dtype
- error(self.pos, msg % tup)
- elif dst_type.is_pyobject:
- if not src.type.is_pyobject:
- if dst_type is bytes_type and src.type.is_int:
- src = CoerceIntToBytesNode(src, env)
- else:
- src = CoerceToPyTypeNode(src, env, type=dst_type)
- if not src.type.subtype_of(dst_type):
- if src.constant_result is not None:
- src = PyTypeTestNode(src, dst_type, env)
- elif is_pythran_expr(dst_type) and is_pythran_supported_type(src.type):
- # We let the compiler decide whether this is valid
- return src
- elif is_pythran_expr(src.type):
- if is_pythran_supported_type(dst_type):
- # Match the case were a pythran expr is assigned to a value, or vice versa.
- # We let the C++ compiler decide whether this is valid or not!
- return src
- # Else, we need to convert the Pythran expression to a Python object
- src = CoerceToPyTypeNode(src, env, type=dst_type)
- elif src.type.is_pyobject:
- if used_as_reference and dst_type.is_cpp_class:
- warning(
- self.pos,
- "Cannot pass Python object as C++ data structure reference (%s &), will pass by copy." % dst_type)
- src = CoerceFromPyTypeNode(dst_type, src, env)
- elif (dst_type.is_complex
- and src_type != dst_type
- and dst_type.assignable_from(src_type)):
- src = CoerceToComplexNode(src, dst_type, env)
- else: # neither src nor dst are py types
- # Added the string comparison, since for c types that
- # is enough, but Cython gets confused when the types are
- # in different pxi files.
- # TODO: Remove this hack and require shared declarations.
- if not (src.type == dst_type or str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
- self.fail_assignment(dst_type)
- return src
- def fail_assignment(self, dst_type):
- error(self.pos, "Cannot assign type '%s' to '%s'" % (self.type, dst_type))
- def check_for_coercion_error(self, dst_type, env, fail=False, default=None):
- if fail and not default:
- default = "Cannot assign type '%(FROM)s' to '%(TO)s'"
- message = find_coercion_error((self.type, dst_type), default, env)
- if message is not None:
- error(self.pos, message % {'FROM': self.type, 'TO': dst_type})
- return True
- if fail:
- self.fail_assignment(dst_type)
- return True
- return False
- def coerce_to_pyobject(self, env):
- return self.coerce_to(PyrexTypes.py_object_type, env)
- def coerce_to_boolean(self, env):
- # Coerce result to something acceptable as
- # a boolean value.
- # if it's constant, calculate the result now
- if self.has_constant_result():
- bool_value = bool(self.constant_result)
- return BoolNode(self.pos, value=bool_value,
- constant_result=bool_value)
- type = self.type
- if type.is_enum or type.is_error:
- return self
- elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
- return CoerceToBooleanNode(self, env)
- elif type.is_cpp_class and type.scope and type.scope.lookup("operator bool"):
- return SimpleCallNode(
- self.pos,
- function=AttributeNode(
- self.pos, obj=self, attribute=StringEncoding.EncodedString('operator bool')),
- args=[]).analyse_types(env)
- elif type.is_ctuple:
- bool_value = len(type.components) == 0
- return BoolNode(self.pos, value=bool_value,
- constant_result=bool_value)
- else:
- error(self.pos, "Type '%s' not acceptable as a boolean" % type)
- return self
- def coerce_to_integer(self, env):
- # If not already some C integer type, coerce to longint.
- if self.type.is_int:
- return self
- else:
- return self.coerce_to(PyrexTypes.c_long_type, env)
- def coerce_to_temp(self, env):
- # Ensure that the result is in a temporary.
- if self.result_in_temp():
- return self
- else:
- return CoerceToTempNode(self, env)
- def coerce_to_simple(self, env):
- # Ensure that the result is simple (see is_simple).
- if self.is_simple():
- return self
- else:
- return self.coerce_to_temp(env)
- def is_simple(self):
- # A node is simple if its result is something that can
- # be referred to without performing any operations, e.g.
- # a constant, local var, C global var, struct member
- # reference, or temporary.
- return self.result_in_temp()
- def may_be_none(self):
- if self.type and not (self.type.is_pyobject or
- self.type.is_memoryviewslice):
- return False
- if self.has_constant_result():
- return self.constant_result is not None
- return True
- def as_cython_attribute(self):
- return None
- def as_none_safe_node(self, message, error="PyExc_TypeError", format_args=()):
- # Wraps the node in a NoneCheckNode if it is not known to be
- # not-None (e.g. because it is a Python literal).
- if self.may_be_none():
- return NoneCheckNode(self, error, message, format_args)
- else:
- return self
- @classmethod
- def from_node(cls, node, **kwargs):
- """Instantiate this node class from another node, properly
- copying over all attributes that one would forget otherwise.
- """
- attributes = "cf_state cf_maybe_null cf_is_null constant_result".split()
- for attr_name in attributes:
- if attr_name in kwargs:
- continue
- try:
- value = getattr(node, attr_name)
- except AttributeError:
- pass
- else:
- kwargs[attr_name] = value
- return cls(node.pos, **kwargs)
- class AtomicExprNode(ExprNode):
- # Abstract base class for expression nodes which have
- # no sub-expressions.
- subexprs = []
- # Override to optimize -- we know we have no children
- def generate_subexpr_evaluation_code(self, code):
- pass
- def generate_subexpr_disposal_code(self, code):
- pass
- class PyConstNode(AtomicExprNode):
- # Abstract base class for constant Python values.
- is_literal = 1
- type = py_object_type
- def is_simple(self):
- return 1
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- return self
- def calculate_result_code(self):
- return self.value
- def generate_result_code(self, code):
- pass
- class NoneNode(PyConstNode):
- # The constant value None
- is_none = 1
- value = "Py_None"
- constant_result = None
- nogil_check = None
- def compile_time_value(self, denv):
- return None
- def may_be_none(self):
- return True
- def coerce_to(self, dst_type, env):
- if not (dst_type.is_pyobject or dst_type.is_memoryviewslice or dst_type.is_error):
- # Catch this error early and loudly.
- error(self.pos, "Cannot assign None to %s" % dst_type)
- return super(NoneNode, self).coerce_to(dst_type, env)
- class EllipsisNode(PyConstNode):
- # '...' in a subscript list.
- value = "Py_Ellipsis"
- constant_result = Ellipsis
- def compile_time_value(self, denv):
- return Ellipsis
- class ConstNode(AtomicExprNode):
- # Abstract base type for literal constant nodes.
- #
- # value string C code fragment
- is_literal = 1
- nogil_check = None
- def is_simple(self):
- return 1
- def nonlocally_immutable(self):
- return 1
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- return self # Types are held in class variables
- def check_const(self):
- return True
- def get_constant_c_result_code(self):
- return self.calculate_result_code()
- def calculate_result_code(self):
- return str(self.value)
- def generate_result_code(self, code):
- pass
- class BoolNode(ConstNode):
- type = PyrexTypes.c_bint_type
- # The constant value True or False
- def calculate_constant_result(self):
- self.constant_result = self.value
- def compile_time_value(self, denv):
- return self.value
- def calculate_result_code(self):
- if self.type.is_pyobject:
- return self.value and 'Py_True' or 'Py_False'
- else:
- return str(int(self.value))
- def coerce_to(self, dst_type, env):
- if dst_type == self.type:
- return self
- if dst_type is py_object_type and self.type is Builtin.bool_type:
- return self
- if dst_type.is_pyobject and self.type.is_int:
- return BoolNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=Builtin.bool_type)
- if dst_type.is_int and self.type.is_pyobject:
- return BoolNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=PyrexTypes.c_bint_type)
- return ConstNode.coerce_to(self, dst_type, env)
- class NullNode(ConstNode):
- type = PyrexTypes.c_null_ptr_type
- value = "NULL"
- constant_result = 0
- def get_constant_c_result_code(self):
- return self.value
- class CharNode(ConstNode):
- type = PyrexTypes.c_char_type
- def calculate_constant_result(self):
- self.constant_result = ord(self.value)
- def compile_time_value(self, denv):
- return ord(self.value)
- def calculate_result_code(self):
- return "'%s'" % StringEncoding.escape_char(self.value)
- class IntNode(ConstNode):
- # unsigned "" or "U"
- # longness "" or "L" or "LL"
- # is_c_literal True/False/None creator considers this a C integer literal
- unsigned = ""
- longness = ""
- is_c_literal = None # unknown
- def __init__(self, pos, **kwds):
- ExprNode.__init__(self, pos, **kwds)
- if 'type' not in kwds:
- self.type = self.find_suitable_type_for_value()
- def find_suitable_type_for_value(self):
- if self.constant_result is constant_value_not_set:
- try:
- self.calculate_constant_result()
- except ValueError:
- pass
- # we ignore 'is_c_literal = True' and instead map signed 32bit
- # integers as C long values
- if self.is_c_literal or \
- not self.has_constant_result() or \
- self.unsigned or self.longness == 'LL':
- # clearly a C literal
- rank = (self.longness == 'LL') and 2 or 1
- suitable_type = PyrexTypes.modifiers_and_name_to_type[not self.unsigned, rank, "int"]
- if self.type:
- suitable_type = PyrexTypes.widest_numeric_type(suitable_type, self.type)
- else:
- # C literal or Python literal - split at 32bit boundary
- if -2**31 <= self.constant_result < 2**31:
- if self.type and self.type.is_int:
- suitable_type = self.type
- else:
- suitable_type = PyrexTypes.c_long_type
- else:
- suitable_type = PyrexTypes.py_object_type
- return suitable_type
- def coerce_to(self, dst_type, env):
- if self.type is dst_type:
- return self
- elif dst_type.is_float:
- if self.has_constant_result():
- return FloatNode(self.pos, value='%d.0' % int(self.constant_result), type=dst_type,
- constant_result=float(self.constant_result))
- else:
- return FloatNode(self.pos, value=self.value, type=dst_type,
- constant_result=not_a_constant)
- if dst_type.is_numeric and not dst_type.is_complex:
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- type=dst_type, is_c_literal=True,
- unsigned=self.unsigned, longness=self.longness)
- return node
- elif dst_type.is_pyobject:
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- type=PyrexTypes.py_object_type, is_c_literal=False,
- unsigned=self.unsigned, longness=self.longness)
- else:
- # FIXME: not setting the type here to keep it working with
- # complex numbers. Should they be special cased?
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- unsigned=self.unsigned, longness=self.longness)
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return ConstNode.coerce_to(node, dst_type, env)
- def coerce_to_boolean(self, env):
- return IntNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=PyrexTypes.c_bint_type,
- unsigned=self.unsigned, longness=self.longness)
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- # pre-allocate a Python version of the number
- plain_integer_string = str(Utils.str_to_number(self.value))
- self.result_code = code.get_py_int(plain_integer_string, self.longness)
- else:
- self.result_code = self.get_constant_c_result_code()
- def get_constant_c_result_code(self):
- unsigned, longness = self.unsigned, self.longness
- literal = self.value_as_c_integer_string()
- if not (unsigned or longness) and self.type.is_int and literal[0] == '-' and literal[1] != '0':
- # negative decimal literal => guess longness from type to prevent wrap-around
- if self.type.rank >= PyrexTypes.c_longlong_type.rank:
- longness = 'LL'
- elif self.type.rank >= PyrexTypes.c_long_type.rank:
- longness = 'L'
- return literal + unsigned + longness
- def value_as_c_integer_string(self):
- value = self.value
- if len(value) <= 2:
- # too short to go wrong (and simplifies code below)
- return value
- neg_sign = ''
- if value[0] == '-':
- neg_sign = '-'
- value = value[1:]
- if value[0] == '0':
- literal_type = value[1] # 0'o' - 0'b' - 0'x'
- # 0x123 hex literals and 0123 octal literals work nicely in C
- # but C-incompatible Py3 oct/bin notations need conversion
- if neg_sign and literal_type in 'oOxX0123456789' and value[2:].isdigit():
- # negative hex/octal literal => prevent C compiler from using
- # unsigned integer types by converting to decimal (see C standard 6.4.4.1)
- value = str(Utils.str_to_number(value))
- elif literal_type in 'oO':
- value = '0' + value[2:] # '0o123' => '0123'
- elif literal_type in 'bB':
- value = str(int(value[2:], 2))
- elif value.isdigit() and not self.unsigned and not self.longness:
- if not neg_sign:
- # C compilers do not consider unsigned types for decimal literals,
- # but they do for hex (see C standard 6.4.4.1)
- value = '0x%X' % int(value)
- return neg_sign + value
- def calculate_result_code(self):
- return self.result_code
- def calculate_constant_result(self):
- self.constant_result = Utils.str_to_number(self.value)
- def compile_time_value(self, denv):
- return Utils.str_to_number(self.value)
- class FloatNode(ConstNode):
- type = PyrexTypes.c_double_type
- def calculate_constant_result(self):
- self.constant_result = float(self.value)
- def compile_time_value(self, denv):
- return float(self.value)
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject and self.type.is_float:
- return FloatNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=Builtin.float_type)
- if dst_type.is_float and self.type.is_pyobject:
- return FloatNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=dst_type)
- return ConstNode.coerce_to(self, dst_type, env)
- def calculate_result_code(self):
- return self.result_code
- def get_constant_c_result_code(self):
- strval = self.value
- assert isinstance(strval, basestring)
- cmpval = repr(float(strval))
- if cmpval == 'nan':
- return "(Py_HUGE_VAL * 0)"
- elif cmpval == 'inf':
- return "Py_HUGE_VAL"
- elif cmpval == '-inf':
- return "(-Py_HUGE_VAL)"
- else:
- return strval
- def generate_evaluation_code(self, code):
- c_value = self.get_constant_c_result_code()
- if self.type.is_pyobject:
- self.result_code = code.get_py_float(self.value, c_value)
- else:
- self.result_code = c_value
- def _analyse_name_as_type(name, pos, env):
- type = PyrexTypes.parse_basic_type(name)
- if type is not None:
- return type
- global_entry = env.global_scope().lookup(name)
- if global_entry and global_entry.type and (
- global_entry.type.is_extension_type
- or global_entry.type.is_struct_or_union
- or global_entry.type.is_builtin_type
- or global_entry.type.is_cpp_class):
- return global_entry.type
- from .TreeFragment import TreeFragment
- with local_errors(ignore=True):
- pos = (pos[0], pos[1], pos[2]-7)
- try:
- declaration = TreeFragment(u"sizeof(%s)" % name, name=pos[0].filename, initial_pos=pos)
- except CompileError:
- pass
- else:
- sizeof_node = declaration.root.stats[0].expr
- if isinstance(sizeof_node, SizeofTypeNode):
- sizeof_node = sizeof_node.analyse_types(env)
- if isinstance(sizeof_node, SizeofTypeNode):
- return sizeof_node.arg_type
- return None
- class BytesNode(ConstNode):
- # A char* or bytes literal
- #
- # value BytesLiteral
- is_string_literal = True
- # start off as Python 'bytes' to support len() in O(1)
- type = bytes_type
- def calculate_constant_result(self):
- self.constant_result = self.value
- def as_sliced_node(self, start, stop, step=None):
- value = StringEncoding.bytes_literal(self.value[start:stop:step], self.value.encoding)
- return BytesNode(self.pos, value=value, constant_result=value)
- def compile_time_value(self, denv):
- return self.value.byteencode()
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.value.decode('ISO8859-1'), self.pos, env)
- def can_coerce_to_char_literal(self):
- return len(self.value) == 1
- def coerce_to_boolean(self, env):
- # This is special because testing a C char* for truth directly
- # would yield the wrong result.
- bool_value = bool(self.value)
- return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
- def coerce_to(self, dst_type, env):
- if self.type == dst_type:
- return self
- if dst_type.is_int:
- if not self.can_coerce_to_char_literal():
- error(self.pos, "Only single-character string literals can be coerced into ints.")
- return self
- if dst_type.is_unicode_char:
- error(self.pos, "Bytes literals cannot coerce to Py_UNICODE/Py_UCS4, use a unicode literal instead.")
- return self
- return CharNode(self.pos, value=self.value,
- constant_result=ord(self.value))
- node = BytesNode(self.pos, value=self.value, constant_result=self.constant_result)
- if dst_type.is_pyobject:
- if dst_type in (py_object_type, Builtin.bytes_type):
- node.type = Builtin.bytes_type
- else:
- self.check_for_coercion_error(dst_type, env, fail=True)
- return node
- elif dst_type in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
- node.type = dst_type
- return node
- elif dst_type in (PyrexTypes.c_uchar_ptr_type, PyrexTypes.c_const_uchar_ptr_type, PyrexTypes.c_void_ptr_type):
- node.type = (PyrexTypes.c_const_char_ptr_type if dst_type == PyrexTypes.c_const_uchar_ptr_type
- else PyrexTypes.c_char_ptr_type)
- return CastNode(node, dst_type)
- elif dst_type.assignable_from(PyrexTypes.c_char_ptr_type):
- # Exclude the case of passing a C string literal into a non-const C++ string.
- if not dst_type.is_cpp_class or dst_type.is_const:
- node.type = dst_type
- return node
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return ConstNode.coerce_to(node, dst_type, env)
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- result = code.get_py_string_const(self.value)
- elif self.type.is_const:
- result = code.get_string_const(self.value)
- else:
- # not const => use plain C string literal and cast to mutable type
- literal = self.value.as_c_string_literal()
- # C++ may require a cast
- result = typecast(self.type, PyrexTypes.c_void_ptr_type, literal)
- self.result_code = result
- def get_constant_c_result_code(self):
- return None # FIXME
- def calculate_result_code(self):
- return self.result_code
- class UnicodeNode(ConstNode):
- # A Py_UNICODE* or unicode literal
- #
- # value EncodedString
- # bytes_value BytesLiteral the literal parsed as bytes string
- # ('-3' unicode literals only)
- is_string_literal = True
- bytes_value = None
- type = unicode_type
- def calculate_constant_result(self):
- self.constant_result = self.value
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.value, self.pos, env)
- def as_sliced_node(self, start, stop, step=None):
- if StringEncoding.string_contains_surrogates(self.value[:stop]):
- # this is unsafe as it may give different results
- # in different runtimes
- return None
- value = StringEncoding.EncodedString(self.value[start:stop:step])
- value.encoding = self.value.encoding
- if self.bytes_value is not None:
- bytes_value = StringEncoding.bytes_literal(
- self.bytes_value[start:stop:step], self.bytes_value.encoding)
- else:
- bytes_value = None
- return UnicodeNode(
- self.pos, value=value, bytes_value=bytes_value,
- constant_result=value)
- def coerce_to(self, dst_type, env):
- if dst_type is self.type:
- pass
- elif dst_type.is_unicode_char:
- if not self.can_coerce_to_char_literal():
- error(self.pos,
- "Only single-character Unicode string literals or "
- "surrogate pairs can be coerced into Py_UCS4/Py_UNICODE.")
- return self
- int_value = ord(self.value)
- return IntNode(self.pos, type=dst_type, value=str(int_value),
- constant_result=int_value)
- elif not dst_type.is_pyobject:
- if dst_type.is_string and self.bytes_value is not None:
- # special case: '-3' enforced unicode literal used in a
- # C char* context
- return BytesNode(self.pos, value=self.bytes_value
- ).coerce_to(dst_type, env)
- if dst_type.is_pyunicode_ptr:
- node = UnicodeNode(self.pos, value=self.value)
- node.type = dst_type
- return node
- error(self.pos,
- "Unicode literals do not support coercion to C types other "
- "than Py_UNICODE/Py_UCS4 (for characters) or Py_UNICODE* "
- "(for strings).")
- elif dst_type not in (py_object_type, Builtin.basestring_type):
- self.check_for_coercion_error(dst_type, env, fail=True)
- return self
- def can_coerce_to_char_literal(self):
- return len(self.value) == 1
- ## or (len(self.value) == 2
- ## and (0xD800 <= self.value[0] <= 0xDBFF)
- ## and (0xDC00 <= self.value[1] <= 0xDFFF))
- def coerce_to_boolean(self, env):
- bool_value = bool(self.value)
- return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
- def contains_surrogates(self):
- return StringEncoding.string_contains_surrogates(self.value)
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- # FIXME: this should go away entirely!
- # Since string_contains_lone_surrogates() returns False for surrogate pairs in Py2/UCS2,
- # Py2 can generate different code from Py3 here. Let's hope we get away with claiming that
- # the processing of surrogate pairs in code was always ambiguous and lead to different results
- # on P16/32bit Unicode platforms.
- if StringEncoding.string_contains_lone_surrogates(self.value):
- # lone (unpaired) surrogates are not really portable and cannot be
- # decoded by the UTF-8 codec in Py3.3
- self.result_code = code.get_py_const(py_object_type, 'ustring')
- data_cname = code.get_string_const(
- StringEncoding.BytesLiteral(self.value.encode('unicode_escape')))
- const_code = code.get_cached_constants_writer(self.result_code)
- if const_code is None:
- return # already initialised
- const_code.mark_pos(self.pos)
- const_code.putln(
- "%s = PyUnicode_DecodeUnicodeEscape(%s, sizeof(%s) - 1, NULL); %s" % (
- self.result_code,
- data_cname,
- data_cname,
- const_code.error_goto_if_null(self.result_code, self.pos)))
- const_code.put_error_if_neg(
- self.pos, "__Pyx_PyUnicode_READY(%s)" % self.result_code)
- else:
- self.result_code = code.get_py_string_const(self.value)
- else:
- self.result_code = code.get_pyunicode_ptr_const(self.value)
- def calculate_result_code(self):
- return self.result_code
- def compile_time_value(self, env):
- return self.value
- class StringNode(PyConstNode):
- # A Python str object, i.e. a byte string in Python 2.x and a
- # unicode string in Python 3.x
- #
- # value BytesLiteral (or EncodedString with ASCII content)
- # unicode_value EncodedString or None
- # is_identifier boolean
- type = str_type
- is_string_literal = True
- is_identifier = None
- unicode_value = None
- def calculate_constant_result(self):
- if self.unicode_value is not None:
- # only the Unicode value is portable across Py2/3
- self.constant_result = self.unicode_value
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.unicode_value or self.value.decode('ISO8859-1'), self.pos, env)
- def as_sliced_node(self, start, stop, step=None):
- value = type(self.value)(self.value[start:stop:step])
- value.encoding = self.value.encoding
- if self.unicode_value is not None:
- if StringEncoding.string_contains_surrogates(self.unicode_value[:stop]):
- # this is unsafe as it may give different results in different runtimes
- return None
- unicode_value = StringEncoding.EncodedString(
- self.unicode_value[start:stop:step])
- else:
- unicode_value = None
- return StringNode(
- self.pos, value=value, unicode_value=unicode_value,
- constant_result=value, is_identifier=self.is_identifier)
- def coerce_to(self, dst_type, env):
- if dst_type is not py_object_type and not str_type.subtype_of(dst_type):
- # if dst_type is Builtin.bytes_type:
- # # special case: bytes = 'str literal'
- # return BytesNode(self.pos, value=self.value)
- if not dst_type.is_pyobject:
- return BytesNode(self.pos, value=self.value).coerce_to(dst_type, env)
- if dst_type is not Builtin.basestring_type:
- self.check_for_coercion_error(dst_type, env, fail=True)
- return self
- def can_coerce_to_char_literal(self):
- return not self.is_identifier and len(self.value) == 1
- def generate_evaluation_code(self, code):
- self.result_code = code.get_py_string_const(
- self.value, identifier=self.is_identifier, is_str=True,
- unicode_value=self.unicode_value)
- def get_constant_c_result_code(self):
- return None
- def calculate_result_code(self):
- return self.result_code
- def compile_time_value(self, env):
- if self.value.is_unicode:
- return self.value
- if not IS_PYTHON3:
- # use plain str/bytes object in Py2
- return self.value.byteencode()
- # in Py3, always return a Unicode string
- if self.unicode_value is not None:
- return self.unicode_value
- return self.value.decode('iso8859-1')
- class IdentifierStringNode(StringNode):
- # A special str value that represents an identifier (bytes in Py2,
- # unicode in Py3).
- is_identifier = True
- class ImagNode(AtomicExprNode):
- # Imaginary number literal
- #
- # value string imaginary part (float value)
- type = PyrexTypes.c_double_complex_type
- def calculate_constant_result(self):
- self.constant_result = complex(0.0, float(self.value))
- def compile_time_value(self, denv):
- return complex(0.0, float(self.value))
- def analyse_types(self, env):
- self.type.create_declaration_utility_code(env)
- return self
- def may_be_none(self):
- return False
- def coerce_to(self, dst_type, env):
- if self.type is dst_type:
- return self
- node = ImagNode(self.pos, value=self.value)
- if dst_type.is_pyobject:
- node.is_temp = 1
- node.type = Builtin.complex_type
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return AtomicExprNode.coerce_to(node, dst_type, env)
- gil_message = "Constructing complex number"
- def calculate_result_code(self):
- if self.type.is_pyobject:
- return self.result()
- else:
- return "%s(0, %r)" % (self.type.from_parts, float(self.value))
- def generate_result_code(self, code):
- if self.type.is_pyobject:
- code.putln(
- "%s = PyComplex_FromDoubles(0.0, %r); %s" % (
- self.result(),
- float(self.value),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class NewExprNode(AtomicExprNode):
- # C++ new statement
- #
- # cppclass node c++ class to create
- type = None
- def infer_type(self, env):
- type = self.cppclass.analyse_as_type(env)
- if type is None or not type.is_cpp_class:
- error(self.pos, "new operator can only be applied to a C++ class")
- self.type = error_type
- return
- self.cpp_check(env)
- constructor = type.get_constructor(self.pos)
- self.class_type = type
- self.entry = constructor
- self.type = constructor.type
- return self.type
- def analyse_types(self, env):
- if self.type is None:
- self.infer_type(env)
- return self
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- pass
- def calculate_result_code(self):
- return "new " + self.class_type.empty_declaration_code()
- class NameNode(AtomicExprNode):
- # Reference to a local or global variable name.
- #
- # name string Python name of the variable
- # entry Entry Symbol table entry
- # type_entry Entry For extension type names, the original type entry
- # cf_is_null boolean Is uninitialized before this node
- # cf_maybe_null boolean Maybe uninitialized before this node
- # allow_null boolean Don't raise UnboundLocalError
- # nogil boolean Whether it is used in a nogil context
- is_name = True
- is_cython_module = False
- cython_attribute = None
- lhs_of_first_assignment = False # TODO: remove me
- is_used_as_rvalue = 0
- entry = None
- type_entry = None
- cf_maybe_null = True
- cf_is_null = False
- allow_null = False
- nogil = False
- inferred_type = None
- def as_cython_attribute(self):
- return self.cython_attribute
- def type_dependencies(self, env):
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if self.entry is not None and self.entry.type.is_unspecified:
- return (self,)
- else:
- return ()
- def infer_type(self, env):
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if self.entry is None or self.entry.type is unspecified_type:
- if self.inferred_type is not None:
- return self.inferred_type
- return py_object_type
- elif (self.entry.type.is_extension_type or self.entry.type.is_builtin_type) and \
- self.name == self.entry.type.name:
- # Unfortunately the type attribute of type objects
- # is used for the pointer to the type they represent.
- return type_type
- elif self.entry.type.is_cfunction:
- if self.entry.scope.is_builtin_scope:
- # special case: optimised builtin functions must be treated as Python objects
- return py_object_type
- else:
- # special case: referring to a C function must return its pointer
- return PyrexTypes.CPtrType(self.entry.type)
- else:
- # If entry is inferred as pyobject it's safe to use local
- # NameNode's inferred_type.
- if self.entry.type.is_pyobject and self.inferred_type:
- # Overflow may happen if integer
- if not (self.inferred_type.is_int and self.entry.might_overflow):
- return self.inferred_type
- return self.entry.type
- def compile_time_value(self, denv):
- try:
- return denv.lookup(self.name)
- except KeyError:
- error(self.pos, "Compile-time name '%s' not defined" % self.name)
- def get_constant_c_result_code(self):
- if not self.entry or self.entry.type.is_pyobject:
- return None
- return self.entry.cname
- def coerce_to(self, dst_type, env):
- # If coercing to a generic pyobject and this is a builtin
- # C function with a Python equivalent, manufacture a NameNode
- # referring to the Python builtin.
- #print "NameNode.coerce_to:", self.name, dst_type ###
- if dst_type is py_object_type:
- entry = self.entry
- if entry and entry.is_cfunction:
- var_entry = entry.as_variable
- if var_entry:
- if var_entry.is_builtin and var_entry.is_const:
- var_entry = env.declare_builtin(var_entry.name, self.pos)
- node = NameNode(self.pos, name = self.name)
- node.entry = var_entry
- node.analyse_rvalue_entry(env)
- return node
- return super(NameNode, self).coerce_to(dst_type, env)
- def declare_from_annotation(self, env, as_target=False):
- """Implements PEP 526 annotation typing in a fairly relaxed way.
- Annotations are ignored for global variables, Python class attributes and already declared variables.
- String literals are allowed and ignored.
- The ambiguous Python types 'int' and 'long' are ignored and the 'cython.int' form must be used instead.
- """
- if not env.directives['annotation_typing']:
- return
- if env.is_module_scope or env.is_py_class_scope:
- # annotations never create global cdef names and Python classes don't support them anyway
- return
- name = self.name
- if self.entry or env.lookup_here(name) is not None:
- # already declared => ignore annotation
- return
- annotation = self.annotation
- if annotation.is_string_literal:
- # name: "description" => not a type, but still a declared variable or attribute
- atype = None
- else:
- _, atype = analyse_type_annotation(annotation, env)
- if atype is None:
- atype = unspecified_type if as_target and env.directives['infer_types'] != False else py_object_type
- self.entry = env.declare_var(name, atype, self.pos, is_cdef=not as_target)
- self.entry.annotation = annotation
- def analyse_as_module(self, env):
- # Try to interpret this as a reference to a cimported module.
- # Returns the module scope, or None.
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.as_module:
- return entry.as_module
- return None
- def analyse_as_type(self, env):
- if self.cython_attribute:
- type = PyrexTypes.parse_basic_type(self.cython_attribute)
- else:
- type = PyrexTypes.parse_basic_type(self.name)
- if type:
- return type
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.is_type:
- return entry.type
- else:
- return None
- def analyse_as_extension_type(self, env):
- # Try to interpret this as a reference to an extension type.
- # Returns the extension type, or None.
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.is_type:
- if entry.type.is_extension_type or entry.type.is_builtin_type:
- return entry.type
- return None
- def analyse_target_declaration(self, env):
- if not self.entry:
- self.entry = env.lookup_here(self.name)
- if not self.entry and self.annotation is not None:
- # name : type = ...
- self.declare_from_annotation(env, as_target=True)
- if not self.entry:
- if env.directives['warn.undeclared']:
- warning(self.pos, "implicit declaration of '%s'" % self.name, 1)
- if env.directives['infer_types'] != False:
- type = unspecified_type
- else:
- type = py_object_type
- self.entry = env.declare_var(self.name, type, self.pos)
- if self.entry.is_declared_generic:
- self.result_ctype = py_object_type
- if self.entry.as_module:
- # cimported modules namespace can shadow actual variables
- self.entry.is_variable = 1
- def analyse_types(self, env):
- self.initialized_check = env.directives['initializedcheck']
- entry = self.entry
- if entry is None:
- entry = env.lookup(self.name)
- if not entry:
- entry = env.declare_builtin(self.name, self.pos)
- if entry and entry.is_builtin and entry.is_const:
- self.is_literal = True
- if not entry:
- self.type = PyrexTypes.error_type
- return self
- self.entry = entry
- entry.used = 1
- if entry.type.is_buffer:
- from . import Buffer
- Buffer.used_buffer_aux_vars(entry)
- self.analyse_rvalue_entry(env)
- return self
- def analyse_target_types(self, env):
- self.analyse_entry(env, is_target=True)
- entry = self.entry
- if entry.is_cfunction and entry.as_variable:
- # FIXME: unify "is_overridable" flags below
- if (entry.is_overridable or entry.type.is_overridable) or not self.is_lvalue() and entry.fused_cfunction:
- # We need this for assigning to cpdef names and for the fused 'def' TreeFragment
- entry = self.entry = entry.as_variable
- self.type = entry.type
- if self.type.is_const:
- error(self.pos, "Assignment to const '%s'" % self.name)
- if self.type.is_reference:
- error(self.pos, "Assignment to reference '%s'" % self.name)
- if not self.is_lvalue():
- error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
- self.type = PyrexTypes.error_type
- entry.used = 1
- if entry.type.is_buffer:
- from . import Buffer
- Buffer.used_buffer_aux_vars(entry)
- return self
- def analyse_rvalue_entry(self, env):
- #print "NameNode.analyse_rvalue_entry:", self.name ###
- #print "Entry:", self.entry.__dict__ ###
- self.analyse_entry(env)
- entry = self.entry
- if entry.is_declared_generic:
- self.result_ctype = py_object_type
- if entry.is_pyglobal or entry.is_builtin:
- if entry.is_builtin and entry.is_const:
- self.is_temp = 0
- else:
- self.is_temp = 1
- self.is_used_as_rvalue = 1
- elif entry.type.is_memoryviewslice:
- self.is_temp = False
- self.is_used_as_rvalue = True
- self.use_managed_ref = True
- return self
- def nogil_check(self, env):
- self.nogil = True
- if self.is_used_as_rvalue:
- entry = self.entry
- if entry.is_builtin:
- if not entry.is_const: # cached builtins are ok
- self.gil_error()
- elif entry.is_pyglobal:
- self.gil_error()
- gil_message = "Accessing Python global or builtin"
- def analyse_entry(self, env, is_target=False):
- #print "NameNode.analyse_entry:", self.name ###
- self.check_identifier_kind()
- entry = self.entry
- type = entry.type
- if (not is_target and type.is_pyobject and self.inferred_type and
- self.inferred_type.is_builtin_type):
- # assume that type inference is smarter than the static entry
- type = self.inferred_type
- self.type = type
- def check_identifier_kind(self):
- # Check that this is an appropriate kind of name for use in an
- # expression. Also finds the variable entry associated with
- # an extension type.
- entry = self.entry
- if entry.is_type and entry.type.is_extension_type:
- self.type_entry = entry
- if entry.is_type and entry.type.is_enum:
- py_entry = Symtab.Entry(self.name, None, py_object_type)
- py_entry.is_pyglobal = True
- py_entry.scope = self.entry.scope
- self.entry = py_entry
- elif not (entry.is_const or entry.is_variable or
- entry.is_builtin or entry.is_cfunction or
- entry.is_cpp_class):
- if self.entry.as_variable:
- self.entry = self.entry.as_variable
- elif not self.is_cython_module:
- error(self.pos, "'%s' is not a constant, variable or function identifier" % self.name)
- def is_cimported_module_without_shadow(self, env):
- if self.is_cython_module or self.cython_attribute:
- return False
- entry = self.entry or env.lookup(self.name)
- return entry.as_module and not entry.is_variable
- def is_simple(self):
- # If it's not a C variable, it'll be in a temp.
- return 1
- def may_be_none(self):
- if self.cf_state and self.type and (self.type.is_pyobject or
- self.type.is_memoryviewslice):
- # gard against infinite recursion on self-dependencies
- if getattr(self, '_none_checking', False):
- # self-dependency - either this node receives a None
- # value from *another* node, or it can not reference
- # None at this point => safe to assume "not None"
- return False
- self._none_checking = True
- # evaluate control flow state to see if there were any
- # potential None values assigned to the node so far
- may_be_none = False
- for assignment in self.cf_state:
- if assignment.rhs.may_be_none():
- may_be_none = True
- break
- del self._none_checking
- return may_be_none
- return super(NameNode, self).may_be_none()
- def nonlocally_immutable(self):
- if ExprNode.nonlocally_immutable(self):
- return True
- entry = self.entry
- if not entry or entry.in_closure:
- return False
- return entry.is_local or entry.is_arg or entry.is_builtin or entry.is_readonly
- def calculate_target_results(self, env):
- pass
- def check_const(self):
- entry = self.entry
- if entry is not None and not (
- entry.is_const or
- entry.is_cfunction or
- entry.is_builtin or
- entry.type.is_const):
- self.not_const()
- return False
- return True
- def check_const_addr(self):
- entry = self.entry
- if not (entry.is_cglobal or entry.is_cfunction or entry.is_builtin):
- self.addr_not_const()
- return False
- return True
- def is_lvalue(self):
- return (
- self.entry.is_variable and
- not self.entry.is_readonly
- ) or (
- self.entry.is_cfunction and
- self.entry.is_overridable
- )
- def is_addressable(self):
- return self.entry.is_variable and not self.type.is_memoryviewslice
- def is_ephemeral(self):
- # Name nodes are never ephemeral, even if the
- # result is in a temporary.
- return 0
- def calculate_result_code(self):
- entry = self.entry
- if not entry:
- return "<error>" # There was an error earlier
- return entry.cname
- def generate_result_code(self, code):
- assert hasattr(self, 'entry')
- entry = self.entry
- if entry is None:
- return # There was an error earlier
- if entry.utility_code:
- code.globalstate.use_utility_code(entry.utility_code)
- if entry.is_builtin and entry.is_const:
- return # Lookup already cached
- elif entry.is_pyclass_attr:
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- if entry.is_builtin:
- namespace = Naming.builtins_cname
- else: # entry.is_pyglobal
- namespace = entry.scope.namespace_cname
- if not self.cf_is_null:
- code.putln(
- '%s = PyObject_GetItem(%s, %s);' % (
- self.result(),
- namespace,
- interned_cname))
- code.putln('if (unlikely(!%s)) {' % self.result())
- code.putln('PyErr_Clear();')
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
- code.putln(
- '__Pyx_GetModuleGlobalName(%s, %s);' % (
- self.result(),
- interned_cname))
- if not self.cf_is_null:
- code.putln("}")
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- elif entry.is_builtin and not entry.scope.is_module_scope:
- # known builtin
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c"))
- code.putln(
- '%s = __Pyx_GetBuiltinName(%s); %s' % (
- self.result(),
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope):
- # name in class body, global name or unknown builtin
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- if entry.scope.is_module_scope:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
- code.putln(
- '__Pyx_GetModuleGlobalName(%s, %s); %s' % (
- self.result(),
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- else:
- # FIXME: is_pyglobal is also used for class namespace
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetNameInClass", "ObjectHandling.c"))
- code.putln(
- '__Pyx_GetNameInClass(%s, %s, %s); %s' % (
- self.result(),
- entry.scope.namespace_cname,
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif entry.is_local or entry.in_closure or entry.from_closure or entry.type.is_memoryviewslice:
- # Raise UnboundLocalError for objects and memoryviewslices
- raise_unbound = (
- (self.cf_maybe_null or self.cf_is_null) and not self.allow_null)
- null_code = entry.type.check_for_null_code(entry.cname)
- memslice_check = entry.type.is_memoryviewslice and self.initialized_check
- if null_code and raise_unbound and (entry.type.is_pyobject or memslice_check):
- code.put_error_if_unbound(self.pos, entry, self.in_nogil_context)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- #print "NameNode.generate_assignment_code:", self.name ###
- entry = self.entry
- if entry is None:
- return # There was an error earlier
- if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
- and not self.lhs_of_first_assignment and not rhs.in_module_scope):
- error(self.pos, "Literal list must be assigned to pointer at time of declaration")
- # is_pyglobal seems to be True for module level-globals only.
- # We use this to access class->tp_dict if necessary.
- if entry.is_pyglobal:
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- namespace = self.entry.scope.namespace_cname
- if entry.is_member:
- # if the entry is a member we have to cheat: SetAttr does not work
- # on types, so we create a descriptor which is then added to tp_dict
- setter = 'PyDict_SetItem'
- namespace = '%s->tp_dict' % namespace
- elif entry.scope.is_module_scope:
- setter = 'PyDict_SetItem'
- namespace = Naming.moddict_cname
- elif entry.is_pyclass_attr:
- code.globalstate.use_utility_code(UtilityCode.load_cached("SetNameInClass", "ObjectHandling.c"))
- setter = '__Pyx_SetNameInClass'
- else:
- assert False, repr(entry)
- code.put_error_if_neg(
- self.pos,
- '%s(%s, %s, %s)' % (
- setter,
- namespace,
- interned_cname,
- rhs.py_result()))
- if debug_disposal_code:
- print("NameNode.generate_assignment_code:")
- print("...generating disposal code for %s" % rhs)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- if entry.is_member:
- # in Py2.6+, we need to invalidate the method cache
- code.putln("PyType_Modified(%s);" %
- entry.scope.parent_type.typeptr_cname)
- else:
- if self.type.is_memoryviewslice:
- self.generate_acquire_memoryviewslice(rhs, code)
- elif self.type.is_buffer:
- # Generate code for doing the buffer release/acquisition.
- # This might raise an exception in which case the assignment (done
- # below) will not happen.
- #
- # The reason this is not in a typetest-like node is because the
- # variables that the acquired buffer info is stored to is allocated
- # per entry and coupled with it.
- self.generate_acquire_buffer(rhs, code)
- assigned = False
- if self.type.is_pyobject:
- #print "NameNode.generate_assignment_code: to", self.name ###
- #print "...from", rhs ###
- #print "...LHS type", self.type, "ctype", self.ctype() ###
- #print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
- if self.use_managed_ref:
- rhs.make_owned_reference(code)
- is_external_ref = entry.is_cglobal or self.entry.in_closure or self.entry.from_closure
- if is_external_ref:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xgotref(self.py_result())
- else:
- code.put_gotref(self.py_result())
- assigned = True
- if entry.is_cglobal:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xdecref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- assigned = False
- if is_external_ref:
- code.put_giveref(rhs.py_result())
- if not self.type.is_memoryviewslice:
- if not assigned:
- if overloaded_assignment:
- result = rhs.result()
- if exception_check == '+':
- translate_cpp_exception(
- code, self.pos,
- '%s = %s;' % (self.result(), result),
- self.result() if self.type.is_pyobject else None,
- exception_value, self.in_nogil_context)
- else:
- code.putln('%s = %s;' % (self.result(), result))
- else:
- result = rhs.result_as(self.ctype())
- if is_pythran_expr(self.type):
- code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result))
- elif result != self.result():
- code.putln('%s = %s;' % (self.result(), result))
- if debug_disposal_code:
- print("NameNode.generate_assignment_code:")
- print("...generating post-assignment code for %s" % rhs)
- rhs.generate_post_assignment_code(code)
- elif rhs.result_in_temp():
- rhs.generate_post_assignment_code(code)
- rhs.free_temps(code)
- def generate_acquire_memoryviewslice(self, rhs, code):
- """
- Slices, coercions from objects, return values etc are new references.
- We have a borrowed reference in case of dst = src
- """
- from . import MemoryView
- MemoryView.put_acquire_memoryviewslice(
- lhs_cname=self.result(),
- lhs_type=self.type,
- lhs_pos=self.pos,
- rhs=rhs,
- code=code,
- have_gil=not self.in_nogil_context,
- first_assignment=self.cf_is_null)
- def generate_acquire_buffer(self, rhs, code):
- # rhstmp is only used in case the rhs is a complicated expression leading to
- # the object, to avoid repeating the same C expression for every reference
- # to the rhs. It does NOT hold a reference.
- pretty_rhs = isinstance(rhs, NameNode) or rhs.is_temp
- if pretty_rhs:
- rhstmp = rhs.result_as(self.ctype())
- else:
- rhstmp = code.funcstate.allocate_temp(self.entry.type, manage_ref=False)
- code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
- from . import Buffer
- Buffer.put_assign_to_buffer(self.result(), rhstmp, self.entry,
- is_initialized=not self.lhs_of_first_assignment,
- pos=self.pos, code=code)
- if not pretty_rhs:
- code.putln("%s = 0;" % rhstmp)
- code.funcstate.release_temp(rhstmp)
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- if self.entry is None:
- return # There was an error earlier
- elif self.entry.is_pyclass_attr:
- namespace = self.entry.scope.namespace_cname
- interned_cname = code.intern_identifier(self.entry.name)
- if ignore_nonexisting:
- key_error_code = 'PyErr_Clear(); else'
- else:
- # minor hack: fake a NameError on KeyError
- key_error_code = (
- '{ PyErr_Clear(); PyErr_Format(PyExc_NameError, "name \'%%s\' is not defined", "%s"); }' %
- self.entry.name)
- code.putln(
- 'if (unlikely(PyObject_DelItem(%s, %s) < 0)) {'
- ' if (likely(PyErr_ExceptionMatches(PyExc_KeyError))) %s'
- ' %s '
- '}' % (namespace, interned_cname,
- key_error_code,
- code.error_goto(self.pos)))
- elif self.entry.is_pyglobal:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- interned_cname = code.intern_identifier(self.entry.name)
- del_code = '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
- Naming.module_cname, interned_cname)
- if ignore_nonexisting:
- code.putln(
- 'if (unlikely(%s < 0)) {'
- ' if (likely(PyErr_ExceptionMatches(PyExc_AttributeError))) PyErr_Clear(); else %s '
- '}' % (del_code, code.error_goto(self.pos)))
- else:
- code.put_error_if_neg(self.pos, del_code)
- elif self.entry.type.is_pyobject or self.entry.type.is_memoryviewslice:
- if not self.cf_is_null:
- if self.cf_maybe_null and not ignore_nonexisting:
- code.put_error_if_unbound(self.pos, self.entry)
- if self.entry.type.is_pyobject:
- if self.entry.in_closure:
- # generator
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xgotref(self.result())
- else:
- code.put_gotref(self.result())
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xdecref(self.result(), self.ctype())
- else:
- code.put_decref(self.result(), self.ctype())
- code.putln('%s = NULL;' % self.result())
- else:
- code.put_xdecref_memoryviewslice(self.entry.cname,
- have_gil=not self.nogil)
- else:
- error(self.pos, "Deletion of C names not supported")
- def annotate(self, code):
- if hasattr(self, 'is_called') and self.is_called:
- pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
- if self.type.is_pyobject:
- style, text = 'py_call', 'python function (%s)'
- else:
- style, text = 'c_call', 'c function (%s)'
- code.annotate(pos, AnnotationItem(style, text % self.type, size=len(self.name)))
- class BackquoteNode(ExprNode):
- # `expr`
- #
- # arg ExprNode
- type = py_object_type
- subexprs = ['arg']
- def analyse_types(self, env):
- self.arg = self.arg.analyse_types(env)
- self.arg = self.arg.coerce_to_pyobject(env)
- self.is_temp = 1
- return self
- gil_message = "Backquote expression"
- def calculate_constant_result(self):
- self.constant_result = repr(self.arg.constant_result)
- def generate_result_code(self, code):
- code.putln(
- "%s = PyObject_Repr(%s); %s" % (
- self.result(),
- self.arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class ImportNode(ExprNode):
- # Used as part of import statement implementation.
- # Implements result =
- # __import__(module_name, globals(), None, name_list, level)
- #
- # module_name StringNode dotted name of module. Empty module
- # name means importing the parent package according
- # to level
- # name_list ListNode or None list of names to be imported
- # level int relative import level:
- # -1: attempt both relative import and absolute import;
- # 0: absolute import;
- # >0: the number of parent directories to search
- # relative to the current module.
- # None: decide the level according to language level and
- # directives
- type = py_object_type
- subexprs = ['module_name', 'name_list']
- def analyse_types(self, env):
- if self.level is None:
- if (env.directives['py2_import'] or
- Future.absolute_import not in env.global_scope().context.future_directives):
- self.level = -1
- else:
- self.level = 0
- module_name = self.module_name.analyse_types(env)
- self.module_name = module_name.coerce_to_pyobject(env)
- if self.name_list:
- name_list = self.name_list.analyse_types(env)
- self.name_list = name_list.coerce_to_pyobject(env)
- self.is_temp = 1
- return self
- gil_message = "Python import"
- def generate_result_code(self, code):
- if self.name_list:
- name_list_code = self.name_list.py_result()
- else:
- name_list_code = "0"
- code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c"))
- import_code = "__Pyx_Import(%s, %s, %d)" % (
- self.module_name.py_result(),
- name_list_code,
- self.level)
- if (self.level <= 0 and
- self.module_name.is_string_literal and
- self.module_name.value in utility_code_for_imports):
- helper_func, code_name, code_file = utility_code_for_imports[self.module_name.value]
- code.globalstate.use_utility_code(UtilityCode.load_cached(code_name, code_file))
- import_code = '%s(%s)' % (helper_func, import_code)
- code.putln("%s = %s; %s" % (
- self.result(),
- import_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class IteratorNode(ExprNode):
- # Used as part of for statement implementation.
- #
- # Implements result = iter(sequence)
- #
- # sequence ExprNode
- type = py_object_type
- iter_func_ptr = None
- counter_cname = None
- cpp_iterator_cname = None
- reversed = False # currently only used for list/tuple types (see Optimize.py)
- is_async = False
- subexprs = ['sequence']
- def analyse_types(self, env):
- self.sequence = self.sequence.analyse_types(env)
- if (self.sequence.type.is_array or self.sequence.type.is_ptr) and \
- not self.sequence.type.is_string:
- # C array iteration will be transformed later on
- self.type = self.sequence.type
- elif self.sequence.type.is_cpp_class:
- self.analyse_cpp_types(env)
- else:
- self.sequence = self.sequence.coerce_to_pyobject(env)
- if self.sequence.type in (list_type, tuple_type):
- self.sequence = self.sequence.as_none_safe_node("'NoneType' object is not iterable")
- self.is_temp = 1
- return self
- gil_message = "Iterating over Python object"
- _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
- PyrexTypes.py_object_type, [
- PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
- ]))
- def type_dependencies(self, env):
- return self.sequence.type_dependencies(env)
- def infer_type(self, env):
- sequence_type = self.sequence.infer_type(env)
- if sequence_type.is_array or sequence_type.is_ptr:
- return sequence_type
- elif sequence_type.is_cpp_class:
- begin = sequence_type.scope.lookup("begin")
- if begin is not None:
- return begin.type.return_type
- elif sequence_type.is_pyobject:
- return sequence_type
- return py_object_type
- def analyse_cpp_types(self, env):
- sequence_type = self.sequence.type
- if sequence_type.is_ptr:
- sequence_type = sequence_type.base_type
- begin = sequence_type.scope.lookup("begin")
- end = sequence_type.scope.lookup("end")
- if (begin is None
- or not begin.type.is_cfunction
- or begin.type.args):
- error(self.pos, "missing begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if (end is None
- or not end.type.is_cfunction
- or end.type.args):
- error(self.pos, "missing end() on %s" % self.sequence.type)
- self.type = error_type
- return
- iter_type = begin.type.return_type
- if iter_type.is_cpp_class:
- if env.lookup_operator_for_types(
- self.pos,
- "!=",
- [iter_type, end.type.return_type]) is None:
- error(self.pos, "missing operator!= on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '++', [iter_type]) is None:
- error(self.pos, "missing operator++ on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '*', [iter_type]) is None:
- error(self.pos, "missing operator* on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- self.type = iter_type
- elif iter_type.is_ptr:
- if not (iter_type == end.type.return_type):
- error(self.pos, "incompatible types for begin() and end()")
- self.type = iter_type
- else:
- error(self.pos, "result type of begin() on %s must be a C++ class or pointer" % self.sequence.type)
- self.type = error_type
- return
- def generate_result_code(self, code):
- sequence_type = self.sequence.type
- if sequence_type.is_cpp_class:
- if self.sequence.is_name:
- # safe: C++ won't allow you to reassign to class references
- begin_func = "%s.begin" % self.sequence.result()
- else:
- sequence_type = PyrexTypes.c_ptr_type(sequence_type)
- self.cpp_iterator_cname = code.funcstate.allocate_temp(sequence_type, manage_ref=False)
- code.putln("%s = &%s;" % (self.cpp_iterator_cname, self.sequence.result()))
- begin_func = "%s->begin" % self.cpp_iterator_cname
- # TODO: Limit scope.
- code.putln("%s = %s();" % (self.result(), begin_func))
- return
- if sequence_type.is_array or sequence_type.is_ptr:
- raise InternalError("for in carray slice not transformed")
- is_builtin_sequence = sequence_type in (list_type, tuple_type)
- if not is_builtin_sequence:
- # reversed() not currently optimised (see Optimize.py)
- assert not self.reversed, "internal error: reversed() only implemented for list/tuple objects"
- self.may_be_a_sequence = not sequence_type.is_builtin_type
- if self.may_be_a_sequence:
- code.putln(
- "if (likely(PyList_CheckExact(%s)) || PyTuple_CheckExact(%s)) {" % (
- self.sequence.py_result(),
- self.sequence.py_result()))
- if is_builtin_sequence or self.may_be_a_sequence:
- self.counter_cname = code.funcstate.allocate_temp(
- PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- if self.reversed:
- if sequence_type is list_type:
- init_value = 'PyList_GET_SIZE(%s) - 1' % self.result()
- else:
- init_value = 'PyTuple_GET_SIZE(%s) - 1' % self.result()
- else:
- init_value = '0'
- code.putln("%s = %s; __Pyx_INCREF(%s); %s = %s;" % (
- self.result(),
- self.sequence.py_result(),
- self.result(),
- self.counter_cname,
- init_value))
- if not is_builtin_sequence:
- self.iter_func_ptr = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- if self.may_be_a_sequence:
- code.putln("%s = NULL;" % self.iter_func_ptr)
- code.putln("} else {")
- code.put("%s = -1; " % self.counter_cname)
- code.putln("%s = PyObject_GetIter(%s); %s" % (
- self.result(),
- self.sequence.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- # PyObject_GetIter() fails if "tp_iternext" is not set, but the check below
- # makes it visible to the C compiler that the pointer really isn't NULL, so that
- # it can distinguish between the special cases and the generic case
- code.putln("%s = Py_TYPE(%s)->tp_iternext; %s" % (
- self.iter_func_ptr, self.py_result(),
- code.error_goto_if_null(self.iter_func_ptr, self.pos)))
- if self.may_be_a_sequence:
- code.putln("}")
- def generate_next_sequence_item(self, test_name, result_name, code):
- assert self.counter_cname, "internal error: counter_cname temp not prepared"
- final_size = 'Py%s_GET_SIZE(%s)' % (test_name, self.py_result())
- if self.sequence.is_sequence_constructor:
- item_count = len(self.sequence.args)
- if self.sequence.mult_factor is None:
- final_size = item_count
- elif isinstance(self.sequence.mult_factor.constant_result, _py_int_types):
- final_size = item_count * self.sequence.mult_factor.constant_result
- code.putln("if (%s >= %s) break;" % (self.counter_cname, final_size))
- if self.reversed:
- inc_dec = '--'
- else:
- inc_dec = '++'
- code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS")
- code.putln(
- "%s = Py%s_GET_ITEM(%s, %s); __Pyx_INCREF(%s); %s%s; %s" % (
- result_name,
- test_name,
- self.py_result(),
- self.counter_cname,
- result_name,
- self.counter_cname,
- inc_dec,
- # use the error label to avoid C compiler warnings if we only use it below
- code.error_goto_if_neg('0', self.pos)
- ))
- code.putln("#else")
- code.putln(
- "%s = PySequence_ITEM(%s, %s); %s%s; %s" % (
- result_name,
- self.py_result(),
- self.counter_cname,
- self.counter_cname,
- inc_dec,
- code.error_goto_if_null(result_name, self.pos)))
- code.put_gotref(result_name)
- code.putln("#endif")
- def generate_iter_next_result_code(self, result_name, code):
- sequence_type = self.sequence.type
- if self.reversed:
- code.putln("if (%s < 0) break;" % self.counter_cname)
- if sequence_type.is_cpp_class:
- if self.cpp_iterator_cname:
- end_func = "%s->end" % self.cpp_iterator_cname
- else:
- end_func = "%s.end" % self.sequence.result()
- # TODO: Cache end() call?
- code.putln("if (!(%s != %s())) break;" % (
- self.result(),
- end_func))
- code.putln("%s = *%s;" % (
- result_name,
- self.result()))
- code.putln("++%s;" % self.result())
- return
- elif sequence_type is list_type:
- self.generate_next_sequence_item('List', result_name, code)
- return
- elif sequence_type is tuple_type:
- self.generate_next_sequence_item('Tuple', result_name, code)
- return
- if self.may_be_a_sequence:
- code.putln("if (likely(!%s)) {" % self.iter_func_ptr)
- code.putln("if (likely(PyList_CheckExact(%s))) {" % self.py_result())
- self.generate_next_sequence_item('List', result_name, code)
- code.putln("} else {")
- self.generate_next_sequence_item('Tuple', result_name, code)
- code.putln("}")
- code.put("} else ")
- code.putln("{")
- code.putln(
- "%s = %s(%s);" % (
- result_name,
- self.iter_func_ptr,
- self.py_result()))
- code.putln("if (unlikely(!%s)) {" % result_name)
- code.putln("PyObject* exc_type = PyErr_Occurred();")
- code.putln("if (exc_type) {")
- code.putln("if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();")
- code.putln("else %s" % code.error_goto(self.pos))
- code.putln("}")
- code.putln("break;")
- code.putln("}")
- code.put_gotref(result_name)
- code.putln("}")
- def free_temps(self, code):
- if self.counter_cname:
- code.funcstate.release_temp(self.counter_cname)
- if self.iter_func_ptr:
- code.funcstate.release_temp(self.iter_func_ptr)
- self.iter_func_ptr = None
- if self.cpp_iterator_cname:
- code.funcstate.release_temp(self.cpp_iterator_cname)
- ExprNode.free_temps(self, code)
- class NextNode(AtomicExprNode):
- # Used as part of for statement implementation.
- # Implements result = next(iterator)
- # Created during analyse_types phase.
- # The iterator is not owned by this node.
- #
- # iterator IteratorNode
- def __init__(self, iterator):
- AtomicExprNode.__init__(self, iterator.pos)
- self.iterator = iterator
- def nogil_check(self, env):
- # ignore - errors (if any) are already handled by IteratorNode
- pass
- def type_dependencies(self, env):
- return self.iterator.type_dependencies(env)
- def infer_type(self, env, iterator_type=None):
- if iterator_type is None:
- iterator_type = self.iterator.infer_type(env)
- if iterator_type.is_ptr or iterator_type.is_array:
- return iterator_type.base_type
- elif self.iterator.sequence.type is bytearray_type:
- # This is a temporary work-around to fix bytearray iteration in 0.29.x
- # It has been fixed properly in master, refer to ticket: 3473
- return py_object_type
- elif iterator_type.is_cpp_class:
- item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
- if item_type.is_reference:
- item_type = item_type.ref_base_type
- if item_type.is_const:
- item_type = item_type.const_base_type
- return item_type
- else:
- # Avoid duplication of complicated logic.
- fake_index_node = IndexNode(
- self.pos,
- base=self.iterator.sequence,
- index=IntNode(self.pos, value='PY_SSIZE_T_MAX',
- type=PyrexTypes.c_py_ssize_t_type))
- return fake_index_node.infer_type(env)
- def analyse_types(self, env):
- self.type = self.infer_type(env, self.iterator.type)
- self.is_temp = 1
- return self
- def generate_result_code(self, code):
- self.iterator.generate_iter_next_result_code(self.result(), code)
- class AsyncIteratorNode(ExprNode):
- # Used as part of 'async for' statement implementation.
- #
- # Implements result = sequence.__aiter__()
- #
- # sequence ExprNode
- subexprs = ['sequence']
- is_async = True
- type = py_object_type
- is_temp = 1
- def infer_type(self, env):
- return py_object_type
- def analyse_types(self, env):
- self.sequence = self.sequence.analyse_types(env)
- if not self.sequence.type.is_pyobject:
- error(self.pos, "async for loops not allowed on C/C++ types")
- self.sequence = self.sequence.coerce_to_pyobject(env)
- return self
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("AsyncIter", "Coroutine.c"))
- code.putln("%s = __Pyx_Coroutine_GetAsyncIter(%s); %s" % (
- self.result(),
- self.sequence.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class AsyncNextNode(AtomicExprNode):
- # Used as part of 'async for' statement implementation.
- # Implements result = iterator.__anext__()
- # Created during analyse_types phase.
- # The iterator is not owned by this node.
- #
- # iterator IteratorNode
- type = py_object_type
- is_temp = 1
- def __init__(self, iterator):
- AtomicExprNode.__init__(self, iterator.pos)
- self.iterator = iterator
- def infer_type(self, env):
- return py_object_type
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("AsyncIter", "Coroutine.c"))
- code.putln("%s = __Pyx_Coroutine_AsyncIterNext(%s); %s" % (
- self.result(),
- self.iterator.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class WithExitCallNode(ExprNode):
- # The __exit__() call of a 'with' statement. Used in both the
- # except and finally clauses.
- # with_stat WithStatNode the surrounding 'with' statement
- # args TupleNode or ResultStatNode the exception info tuple
- # await_expr AwaitExprNode the await expression of an 'async with' statement
- subexprs = ['args', 'await_expr']
- test_if_run = True
- await_expr = None
- def analyse_types(self, env):
- self.args = self.args.analyse_types(env)
- if self.await_expr:
- self.await_expr = self.await_expr.analyse_types(env)
- self.type = PyrexTypes.c_bint_type
- self.is_temp = True
- return self
- def generate_evaluation_code(self, code):
- if self.test_if_run:
- # call only if it was not already called (and decref-cleared)
- code.putln("if (%s) {" % self.with_stat.exit_var)
- self.args.generate_evaluation_code(code)
- result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
- code.mark_pos(self.pos)
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln("%s = __Pyx_PyObject_Call(%s, %s, NULL);" % (
- result_var,
- self.with_stat.exit_var,
- self.args.result()))
- code.put_decref_clear(self.with_stat.exit_var, type=py_object_type)
- self.args.generate_disposal_code(code)
- self.args.free_temps(code)
- code.putln(code.error_goto_if_null(result_var, self.pos))
- code.put_gotref(result_var)
- if self.await_expr:
- # FIXME: result_var temp currently leaks into the closure
- self.await_expr.generate_evaluation_code(code, source_cname=result_var, decref_source=True)
- code.putln("%s = %s;" % (result_var, self.await_expr.py_result()))
- self.await_expr.generate_post_assignment_code(code)
- self.await_expr.free_temps(code)
- if self.result_is_used:
- self.allocate_temp_result(code)
- code.putln("%s = __Pyx_PyObject_IsTrue(%s);" % (self.result(), result_var))
- code.put_decref_clear(result_var, type=py_object_type)
- if self.result_is_used:
- code.put_error_if_neg(self.pos, self.result())
- code.funcstate.release_temp(result_var)
- if self.test_if_run:
- code.putln("}")
- class ExcValueNode(AtomicExprNode):
- # Node created during analyse_types phase
- # of an ExceptClauseNode to fetch the current
- # exception value.
- type = py_object_type
- def __init__(self, pos):
- ExprNode.__init__(self, pos)
- def set_var(self, var):
- self.var = var
- def calculate_result_code(self):
- return self.var
- def generate_result_code(self, code):
- pass
- def analyse_types(self, env):
- return self
- class TempNode(ExprNode):
- # Node created during analyse_types phase
- # of some nodes to hold a temporary value.
- #
- # Note: One must call "allocate" and "release" on
- # the node during code generation to get/release the temp.
- # This is because the temp result is often used outside of
- # the regular cycle.
- subexprs = []
- def __init__(self, pos, type, env=None):
- ExprNode.__init__(self, pos)
- self.type = type
- if type.is_pyobject:
- self.result_ctype = py_object_type
- self.is_temp = 1
- def analyse_types(self, env):
- return self
- def analyse_target_declaration(self, env):
- pass
- def generate_result_code(self, code):
- pass
- def allocate(self, code):
- self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True)
- def release(self, code):
- code.funcstate.release_temp(self.temp_cname)
- self.temp_cname = None
- def result(self):
- try:
- return self.temp_cname
- except:
- assert False, "Remember to call allocate/release on TempNode"
- raise
- # Do not participate in normal temp alloc/dealloc:
- def allocate_temp_result(self, code):
- pass
- def release_temp_result(self, code):
- pass
- class PyTempNode(TempNode):
- # TempNode holding a Python value.
- def __init__(self, pos, env):
- TempNode.__init__(self, pos, PyrexTypes.py_object_type, env)
- class RawCNameExprNode(ExprNode):
- subexprs = []
- def __init__(self, pos, type=None, cname=None):
- ExprNode.__init__(self, pos, type=type)
- if cname is not None:
- self.cname = cname
- def analyse_types(self, env):
- return self
- def set_cname(self, cname):
- self.cname = cname
- def result(self):
- return self.cname
- def generate_result_code(self, code):
- pass
- #-------------------------------------------------------------------
- #
- # F-strings
- #
- #-------------------------------------------------------------------
- class JoinedStrNode(ExprNode):
- # F-strings
- #
- # values [UnicodeNode|FormattedValueNode] Substrings of the f-string
- #
- type = unicode_type
- is_temp = True
- subexprs = ['values']
- def analyse_types(self, env):
- self.values = [v.analyse_types(env).coerce_to_pyobject(env) for v in self.values]
- return self
- def may_be_none(self):
- # PyUnicode_Join() always returns a Unicode string or raises an exception
- return False
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- num_items = len(self.values)
- list_var = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- ulength_var = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- max_char_var = code.funcstate.allocate_temp(PyrexTypes.c_py_ucs4_type, manage_ref=False)
- code.putln('%s = PyTuple_New(%s); %s' % (
- list_var,
- num_items,
- code.error_goto_if_null(list_var, self.pos)))
- code.put_gotref(list_var)
- code.putln("%s = 0;" % ulength_var)
- code.putln("%s = 127;" % max_char_var) # at least ASCII character range
- for i, node in enumerate(self.values):
- node.generate_evaluation_code(code)
- node.make_owned_reference(code)
- ulength = "__Pyx_PyUnicode_GET_LENGTH(%s)" % node.py_result()
- max_char_value = "__Pyx_PyUnicode_MAX_CHAR_VALUE(%s)" % node.py_result()
- is_ascii = False
- if isinstance(node, UnicodeNode):
- try:
- # most strings will be ASCII or at least Latin-1
- node.value.encode('iso8859-1')
- max_char_value = '255'
- node.value.encode('us-ascii')
- is_ascii = True
- except UnicodeEncodeError:
- if max_char_value != '255':
- # not ISO8859-1 => check BMP limit
- max_char = max(map(ord, node.value))
- if max_char < 0xD800:
- # BMP-only, no surrogate pairs used
- max_char_value = '65535'
- ulength = str(len(node.value))
- elif max_char >= 65536:
- # cleary outside of BMP, and not on a 16-bit Unicode system
- max_char_value = '1114111'
- ulength = str(len(node.value))
- else:
- # not really worth implementing a check for surrogate pairs here
- # drawback: C code can differ when generating on Py2 with 2-byte Unicode
- pass
- else:
- ulength = str(len(node.value))
- elif isinstance(node, FormattedValueNode) and node.value.type.is_numeric:
- is_ascii = True # formatted C numbers are always ASCII
- if not is_ascii:
- code.putln("%s = (%s > %s) ? %s : %s;" % (
- max_char_var, max_char_value, max_char_var, max_char_value, max_char_var))
- code.putln("%s += %s;" % (ulength_var, ulength))
- code.put_giveref(node.py_result())
- code.putln('PyTuple_SET_ITEM(%s, %s, %s);' % (list_var, i, node.py_result()))
- node.generate_post_assignment_code(code)
- node.free_temps(code)
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- code.globalstate.use_utility_code(UtilityCode.load_cached("JoinPyUnicode", "StringTools.c"))
- code.putln('%s = __Pyx_PyUnicode_Join(%s, %d, %s, %s); %s' % (
- self.result(),
- list_var,
- num_items,
- ulength_var,
- max_char_var,
- code.error_goto_if_null(self.py_result(), self.pos)))
- code.put_gotref(self.py_result())
- code.put_decref_clear(list_var, py_object_type)
- code.funcstate.release_temp(list_var)
- code.funcstate.release_temp(ulength_var)
- code.funcstate.release_temp(max_char_var)
- class FormattedValueNode(ExprNode):
- # {}-delimited portions of an f-string
- #
- # value ExprNode The expression itself
- # conversion_char str or None Type conversion (!s, !r, !a, or none, or 'd' for integer conversion)
- # format_spec JoinedStrNode or None Format string passed to __format__
- # c_format_spec str or None If not None, formatting can be done at the C level
- subexprs = ['value', 'format_spec']
- type = unicode_type
- is_temp = True
- c_format_spec = None
- find_conversion_func = {
- 's': 'PyObject_Unicode',
- 'r': 'PyObject_Repr',
- 'a': 'PyObject_ASCII', # NOTE: mapped to PyObject_Repr() in Py2
- 'd': '__Pyx_PyNumber_IntOrLong', # NOTE: internal mapping for '%d' formatting
- }.get
- def may_be_none(self):
- # PyObject_Format() always returns a Unicode string or raises an exception
- return False
- def analyse_types(self, env):
- self.value = self.value.analyse_types(env)
- if not self.format_spec or self.format_spec.is_string_literal:
- c_format_spec = self.format_spec.value if self.format_spec else self.value.type.default_format_spec
- if self.value.type.can_coerce_to_pystring(env, format_spec=c_format_spec):
- self.c_format_spec = c_format_spec
- if self.format_spec:
- self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env)
- if self.c_format_spec is None:
- self.value = self.value.coerce_to_pyobject(env)
- if not self.format_spec and (not self.conversion_char or self.conversion_char == 's'):
- if self.value.type is unicode_type and not self.value.may_be_none():
- # value is definitely a unicode string and we don't format it any special
- return self.value
- return self
- def generate_result_code(self, code):
- if self.c_format_spec is not None and not self.value.type.is_pyobject:
- convert_func_call = self.value.type.convert_to_pystring(
- self.value.result(), code, self.c_format_spec)
- code.putln("%s = %s; %s" % (
- self.result(),
- convert_func_call,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- return
- value_result = self.value.py_result()
- value_is_unicode = self.value.type is unicode_type and not self.value.may_be_none()
- if self.format_spec:
- format_func = '__Pyx_PyObject_Format'
- format_spec = self.format_spec.py_result()
- else:
- # common case: expect simple Unicode pass-through if no format spec
- format_func = '__Pyx_PyObject_FormatSimple'
- # passing a Unicode format string in Py2 forces PyObject_Format() to also return a Unicode string
- format_spec = Naming.empty_unicode
- conversion_char = self.conversion_char
- if conversion_char == 's' and value_is_unicode:
- # no need to pipe unicode strings through str()
- conversion_char = None
- if conversion_char:
- fn = self.find_conversion_func(conversion_char)
- assert fn is not None, "invalid conversion character found: '%s'" % conversion_char
- value_result = '%s(%s)' % (fn, value_result)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectFormatAndDecref", "StringTools.c"))
- format_func += 'AndDecref'
- elif self.format_spec:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectFormat", "StringTools.c"))
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectFormatSimple", "StringTools.c"))
- code.putln("%s = %s(%s, %s); %s" % (
- self.result(),
- format_func,
- value_result,
- format_spec,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- #-------------------------------------------------------------------
- #
- # Parallel nodes (cython.parallel.thread(savailable|id))
- #
- #-------------------------------------------------------------------
- class ParallelThreadsAvailableNode(AtomicExprNode):
- """
- Note: this is disabled and not a valid directive at this moment
- Implements cython.parallel.threadsavailable(). If we are called from the
- sequential part of the application, we need to call omp_get_max_threads(),
- and in the parallel part we can just call omp_get_num_threads()
- """
- type = PyrexTypes.c_int_type
- def analyse_types(self, env):
- self.is_temp = True
- # env.add_include_file("omp.h")
- return self
- def generate_result_code(self, code):
- code.putln("#ifdef _OPENMP")
- code.putln("if (omp_in_parallel()) %s = omp_get_max_threads();" %
- self.temp_code)
- code.putln("else %s = omp_get_num_threads();" % self.temp_code)
- code.putln("#else")
- code.putln("%s = 1;" % self.temp_code)
- code.putln("#endif")
- def result(self):
- return self.temp_code
- class ParallelThreadIdNode(AtomicExprNode): #, Nodes.ParallelNode):
- """
- Implements cython.parallel.threadid()
- """
- type = PyrexTypes.c_int_type
- def analyse_types(self, env):
- self.is_temp = True
- # env.add_include_file("omp.h")
- return self
- def generate_result_code(self, code):
- code.putln("#ifdef _OPENMP")
- code.putln("%s = omp_get_thread_num();" % self.temp_code)
- code.putln("#else")
- code.putln("%s = 0;" % self.temp_code)
- code.putln("#endif")
- def result(self):
- return self.temp_code
- #-------------------------------------------------------------------
- #
- # Trailer nodes
- #
- #-------------------------------------------------------------------
- class _IndexingBaseNode(ExprNode):
- # Base class for indexing nodes.
- #
- # base ExprNode the value being indexed
- def is_ephemeral(self):
- # in most cases, indexing will return a safe reference to an object in a container,
- # so we consider the result safe if the base object is
- return self.base.is_ephemeral() or self.base.type in (
- basestring_type, str_type, bytes_type, bytearray_type, unicode_type)
- def check_const_addr(self):
- return self.base.check_const_addr() and self.index.check_const()
- def is_lvalue(self):
- # NOTE: references currently have both is_reference and is_ptr
- # set. Since pointers and references have different lvalue
- # rules, we must be careful to separate the two.
- if self.type.is_reference:
- if self.type.ref_base_type.is_array:
- # fixed-sized arrays aren't l-values
- return False
- elif self.type.is_ptr:
- # non-const pointers can always be reassigned
- return True
- # Just about everything else returned by the index operator
- # can be an lvalue.
- return True
- class IndexNode(_IndexingBaseNode):
- # Sequence indexing.
- #
- # base ExprNode
- # index ExprNode
- # type_indices [PyrexType]
- #
- # is_fused_index boolean Whether the index is used to specialize a
- # c(p)def function
- subexprs = ['base', 'index']
- type_indices = None
- is_subscript = True
- is_fused_index = False
- def calculate_constant_result(self):
- self.constant_result = self.base.constant_result[self.index.constant_result]
- def compile_time_value(self, denv):
- base = self.base.compile_time_value(denv)
- index = self.index.compile_time_value(denv)
- try:
- return base[index]
- except Exception as e:
- self.compile_time_value_error(e)
- def is_simple(self):
- base = self.base
- return (base.is_simple() and self.index.is_simple()
- and base.type and (base.type.is_ptr or base.type.is_array))
- def may_be_none(self):
- base_type = self.base.type
- if base_type:
- if base_type.is_string:
- return False
- if isinstance(self.index, SliceNode):
- # slicing!
- if base_type in (bytes_type, bytearray_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return False
- return ExprNode.may_be_none(self)
- def analyse_target_declaration(self, env):
- pass
- def analyse_as_type(self, env):
- base_type = self.base.analyse_as_type(env)
- if base_type and not base_type.is_pyobject:
- if base_type.is_cpp_class:
- if isinstance(self.index, TupleNode):
- template_values = self.index.args
- else:
- template_values = [self.index]
- type_node = Nodes.TemplatedTypeNode(
- pos=self.pos,
- positional_args=template_values,
- keyword_args=None)
- return type_node.analyse(env, base_type=base_type)
- elif self.index.is_slice or self.index.is_sequence_constructor:
- # memory view
- from . import MemoryView
- env.use_utility_code(MemoryView.view_utility_code)
- axes = [self.index] if self.index.is_slice else list(self.index.args)
- return PyrexTypes.MemoryViewSliceType(base_type, MemoryView.get_axes_specs(env, axes))
- else:
- # C array
- index = self.index.compile_time_value(env)
- if index is not None:
- try:
- index = int(index)
- except (ValueError, TypeError):
- pass
- else:
- return PyrexTypes.CArrayType(base_type, index)
- error(self.pos, "Array size must be a compile time constant")
- return None
- def type_dependencies(self, env):
- return self.base.type_dependencies(env) + self.index.type_dependencies(env)
- def infer_type(self, env):
- base_type = self.base.infer_type(env)
- if self.index.is_slice:
- # slicing!
- if base_type.is_string:
- # sliced C strings must coerce to Python
- return bytes_type
- elif base_type.is_pyunicode_ptr:
- # sliced Py_UNICODE* strings must coerce to Python
- return unicode_type
- elif base_type in (unicode_type, bytes_type, str_type,
- bytearray_type, list_type, tuple_type):
- # slicing these returns the same type
- return base_type
- else:
- # TODO: Handle buffers (hopefully without too much redundancy).
- return py_object_type
- index_type = self.index.infer_type(env)
- if index_type and index_type.is_int or isinstance(self.index, IntNode):
- # indexing!
- if base_type is unicode_type:
- # Py_UCS4 will automatically coerce to a unicode string
- # if required, so this is safe. We only infer Py_UCS4
- # when the index is a C integer type. Otherwise, we may
- # need to use normal Python item access, in which case
- # it's faster to return the one-char unicode string than
- # to receive it, throw it away, and potentially rebuild it
- # on a subsequent PyObject coercion.
- return PyrexTypes.c_py_ucs4_type
- elif base_type is str_type:
- # always returns str - Py2: bytes, Py3: unicode
- return base_type
- elif base_type is bytearray_type:
- return PyrexTypes.c_uchar_type
- elif isinstance(self.base, BytesNode):
- #if env.global_scope().context.language_level >= 3:
- # # inferring 'char' can be made to work in Python 3 mode
- # return PyrexTypes.c_char_type
- # Py2/3 return different types on indexing bytes objects
- return py_object_type
- elif base_type in (tuple_type, list_type):
- # if base is a literal, take a look at its values
- item_type = infer_sequence_item_type(
- env, self.base, self.index, seq_type=base_type)
- if item_type is not None:
- return item_type
- elif base_type.is_ptr or base_type.is_array:
- return base_type.base_type
- elif base_type.is_ctuple and isinstance(self.index, IntNode):
- if self.index.has_constant_result():
- index = self.index.constant_result
- if index < 0:
- index += base_type.size
- if 0 <= index < base_type.size:
- return base_type.components[index]
- if base_type.is_cpp_class:
- class FakeOperand:
- def __init__(self, **kwds):
- self.__dict__.update(kwds)
- operands = [
- FakeOperand(pos=self.pos, type=base_type),
- FakeOperand(pos=self.pos, type=index_type),
- ]
- index_func = env.lookup_operator('[]', operands)
- if index_func is not None:
- return index_func.type.return_type
- if is_pythran_expr(base_type) and is_pythran_expr(index_type):
- index_with_type = (self.index, index_type)
- return PythranExpr(pythran_indexing_type(base_type, [index_with_type]))
- # may be slicing or indexing, we don't know
- if base_type in (unicode_type, str_type):
- # these types always returns their own type on Python indexing/slicing
- return base_type
- else:
- # TODO: Handle buffers (hopefully without too much redundancy).
- return py_object_type
- def analyse_types(self, env):
- return self.analyse_base_and_index_types(env, getting=True)
- def analyse_target_types(self, env):
- node = self.analyse_base_and_index_types(env, setting=True)
- if node.type.is_const:
- error(self.pos, "Assignment to const dereference")
- if node is self and not node.is_lvalue():
- error(self.pos, "Assignment to non-lvalue of type '%s'" % node.type)
- return node
- def analyse_base_and_index_types(self, env, getting=False, setting=False,
- analyse_base=True):
- # Note: This might be cleaned up by having IndexNode
- # parsed in a saner way and only construct the tuple if
- # needed.
- if analyse_base:
- self.base = self.base.analyse_types(env)
- if self.base.type.is_error:
- # Do not visit child tree if base is undeclared to avoid confusing
- # error messages
- self.type = PyrexTypes.error_type
- return self
- is_slice = self.index.is_slice
- if not env.directives['wraparound']:
- if is_slice:
- check_negative_indices(self.index.start, self.index.stop)
- else:
- check_negative_indices(self.index)
- # Potentially overflowing index value.
- if not is_slice and isinstance(self.index, IntNode) and Utils.long_literal(self.index.value):
- self.index = self.index.coerce_to_pyobject(env)
- is_memslice = self.base.type.is_memoryviewslice
- # Handle the case where base is a literal char* (and we expect a string, not an int)
- if not is_memslice and (isinstance(self.base, BytesNode) or is_slice):
- if self.base.type.is_string or not (self.base.type.is_ptr or self.base.type.is_array):
- self.base = self.base.coerce_to_pyobject(env)
- replacement_node = self.analyse_as_buffer_operation(env, getting)
- if replacement_node is not None:
- return replacement_node
- self.nogil = env.nogil
- base_type = self.base.type
- if not base_type.is_cfunction:
- self.index = self.index.analyse_types(env)
- self.original_index_type = self.index.type
- if base_type.is_unicode_char:
- # we infer Py_UNICODE/Py_UCS4 for unicode strings in some
- # cases, but indexing must still work for them
- if setting:
- warning(self.pos, "cannot assign to Unicode string index", level=1)
- elif self.index.constant_result in (0, -1):
- # uchar[0] => uchar
- return self.base
- self.base = self.base.coerce_to_pyobject(env)
- base_type = self.base.type
- if base_type.is_pyobject:
- return self.analyse_as_pyobject(env, is_slice, getting, setting)
- elif base_type.is_ptr or base_type.is_array:
- return self.analyse_as_c_array(env, is_slice)
- elif base_type.is_cpp_class:
- return self.analyse_as_cpp(env, setting)
- elif base_type.is_cfunction:
- return self.analyse_as_c_function(env)
- elif base_type.is_ctuple:
- return self.analyse_as_c_tuple(env, getting, setting)
- else:
- error(self.pos,
- "Attempting to index non-array type '%s'" %
- base_type)
- self.type = PyrexTypes.error_type
- return self
- def analyse_as_pyobject(self, env, is_slice, getting, setting):
- base_type = self.base.type
- if self.index.type.is_unicode_char and base_type is not dict_type:
- # TODO: eventually fold into case below and remove warning, once people have adapted their code
- warning(self.pos,
- "Item lookup of unicode character codes now always converts to a Unicode string. "
- "Use an explicit C integer cast to get back the previous integer lookup behaviour.", level=1)
- self.index = self.index.coerce_to_pyobject(env)
- self.is_temp = 1
- elif self.index.type.is_int and base_type is not dict_type:
- if (getting
- and (base_type in (list_type, tuple_type, bytearray_type))
- and (not self.index.type.signed
- or not env.directives['wraparound']
- or (isinstance(self.index, IntNode) and
- self.index.has_constant_result() and self.index.constant_result >= 0))
- and not env.directives['boundscheck']):
- self.is_temp = 0
- else:
- self.is_temp = 1
- self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env).coerce_to_simple(env)
- self.original_index_type.create_to_py_utility_code(env)
- else:
- self.index = self.index.coerce_to_pyobject(env)
- self.is_temp = 1
- if self.index.type.is_int and base_type is unicode_type:
- # Py_UNICODE/Py_UCS4 will automatically coerce to a unicode string
- # if required, so this is fast and safe
- self.type = PyrexTypes.c_py_ucs4_type
- elif self.index.type.is_int and base_type is bytearray_type:
- if setting:
- self.type = PyrexTypes.c_uchar_type
- else:
- # not using 'uchar' to enable fast and safe error reporting as '-1'
- self.type = PyrexTypes.c_int_type
- elif is_slice and base_type in (bytes_type, bytearray_type, str_type, unicode_type, list_type, tuple_type):
- self.type = base_type
- else:
- item_type = None
- if base_type in (list_type, tuple_type) and self.index.type.is_int:
- item_type = infer_sequence_item_type(
- env, self.base, self.index, seq_type=base_type)
- if item_type is None:
- item_type = py_object_type
- self.type = item_type
- if base_type in (list_type, tuple_type, dict_type):
- # do the None check explicitly (not in a helper) to allow optimising it away
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- self.wrap_in_nonecheck_node(env, getting)
- return self
- def analyse_as_c_array(self, env, is_slice):
- base_type = self.base.type
- self.type = base_type.base_type
- if is_slice:
- self.type = base_type
- elif self.index.type.is_pyobject:
- self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env)
- elif not self.index.type.is_int:
- error(self.pos, "Invalid index type '%s'" % self.index.type)
- return self
- def analyse_as_cpp(self, env, setting):
- base_type = self.base.type
- function = env.lookup_operator("[]", [self.base, self.index])
- if function is None:
- error(self.pos, "Indexing '%s' not supported for index type '%s'" % (base_type, self.index.type))
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return self
- func_type = function.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- self.exception_check = func_type.exception_check
- self.exception_value = func_type.exception_value
- if self.exception_check:
- if not setting:
- self.is_temp = True
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- self.index = self.index.coerce_to(func_type.args[0].type, env)
- self.type = func_type.return_type
- if setting and not func_type.return_type.is_reference:
- error(self.pos, "Can't set non-reference result '%s'" % self.type)
- return self
- def analyse_as_c_function(self, env):
- base_type = self.base.type
- if base_type.is_fused:
- self.parse_indexed_fused_cdef(env)
- else:
- self.type_indices = self.parse_index_as_types(env)
- self.index = None # FIXME: use a dedicated Node class instead of generic IndexNode
- if base_type.templates is None:
- error(self.pos, "Can only parameterize template functions.")
- self.type = error_type
- elif self.type_indices is None:
- # Error recorded earlier.
- self.type = error_type
- elif len(base_type.templates) != len(self.type_indices):
- error(self.pos, "Wrong number of template arguments: expected %s, got %s" % (
- (len(base_type.templates), len(self.type_indices))))
- self.type = error_type
- else:
- self.type = base_type.specialize(dict(zip(base_type.templates, self.type_indices)))
- # FIXME: use a dedicated Node class instead of generic IndexNode
- return self
- def analyse_as_c_tuple(self, env, getting, setting):
- base_type = self.base.type
- if isinstance(self.index, IntNode) and self.index.has_constant_result():
- index = self.index.constant_result
- if -base_type.size <= index < base_type.size:
- if index < 0:
- index += base_type.size
- self.type = base_type.components[index]
- else:
- error(self.pos,
- "Index %s out of bounds for '%s'" %
- (index, base_type))
- self.type = PyrexTypes.error_type
- return self
- else:
- self.base = self.base.coerce_to_pyobject(env)
- return self.analyse_base_and_index_types(env, getting=getting, setting=setting, analyse_base=False)
- def analyse_as_buffer_operation(self, env, getting):
- """
- Analyse buffer indexing and memoryview indexing/slicing
- """
- if isinstance(self.index, TupleNode):
- indices = self.index.args
- else:
- indices = [self.index]
- base = self.base
- base_type = base.type
- replacement_node = None
- if base_type.is_memoryviewslice:
- # memoryviewslice indexing or slicing
- from . import MemoryView
- if base.is_memview_slice:
- # For memory views, "view[i][j]" is the same as "view[i, j]" => use the latter for speed.
- merged_indices = base.merged_indices(indices)
- if merged_indices is not None:
- base = base.base
- base_type = base.type
- indices = merged_indices
- have_slices, indices, newaxes = MemoryView.unellipsify(indices, base_type.ndim)
- if have_slices:
- replacement_node = MemoryViewSliceNode(self.pos, indices=indices, base=base)
- else:
- replacement_node = MemoryViewIndexNode(self.pos, indices=indices, base=base)
- elif base_type.is_buffer or base_type.is_pythran_expr:
- if base_type.is_pythran_expr or len(indices) == base_type.ndim:
- # Buffer indexing
- is_buffer_access = True
- indices = [index.analyse_types(env) for index in indices]
- if base_type.is_pythran_expr:
- do_replacement = all(
- index.type.is_int or index.is_slice or index.type.is_pythran_expr
- for index in indices)
- if do_replacement:
- for i,index in enumerate(indices):
- if index.is_slice:
- index = SliceIntNode(index.pos, start=index.start, stop=index.stop, step=index.step)
- index = index.analyse_types(env)
- indices[i] = index
- else:
- do_replacement = all(index.type.is_int for index in indices)
- if do_replacement:
- replacement_node = BufferIndexNode(self.pos, indices=indices, base=base)
- # On cloning, indices is cloned. Otherwise, unpack index into indices.
- assert not isinstance(self.index, CloneNode)
- if replacement_node is not None:
- replacement_node = replacement_node.analyse_types(env, getting)
- return replacement_node
- def wrap_in_nonecheck_node(self, env, getting):
- if not env.directives['nonecheck'] or not self.base.may_be_none():
- return
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- def parse_index_as_types(self, env, required=True):
- if isinstance(self.index, TupleNode):
- indices = self.index.args
- else:
- indices = [self.index]
- type_indices = []
- for index in indices:
- type_indices.append(index.analyse_as_type(env))
- if type_indices[-1] is None:
- if required:
- error(index.pos, "not parsable as a type")
- return None
- return type_indices
- def parse_indexed_fused_cdef(self, env):
- """
- Interpret fused_cdef_func[specific_type1, ...]
- Note that if this method is called, we are an indexed cdef function
- with fused argument types, and this IndexNode will be replaced by the
- NameNode with specific entry just after analysis of expressions by
- AnalyseExpressionsTransform.
- """
- self.type = PyrexTypes.error_type
- self.is_fused_index = True
- base_type = self.base.type
- positions = []
- if self.index.is_name or self.index.is_attribute:
- positions.append(self.index.pos)
- elif isinstance(self.index, TupleNode):
- for arg in self.index.args:
- positions.append(arg.pos)
- specific_types = self.parse_index_as_types(env, required=False)
- if specific_types is None:
- self.index = self.index.analyse_types(env)
- if not self.base.entry.as_variable:
- error(self.pos, "Can only index fused functions with types")
- else:
- # A cpdef function indexed with Python objects
- self.base.entry = self.entry = self.base.entry.as_variable
- self.base.type = self.type = self.entry.type
- self.base.is_temp = True
- self.is_temp = True
- self.entry.used = True
- self.is_fused_index = False
- return
- for i, type in enumerate(specific_types):
- specific_types[i] = type.specialize_fused(env)
- fused_types = base_type.get_fused_types()
- if len(specific_types) > len(fused_types):
- return error(self.pos, "Too many types specified")
- elif len(specific_types) < len(fused_types):
- t = fused_types[len(specific_types)]
- return error(self.pos, "Not enough types specified to specialize "
- "the function, %s is still fused" % t)
- # See if our index types form valid specializations
- for pos, specific_type, fused_type in zip(positions,
- specific_types,
- fused_types):
- if not any([specific_type.same_as(t) for t in fused_type.types]):
- return error(pos, "Type not in fused type")
- if specific_type is None or specific_type.is_error:
- return
- fused_to_specific = dict(zip(fused_types, specific_types))
- type = base_type.specialize(fused_to_specific)
- if type.is_fused:
- # Only partially specific, this is invalid
- error(self.pos,
- "Index operation makes function only partially specific")
- else:
- # Fully specific, find the signature with the specialized entry
- for signature in self.base.type.get_all_specialized_function_types():
- if type.same_as(signature):
- self.type = signature
- if self.base.is_attribute:
- # Pretend to be a normal attribute, for cdef extension
- # methods
- self.entry = signature.entry
- self.is_attribute = True
- self.obj = self.base.obj
- self.type.entry.used = True
- self.base.type = signature
- self.base.entry = signature.entry
- break
- else:
- # This is a bug
- raise InternalError("Couldn't find the right signature")
- gil_message = "Indexing Python object"
- def calculate_result_code(self):
- if self.base.type in (list_type, tuple_type, bytearray_type):
- if self.base.type is list_type:
- index_code = "PyList_GET_ITEM(%s, %s)"
- elif self.base.type is tuple_type:
- index_code = "PyTuple_GET_ITEM(%s, %s)"
- elif self.base.type is bytearray_type:
- index_code = "((unsigned char)(PyByteArray_AS_STRING(%s)[%s]))"
- else:
- assert False, "unexpected base type in indexing: %s" % self.base.type
- elif self.base.type.is_cfunction:
- return "%s<%s>" % (
- self.base.result(),
- ",".join([param.empty_declaration_code() for param in self.type_indices]))
- elif self.base.type.is_ctuple:
- index = self.index.constant_result
- if index < 0:
- index += self.base.type.size
- return "%s.f%s" % (self.base.result(), index)
- else:
- if (self.type.is_ptr or self.type.is_array) and self.type == self.base.type:
- error(self.pos, "Invalid use of pointer slice")
- return
- index_code = "(%s[%s])"
- return index_code % (self.base.result(), self.index.result())
- def extra_index_params(self, code):
- if self.index.type.is_int:
- is_list = self.base.type is list_type
- wraparound = (
- bool(code.globalstate.directives['wraparound']) and
- self.original_index_type.signed and
- not (isinstance(self.index.constant_result, _py_int_types)
- and self.index.constant_result >= 0))
- boundscheck = bool(code.globalstate.directives['boundscheck'])
- return ", %s, %d, %s, %d, %d, %d" % (
- self.original_index_type.empty_declaration_code(),
- self.original_index_type.signed and 1 or 0,
- self.original_index_type.to_py_function,
- is_list, wraparound, boundscheck)
- else:
- return ""
- def generate_result_code(self, code):
- if not self.is_temp:
- # all handled in self.calculate_result_code()
- return
- utility_code = None
- if self.type.is_pyobject:
- error_value = 'NULL'
- if self.index.type.is_int:
- if self.base.type is list_type:
- function = "__Pyx_GetItemInt_List"
- elif self.base.type is tuple_type:
- function = "__Pyx_GetItemInt_Tuple"
- else:
- function = "__Pyx_GetItemInt"
- utility_code = TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c")
- else:
- if self.base.type is dict_type:
- function = "__Pyx_PyDict_GetItem"
- utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
- elif self.base.type is py_object_type and self.index.type in (str_type, unicode_type):
- # obj[str] is probably doing a dict lookup
- function = "__Pyx_PyObject_Dict_GetItem"
- utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
- else:
- function = "__Pyx_PyObject_GetItem"
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c"))
- utility_code = UtilityCode.load_cached("ObjectGetItem", "ObjectHandling.c")
- elif self.type.is_unicode_char and self.base.type is unicode_type:
- assert self.index.type.is_int
- function = "__Pyx_GetItemInt_Unicode"
- error_value = '(Py_UCS4)-1'
- utility_code = UtilityCode.load_cached("GetItemIntUnicode", "StringTools.c")
- elif self.base.type is bytearray_type:
- assert self.index.type.is_int
- assert self.type.is_int
- function = "__Pyx_GetItemInt_ByteArray"
- error_value = '-1'
- utility_code = UtilityCode.load_cached("GetItemIntByteArray", "StringTools.c")
- elif not (self.base.type.is_cpp_class and self.exception_check):
- assert False, "unexpected type %s and base type %s for indexing" % (
- self.type, self.base.type)
- if utility_code is not None:
- code.globalstate.use_utility_code(utility_code)
- if self.index.type.is_int:
- index_code = self.index.result()
- else:
- index_code = self.index.py_result()
- if self.base.type.is_cpp_class and self.exception_check:
- translate_cpp_exception(code, self.pos,
- "%s = %s[%s];" % (self.result(), self.base.result(),
- self.index.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
- code.putln(
- "%s = %s(%s, %s%s); %s" % (
- self.result(),
- function,
- self.base.py_result(),
- index_code,
- self.extra_index_params(code),
- code.error_goto_if(error_check % self.result(), self.pos)))
- if self.type.is_pyobject:
- code.put_gotref(self.py_result())
- def generate_setitem_code(self, value_code, code):
- if self.index.type.is_int:
- if self.base.type is bytearray_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("SetItemIntByteArray", "StringTools.c"))
- function = "__Pyx_SetItemInt_ByteArray"
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("SetItemInt", "ObjectHandling.c"))
- function = "__Pyx_SetItemInt"
- index_code = self.index.result()
- else:
- index_code = self.index.py_result()
- if self.base.type is dict_type:
- function = "PyDict_SetItem"
- # It would seem that we could specialized lists/tuples, but that
- # shouldn't happen here.
- # Both PyList_SetItem() and PyTuple_SetItem() take a Py_ssize_t as
- # index instead of an object, and bad conversion here would give
- # the wrong exception. Also, tuples are supposed to be immutable,
- # and raise a TypeError when trying to set their entries
- # (PyTuple_SetItem() is for creating new tuples from scratch).
- else:
- function = "PyObject_SetItem"
- code.putln(code.error_goto_if_neg(
- "%s(%s, %s, %s%s)" % (
- function,
- self.base.py_result(),
- index_code,
- value_code,
- self.extra_index_params(code)),
- self.pos))
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- self.generate_subexpr_evaluation_code(code)
- if self.type.is_pyobject:
- self.generate_setitem_code(rhs.py_result(), code)
- elif self.base.type is bytearray_type:
- value_code = self._check_byte_value(code, rhs)
- self.generate_setitem_code(value_code, code)
- elif self.base.type.is_cpp_class and self.exception_check and self.exception_check == '+':
- if overloaded_assignment and exception_check and \
- self.exception_value != exception_value:
- # Handle the case that both the index operator and the assignment
- # operator have a c++ exception handler and they are not the same.
- translate_double_cpp_exception(code, self.pos, self.type,
- self.result(), rhs.result(), self.exception_value,
- exception_value, self.in_nogil_context)
- else:
- # Handle the case that only the index operator has a
- # c++ exception handler, or that
- # both exception handlers are the same.
- translate_cpp_exception(code, self.pos,
- "%s = %s;" % (self.result(), rhs.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- code.putln(
- "%s = %s;" % (self.result(), rhs.result()))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- def _check_byte_value(self, code, rhs):
- # TODO: should we do this generally on downcasts, or just here?
- assert rhs.type.is_int, repr(rhs.type)
- value_code = rhs.result()
- if rhs.has_constant_result():
- if 0 <= rhs.constant_result < 256:
- return value_code
- needs_cast = True # make at least the C compiler happy
- warning(rhs.pos,
- "value outside of range(0, 256)"
- " when assigning to byte: %s" % rhs.constant_result,
- level=1)
- else:
- needs_cast = rhs.type != PyrexTypes.c_uchar_type
- if not self.nogil:
- conditions = []
- if rhs.is_literal or rhs.type.signed:
- conditions.append('%s < 0' % value_code)
- if (rhs.is_literal or not
- (rhs.is_temp and rhs.type in (
- PyrexTypes.c_uchar_type, PyrexTypes.c_char_type,
- PyrexTypes.c_schar_type))):
- conditions.append('%s > 255' % value_code)
- if conditions:
- code.putln("if (unlikely(%s)) {" % ' || '.join(conditions))
- code.putln(
- 'PyErr_SetString(PyExc_ValueError,'
- ' "byte must be in range(0, 256)"); %s' %
- code.error_goto(self.pos))
- code.putln("}")
- if needs_cast:
- value_code = '((unsigned char)%s)' % value_code
- return value_code
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- self.generate_subexpr_evaluation_code(code)
- #if self.type.is_pyobject:
- if self.index.type.is_int:
- function = "__Pyx_DelItemInt"
- index_code = self.index.result()
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("DelItemInt", "ObjectHandling.c"))
- else:
- index_code = self.index.py_result()
- if self.base.type is dict_type:
- function = "PyDict_DelItem"
- else:
- function = "PyObject_DelItem"
- code.putln(code.error_goto_if_neg(
- "%s(%s, %s%s)" % (
- function,
- self.base.py_result(),
- index_code,
- self.extra_index_params(code)),
- self.pos))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- class BufferIndexNode(_IndexingBaseNode):
- """
- Indexing of buffers and memoryviews. This node is created during type
- analysis from IndexNode and replaces it.
- Attributes:
- base - base node being indexed
- indices - list of indexing expressions
- """
- subexprs = ['base', 'indices']
- is_buffer_access = True
- # Whether we're assigning to a buffer (in that case it needs to be writable)
- writable_needed = False
- # Any indexing temp variables that we need to clean up.
- index_temps = ()
- def analyse_target_types(self, env):
- self.analyse_types(env, getting=False)
- def analyse_types(self, env, getting=True):
- """
- Analyse types for buffer indexing only. Overridden by memoryview
- indexing and slicing subclasses
- """
- # self.indices are already analyzed
- if not self.base.is_name and not is_pythran_expr(self.base.type):
- error(self.pos, "Can only index buffer variables")
- self.type = error_type
- return self
- if not getting:
- if not self.base.entry.type.writable:
- error(self.pos, "Writing to readonly buffer")
- else:
- self.writable_needed = True
- if self.base.type.is_buffer:
- self.base.entry.buffer_aux.writable_needed = True
- self.none_error_message = "'NoneType' object is not subscriptable"
- self.analyse_buffer_index(env, getting)
- self.wrap_in_nonecheck_node(env)
- return self
- def analyse_buffer_index(self, env, getting):
- if is_pythran_expr(self.base.type):
- index_with_type_list = [(idx, idx.type) for idx in self.indices]
- self.type = PythranExpr(pythran_indexing_type(self.base.type, index_with_type_list))
- else:
- self.base = self.base.coerce_to_simple(env)
- self.type = self.base.type.dtype
- self.buffer_type = self.base.type
- if getting and (self.type.is_pyobject or self.type.is_pythran_expr):
- self.is_temp = True
- def analyse_assignment(self, rhs):
- """
- Called by IndexNode when this node is assigned to,
- with the rhs of the assignment
- """
- def wrap_in_nonecheck_node(self, env):
- if not env.directives['nonecheck'] or not self.base.may_be_none():
- return
- self.base = self.base.as_none_safe_node(self.none_error_message)
- def nogil_check(self, env):
- if self.is_buffer_access or self.is_memview_index:
- if self.type.is_pyobject:
- error(self.pos, "Cannot access buffer with object dtype without gil")
- self.type = error_type
- def calculate_result_code(self):
- return "(*%s)" % self.buffer_ptr_code
- def buffer_entry(self):
- base = self.base
- if self.base.is_nonecheck:
- base = base.arg
- return base.type.get_entry(base)
- def get_index_in_temp(self, code, ivar):
- ret = code.funcstate.allocate_temp(
- PyrexTypes.widest_numeric_type(
- ivar.type,
- PyrexTypes.c_ssize_t_type if ivar.type.signed else PyrexTypes.c_size_t_type),
- manage_ref=False)
- code.putln("%s = %s;" % (ret, ivar.result()))
- return ret
- def buffer_lookup_code(self, code):
- """
- ndarray[1, 2, 3] and memslice[1, 2, 3]
- """
- if self.in_nogil_context:
- if self.is_buffer_access or self.is_memview_index:
- if code.globalstate.directives['boundscheck']:
- warning(self.pos, "Use boundscheck(False) for faster access", level=1)
- # Assign indices to temps of at least (s)size_t to allow further index calculations.
- self.index_temps = index_temps = [self.get_index_in_temp(code,ivar) for ivar in self.indices]
- # Generate buffer access code using these temps
- from . import Buffer
- buffer_entry = self.buffer_entry()
- if buffer_entry.type.is_buffer:
- negative_indices = buffer_entry.type.negative_indices
- else:
- negative_indices = Buffer.buffer_defaults['negative_indices']
- return buffer_entry, Buffer.put_buffer_lookup_code(
- entry=buffer_entry,
- index_signeds=[ivar.type.signed for ivar in self.indices],
- index_cnames=index_temps,
- directives=code.globalstate.directives,
- pos=self.pos, code=code,
- negative_indices=negative_indices,
- in_nogil_context=self.in_nogil_context)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
- self.generate_subexpr_evaluation_code(code)
- self.generate_buffer_setitem_code(rhs, code)
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- def generate_buffer_setitem_code(self, rhs, code, op=""):
- base_type = self.base.type
- if is_pythran_expr(base_type) and is_pythran_supported_type(rhs.type):
- obj = code.funcstate.allocate_temp(PythranExpr(pythran_type(self.base.type)), manage_ref=False)
- # We have got to do this because we have to declare pythran objects
- # at the beginning of the functions.
- # Indeed, Cython uses "goto" statement for error management, and
- # RAII doesn't work with that kind of construction.
- # Moreover, the way Pythran expressions are made is that they don't
- # support move-assignation easily.
- # This, we explicitly destroy then in-place new objects in this
- # case.
- code.putln("__Pyx_call_destructor(%s);" % obj)
- code.putln("new (&%s) decltype(%s){%s};" % (obj, obj, self.base.pythran_result()))
- code.putln("%s%s %s= %s;" % (
- obj,
- pythran_indexing_code(self.indices),
- op,
- rhs.pythran_result()))
- code.funcstate.release_temp(obj)
- return
- # Used from generate_assignment_code and InPlaceAssignmentNode
- buffer_entry, ptrexpr = self.buffer_lookup_code(code)
- if self.buffer_type.dtype.is_pyobject:
- # Must manage refcounts. XDecref what is already there
- # and incref what we put in (NumPy allows there to be NULL)
- ptr = code.funcstate.allocate_temp(buffer_entry.buf_ptr_type,
- manage_ref=False)
- rhs_code = rhs.result()
- code.putln("%s = %s;" % (ptr, ptrexpr))
- code.put_xgotref("*%s" % ptr)
- code.putln("__Pyx_INCREF(%s); __Pyx_XDECREF(*%s);" % (
- rhs_code, ptr))
- code.putln("*%s %s= %s;" % (ptr, op, rhs_code))
- code.put_xgiveref("*%s" % ptr)
- code.funcstate.release_temp(ptr)
- else:
- # Simple case
- code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result()))
- def generate_result_code(self, code):
- if is_pythran_expr(self.base.type):
- res = self.result()
- code.putln("__Pyx_call_destructor(%s);" % res)
- code.putln("new (&%s) decltype(%s){%s%s};" % (
- res,
- res,
- self.base.pythran_result(),
- pythran_indexing_code(self.indices)))
- return
- buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code)
- if self.type.is_pyobject:
- # is_temp is True, so must pull out value and incref it.
- # NOTE: object temporary results for nodes are declared
- # as PyObject *, so we need a cast
- res = self.result()
- code.putln("%s = (PyObject *) *%s;" % (res, self.buffer_ptr_code))
- # NumPy does (occasionally) allow NULL to denote None.
- code.putln("if (unlikely(%s == NULL)) %s = Py_None;" % (res, res))
- code.putln("__Pyx_INCREF((PyObject*)%s);" % res)
- def free_subexpr_temps(self, code):
- for temp in self.index_temps:
- code.funcstate.release_temp(temp)
- self.index_temps = ()
- super(BufferIndexNode, self).free_subexpr_temps(code)
- class MemoryViewIndexNode(BufferIndexNode):
- is_memview_index = True
- is_buffer_access = False
- warned_untyped_idx = False
- def analyse_types(self, env, getting=True):
- # memoryviewslice indexing or slicing
- from . import MemoryView
- self.is_pythran_mode = has_np_pythran(env)
- indices = self.indices
- have_slices, indices, newaxes = MemoryView.unellipsify(indices, self.base.type.ndim)
- if not getting:
- self.writable_needed = True
- if self.base.is_name or self.base.is_attribute:
- self.base.entry.type.writable_needed = True
- self.memslice_index = (not newaxes and len(indices) == self.base.type.ndim)
- axes = []
- index_type = PyrexTypes.c_py_ssize_t_type
- new_indices = []
- if len(indices) - len(newaxes) > self.base.type.ndim:
- self.type = error_type
- error(indices[self.base.type.ndim].pos,
- "Too many indices specified for type %s" % self.base.type)
- return self
- axis_idx = 0
- for i, index in enumerate(indices[:]):
- index = index.analyse_types(env)
- if index.is_none:
- self.is_memview_slice = True
- new_indices.append(index)
- axes.append(('direct', 'strided'))
- continue
- access, packing = self.base.type.axes[axis_idx]
- axis_idx += 1
- if index.is_slice:
- self.is_memview_slice = True
- if index.step.is_none:
- axes.append((access, packing))
- else:
- axes.append((access, 'strided'))
- # Coerce start, stop and step to temps of the right type
- for attr in ('start', 'stop', 'step'):
- value = getattr(index, attr)
- if not value.is_none:
- value = value.coerce_to(index_type, env)
- #value = value.coerce_to_temp(env)
- setattr(index, attr, value)
- new_indices.append(value)
- elif index.type.is_int or index.type.is_pyobject:
- if index.type.is_pyobject and not self.warned_untyped_idx:
- warning(index.pos, "Index should be typed for more efficient access", level=2)
- MemoryViewIndexNode.warned_untyped_idx = True
- self.is_memview_index = True
- index = index.coerce_to(index_type, env)
- indices[i] = index
- new_indices.append(index)
- else:
- self.type = error_type
- error(index.pos, "Invalid index for memoryview specified, type %s" % index.type)
- return self
- ### FIXME: replace by MemoryViewSliceNode if is_memview_slice ?
- self.is_memview_index = self.is_memview_index and not self.is_memview_slice
- self.indices = new_indices
- # All indices with all start/stop/step for slices.
- # We need to keep this around.
- self.original_indices = indices
- self.nogil = env.nogil
- self.analyse_operation(env, getting, axes)
- self.wrap_in_nonecheck_node(env)
- return self
- def analyse_operation(self, env, getting, axes):
- self.none_error_message = "Cannot index None memoryview slice"
- self.analyse_buffer_index(env, getting)
- def analyse_broadcast_operation(self, rhs):
- """
- Support broadcasting for slice assignment.
- E.g.
- m_2d[...] = m_1d # or,
- m_1d[...] = m_2d # if the leading dimension has extent 1
- """
- if self.type.is_memoryviewslice:
- lhs = self
- if lhs.is_memview_broadcast or rhs.is_memview_broadcast:
- lhs.is_memview_broadcast = True
- rhs.is_memview_broadcast = True
- def analyse_as_memview_scalar_assignment(self, rhs):
- lhs = self.analyse_assignment(rhs)
- if lhs:
- rhs.is_memview_copy_assignment = lhs.is_memview_copy_assignment
- return lhs
- return self
- class MemoryViewSliceNode(MemoryViewIndexNode):
- is_memview_slice = True
- # No-op slicing operation, this node will be replaced
- is_ellipsis_noop = False
- is_memview_scalar_assignment = False
- is_memview_index = False
- is_memview_broadcast = False
- def analyse_ellipsis_noop(self, env, getting):
- """Slicing operations needing no evaluation, i.e. m[...] or m[:, :]"""
- ### FIXME: replace directly
- self.is_ellipsis_noop = all(
- index.is_slice and index.start.is_none and index.stop.is_none and index.step.is_none
- for index in self.indices)
- if self.is_ellipsis_noop:
- self.type = self.base.type
- def analyse_operation(self, env, getting, axes):
- from . import MemoryView
- if not getting:
- self.is_memview_broadcast = True
- self.none_error_message = "Cannot assign to None memoryview slice"
- else:
- self.none_error_message = "Cannot slice None memoryview slice"
- self.analyse_ellipsis_noop(env, getting)
- if self.is_ellipsis_noop:
- return
- self.index = None
- self.is_temp = True
- self.use_managed_ref = True
- if not MemoryView.validate_axes(self.pos, axes):
- self.type = error_type
- return
- self.type = PyrexTypes.MemoryViewSliceType(self.base.type.dtype, axes)
- if not (self.base.is_simple() or self.base.result_in_temp()):
- self.base = self.base.coerce_to_temp(env)
- def analyse_assignment(self, rhs):
- if not rhs.type.is_memoryviewslice and (
- self.type.dtype.assignable_from(rhs.type) or
- rhs.type.is_pyobject):
- # scalar assignment
- return MemoryCopyScalar(self.pos, self)
- else:
- return MemoryCopySlice(self.pos, self)
- def merged_indices(self, indices):
- """Return a new list of indices/slices with 'indices' merged into the current ones
- according to slicing rules.
- Is used to implement "view[i][j]" => "view[i, j]".
- Return None if the indices cannot (easily) be merged at compile time.
- """
- if not indices:
- return None
- # NOTE: Need to evaluate "self.original_indices" here as they might differ from "self.indices".
- new_indices = self.original_indices[:]
- indices = indices[:]
- for i, s in enumerate(self.original_indices):
- if s.is_slice:
- if s.start.is_none and s.stop.is_none and s.step.is_none:
- # Full slice found, replace by index.
- new_indices[i] = indices[0]
- indices.pop(0)
- if not indices:
- return new_indices
- else:
- # Found something non-trivial, e.g. a partial slice.
- return None
- elif not s.type.is_int:
- # Not a slice, not an integer index => could be anything...
- return None
- if indices:
- if len(new_indices) + len(indices) > self.base.type.ndim:
- return None
- new_indices += indices
- return new_indices
- def is_simple(self):
- if self.is_ellipsis_noop:
- # TODO: fix SimpleCallNode.is_simple()
- return self.base.is_simple() or self.base.result_in_temp()
- return self.result_in_temp()
- def calculate_result_code(self):
- """This is called in case this is a no-op slicing node"""
- return self.base.result()
- def generate_result_code(self, code):
- if self.is_ellipsis_noop:
- return ### FIXME: remove
- buffer_entry = self.buffer_entry()
- have_gil = not self.in_nogil_context
- # TODO Mark: this is insane, do it better
- have_slices = False
- it = iter(self.indices)
- for index in self.original_indices:
- if index.is_slice:
- have_slices = True
- if not index.start.is_none:
- index.start = next(it)
- if not index.stop.is_none:
- index.stop = next(it)
- if not index.step.is_none:
- index.step = next(it)
- else:
- next(it)
- assert not list(it)
- buffer_entry.generate_buffer_slice_code(
- code, self.original_indices, self.result(),
- have_gil=have_gil, have_slices=have_slices,
- directives=code.globalstate.directives)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
- if self.is_ellipsis_noop:
- self.generate_subexpr_evaluation_code(code)
- else:
- self.generate_evaluation_code(code)
- if self.is_memview_scalar_assignment:
- self.generate_memoryviewslice_assign_scalar_code(rhs, code)
- else:
- self.generate_memoryviewslice_setslice_code(rhs, code)
- if self.is_ellipsis_noop:
- self.generate_subexpr_disposal_code(code)
- else:
- self.generate_disposal_code(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- class MemoryCopyNode(ExprNode):
- """
- Wraps a memoryview slice for slice assignment.
- dst: destination mememoryview slice
- """
- subexprs = ['dst']
- def __init__(self, pos, dst):
- super(MemoryCopyNode, self).__init__(pos)
- self.dst = dst
- self.type = dst.type
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
- self.dst.generate_evaluation_code(code)
- self._generate_assignment_code(rhs, code)
- self.dst.generate_disposal_code(code)
- self.dst.free_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- class MemoryCopySlice(MemoryCopyNode):
- """
- Copy the contents of slice src to slice dst. Does not support indirect
- slices.
- memslice1[...] = memslice2
- memslice1[:] = memslice2
- """
- is_memview_copy_assignment = True
- copy_slice_cname = "__pyx_memoryview_copy_contents"
- def _generate_assignment_code(self, src, code):
- dst = self.dst
- src.type.assert_direct_dims(src.pos)
- dst.type.assert_direct_dims(dst.pos)
- code.putln(code.error_goto_if_neg(
- "%s(%s, %s, %d, %d, %d)" % (self.copy_slice_cname,
- src.result(), dst.result(),
- src.type.ndim, dst.type.ndim,
- dst.type.dtype.is_pyobject),
- dst.pos))
- class MemoryCopyScalar(MemoryCopyNode):
- """
- Assign a scalar to a slice. dst must be simple, scalar will be assigned
- to a correct type and not just something assignable.
- memslice1[...] = 0.0
- memslice1[:] = 0.0
- """
- def __init__(self, pos, dst):
- super(MemoryCopyScalar, self).__init__(pos, dst)
- self.type = dst.type.dtype
- def _generate_assignment_code(self, scalar, code):
- from . import MemoryView
- self.dst.type.assert_direct_dims(self.dst.pos)
- dtype = self.dst.type.dtype
- type_decl = dtype.declaration_code("")
- slice_decl = self.dst.type.declaration_code("")
- code.begin_block()
- code.putln("%s __pyx_temp_scalar = %s;" % (type_decl, scalar.result()))
- if self.dst.result_in_temp() or self.dst.is_simple():
- dst_temp = self.dst.result()
- else:
- code.putln("%s __pyx_temp_slice = %s;" % (slice_decl, self.dst.result()))
- dst_temp = "__pyx_temp_slice"
- slice_iter_obj = MemoryView.slice_iter(self.dst.type, dst_temp,
- self.dst.type.ndim, code)
- p = slice_iter_obj.start_loops()
- if dtype.is_pyobject:
- code.putln("Py_DECREF(*(PyObject **) %s);" % p)
- code.putln("*((%s *) %s) = __pyx_temp_scalar;" % (type_decl, p))
- if dtype.is_pyobject:
- code.putln("Py_INCREF(__pyx_temp_scalar);")
- slice_iter_obj.end_loops()
- code.end_block()
- class SliceIndexNode(ExprNode):
- # 2-element slice indexing
- #
- # base ExprNode
- # start ExprNode or None
- # stop ExprNode or None
- # slice ExprNode or None constant slice object
- subexprs = ['base', 'start', 'stop', 'slice']
- slice = None
- def infer_type(self, env):
- base_type = self.base.infer_type(env)
- if base_type.is_string or base_type.is_cpp_class:
- return bytes_type
- elif base_type.is_pyunicode_ptr:
- return unicode_type
- elif base_type in (bytes_type, bytearray_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return base_type
- elif base_type.is_ptr or base_type.is_array:
- return PyrexTypes.c_array_type(base_type.base_type, None)
- return py_object_type
- def inferable_item_node(self, index=0):
- # slicing shouldn't change the result type of the base, but the index might
- if index is not not_a_constant and self.start:
- if self.start.has_constant_result():
- index += self.start.constant_result
- else:
- index = not_a_constant
- return self.base.inferable_item_node(index)
- def may_be_none(self):
- base_type = self.base.type
- if base_type:
- if base_type.is_string:
- return False
- if base_type in (bytes_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return False
- return ExprNode.may_be_none(self)
- def calculate_constant_result(self):
- if self.start is None:
- start = None
- else:
- start = self.start.constant_result
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.constant_result
- self.constant_result = self.base.constant_result[start:stop]
- def compile_time_value(self, denv):
- base = self.base.compile_time_value(denv)
- if self.start is None:
- start = 0
- else:
- start = self.start.compile_time_value(denv)
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.compile_time_value(denv)
- try:
- return base[start:stop]
- except Exception as e:
- self.compile_time_value_error(e)
- def analyse_target_declaration(self, env):
- pass
- def analyse_target_types(self, env):
- node = self.analyse_types(env, getting=False)
- # when assigning, we must accept any Python type
- if node.type.is_pyobject:
- node.type = py_object_type
- return node
- def analyse_types(self, env, getting=True):
- self.base = self.base.analyse_types(env)
- if self.base.type.is_buffer or self.base.type.is_pythran_expr or self.base.type.is_memoryviewslice:
- none_node = NoneNode(self.pos)
- index = SliceNode(self.pos,
- start=self.start or none_node,
- stop=self.stop or none_node,
- step=none_node)
- index_node = IndexNode(self.pos, index=index, base=self.base)
- return index_node.analyse_base_and_index_types(
- env, getting=getting, setting=not getting,
- analyse_base=False)
- if self.start:
- self.start = self.start.analyse_types(env)
- if self.stop:
- self.stop = self.stop.analyse_types(env)
- if not env.directives['wraparound']:
- check_negative_indices(self.start, self.stop)
- base_type = self.base.type
- if base_type.is_array and not getting:
- # cannot assign directly to C array => try to assign by making a copy
- if not self.start and not self.stop:
- self.type = base_type
- else:
- self.type = PyrexTypes.CPtrType(base_type.base_type)
- elif base_type.is_string or base_type.is_cpp_string:
- self.type = default_str_type(env)
- elif base_type.is_pyunicode_ptr:
- self.type = unicode_type
- elif base_type.is_ptr:
- self.type = base_type
- elif base_type.is_array:
- # we need a ptr type here instead of an array type, as
- # array types can result in invalid type casts in the C
- # code
- self.type = PyrexTypes.CPtrType(base_type.base_type)
- else:
- self.base = self.base.coerce_to_pyobject(env)
- self.type = py_object_type
- if base_type.is_builtin_type:
- # slicing builtin types returns something of the same type
- self.type = base_type
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- if self.type is py_object_type:
- if (not self.start or self.start.is_literal) and \
- (not self.stop or self.stop.is_literal):
- # cache the constant slice object, in case we need it
- none_node = NoneNode(self.pos)
- self.slice = SliceNode(
- self.pos,
- start=copy.deepcopy(self.start or none_node),
- stop=copy.deepcopy(self.stop or none_node),
- step=none_node
- ).analyse_types(env)
- else:
- c_int = PyrexTypes.c_py_ssize_t_type
- def allow_none(node, default_value, env):
- # Coerce to Py_ssize_t, but allow None as meaning the default slice bound.
- from .UtilNodes import EvalWithTempExprNode, ResultRefNode
- node_ref = ResultRefNode(node)
- new_expr = CondExprNode(
- node.pos,
- true_val=IntNode(
- node.pos,
- type=c_int,
- value=default_value,
- constant_result=int(default_value) if default_value.isdigit() else not_a_constant,
- ),
- false_val=node_ref.coerce_to(c_int, env),
- test=PrimaryCmpNode(
- node.pos,
- operand1=node_ref,
- operator='is',
- operand2=NoneNode(node.pos),
- ).analyse_types(env)
- ).analyse_result_type(env)
- return EvalWithTempExprNode(node_ref, new_expr)
- if self.start:
- if self.start.type.is_pyobject:
- self.start = allow_none(self.start, '0', env)
- self.start = self.start.coerce_to(c_int, env)
- if self.stop:
- if self.stop.type.is_pyobject:
- self.stop = allow_none(self.stop, 'PY_SSIZE_T_MAX', env)
- self.stop = self.stop.coerce_to(c_int, env)
- self.is_temp = 1
- return self
- def analyse_as_type(self, env):
- base_type = self.base.analyse_as_type(env)
- if base_type and not base_type.is_pyobject:
- if not self.start and not self.stop:
- # memory view
- from . import MemoryView
- env.use_utility_code(MemoryView.view_utility_code)
- none_node = NoneNode(self.pos)
- slice_node = SliceNode(
- self.pos,
- start=none_node,
- stop=none_node,
- step=none_node,
- )
- return PyrexTypes.MemoryViewSliceType(
- base_type, MemoryView.get_axes_specs(env, [slice_node]))
- return None
- nogil_check = Node.gil_error
- gil_message = "Slicing Python object"
- get_slice_utility_code = TempitaUtilityCode.load(
- "SliceObject", "ObjectHandling.c", context={'access': 'Get'})
- set_slice_utility_code = TempitaUtilityCode.load(
- "SliceObject", "ObjectHandling.c", context={'access': 'Set'})
- def coerce_to(self, dst_type, env):
- if ((self.base.type.is_string or self.base.type.is_cpp_string)
- and dst_type in (bytes_type, bytearray_type, str_type, unicode_type)):
- if (dst_type not in (bytes_type, bytearray_type)
- and not env.directives['c_string_encoding']):
- error(self.pos,
- "default encoding required for conversion from '%s' to '%s'" %
- (self.base.type, dst_type))
- self.type = dst_type
- if dst_type.is_array and self.base.type.is_array:
- if not self.start and not self.stop:
- # redundant slice building, copy C arrays directly
- return self.base.coerce_to(dst_type, env)
- # else: check array size if possible
- return super(SliceIndexNode, self).coerce_to(dst_type, env)
- def generate_result_code(self, code):
- if not self.type.is_pyobject:
- error(self.pos,
- "Slicing is not currently supported for '%s'." % self.type)
- return
- base_result = self.base.result()
- result = self.result()
- start_code = self.start_code()
- stop_code = self.stop_code()
- if self.base.type.is_string:
- base_result = self.base.result()
- if self.base.type not in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
- base_result = '((const char*)%s)' % base_result
- if self.type is bytearray_type:
- type_name = 'ByteArray'
- else:
- type_name = self.type.name.title()
- if self.stop is None:
- code.putln(
- "%s = __Pyx_Py%s_FromString(%s + %s); %s" % (
- result,
- type_name,
- base_result,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- else:
- code.putln(
- "%s = __Pyx_Py%s_FromStringAndSize(%s + %s, %s - %s); %s" % (
- result,
- type_name,
- base_result,
- start_code,
- stop_code,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.base.type.is_pyunicode_ptr:
- base_result = self.base.result()
- if self.base.type != PyrexTypes.c_py_unicode_ptr_type:
- base_result = '((const Py_UNICODE*)%s)' % base_result
- if self.stop is None:
- code.putln(
- "%s = __Pyx_PyUnicode_FromUnicode(%s + %s); %s" % (
- result,
- base_result,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- else:
- code.putln(
- "%s = __Pyx_PyUnicode_FromUnicodeAndLength(%s + %s, %s - %s); %s" % (
- result,
- base_result,
- start_code,
- stop_code,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.base.type is unicode_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyUnicode_Substring", "StringTools.c"))
- code.putln(
- "%s = __Pyx_PyUnicode_Substring(%s, %s, %s); %s" % (
- result,
- base_result,
- start_code,
- stop_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.type is py_object_type:
- code.globalstate.use_utility_code(self.get_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.putln(
- "%s = __Pyx_PyObject_GetSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % (
- result,
- self.base.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound']),
- code.error_goto_if_null(result, self.pos)))
- else:
- if self.base.type is list_type:
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
- cfunc = '__Pyx_PyList_GetSlice'
- elif self.base.type is tuple_type:
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
- cfunc = '__Pyx_PyTuple_GetSlice'
- else:
- cfunc = 'PySequence_GetSlice'
- code.putln(
- "%s = %s(%s, %s, %s); %s" % (
- result,
- cfunc,
- self.base.py_result(),
- start_code,
- stop_code,
- code.error_goto_if_null(result, self.pos)))
- code.put_gotref(self.py_result())
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- self.generate_subexpr_evaluation_code(code)
- if self.type.is_pyobject:
- code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.put_error_if_neg(self.pos,
- "__Pyx_PyObject_SetSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
- self.base.py_result(),
- rhs.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound'])))
- else:
- start_offset = self.start_code() if self.start else '0'
- if rhs.type.is_array:
- array_length = rhs.type.size
- self.generate_slice_guard_code(code, array_length)
- else:
- array_length = '%s - %s' % (self.stop_code(), start_offset)
- code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStringH", "StringTools.c"))
- code.putln("memcpy(&(%s[%s]), %s, sizeof(%s[0]) * (%s));" % (
- self.base.result(), start_offset,
- rhs.result(),
- self.base.result(), array_length
- ))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- if not self.base.type.is_pyobject:
- error(self.pos,
- "Deleting slices is only supported for Python types, not '%s'." % self.type)
- return
- self.generate_subexpr_evaluation_code(code)
- code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.put_error_if_neg(self.pos,
- "__Pyx_PyObject_DelSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
- self.base.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound'])))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- def get_slice_config(self):
- has_c_start, c_start, py_start = False, '0', 'NULL'
- if self.start:
- has_c_start = not self.start.type.is_pyobject
- if has_c_start:
- c_start = self.start.result()
- else:
- py_start = '&%s' % self.start.py_result()
- has_c_stop, c_stop, py_stop = False, '0', 'NULL'
- if self.stop:
- has_c_stop = not self.stop.type.is_pyobject
- if has_c_stop:
- c_stop = self.stop.result()
- else:
- py_stop = '&%s' % self.stop.py_result()
- py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
- return (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice)
- def generate_slice_guard_code(self, code, target_size):
- if not self.base.type.is_array:
- return
- slice_size = self.base.type.size
- try:
- total_length = slice_size = int(slice_size)
- except ValueError:
- total_length = None
- start = stop = None
- if self.stop:
- stop = self.stop.result()
- try:
- stop = int(stop)
- if stop < 0:
- if total_length is None:
- slice_size = '%s + %d' % (slice_size, stop)
- else:
- slice_size += stop
- else:
- slice_size = stop
- stop = None
- except ValueError:
- pass
- if self.start:
- start = self.start.result()
- try:
- start = int(start)
- if start < 0:
- if total_length is None:
- start = '%s + %d' % (self.base.type.size, start)
- else:
- start += total_length
- if isinstance(slice_size, _py_int_types):
- slice_size -= start
- else:
- slice_size = '%s - (%s)' % (slice_size, start)
- start = None
- except ValueError:
- pass
- runtime_check = None
- compile_time_check = False
- try:
- int_target_size = int(target_size)
- except ValueError:
- int_target_size = None
- else:
- compile_time_check = isinstance(slice_size, _py_int_types)
- if compile_time_check and slice_size < 0:
- if int_target_size > 0:
- error(self.pos, "Assignment to empty slice.")
- elif compile_time_check and start is None and stop is None:
- # we know the exact slice length
- if int_target_size != slice_size:
- error(self.pos, "Assignment to slice of wrong length, expected %s, got %s" % (
- slice_size, target_size))
- elif start is not None:
- if stop is None:
- stop = slice_size
- runtime_check = "(%s)-(%s)" % (stop, start)
- elif stop is not None:
- runtime_check = stop
- else:
- runtime_check = slice_size
- if runtime_check:
- code.putln("if (unlikely((%s) != (%s))) {" % (runtime_check, target_size))
- code.putln(
- 'PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length,'
- ' expected %%" CYTHON_FORMAT_SSIZE_T "d, got %%" CYTHON_FORMAT_SSIZE_T "d",'
- ' (Py_ssize_t)(%s), (Py_ssize_t)(%s));' % (
- target_size, runtime_check))
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- def start_code(self):
- if self.start:
- return self.start.result()
- else:
- return "0"
- def stop_code(self):
- if self.stop:
- return self.stop.result()
- elif self.base.type.is_array:
- return self.base.type.size
- else:
- return "PY_SSIZE_T_MAX"
- def calculate_result_code(self):
- # self.result() is not used, but this method must exist
- return "<unused>"
- class SliceNode(ExprNode):
- # start:stop:step in subscript list
- #
- # start ExprNode
- # stop ExprNode
- # step ExprNode
- subexprs = ['start', 'stop', 'step']
- is_slice = True
- type = slice_type
- is_temp = 1
- def calculate_constant_result(self):
- self.constant_result = slice(
- self.start.constant_result,
- self.stop.constant_result,
- self.step.constant_result)
- def compile_time_value(self, denv):
- start = self.start.compile_time_value(denv)
- stop = self.stop.compile_time_value(denv)
- step = self.step.compile_time_value(denv)
- try:
- return slice(start, stop, step)
- except Exception as e:
- self.compile_time_value_error(e)
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- start = self.start.analyse_types(env)
- stop = self.stop.analyse_types(env)
- step = self.step.analyse_types(env)
- self.start = start.coerce_to_pyobject(env)
- self.stop = stop.coerce_to_pyobject(env)
- self.step = step.coerce_to_pyobject(env)
- if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
- self.is_literal = True
- self.is_temp = False
- return self
- gil_message = "Constructing Python slice object"
- def calculate_result_code(self):
- return self.result_code
- def generate_result_code(self, code):
- if self.is_literal:
- dedup_key = make_dedup_key(self.type, (self,))
- self.result_code = code.get_py_const(py_object_type, 'slice', cleanup_level=2, dedup_key=dedup_key)
- code = code.get_cached_constants_writer(self.result_code)
- if code is None:
- return # already initialised
- code.mark_pos(self.pos)
- code.putln(
- "%s = PySlice_New(%s, %s, %s); %s" % (
- self.result(),
- self.start.py_result(),
- self.stop.py_result(),
- self.step.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- if self.is_literal:
- code.put_giveref(self.py_result())
- class SliceIntNode(SliceNode):
- # start:stop:step in subscript list
- # This is just a node to hold start,stop and step nodes that can be
- # converted to integers. This does not generate a slice python object.
- #
- # start ExprNode
- # stop ExprNode
- # step ExprNode
- is_temp = 0
- def calculate_constant_result(self):
- self.constant_result = slice(
- self.start.constant_result,
- self.stop.constant_result,
- self.step.constant_result)
- def compile_time_value(self, denv):
- start = self.start.compile_time_value(denv)
- stop = self.stop.compile_time_value(denv)
- step = self.step.compile_time_value(denv)
- try:
- return slice(start, stop, step)
- except Exception as e:
- self.compile_time_value_error(e)
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- self.start = self.start.analyse_types(env)
- self.stop = self.stop.analyse_types(env)
- self.step = self.step.analyse_types(env)
- if not self.start.is_none:
- self.start = self.start.coerce_to_integer(env)
- if not self.stop.is_none:
- self.stop = self.stop.coerce_to_integer(env)
- if not self.step.is_none:
- self.step = self.step.coerce_to_integer(env)
- if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
- self.is_literal = True
- self.is_temp = False
- return self
- def calculate_result_code(self):
- pass
- def generate_result_code(self, code):
- for a in self.start,self.stop,self.step:
- if isinstance(a, CloneNode):
- a.arg.result()
- class CallNode(ExprNode):
- # allow overriding the default 'may_be_none' behaviour
- may_return_none = None
- def infer_type(self, env):
- # TODO(robertwb): Reduce redundancy with analyse_types.
- function = self.function
- func_type = function.infer_type(env)
- if isinstance(function, NewExprNode):
- # note: needs call to infer_type() above
- return PyrexTypes.CPtrType(function.class_type)
- if func_type is py_object_type:
- # function might have lied for safety => try to find better type
- entry = getattr(function, 'entry', None)
- if entry is not None:
- func_type = entry.type or func_type
- if func_type.is_ptr:
- func_type = func_type.base_type
- if func_type.is_cfunction:
- if getattr(self.function, 'entry', None) and hasattr(self, 'args'):
- alternatives = self.function.entry.all_alternatives()
- arg_types = [arg.infer_type(env) for arg in self.args]
- func_entry = PyrexTypes.best_match(arg_types, alternatives)
- if func_entry:
- func_type = func_entry.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- return func_type.return_type
- return func_type.return_type
- elif func_type is type_type:
- if function.is_name and function.entry and function.entry.type:
- result_type = function.entry.type
- if result_type.is_extension_type:
- return result_type
- elif result_type.is_builtin_type:
- if function.entry.name == 'float':
- return PyrexTypes.c_double_type
- elif function.entry.name in Builtin.types_that_construct_their_instance:
- return result_type
- return py_object_type
- def type_dependencies(self, env):
- # TODO: Update when Danilo's C++ code merged in to handle the
- # the case of function overloading.
- return self.function.type_dependencies(env)
- def is_simple(self):
- # C function calls could be considered simple, but they may
- # have side-effects that may hit when multiple operations must
- # be effected in order, e.g. when constructing the argument
- # sequence for a function call or comparing values.
- return False
- def may_be_none(self):
- if self.may_return_none is not None:
- return self.may_return_none
- func_type = self.function.type
- if func_type is type_type and self.function.is_name:
- entry = self.function.entry
- if entry.type.is_extension_type:
- return False
- if (entry.type.is_builtin_type and
- entry.name in Builtin.types_that_construct_their_instance):
- return False
- return ExprNode.may_be_none(self)
- def set_py_result_type(self, function, func_type=None):
- if func_type is None:
- func_type = function.type
- if func_type is Builtin.type_type and (
- function.is_name and
- function.entry and
- function.entry.is_builtin and
- function.entry.name in Builtin.types_that_construct_their_instance):
- # calling a builtin type that returns a specific object type
- if function.entry.name == 'float':
- # the following will come true later on in a transform
- self.type = PyrexTypes.c_double_type
- self.result_ctype = PyrexTypes.c_double_type
- else:
- self.type = Builtin.builtin_types[function.entry.name]
- self.result_ctype = py_object_type
- self.may_return_none = False
- elif function.is_name and function.type_entry:
- # We are calling an extension type constructor. As long as we do not
- # support __new__(), the result type is clear
- self.type = function.type_entry.type
- self.result_ctype = py_object_type
- self.may_return_none = False
- else:
- self.type = py_object_type
- def analyse_as_type_constructor(self, env):
- type = self.function.analyse_as_type(env)
- if type and type.is_struct_or_union:
- args, kwds = self.explicit_args_kwds()
- items = []
- for arg, member in zip(args, type.scope.var_entries):
- items.append(DictItemNode(pos=arg.pos, key=StringNode(pos=arg.pos, value=member.name), value=arg))
- if kwds:
- items += kwds.key_value_pairs
- self.key_value_pairs = items
- self.__class__ = DictNode
- self.analyse_types(env) # FIXME
- self.coerce_to(type, env)
- return True
- elif type and type.is_cpp_class:
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- constructor = type.scope.lookup("<init>")
- if not constructor:
- error(self.function.pos, "no constructor found for C++ type '%s'" % self.function.name)
- self.type = error_type
- return self
- self.function = RawCNameExprNode(self.function.pos, constructor.type)
- self.function.entry = constructor
- self.function.set_cname(type.empty_declaration_code())
- self.analyse_c_function_call(env)
- self.type = type
- return True
- def is_lvalue(self):
- return self.type.is_reference
- def nogil_check(self, env):
- func_type = self.function_type()
- if func_type.is_pyobject:
- self.gil_error()
- elif not func_type.is_error and not getattr(func_type, 'nogil', False):
- self.gil_error()
- gil_message = "Calling gil-requiring function"
- class SimpleCallNode(CallNode):
- # Function call without keyword, * or ** args.
- #
- # function ExprNode
- # args [ExprNode]
- # arg_tuple ExprNode or None used internally
- # self ExprNode or None used internally
- # coerced_self ExprNode or None used internally
- # wrapper_call bool used internally
- # has_optional_args bool used internally
- # nogil bool used internally
- subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
- self = None
- coerced_self = None
- arg_tuple = None
- wrapper_call = False
- has_optional_args = False
- nogil = False
- analysed = False
- overflowcheck = False
- def compile_time_value(self, denv):
- function = self.function.compile_time_value(denv)
- args = [arg.compile_time_value(denv) for arg in self.args]
- try:
- return function(*args)
- except Exception as e:
- self.compile_time_value_error(e)
- def analyse_as_type(self, env):
- attr = self.function.as_cython_attribute()
- if attr == 'pointer':
- if len(self.args) != 1:
- error(self.args.pos, "only one type allowed.")
- else:
- type = self.args[0].analyse_as_type(env)
- if not type:
- error(self.args[0].pos, "Unknown type")
- else:
- return PyrexTypes.CPtrType(type)
- elif attr == 'typeof':
- if len(self.args) != 1:
- error(self.args.pos, "only one type allowed.")
- operand = self.args[0].analyse_types(env)
- return operand.type
- def explicit_args_kwds(self):
- return self.args, None
- def analyse_types(self, env):
- if self.analyse_as_type_constructor(env):
- return self
- if self.analysed:
- return self
- self.analysed = True
- self.function.is_called = 1
- self.function = self.function.analyse_types(env)
- function = self.function
- if function.is_attribute and function.entry and function.entry.is_cmethod:
- # Take ownership of the object from which the attribute
- # was obtained, because we need to pass it as 'self'.
- self.self = function.obj
- function.obj = CloneNode(self.self)
- func_type = self.function_type()
- self.is_numpy_call_with_exprs = False
- if (has_np_pythran(env) and function.is_numpy_attribute and
- pythran_is_numpy_func_supported(function)):
- has_pythran_args = True
- self.arg_tuple = TupleNode(self.pos, args = self.args)
- self.arg_tuple = self.arg_tuple.analyse_types(env)
- for arg in self.arg_tuple.args:
- has_pythran_args &= is_pythran_supported_node_or_none(arg)
- self.is_numpy_call_with_exprs = bool(has_pythran_args)
- if self.is_numpy_call_with_exprs:
- env.add_include_file(pythran_get_func_include_file(function))
- return NumPyMethodCallNode.from_node(
- self,
- function_cname=pythran_functor(function),
- arg_tuple=self.arg_tuple,
- type=PythranExpr(pythran_func_type(function, self.arg_tuple.args)),
- )
- elif func_type.is_pyobject:
- self.arg_tuple = TupleNode(self.pos, args = self.args)
- self.arg_tuple = self.arg_tuple.analyse_types(env).coerce_to_pyobject(env)
- self.args = None
- self.set_py_result_type(function, func_type)
- self.is_temp = 1
- else:
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- self.analyse_c_function_call(env)
- if func_type.exception_check == '+':
- self.is_temp = True
- return self
- def function_type(self):
- # Return the type of the function being called, coercing a function
- # pointer to a function if necessary. If the function has fused
- # arguments, return the specific type.
- func_type = self.function.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- return func_type
- def analyse_c_function_call(self, env):
- func_type = self.function.type
- if func_type is error_type:
- self.type = error_type
- return
- if func_type.is_cfunction and func_type.is_static_method:
- if self.self and self.self.type.is_extension_type:
- # To support this we'd need to pass self to determine whether
- # it was overloaded in Python space (possibly via a Cython
- # superclass turning a cdef method into a cpdef one).
- error(self.pos, "Cannot call a static method on an instance variable.")
- args = self.args
- elif self.self:
- args = [self.self] + self.args
- else:
- args = self.args
- if func_type.is_cpp_class:
- overloaded_entry = self.function.type.scope.lookup("operator()")
- if overloaded_entry is None:
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- elif hasattr(self.function, 'entry'):
- overloaded_entry = self.function.entry
- elif self.function.is_subscript and self.function.is_fused_index:
- overloaded_entry = self.function.type.entry
- else:
- overloaded_entry = None
- if overloaded_entry:
- if self.function.type.is_fused:
- functypes = self.function.type.get_all_specialized_function_types()
- alternatives = [f.entry for f in functypes]
- else:
- alternatives = overloaded_entry.all_alternatives()
- entry = PyrexTypes.best_match(
- [arg.type for arg in args], alternatives, self.pos, env, args)
- if not entry:
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- entry.used = True
- if not func_type.is_cpp_class:
- self.function.entry = entry
- self.function.type = entry.type
- func_type = self.function_type()
- else:
- entry = None
- func_type = self.function_type()
- if not func_type.is_cfunction:
- error(self.pos, "Calling non-function type '%s'" % func_type)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- # Check no. of args
- max_nargs = len(func_type.args)
- expected_nargs = max_nargs - func_type.optional_arg_count
- actual_nargs = len(args)
- if func_type.optional_arg_count and expected_nargs != actual_nargs:
- self.has_optional_args = 1
- self.is_temp = 1
- # check 'self' argument
- if entry and entry.is_cmethod and func_type.args and not func_type.is_static_method:
- formal_arg = func_type.args[0]
- arg = args[0]
- if formal_arg.not_none:
- if self.self:
- self.self = self.self.as_none_safe_node(
- "'NoneType' object has no attribute '%{0}s'".format('.30' if len(entry.name) <= 30 else ''),
- error='PyExc_AttributeError',
- format_args=[entry.name])
- else:
- # unbound method
- arg = arg.as_none_safe_node(
- "descriptor '%s' requires a '%s' object but received a 'NoneType'",
- format_args=[entry.name, formal_arg.type.name])
- if self.self:
- if formal_arg.accept_builtin_subtypes:
- arg = CMethodSelfCloneNode(self.self)
- else:
- arg = CloneNode(self.self)
- arg = self.coerced_self = arg.coerce_to(formal_arg.type, env)
- elif formal_arg.type.is_builtin_type:
- # special case: unbound methods of builtins accept subtypes
- arg = arg.coerce_to(formal_arg.type, env)
- if arg.type.is_builtin_type and isinstance(arg, PyTypeTestNode):
- arg.exact_builtin_type = False
- args[0] = arg
- # Coerce arguments
- some_args_in_temps = False
- for i in range(min(max_nargs, actual_nargs)):
- formal_arg = func_type.args[i]
- formal_type = formal_arg.type
- arg = args[i].coerce_to(formal_type, env)
- if formal_arg.not_none:
- # C methods must do the None checks at *call* time
- arg = arg.as_none_safe_node(
- "cannot pass None into a C function argument that is declared 'not None'")
- if arg.is_temp:
- if i > 0:
- # first argument in temp doesn't impact subsequent arguments
- some_args_in_temps = True
- elif arg.type.is_pyobject and not env.nogil:
- if i == 0 and self.self is not None:
- # a method's cloned "self" argument is ok
- pass
- elif arg.nonlocally_immutable():
- # plain local variables are ok
- pass
- else:
- # we do not safely own the argument's reference,
- # but we must make sure it cannot be collected
- # before we return from the function, so we create
- # an owned temp reference to it
- if i > 0: # first argument doesn't matter
- some_args_in_temps = True
- arg = arg.coerce_to_temp(env)
- args[i] = arg
- # handle additional varargs parameters
- for i in range(max_nargs, actual_nargs):
- arg = args[i]
- if arg.type.is_pyobject:
- if arg.type is str_type:
- arg_ctype = PyrexTypes.c_char_ptr_type
- else:
- arg_ctype = arg.type.default_coerced_ctype()
- if arg_ctype is None:
- error(self.args[i].pos,
- "Python object cannot be passed as a varargs parameter")
- else:
- args[i] = arg = arg.coerce_to(arg_ctype, env)
- if arg.is_temp and i > 0:
- some_args_in_temps = True
- if some_args_in_temps:
- # if some args are temps and others are not, they may get
- # constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps (except
- # for the last argument, which is evaluated last in any
- # case)
- for i in range(actual_nargs-1):
- if i == 0 and self.self is not None:
- continue # self is ok
- arg = args[i]
- if arg.nonlocally_immutable():
- # locals, C functions, unassignable types are safe.
- pass
- elif arg.type.is_cpp_class:
- # Assignment has side effects, avoid.
- pass
- elif env.nogil and arg.type.is_pyobject:
- # can't copy a Python reference into a temp in nogil
- # env (this is safe: a construction would fail in
- # nogil anyway)
- pass
- else:
- #self.args[i] = arg.coerce_to_temp(env)
- # instead: issue a warning
- if i > 0 or i == 1 and self.self is not None: # skip first arg
- warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
- break
- self.args[:] = args
- # Calc result type and code fragment
- if isinstance(self.function, NewExprNode):
- self.type = PyrexTypes.CPtrType(self.function.class_type)
- else:
- self.type = func_type.return_type
- if self.function.is_name or self.function.is_attribute:
- func_entry = self.function.entry
- if func_entry and (func_entry.utility_code or func_entry.utility_code_definition):
- self.is_temp = 1 # currently doesn't work for self.calculate_result_code()
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- self.is_temp = 1
- elif func_type.exception_value is not None or func_type.exception_check:
- self.is_temp = 1
- elif self.type.is_memoryviewslice:
- self.is_temp = 1
- # func_type.exception_check = True
- if self.is_temp and self.type.is_reference:
- self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
- # Called in 'nogil' context?
- self.nogil = env.nogil
- if (self.nogil and
- func_type.exception_check and
- func_type.exception_check != '+'):
- env.use_utility_code(pyerr_occurred_withgil_utility_code)
- # C++ exception handler
- if func_type.exception_check == '+':
- if func_type.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- self.overflowcheck = env.directives['overflowcheck']
- def calculate_result_code(self):
- return self.c_call_code()
- def c_call_code(self):
- func_type = self.function_type()
- if self.type is PyrexTypes.error_type or not func_type.is_cfunction:
- return "<error>"
- formal_args = func_type.args
- arg_list_code = []
- args = list(zip(formal_args, self.args))
- max_nargs = len(func_type.args)
- expected_nargs = max_nargs - func_type.optional_arg_count
- actual_nargs = len(self.args)
- for formal_arg, actual_arg in args[:expected_nargs]:
- arg_code = actual_arg.result_as(formal_arg.type)
- arg_list_code.append(arg_code)
- if func_type.is_overridable:
- arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod)))
- if func_type.optional_arg_count:
- if expected_nargs == actual_nargs:
- optional_args = 'NULL'
- else:
- optional_args = "&%s" % self.opt_arg_struct
- arg_list_code.append(optional_args)
- for actual_arg in self.args[len(formal_args):]:
- arg_list_code.append(actual_arg.result())
- result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code))
- return result
- def is_c_result_required(self):
- func_type = self.function_type()
- if not func_type.exception_value or func_type.exception_check == '+':
- return False # skip allocation of unused result temp
- return True
- def generate_evaluation_code(self, code):
- function = self.function
- if function.is_name or function.is_attribute:
- code.globalstate.use_entry_utility_code(function.entry)
- abs_function_cnames = ('abs', 'labs', '__Pyx_abs_longlong')
- is_signed_int = self.type.is_int and self.type.signed
- if self.overflowcheck and is_signed_int and function.result() in abs_function_cnames:
- code.globalstate.use_utility_code(UtilityCode.load_cached("Common", "Overflow.c"))
- code.putln('if (unlikely(%s == __PYX_MIN(%s))) {\
- PyErr_SetString(PyExc_OverflowError,\
- "Trying to take the absolute value of the most negative integer is not defined."); %s; }' % (
- self.args[0].result(),
- self.args[0].type.empty_declaration_code(),
- code.error_goto(self.pos)))
- if not function.type.is_pyobject or len(self.arg_tuple.args) > 1 or (
- self.arg_tuple.args and self.arg_tuple.is_literal):
- super(SimpleCallNode, self).generate_evaluation_code(code)
- return
- # Special case 0-args and try to avoid explicit tuple creation for Python calls with 1 arg.
- arg = self.arg_tuple.args[0] if self.arg_tuple.args else None
- subexprs = (self.self, self.coerced_self, function, arg)
- for subexpr in subexprs:
- if subexpr is not None:
- subexpr.generate_evaluation_code(code)
- code.mark_pos(self.pos)
- assert self.is_temp
- self.allocate_temp_result(code)
- if arg is None:
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCallNoArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallNoArg(%s); %s" % (
- self.result(),
- function.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- else:
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCallOneArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallOneArg(%s, %s); %s" % (
- self.result(),
- function.py_result(),
- arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- for subexpr in subexprs:
- if subexpr is not None:
- subexpr.generate_disposal_code(code)
- subexpr.free_temps(code)
- def generate_result_code(self, code):
- func_type = self.function_type()
- if func_type.is_pyobject:
- arg_code = self.arg_tuple.py_result()
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- self.function.py_result(),
- arg_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif func_type.is_cfunction:
- if self.has_optional_args:
- actual_nargs = len(self.args)
- expected_nargs = len(func_type.args) - func_type.optional_arg_count
- self.opt_arg_struct = code.funcstate.allocate_temp(
- func_type.op_arg_struct.base_type, manage_ref=True)
- code.putln("%s.%s = %s;" % (
- self.opt_arg_struct,
- Naming.pyrex_prefix + "n",
- len(self.args) - expected_nargs))
- args = list(zip(func_type.args, self.args))
- for formal_arg, actual_arg in args[expected_nargs:actual_nargs]:
- code.putln("%s.%s = %s;" % (
- self.opt_arg_struct,
- func_type.opt_arg_cname(formal_arg.name),
- actual_arg.result_as(formal_arg.type)))
- exc_checks = []
- if self.type.is_pyobject and self.is_temp:
- exc_checks.append("!%s" % self.result())
- elif self.type.is_memoryviewslice:
- assert self.is_temp
- exc_checks.append(self.type.error_condition(self.result()))
- elif func_type.exception_check != '+':
- exc_val = func_type.exception_value
- exc_check = func_type.exception_check
- if exc_val is not None:
- exc_checks.append("%s == %s" % (self.result(), func_type.return_type.cast_code(exc_val)))
- if exc_check:
- if self.nogil:
- exc_checks.append("__Pyx_ErrOccurredWithGIL()")
- else:
- exc_checks.append("PyErr_Occurred()")
- if self.is_temp or exc_checks:
- rhs = self.c_call_code()
- if self.result():
- lhs = "%s = " % self.result()
- if self.is_temp and self.type.is_pyobject:
- #return_type = self.type # func_type.return_type
- #print "SimpleCallNode.generate_result_code: casting", rhs, \
- # "from", return_type, "to pyobject" ###
- rhs = typecast(py_object_type, self.type, rhs)
- else:
- lhs = ""
- if func_type.exception_check == '+':
- translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs),
- self.result() if self.type.is_pyobject else None,
- func_type.exception_value, self.nogil)
- else:
- if exc_checks:
- goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos)
- else:
- goto_error = ""
- code.putln("%s%s; %s" % (lhs, rhs, goto_error))
- if self.type.is_pyobject and self.result():
- code.put_gotref(self.py_result())
- if self.has_optional_args:
- code.funcstate.release_temp(self.opt_arg_struct)
- class NumPyMethodCallNode(ExprNode):
- # Pythran call to a NumPy function or method.
- #
- # function_cname string the function/method to call
- # arg_tuple TupleNode the arguments as an args tuple
- subexprs = ['arg_tuple']
- is_temp = True
- may_return_none = True
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- assert self.arg_tuple.mult_factor is None
- args = self.arg_tuple.args
- for arg in args:
- arg.generate_evaluation_code(code)
- code.putln("// function evaluation code for numpy function")
- code.putln("__Pyx_call_destructor(%s);" % self.result())
- code.putln("new (&%s) decltype(%s){%s{}(%s)};" % (
- self.result(),
- self.result(),
- self.function_cname,
- ", ".join(a.pythran_result() for a in args)))
- class PyMethodCallNode(SimpleCallNode):
- # Specialised call to a (potential) PyMethodObject with non-constant argument tuple.
- # Allows the self argument to be injected directly instead of repacking a tuple for it.
- #
- # function ExprNode the function/method object to call
- # arg_tuple TupleNode the arguments for the args tuple
- subexprs = ['function', 'arg_tuple']
- is_temp = True
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- self.function.generate_evaluation_code(code)
- assert self.arg_tuple.mult_factor is None
- args = self.arg_tuple.args
- for arg in args:
- arg.generate_evaluation_code(code)
- # make sure function is in temp so that we can replace the reference below if it's a method
- reuse_function_temp = self.function.is_temp
- if reuse_function_temp:
- function = self.function.result()
- else:
- function = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- self.function.make_owned_reference(code)
- code.put("%s = %s; " % (function, self.function.py_result()))
- self.function.generate_disposal_code(code)
- self.function.free_temps(code)
- self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = NULL;" % self_arg)
- arg_offset_cname = None
- if len(args) > 1:
- arg_offset_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % arg_offset_cname)
- def attribute_is_likely_method(attr):
- obj = attr.obj
- if obj.is_name and obj.entry.is_pyglobal:
- return False # more likely to be a function
- return True
- if self.function.is_attribute:
- likely_method = 'likely' if attribute_is_likely_method(self.function) else 'unlikely'
- elif self.function.is_name and self.function.cf_state:
- # not an attribute itself, but might have been assigned from one (e.g. bound method)
- for assignment in self.function.cf_state:
- value = assignment.rhs
- if value and value.is_attribute and value.obj.type and value.obj.type.is_pyobject:
- if attribute_is_likely_method(value):
- likely_method = 'likely'
- break
- else:
- likely_method = 'unlikely'
- else:
- likely_method = 'unlikely'
- code.putln("if (CYTHON_UNPACK_METHODS && %s(PyMethod_Check(%s))) {" % (likely_method, function))
- code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function))
- # the following is always true in Py3 (kept only for safety),
- # but is false for unbound methods in Py2
- code.putln("if (likely(%s)) {" % self_arg)
- code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function)
- code.put_incref(self_arg, py_object_type)
- code.put_incref("function", py_object_type)
- # free method object as early to possible to enable reuse from CPython's freelist
- code.put_decref_set(function, "function")
- if len(args) > 1:
- code.putln("%s = 1;" % arg_offset_cname)
- code.putln("}")
- code.putln("}")
- if not args:
- # fastest special case: try to avoid tuple creation
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallNoArg", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- code.putln(
- "%s = (%s) ? __Pyx_PyObject_CallOneArg(%s, %s) : __Pyx_PyObject_CallNoArg(%s);" % (
- self.result(), self_arg,
- function, self_arg,
- function))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.funcstate.release_temp(self_arg)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- elif len(args) == 1:
- # fastest special case: try to avoid tuple creation
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCall2Args", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- arg = args[0]
- code.putln(
- "%s = (%s) ? __Pyx_PyObject_Call2Args(%s, %s, %s) : __Pyx_PyObject_CallOneArg(%s, %s);" % (
- self.result(), self_arg,
- function, self_arg, arg.py_result(),
- function, arg.py_result()))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.funcstate.release_temp(self_arg)
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyFunctionFastCall", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyCFunctionFastCall", "ObjectHandling.c"))
- for test_func, call_prefix in [('PyFunction_Check', 'Py'), ('__Pyx_PyFastCFunction_Check', 'PyC')]:
- code.putln("#if CYTHON_FAST_%sCALL" % call_prefix.upper())
- code.putln("if (%s(%s)) {" % (test_func, function))
- code.putln("PyObject *%s[%d] = {%s, %s};" % (
- Naming.quick_temp_cname,
- len(args)+1,
- self_arg,
- ', '.join(arg.py_result() for arg in args)))
- code.putln("%s = __Pyx_%sFunction_FastCall(%s, %s+1-%s, %d+%s); %s" % (
- self.result(),
- call_prefix,
- function,
- Naming.quick_temp_cname,
- arg_offset_cname,
- len(args),
- arg_offset_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.put_gotref(self.py_result())
- for arg in args:
- arg.generate_disposal_code(code)
- code.putln("} else")
- code.putln("#endif")
- code.putln("{")
- args_tuple = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = PyTuple_New(%d+%s); %s" % (
- args_tuple, len(args), arg_offset_cname,
- code.error_goto_if_null(args_tuple, self.pos)))
- code.put_gotref(args_tuple)
- if len(args) > 1:
- code.putln("if (%s) {" % self_arg)
- code.putln("__Pyx_GIVEREF(%s); PyTuple_SET_ITEM(%s, 0, %s); %s = NULL;" % (
- self_arg, args_tuple, self_arg, self_arg)) # stealing owned ref in this case
- code.funcstate.release_temp(self_arg)
- if len(args) > 1:
- code.putln("}")
- for i, arg in enumerate(args):
- arg.make_owned_reference(code)
- code.put_giveref(arg.py_result())
- code.putln("PyTuple_SET_ITEM(%s, %d+%s, %s);" % (
- args_tuple, i, arg_offset_cname, arg.py_result()))
- if len(args) > 1:
- code.funcstate.release_temp(arg_offset_cname)
- for arg in args:
- arg.generate_post_assignment_code(code)
- arg.free_temps(code)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- function, args_tuple,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- code.put_decref_clear(args_tuple, py_object_type)
- code.funcstate.release_temp(args_tuple)
- if len(args) == 1:
- code.putln("}")
- code.putln("}") # !CYTHON_FAST_PYCALL
- if reuse_function_temp:
- self.function.generate_disposal_code(code)
- self.function.free_temps(code)
- else:
- code.put_decref_clear(function, py_object_type)
- code.funcstate.release_temp(function)
- class InlinedDefNodeCallNode(CallNode):
- # Inline call to defnode
- #
- # function PyCFunctionNode
- # function_name NameNode
- # args [ExprNode]
- subexprs = ['args', 'function_name']
- is_temp = 1
- type = py_object_type
- function = None
- function_name = None
- def can_be_inlined(self):
- func_type= self.function.def_node
- if func_type.star_arg or func_type.starstar_arg:
- return False
- if len(func_type.args) != len(self.args):
- return False
- if func_type.num_kwonly_args:
- return False # actually wrong number of arguments
- return True
- def analyse_types(self, env):
- self.function_name = self.function_name.analyse_types(env)
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- func_type = self.function.def_node
- actual_nargs = len(self.args)
- # Coerce arguments
- some_args_in_temps = False
- for i in range(actual_nargs):
- formal_type = func_type.args[i].type
- arg = self.args[i].coerce_to(formal_type, env)
- if arg.is_temp:
- if i > 0:
- # first argument in temp doesn't impact subsequent arguments
- some_args_in_temps = True
- elif arg.type.is_pyobject and not env.nogil:
- if arg.nonlocally_immutable():
- # plain local variables are ok
- pass
- else:
- # we do not safely own the argument's reference,
- # but we must make sure it cannot be collected
- # before we return from the function, so we create
- # an owned temp reference to it
- if i > 0: # first argument doesn't matter
- some_args_in_temps = True
- arg = arg.coerce_to_temp(env)
- self.args[i] = arg
- if some_args_in_temps:
- # if some args are temps and others are not, they may get
- # constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps (except
- # for the last argument, which is evaluated last in any
- # case)
- for i in range(actual_nargs-1):
- arg = self.args[i]
- if arg.nonlocally_immutable():
- # locals, C functions, unassignable types are safe.
- pass
- elif arg.type.is_cpp_class:
- # Assignment has side effects, avoid.
- pass
- elif env.nogil and arg.type.is_pyobject:
- # can't copy a Python reference into a temp in nogil
- # env (this is safe: a construction would fail in
- # nogil anyway)
- pass
- else:
- #self.args[i] = arg.coerce_to_temp(env)
- # instead: issue a warning
- if i > 0:
- warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
- break
- return self
- def generate_result_code(self, code):
- arg_code = [self.function_name.py_result()]
- func_type = self.function.def_node
- for arg, proto_arg in zip(self.args, func_type.args):
- if arg.type.is_pyobject:
- arg_code.append(arg.result_as(proto_arg.type))
- else:
- arg_code.append(arg.result())
- arg_code = ', '.join(arg_code)
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- self.function.def_node.entry.pyfunc_cname,
- arg_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class PythonCapiFunctionNode(ExprNode):
- subexprs = []
- def __init__(self, pos, py_name, cname, func_type, utility_code = None):
- ExprNode.__init__(self, pos, name=py_name, cname=cname,
- type=func_type, utility_code=utility_code)
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- if self.utility_code:
- code.globalstate.use_utility_code(self.utility_code)
- def calculate_result_code(self):
- return self.cname
- class PythonCapiCallNode(SimpleCallNode):
- # Python C-API Function call (only created in transforms)
- # By default, we assume that the call never returns None, as this
- # is true for most C-API functions in CPython. If this does not
- # apply to a call, set the following to True (or None to inherit
- # the default behaviour).
- may_return_none = False
- def __init__(self, pos, function_name, func_type,
- utility_code = None, py_name=None, **kwargs):
- self.type = func_type.return_type
- self.result_ctype = self.type
- self.function = PythonCapiFunctionNode(
- pos, py_name, function_name, func_type,
- utility_code = utility_code)
- # call this last so that we can override the constructed
- # attributes above with explicit keyword arguments if required
- SimpleCallNode.__init__(self, pos, **kwargs)
- class CachedBuiltinMethodCallNode(CallNode):
- # Python call to a method of a known Python builtin (only created in transforms)
- subexprs = ['obj', 'args']
- is_temp = True
- def __init__(self, call_node, obj, method_name, args):
- super(CachedBuiltinMethodCallNode, self).__init__(
- call_node.pos,
- obj=obj, method_name=method_name, args=args,
- may_return_none=call_node.may_return_none,
- type=call_node.type)
- def may_be_none(self):
- if self.may_return_none is not None:
- return self.may_return_none
- return ExprNode.may_be_none(self)
- def generate_result_code(self, code):
- type_cname = self.obj.type.cname
- obj_cname = self.obj.py_result()
- args = [arg.py_result() for arg in self.args]
- call_code = code.globalstate.cached_unbound_method_call_code(
- obj_cname, type_cname, self.method_name, args)
- code.putln("%s = %s; %s" % (
- self.result(), call_code,
- code.error_goto_if_null(self.result(), self.pos)
- ))
- code.put_gotref(self.result())
- class GeneralCallNode(CallNode):
- # General Python function call, including keyword,
- # * and ** arguments.
- #
- # function ExprNode
- # positional_args ExprNode Tuple of positional arguments
- # keyword_args ExprNode or None Dict of keyword arguments
- type = py_object_type
- subexprs = ['function', 'positional_args', 'keyword_args']
- nogil_check = Node.gil_error
- def compile_time_value(self, denv):
- function = self.function.compile_time_value(denv)
- positional_args = self.positional_args.compile_time_value(denv)
- keyword_args = self.keyword_args.compile_time_value(denv)
- try:
- return function(*positional_args, **keyword_args)
- except Exception as e:
- self.compile_time_value_error(e)
- def explicit_args_kwds(self):
- if (self.keyword_args and not self.keyword_args.is_dict_literal or
- not self.positional_args.is_sequence_constructor):
- raise CompileError(self.pos,
- 'Compile-time keyword arguments must be explicit.')
- return self.positional_args.args, self.keyword_args
- def analyse_types(self, env):
- if self.analyse_as_type_constructor(env):
- return self
- self.function = self.function.analyse_types(env)
- if not self.function.type.is_pyobject:
- if self.function.type.is_error:
- self.type = error_type
- return self
- if hasattr(self.function, 'entry'):
- node = self.map_to_simple_call_node()
- if node is not None and node is not self:
- return node.analyse_types(env)
- elif self.function.entry.as_variable:
- self.function = self.function.coerce_to_pyobject(env)
- elif node is self:
- error(self.pos,
- "Non-trivial keyword arguments and starred "
- "arguments not allowed in cdef functions.")
- else:
- # error was already reported
- pass
- else:
- self.function = self.function.coerce_to_pyobject(env)
- if self.keyword_args:
- self.keyword_args = self.keyword_args.analyse_types(env)
- self.positional_args = self.positional_args.analyse_types(env)
- self.positional_args = \
- self.positional_args.coerce_to_pyobject(env)
- self.set_py_result_type(self.function)
- self.is_temp = 1
- return self
- def map_to_simple_call_node(self):
- """
- Tries to map keyword arguments to declared positional arguments.
- Returns self to try a Python call, None to report an error
- or a SimpleCallNode if the mapping succeeds.
- """
- if not isinstance(self.positional_args, TupleNode):
- # has starred argument
- return self
- if not self.keyword_args.is_dict_literal:
- # keywords come from arbitrary expression => nothing to do here
- return self
- function = self.function
- entry = getattr(function, 'entry', None)
- if not entry:
- return self
- function_type = entry.type
- if function_type.is_ptr:
- function_type = function_type.base_type
- if not function_type.is_cfunction:
- return self
- pos_args = self.positional_args.args
- kwargs = self.keyword_args
- declared_args = function_type.args
- if entry.is_cmethod:
- declared_args = declared_args[1:] # skip 'self'
- if len(pos_args) > len(declared_args):
- error(self.pos, "function call got too many positional arguments, "
- "expected %d, got %s" % (len(declared_args),
- len(pos_args)))
- return None
- matched_args = set([ arg.name for arg in declared_args[:len(pos_args)]
- if arg.name ])
- unmatched_args = declared_args[len(pos_args):]
- matched_kwargs_count = 0
- args = list(pos_args)
- # check for duplicate keywords
- seen = set(matched_args)
- has_errors = False
- for arg in kwargs.key_value_pairs:
- name = arg.key.value
- if name in seen:
- error(arg.pos, "argument '%s' passed twice" % name)
- has_errors = True
- # continue to report more errors if there are any
- seen.add(name)
- # match keywords that are passed in order
- for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
- name = arg.key.value
- if decl_arg.name == name:
- matched_args.add(name)
- matched_kwargs_count += 1
- args.append(arg.value)
- else:
- break
- # match keyword arguments that are passed out-of-order, but keep
- # the evaluation of non-simple arguments in order by moving them
- # into temps
- from .UtilNodes import EvalWithTempExprNode, LetRefNode
- temps = []
- if len(kwargs.key_value_pairs) > matched_kwargs_count:
- unmatched_args = declared_args[len(args):]
- keywords = dict([ (arg.key.value, (i+len(pos_args), arg))
- for i, arg in enumerate(kwargs.key_value_pairs) ])
- first_missing_keyword = None
- for decl_arg in unmatched_args:
- name = decl_arg.name
- if name not in keywords:
- # missing keyword argument => either done or error
- if not first_missing_keyword:
- first_missing_keyword = name
- continue
- elif first_missing_keyword:
- if entry.as_variable:
- # we might be able to convert the function to a Python
- # object, which then allows full calling semantics
- # with default values in gaps - currently, we only
- # support optional arguments at the end
- return self
- # wasn't the last keyword => gaps are not supported
- error(self.pos, "C function call is missing "
- "argument '%s'" % first_missing_keyword)
- return None
- pos, arg = keywords[name]
- matched_args.add(name)
- matched_kwargs_count += 1
- if arg.value.is_simple():
- args.append(arg.value)
- else:
- temp = LetRefNode(arg.value)
- assert temp.is_simple()
- args.append(temp)
- temps.append((pos, temp))
- if temps:
- # may have to move preceding non-simple args into temps
- final_args = []
- new_temps = []
- first_temp_arg = temps[0][-1]
- for arg_value in args:
- if arg_value is first_temp_arg:
- break # done
- if arg_value.is_simple():
- final_args.append(arg_value)
- else:
- temp = LetRefNode(arg_value)
- new_temps.append(temp)
- final_args.append(temp)
- if new_temps:
- args = final_args
- temps = new_temps + [ arg for i,arg in sorted(temps) ]
- # check for unexpected keywords
- for arg in kwargs.key_value_pairs:
- name = arg.key.value
- if name not in matched_args:
- has_errors = True
- error(arg.pos,
- "C function got unexpected keyword argument '%s'" %
- name)
- if has_errors:
- # error was reported already
- return None
- # all keywords mapped to positional arguments
- # if we are missing arguments, SimpleCallNode will figure it out
- node = SimpleCallNode(self.pos, function=function, args=args)
- for temp in temps[::-1]:
- node = EvalWithTempExprNode(temp, node)
- return node
- def generate_result_code(self, code):
- if self.type.is_error: return
- if self.keyword_args:
- kwargs = self.keyword_args.py_result()
- else:
- kwargs = 'NULL'
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % (
- self.result(),
- self.function.py_result(),
- self.positional_args.py_result(),
- kwargs,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class AsTupleNode(ExprNode):
- # Convert argument to tuple. Used for normalising
- # the * argument of a function call.
- #
- # arg ExprNode
- subexprs = ['arg']
- is_temp = 1
- def calculate_constant_result(self):
- self.constant_result = tuple(self.arg.constant_result)
- def compile_time_value(self, denv):
- arg = self.arg.compile_time_value(denv)
- try:
- return tuple(arg)
- except Exception as e:
- self.compile_time_value_error(e)
- def analyse_types(self, env):
- self.arg = self.arg.analyse_types(env).coerce_to_pyobject(env)
- if self.arg.type is tuple_type:
- return self.arg.as_none_safe_node("'NoneType' object is not iterable")
- self.type = tuple_type
- return self
- def may_be_none(self):
- return False
- nogil_check = Node.gil_error
- gil_message = "Constructing Python tuple"
- def generate_result_code(self, code):
- cfunc = "__Pyx_PySequence_Tuple" if self.arg.type in (py_object_type, tuple_type) else "PySequence_Tuple"
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- cfunc, self.arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class MergedDictNode(ExprNode):
- # Helper class for keyword arguments and other merged dicts.
- #
- # keyword_args [DictNode or other ExprNode]
- subexprs = ['keyword_args']
- is_temp = 1
- type = dict_type
- reject_duplicates = True
- def calculate_constant_result(self):
- result = {}
- reject_duplicates = self.reject_duplicates
- for item in self.keyword_args:
- if item.is_dict_literal:
- # process items in order
- items = ((key.constant_result, value.constant_result)
- for key, value in item.key_value_pairs)
- else:
- items = item.constant_result.iteritems()
- for key, value in items:
- if reject_duplicates and key in result:
- raise ValueError("duplicate keyword argument found: %s" % key)
- result[key] = value
- self.constant_result = result
- def compile_time_value(self, denv):
- result = {}
- reject_duplicates = self.reject_duplicates
- for item in self.keyword_args:
- if item.is_dict_literal:
- # process items in order
- items = [(key.compile_time_value(denv), value.compile_time_value(denv))
- for key, value in item.key_value_pairs]
- else:
- items = item.compile_time_value(denv).iteritems()
- try:
- for key, value in items:
- if reject_duplicates and key in result:
- raise ValueError("duplicate keyword argument found: %s" % key)
- result[key] = value
- except Exception as e:
- self.compile_time_value_error(e)
- return result
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- return dict_type
- def analyse_types(self, env):
- self.keyword_args = [
- arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node(
- # FIXME: CPython's error message starts with the runtime function name
- 'argument after ** must be a mapping, not NoneType')
- for arg in self.keyword_args
- ]
- return self
- def may_be_none(self):
- return False
- gil_message = "Constructing Python dict"
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- args = iter(self.keyword_args)
- item = next(args)
- item.generate_evaluation_code(code)
- if item.type is not dict_type:
- # CPython supports calling functions with non-dicts, so do we
- code.putln('if (likely(PyDict_CheckExact(%s))) {' %
- item.py_result())
- if item.is_dict_literal:
- item.make_owned_reference(code)
- code.putln("%s = %s;" % (self.result(), item.py_result()))
- item.generate_post_assignment_code(code)
- else:
- code.putln("%s = PyDict_Copy(%s); %s" % (
- self.result(),
- item.py_result(),
- code.error_goto_if_null(self.result(), item.pos)))
- code.put_gotref(self.result())
- item.generate_disposal_code(code)
- if item.type is not dict_type:
- code.putln('} else {')
- code.putln("%s = PyObject_CallFunctionObjArgs((PyObject*)&PyDict_Type, %s, NULL); %s" % (
- self.result(),
- item.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- item.generate_disposal_code(code)
- code.putln('}')
- item.free_temps(code)
- helpers = set()
- for item in args:
- if item.is_dict_literal:
- # inline update instead of creating an intermediate dict
- for arg in item.key_value_pairs:
- arg.generate_evaluation_code(code)
- if self.reject_duplicates:
- code.putln("if (unlikely(PyDict_Contains(%s, %s))) {" % (
- self.result(),
- arg.key.py_result()))
- helpers.add("RaiseDoubleKeywords")
- # FIXME: find out function name at runtime!
- code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % (
- arg.key.py_result(),
- code.error_goto(self.pos)))
- code.putln("}")
- code.put_error_if_neg(arg.key.pos, "PyDict_SetItem(%s, %s, %s)" % (
- self.result(),
- arg.key.py_result(),
- arg.value.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- else:
- item.generate_evaluation_code(code)
- if self.reject_duplicates:
- # merge mapping into kwdict one by one as we need to check for duplicates
- helpers.add("MergeKeywords")
- code.put_error_if_neg(item.pos, "__Pyx_MergeKeywords(%s, %s)" % (
- self.result(), item.py_result()))
- else:
- # simple case, just add all entries
- helpers.add("RaiseMappingExpected")
- code.putln("if (unlikely(PyDict_Update(%s, %s) < 0)) {" % (
- self.result(), item.py_result()))
- code.putln("if (PyErr_ExceptionMatches(PyExc_AttributeError)) "
- "__Pyx_RaiseMappingExpectedError(%s);" % item.py_result())
- code.putln(code.error_goto(item.pos))
- code.putln("}")
- item.generate_disposal_code(code)
- item.free_temps(code)
- for helper in sorted(helpers):
- code.globalstate.use_utility_code(UtilityCode.load_cached(helper, "FunctionArguments.c"))
- def annotate(self, code):
- for item in self.keyword_args:
- item.annotate(code)
- class AttributeNode(ExprNode):
- # obj.attribute
- #
- # obj ExprNode
- # attribute string
- # needs_none_check boolean Used if obj is an extension type.
- # If set to True, it is known that the type is not None.
- #
- # Used internally:
- #
- # is_py_attr boolean Is a Python getattr operation
- # member string C name of struct member
- # is_called boolean Function call is being done on result
- # entry Entry Symbol table entry of attribute
- is_attribute = 1
- subexprs = ['obj']
- type = PyrexTypes.error_type
- entry = None
- is_called = 0
- needs_none_check = True
- is_memslice_transpose = False
- is_special_lookup = False
- is_py_attr = 0
- def as_cython_attribute(self):
- if (isinstance(self.obj, NameNode) and
- self.obj.is_cython_module and not
- self.attribute == u"parallel"):
- return self.attribute
- cy = self.obj.as_cython_attribute()
- if cy:
- return "%s.%s" % (cy, self.attribute)
- return None
- def coerce_to(self, dst_type, env):
- # If coercing to a generic pyobject and this is a cpdef function
- # we can create the corresponding attribute
- if dst_type is py_object_type:
- entry = self.entry
- if entry and entry.is_cfunction and entry.as_variable:
- # must be a cpdef function
- self.is_temp = 1
- self.entry = entry.as_variable
- self.analyse_as_python_attribute(env)
- return self
- return ExprNode.coerce_to(self, dst_type, env)
- def calculate_constant_result(self):
- attr = self.attribute
- if attr.startswith("__") and attr.endswith("__"):
- return
- self.constant_result = getattr(self.obj.constant_result, attr)
- def compile_time_value(self, denv):
- attr = self.attribute
- if attr.startswith("__") and attr.endswith("__"):
- error(self.pos,
- "Invalid attribute name '%s' in compile-time expression" % attr)
- return None
- obj = self.obj.compile_time_value(denv)
- try:
- return getattr(obj, attr)
- except Exception as e:
- self.compile_time_value_error(e)
- def type_dependencies(self, env):
- return self.obj.type_dependencies(env)
- def infer_type(self, env):
- # FIXME: this is way too redundant with analyse_types()
- node = self.analyse_as_cimported_attribute_node(env, target=False)
- if node is not None:
- if node.entry.type and node.entry.type.is_cfunction:
- # special-case - function converted to pointer
- return PyrexTypes.CPtrType(node.entry.type)
- else:
- return node.entry.type
- node = self.analyse_as_type_attribute(env)
- if node is not None:
- return node.entry.type
- obj_type = self.obj.infer_type(env)
- self.analyse_attribute(env, obj_type=obj_type)
- if obj_type.is_builtin_type and self.type.is_cfunction:
- # special case: C-API replacements for C methods of
- # builtin types cannot be inferred as C functions as
- # that would prevent their use as bound methods
- return py_object_type
- elif self.entry and self.entry.is_cmethod:
- # special case: bound methods should not be inferred
- # as their unbound method types
- return py_object_type
- return self.type
- def analyse_target_declaration(self, env):
- pass
- def analyse_target_types(self, env):
- node = self.analyse_types(env, target = 1)
- if node.type.is_const:
- error(self.pos, "Assignment to const attribute '%s'" % self.attribute)
- if not node.is_lvalue():
- error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
- return node
- def analyse_types(self, env, target = 0):
- self.initialized_check = env.directives['initializedcheck']
- node = self.analyse_as_cimported_attribute_node(env, target)
- if node is None and not target:
- node = self.analyse_as_type_attribute(env)
- if node is None:
- node = self.analyse_as_ordinary_attribute_node(env, target)
- assert node is not None
- if node.entry:
- node.entry.used = True
- if node.is_attribute:
- node.wrap_obj_in_nonecheck(env)
- return node
- def analyse_as_cimported_attribute_node(self, env, target):
- # Try to interpret this as a reference to an imported
- # C const, type, var or function. If successful, mutates
- # this node into a NameNode and returns 1, otherwise
- # returns 0.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and (
- entry.is_cglobal or entry.is_cfunction
- or entry.is_type or entry.is_const):
- return self.as_name_node(env, entry, target)
- if self.is_cimported_module_without_shadow(env):
- error(self.pos, "cimported module has no attribute '%s'" % self.attribute)
- return self
- return None
- def analyse_as_type_attribute(self, env):
- # Try to interpret this as a reference to an unbound
- # C method of an extension type or builtin type. If successful,
- # creates a corresponding NameNode and returns it, otherwise
- # returns None.
- if self.obj.is_string_literal:
- return
- type = self.obj.analyse_as_type(env)
- if type:
- if type.is_extension_type or type.is_builtin_type or type.is_cpp_class:
- entry = type.scope.lookup_here(self.attribute)
- if entry and (entry.is_cmethod or type.is_cpp_class and entry.type.is_cfunction):
- if type.is_builtin_type:
- if not self.is_called:
- # must handle this as Python object
- return None
- ubcm_entry = entry
- else:
- # Create a temporary entry describing the C method
- # as an ordinary function.
- if entry.func_cname and not hasattr(entry.type, 'op_arg_struct'):
- cname = entry.func_cname
- if entry.type.is_static_method or (
- env.parent_scope and env.parent_scope.is_cpp_class_scope):
- ctype = entry.type
- elif type.is_cpp_class:
- error(self.pos, "%s not a static member of %s" % (entry.name, type))
- ctype = PyrexTypes.error_type
- else:
- # Fix self type.
- ctype = copy.copy(entry.type)
- ctype.args = ctype.args[:]
- ctype.args[0] = PyrexTypes.CFuncTypeArg('self', type, 'self', None)
- else:
- cname = "%s->%s" % (type.vtabptr_cname, entry.cname)
- ctype = entry.type
- ubcm_entry = Symtab.Entry(entry.name, cname, ctype)
- ubcm_entry.is_cfunction = 1
- ubcm_entry.func_cname = entry.func_cname
- ubcm_entry.is_unbound_cmethod = 1
- ubcm_entry.scope = entry.scope
- return self.as_name_node(env, ubcm_entry, target=False)
- elif type.is_enum:
- if self.attribute in type.values:
- for entry in type.entry.enum_values:
- if entry.name == self.attribute:
- return self.as_name_node(env, entry, target=False)
- else:
- error(self.pos, "%s not a known value of %s" % (self.attribute, type))
- else:
- error(self.pos, "%s not a known value of %s" % (self.attribute, type))
- return None
- def analyse_as_type(self, env):
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- return module_scope.lookup_type(self.attribute)
- if not self.obj.is_string_literal:
- base_type = self.obj.analyse_as_type(env)
- if base_type and hasattr(base_type, 'scope') and base_type.scope is not None:
- return base_type.scope.lookup_type(self.attribute)
- return None
- def analyse_as_extension_type(self, env):
- # Try to interpret this as a reference to an extension type
- # in a cimported module. Returns the extension type, or None.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and entry.is_type:
- if entry.type.is_extension_type or entry.type.is_builtin_type:
- return entry.type
- return None
- def analyse_as_module(self, env):
- # Try to interpret this as a reference to a cimported module
- # in another cimported module. Returns the module scope, or None.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and entry.as_module:
- return entry.as_module
- return None
- def as_name_node(self, env, entry, target):
- # Create a corresponding NameNode from this node and complete the
- # analyse_types phase.
- node = NameNode.from_node(self, name=self.attribute, entry=entry)
- if target:
- node = node.analyse_target_types(env)
- else:
- node = node.analyse_rvalue_entry(env)
- node.entry.used = 1
- return node
- def analyse_as_ordinary_attribute_node(self, env, target):
- self.obj = self.obj.analyse_types(env)
- self.analyse_attribute(env)
- if self.entry and self.entry.is_cmethod and not self.is_called:
- # error(self.pos, "C method can only be called")
- pass
- ## Reference to C array turns into pointer to first element.
- #while self.type.is_array:
- # self.type = self.type.element_ptr_type()
- if self.is_py_attr:
- if not target:
- self.is_temp = 1
- self.result_ctype = py_object_type
- elif target and self.obj.type.is_builtin_type:
- error(self.pos, "Assignment to an immutable object field")
- #elif self.type.is_memoryviewslice and not target:
- # self.is_temp = True
- return self
- def analyse_attribute(self, env, obj_type = None):
- # Look up attribute and set self.type and self.member.
- immutable_obj = obj_type is not None # used during type inference
- self.is_py_attr = 0
- self.member = self.attribute
- if obj_type is None:
- if self.obj.type.is_string or self.obj.type.is_pyunicode_ptr:
- self.obj = self.obj.coerce_to_pyobject(env)
- obj_type = self.obj.type
- else:
- if obj_type.is_string or obj_type.is_pyunicode_ptr:
- obj_type = py_object_type
- if obj_type.is_ptr or obj_type.is_array:
- obj_type = obj_type.base_type
- self.op = "->"
- elif obj_type.is_extension_type or obj_type.is_builtin_type:
- self.op = "->"
- elif obj_type.is_reference and obj_type.is_fake_reference:
- self.op = "->"
- else:
- self.op = "."
- if obj_type.has_attributes:
- if obj_type.attributes_known():
- entry = obj_type.scope.lookup_here(self.attribute)
- if obj_type.is_memoryviewslice and not entry:
- if self.attribute == 'T':
- self.is_memslice_transpose = True
- self.is_temp = True
- self.use_managed_ref = True
- self.type = self.obj.type.transpose(self.pos)
- return
- else:
- obj_type.declare_attribute(self.attribute, env, self.pos)
- entry = obj_type.scope.lookup_here(self.attribute)
- if entry and entry.is_member:
- entry = None
- else:
- error(self.pos,
- "Cannot select attribute of incomplete type '%s'"
- % obj_type)
- self.type = PyrexTypes.error_type
- return
- self.entry = entry
- if entry:
- if obj_type.is_extension_type and entry.name == "__weakref__":
- error(self.pos, "Illegal use of special attribute __weakref__")
- # def methods need the normal attribute lookup
- # because they do not have struct entries
- # fused function go through assignment synthesis
- # (foo = pycfunction(foo_func_obj)) and need to go through
- # regular Python lookup as well
- if (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
- self.type = entry.type
- self.member = entry.cname
- return
- else:
- # If it's not a variable or C method, it must be a Python
- # method of an extension type, so we treat it like a Python
- # attribute.
- pass
- # If we get here, the base object is not a struct/union/extension
- # type, or it is an extension type and the attribute is either not
- # declared or is declared as a Python method. Treat it as a Python
- # attribute reference.
- self.analyse_as_python_attribute(env, obj_type, immutable_obj)
- def analyse_as_python_attribute(self, env, obj_type=None, immutable_obj=False):
- if obj_type is None:
- obj_type = self.obj.type
- # mangle private '__*' Python attributes used inside of a class
- self.attribute = env.mangle_class_private_name(self.attribute)
- self.member = self.attribute
- self.type = py_object_type
- self.is_py_attr = 1
- if not obj_type.is_pyobject and not obj_type.is_error:
- # Expose python methods for immutable objects.
- if (obj_type.is_string or obj_type.is_cpp_string
- or obj_type.is_buffer or obj_type.is_memoryviewslice
- or obj_type.is_numeric
- or (obj_type.is_ctuple and obj_type.can_coerce_to_pyobject(env))
- or (obj_type.is_struct and obj_type.can_coerce_to_pyobject(env))):
- if not immutable_obj:
- self.obj = self.obj.coerce_to_pyobject(env)
- elif (obj_type.is_cfunction and (self.obj.is_name or self.obj.is_attribute)
- and self.obj.entry.as_variable
- and self.obj.entry.as_variable.type.is_pyobject):
- # might be an optimised builtin function => unpack it
- if not immutable_obj:
- self.obj = self.obj.coerce_to_pyobject(env)
- else:
- error(self.pos,
- "Object of type '%s' has no attribute '%s'" %
- (obj_type, self.attribute))
- def wrap_obj_in_nonecheck(self, env):
- if not env.directives['nonecheck']:
- return
- msg = None
- format_args = ()
- if (self.obj.type.is_extension_type and self.needs_none_check and not
- self.is_py_attr):
- msg = "'NoneType' object has no attribute '%{0}s'".format('.30' if len(self.attribute) <= 30 else '')
- format_args = (self.attribute,)
- elif self.obj.type.is_memoryviewslice:
- if self.is_memslice_transpose:
- msg = "Cannot transpose None memoryview slice"
- else:
- entry = self.obj.type.scope.lookup_here(self.attribute)
- if entry:
- # copy/is_c_contig/shape/strides etc
- msg = "Cannot access '%s' attribute of None memoryview slice"
- format_args = (entry.name,)
- if msg:
- self.obj = self.obj.as_none_safe_node(msg, 'PyExc_AttributeError',
- format_args=format_args)
- def nogil_check(self, env):
- if self.is_py_attr:
- self.gil_error()
- gil_message = "Accessing Python attribute"
- def is_cimported_module_without_shadow(self, env):
- return self.obj.is_cimported_module_without_shadow(env)
- def is_simple(self):
- if self.obj:
- return self.result_in_temp() or self.obj.is_simple()
- else:
- return NameNode.is_simple(self)
- def is_lvalue(self):
- if self.obj:
- return True
- else:
- return NameNode.is_lvalue(self)
- def is_ephemeral(self):
- if self.obj:
- return self.obj.is_ephemeral()
- else:
- return NameNode.is_ephemeral(self)
- def calculate_result_code(self):
- #print "AttributeNode.calculate_result_code:", self.member ###
- #print "...obj node =", self.obj, "code", self.obj.result() ###
- #print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
- obj = self.obj
- obj_code = obj.result_as(obj.type)
- #print "...obj_code =", obj_code ###
- if self.entry and self.entry.is_cmethod:
- if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
- if self.entry.final_func_cname:
- return self.entry.final_func_cname
- if self.type.from_fused:
- # If the attribute was specialized through indexing, make
- # sure to get the right fused name, as our entry was
- # replaced by our parent index node
- # (AnalyseExpressionsTransform)
- self.member = self.entry.cname
- return "((struct %s *)%s%s%s)->%s" % (
- obj.type.vtabstruct_cname, obj_code, self.op,
- obj.type.vtabslot_cname, self.member)
- elif self.result_is_used:
- return self.member
- # Generating no code at all for unused access to optimised builtin
- # methods fixes the problem that some optimisations only exist as
- # macros, i.e. there is no function pointer to them, so we would
- # generate invalid C code here.
- return
- elif obj.type.is_complex:
- return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code)
- else:
- if obj.type.is_builtin_type and self.entry and self.entry.is_variable:
- # accessing a field of a builtin type, need to cast better than result_as() does
- obj_code = obj.type.cast_code(obj.result(), to_object_struct = True)
- return "%s%s%s" % (obj_code, self.op, self.member)
- def generate_result_code(self, code):
- if self.is_py_attr:
- if self.is_special_lookup:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c"))
- lookup_func_name = '__Pyx_PyObject_LookupSpecial'
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
- lookup_func_name = '__Pyx_PyObject_GetAttrStr'
- code.putln(
- '%s = %s(%s, %s); %s' % (
- self.result(),
- lookup_func_name,
- self.obj.py_result(),
- code.intern_identifier(self.attribute),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif self.type.is_memoryviewslice:
- if self.is_memslice_transpose:
- # transpose the slice
- for access, packing in self.type.axes:
- if access == 'ptr':
- error(self.pos, "Transposing not supported for slices "
- "with indirect dimensions")
- return
- code.putln("%s = %s;" % (self.result(), self.obj.result()))
- code.put_incref_memoryviewslice(self.result(), have_gil=True)
- T = "__pyx_memslice_transpose(&%s) == 0"
- code.putln(code.error_goto_if(T % self.result(), self.pos))
- elif self.initialized_check:
- code.putln(
- 'if (unlikely(!%s.memview)) {'
- 'PyErr_SetString(PyExc_AttributeError,'
- '"Memoryview is not initialized");'
- '%s'
- '}' % (self.result(), code.error_goto(self.pos)))
- else:
- # result_code contains what is needed, but we may need to insert
- # a check and raise an exception
- if self.obj.type and self.obj.type.is_extension_type:
- pass
- elif self.entry and self.entry.is_cmethod:
- # C method implemented as function call with utility code
- code.globalstate.use_entry_utility_code(self.entry)
- def generate_disposal_code(self, code):
- if self.is_temp and self.type.is_memoryviewslice and self.is_memslice_transpose:
- # mirror condition for putting the memview incref here:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=True)
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- ExprNode.generate_disposal_code(self, code)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- self.obj.generate_evaluation_code(code)
- if self.is_py_attr:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos,
- '__Pyx_PyObject_SetAttrStr(%s, %s, %s)' % (
- self.obj.py_result(),
- code.intern_identifier(self.attribute),
- rhs.py_result()))
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- elif self.obj.type.is_complex:
- code.putln("__Pyx_SET_C%s(%s, %s);" % (
- self.member.upper(),
- self.obj.result_as(self.obj.type),
- rhs.result_as(self.ctype())))
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- else:
- select_code = self.result()
- if self.type.is_pyobject and self.use_managed_ref:
- rhs.make_owned_reference(code)
- code.put_giveref(rhs.py_result())
- code.put_gotref(select_code)
- code.put_decref(select_code, self.ctype())
- elif self.type.is_memoryviewslice:
- from . import MemoryView
- MemoryView.put_assign_to_memviewslice(
- select_code, rhs, rhs.result(), self.type, code)
- if not self.type.is_memoryviewslice:
- code.putln(
- "%s = %s;" % (
- select_code,
- rhs.result_as(self.ctype())))
- #rhs.result()))
- rhs.generate_post_assignment_code(code)
- rhs.free_temps(code)
- self.obj.generate_disposal_code(code)
- self.obj.free_temps(code)
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- self.obj.generate_evaluation_code(code)
- if self.is_py_attr or (self.entry.scope.is_property_scope
- and u'__del__' in self.entry.scope.entries):
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos,
- '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
- self.obj.py_result(),
- code.intern_identifier(self.attribute)))
- else:
- error(self.pos, "Cannot delete C attribute of extension type")
- self.obj.generate_disposal_code(code)
- self.obj.free_temps(code)
- def annotate(self, code):
- if self.is_py_attr:
- style, text = 'py_attr', 'python attribute (%s)'
- else:
- style, text = 'c_attr', 'c attribute (%s)'
- code.annotate(self.pos, AnnotationItem(style, text % self.type, size=len(self.attribute)))
- #-------------------------------------------------------------------
- #
- # Constructor nodes
- #
- #-------------------------------------------------------------------
- class StarredUnpackingNode(ExprNode):
- # A starred expression like "*a"
- #
- # This is only allowed in sequence assignment or construction such as
- #
- # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4]
- #
- # and will be special cased during type analysis (or generate an error
- # if it's found at unexpected places).
- #
- # target ExprNode
- subexprs = ['target']
- is_starred = 1
- type = py_object_type
- is_temp = 1
- starred_expr_allowed_here = False
- def __init__(self, pos, target):
- ExprNode.__init__(self, pos, target=target)
- def analyse_declarations(self, env):
- if not self.starred_expr_allowed_here:
- error(self.pos, "starred expression is not allowed here")
- self.target.analyse_declarations(env)
- def infer_type(self, env):
- return self.target.infer_type(env)
- def analyse_types(self, env):
- if not self.starred_expr_allowed_here:
- error(self.pos, "starred expression is not allowed here")
- self.target = self.target.analyse_types(env)
- self.type = self.target.type
- return self
- def analyse_target_declaration(self, env):
- self.target.analyse_target_declaration(env)
- def analyse_target_types(self, env):
- self.target = self.target.analyse_target_types(env)
- self.type = self.target.type
- return self
- def calculate_result_code(self):
- return ""
- def generate_result_code(self, code):
- pass
- class SequenceNode(ExprNode):
- # Base class for list and tuple constructor nodes.
- # Contains common code for performing sequence unpacking.
- #
- # args [ExprNode]
- # unpacked_items [ExprNode] or None
- # coerced_unpacked_items [ExprNode] or None
- # mult_factor ExprNode the integer number of content repetitions ([1,2]*3)
- subexprs = ['args', 'mult_factor']
- is_sequence_constructor = 1
- unpacked_items = None
- mult_factor = None
- slow = False # trade speed for code size (e.g. use PyTuple_Pack())
- def compile_time_value_list(self, denv):
- return [arg.compile_time_value(denv) for arg in self.args]
- def replace_starred_target_node(self):
- # replace a starred node in the targets by the contained expression
- self.starred_assignment = False
- args = []
- for arg in self.args:
- if arg.is_starred:
- if self.starred_assignment:
- error(arg.pos, "more than 1 starred expression in assignment")
- self.starred_assignment = True
- arg = arg.target
- arg.is_starred = True
- args.append(arg)
- self.args = args
- def analyse_target_declaration(self, env):
- self.replace_starred_target_node()
- for arg in self.args:
- arg.analyse_target_declaration(env)
- def analyse_types(self, env, skip_children=False):
- for i, arg in enumerate(self.args):
- if not skip_children:
- arg = arg.analyse_types(env)
- self.args[i] = arg.coerce_to_pyobject(env)
- if self.mult_factor:
- self.mult_factor = self.mult_factor.analyse_types(env)
- if not self.mult_factor.type.is_int:
- self.mult_factor = self.mult_factor.coerce_to_pyobject(env)
- self.is_temp = 1
- # not setting self.type here, subtypes do this
- return self
- def coerce_to_ctuple(self, dst_type, env):
- if self.type == dst_type:
- return self
- assert not self.mult_factor
- if len(self.args) != dst_type.size:
- error(self.pos, "trying to coerce sequence to ctuple of wrong length, expected %d, got %d" % (
- dst_type.size, len(self.args)))
- coerced_args = [arg.coerce_to(type, env) for arg, type in zip(self.args, dst_type.components)]
- return TupleNode(self.pos, args=coerced_args, type=dst_type, is_temp=True)
- def _create_merge_node_if_necessary(self, env):
- self._flatten_starred_args()
- if not any(arg.is_starred for arg in self.args):
- return self
- # convert into MergedSequenceNode by building partial sequences
- args = []
- values = []
- for arg in self.args:
- if arg.is_starred:
- if values:
- args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True))
- values = []
- args.append(arg.target)
- else:
- values.append(arg)
- if values:
- args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True))
- node = MergedSequenceNode(self.pos, args, self.type)
- if self.mult_factor:
- node = binop_node(
- self.pos, '*', node, self.mult_factor.coerce_to_pyobject(env),
- inplace=True, type=self.type, is_temp=True)
- return node
- def _flatten_starred_args(self):
- args = []
- for arg in self.args:
- if arg.is_starred and arg.target.is_sequence_constructor and not arg.target.mult_factor:
- args.extend(arg.target.args)
- else:
- args.append(arg)
- self.args[:] = args
- def may_be_none(self):
- return False
- def analyse_target_types(self, env):
- if self.mult_factor:
- error(self.pos, "can't assign to multiplied sequence")
- self.unpacked_items = []
- self.coerced_unpacked_items = []
- self.any_coerced_items = False
- for i, arg in enumerate(self.args):
- arg = self.args[i] = arg.analyse_target_types(env)
- if arg.is_starred:
- if not arg.type.assignable_from(list_type):
- error(arg.pos,
- "starred target must have Python object (list) type")
- if arg.type is py_object_type:
- arg.type = list_type
- unpacked_item = PyTempNode(self.pos, env)
- coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env)
- if unpacked_item is not coerced_unpacked_item:
- self.any_coerced_items = True
- self.unpacked_items.append(unpacked_item)
- self.coerced_unpacked_items.append(coerced_unpacked_item)
- self.type = py_object_type
- return self
- def generate_result_code(self, code):
- self.generate_operation_code(code)
- def generate_sequence_packing_code(self, code, target=None, plain=False):
- if target is None:
- target = self.result()
- size_factor = c_mult = ''
- mult_factor = None
- if self.mult_factor and not plain:
- mult_factor = self.mult_factor
- if mult_factor.type.is_int:
- c_mult = mult_factor.result()
- if (isinstance(mult_factor.constant_result, _py_int_types) and
- mult_factor.constant_result > 0):
- size_factor = ' * %s' % mult_factor.constant_result
- elif mult_factor.type.signed:
- size_factor = ' * ((%s<0) ? 0:%s)' % (c_mult, c_mult)
- else:
- size_factor = ' * (%s)' % (c_mult,)
- if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult:
- # use PyTuple_Pack() to avoid generating huge amounts of one-time code
- code.putln('%s = PyTuple_Pack(%d, %s); %s' % (
- target,
- len(self.args),
- ', '.join(arg.py_result() for arg in self.args),
- code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
- elif self.type.is_ctuple:
- for i, arg in enumerate(self.args):
- code.putln("%s.f%s = %s;" % (
- target, i, arg.result()))
- else:
- # build the tuple/list step by step, potentially multiplying it as we go
- if self.type is list_type:
- create_func, set_item_func = 'PyList_New', 'PyList_SET_ITEM'
- elif self.type is tuple_type:
- create_func, set_item_func = 'PyTuple_New', 'PyTuple_SET_ITEM'
- else:
- raise InternalError("sequence packing for unexpected type %s" % self.type)
- arg_count = len(self.args)
- code.putln("%s = %s(%s%s); %s" % (
- target, create_func, arg_count, size_factor,
- code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
- if c_mult:
- # FIXME: can't use a temp variable here as the code may
- # end up in the constant building function. Temps
- # currently don't work there.
- #counter = code.funcstate.allocate_temp(mult_factor.type, manage_ref=False)
- counter = Naming.quick_temp_cname
- code.putln('{ Py_ssize_t %s;' % counter)
- if arg_count == 1:
- offset = counter
- else:
- offset = '%s * %s' % (counter, arg_count)
- code.putln('for (%s=0; %s < %s; %s++) {' % (
- counter, counter, c_mult, counter
- ))
- else:
- offset = ''
- for i in range(arg_count):
- arg = self.args[i]
- if c_mult or not arg.result_in_temp():
- code.put_incref(arg.result(), arg.ctype())
- code.put_giveref(arg.py_result())
- code.putln("%s(%s, %s, %s);" % (
- set_item_func,
- target,
- (offset and i) and ('%s + %s' % (offset, i)) or (offset or i),
- arg.py_result()))
- if c_mult:
- code.putln('}')
- #code.funcstate.release_temp(counter)
- code.putln('}')
- if mult_factor is not None and mult_factor.type.is_pyobject:
- code.putln('{ PyObject* %s = PyNumber_InPlaceMultiply(%s, %s); %s' % (
- Naming.quick_temp_cname, target, mult_factor.py_result(),
- code.error_goto_if_null(Naming.quick_temp_cname, self.pos)
- ))
- code.put_gotref(Naming.quick_temp_cname)
- code.put_decref(target, py_object_type)
- code.putln('%s = %s;' % (target, Naming.quick_temp_cname))
- code.putln('}')
- def generate_subexpr_disposal_code(self, code):
- if self.mult_factor and self.mult_factor.type.is_int:
- super(SequenceNode, self).generate_subexpr_disposal_code(code)
- elif self.type is tuple_type and (self.is_literal or self.slow):
- super(SequenceNode, self).generate_subexpr_disposal_code(code)
- else:
- # We call generate_post_assignment_code here instead
- # of generate_disposal_code, because values were stored
- # in the tuple using a reference-stealing operation.
- for arg in self.args:
- arg.generate_post_assignment_code(code)
- # Should NOT call free_temps -- this is invoked by the default
- # generate_evaluation_code which will do that.
- if self.mult_factor:
- self.mult_factor.generate_disposal_code(code)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- if self.starred_assignment:
- self.generate_starred_assignment_code(rhs, code)
- else:
- self.generate_parallel_assignment_code(rhs, code)
- for item in self.unpacked_items:
- item.release(code)
- rhs.free_temps(code)
- _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
- PyrexTypes.py_object_type, [
- PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
- ]))
- def generate_parallel_assignment_code(self, rhs, code):
- # Need to work around the fact that generate_evaluation_code
- # allocates the temps in a rather hacky way -- the assignment
- # is evaluated twice, within each if-block.
- for item in self.unpacked_items:
- item.allocate(code)
- special_unpack = (rhs.type is py_object_type
- or rhs.type in (tuple_type, list_type)
- or not rhs.type.is_builtin_type)
- long_enough_for_a_loop = len(self.unpacked_items) > 3
- if special_unpack:
- self.generate_special_parallel_unpacking_code(
- code, rhs, use_loop=long_enough_for_a_loop)
- else:
- code.putln("{")
- self.generate_generic_parallel_unpacking_code(
- code, rhs, self.unpacked_items, use_loop=long_enough_for_a_loop)
- code.putln("}")
- for value_node in self.coerced_unpacked_items:
- value_node.generate_evaluation_code(code)
- for i in range(len(self.args)):
- self.args[i].generate_assignment_code(
- self.coerced_unpacked_items[i], code)
- def generate_special_parallel_unpacking_code(self, code, rhs, use_loop):
- sequence_type_test = '1'
- none_check = "likely(%s != Py_None)" % rhs.py_result()
- if rhs.type is list_type:
- sequence_types = ['List']
- if rhs.may_be_none():
- sequence_type_test = none_check
- elif rhs.type is tuple_type:
- sequence_types = ['Tuple']
- if rhs.may_be_none():
- sequence_type_test = none_check
- else:
- sequence_types = ['Tuple', 'List']
- tuple_check = 'likely(PyTuple_CheckExact(%s))' % rhs.py_result()
- list_check = 'PyList_CheckExact(%s)' % rhs.py_result()
- sequence_type_test = "(%s) || (%s)" % (tuple_check, list_check)
- code.putln("if (%s) {" % sequence_type_test)
- code.putln("PyObject* sequence = %s;" % rhs.py_result())
- # list/tuple => check size
- code.putln("Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);")
- code.putln("if (unlikely(size != %d)) {" % len(self.args))
- code.globalstate.use_utility_code(raise_too_many_values_to_unpack)
- code.putln("if (size > %d) __Pyx_RaiseTooManyValuesError(%d);" % (
- len(self.args), len(self.args)))
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.putln("else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);")
- # < 0 => exception
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS")
- # unpack items from list/tuple in unrolled loop (can't fail)
- if len(sequence_types) == 2:
- code.putln("if (likely(Py%s_CheckExact(sequence))) {" % sequence_types[0])
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
- item.result(), sequence_types[0], i))
- if len(sequence_types) == 2:
- code.putln("} else {")
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
- item.result(), sequence_types[1], i))
- code.putln("}")
- for item in self.unpacked_items:
- code.put_incref(item.result(), item.ctype())
- code.putln("#else")
- # in non-CPython, use the PySequence protocol (which can fail)
- if not use_loop:
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = PySequence_ITEM(sequence, %d); %s" % (
- item.result(), i,
- code.error_goto_if_null(item.result(), self.pos)))
- code.put_gotref(item.result())
- else:
- code.putln("{")
- code.putln("Py_ssize_t i;")
- code.putln("PyObject** temps[%s] = {%s};" % (
- len(self.unpacked_items),
- ','.join(['&%s' % item.result() for item in self.unpacked_items])))
- code.putln("for (i=0; i < %s; i++) {" % len(self.unpacked_items))
- code.putln("PyObject* item = PySequence_ITEM(sequence, i); %s" % (
- code.error_goto_if_null('item', self.pos)))
- code.put_gotref('item')
- code.putln("*(temps[i]) = item;")
- code.putln("}")
- code.putln("}")
- code.putln("#endif")
- rhs.generate_disposal_code(code)
- if sequence_type_test == '1':
- code.putln("}") # all done
- elif sequence_type_test == none_check:
- # either tuple/list or None => save some code by generating the error directly
- code.putln("} else {")
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("RaiseNoneIterError", "ObjectHandling.c"))
- code.putln("__Pyx_RaiseNoneNotIterableError(); %s" % code.error_goto(self.pos))
- code.putln("}") # all done
- else:
- code.putln("} else {") # needs iteration fallback code
- self.generate_generic_parallel_unpacking_code(
- code, rhs, self.unpacked_items, use_loop=use_loop)
- code.putln("}")
- def generate_generic_parallel_unpacking_code(self, code, rhs, unpacked_items, use_loop, terminate=True):
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.globalstate.use_utility_code(UtilityCode.load_cached("IterFinish", "ObjectHandling.c"))
- code.putln("Py_ssize_t index = -1;") # must be at the start of a C block!
- if use_loop:
- code.putln("PyObject** temps[%s] = {%s};" % (
- len(self.unpacked_items),
- ','.join(['&%s' % item.result() for item in unpacked_items])))
- iterator_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln(
- "%s = PyObject_GetIter(%s); %s" % (
- iterator_temp,
- rhs.py_result(),
- code.error_goto_if_null(iterator_temp, self.pos)))
- code.put_gotref(iterator_temp)
- rhs.generate_disposal_code(code)
- iternext_func = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- code.putln("%s = Py_TYPE(%s)->tp_iternext;" % (
- iternext_func, iterator_temp))
- unpacking_error_label = code.new_label('unpacking_failed')
- unpack_code = "%s(%s)" % (iternext_func, iterator_temp)
- if use_loop:
- code.putln("for (index=0; index < %s; index++) {" % len(unpacked_items))
- code.put("PyObject* item = %s; if (unlikely(!item)) " % unpack_code)
- code.put_goto(unpacking_error_label)
- code.put_gotref("item")
- code.putln("*(temps[index]) = item;")
- code.putln("}")
- else:
- for i, item in enumerate(unpacked_items):
- code.put(
- "index = %d; %s = %s; if (unlikely(!%s)) " % (
- i,
- item.result(),
- unpack_code,
- item.result()))
- code.put_goto(unpacking_error_label)
- code.put_gotref(item.py_result())
- if terminate:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("UnpackItemEndCheck", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos, "__Pyx_IternextUnpackEndCheck(%s, %d)" % (
- unpack_code,
- len(unpacked_items)))
- code.putln("%s = NULL;" % iternext_func)
- code.put_decref_clear(iterator_temp, py_object_type)
- unpacking_done_label = code.new_label('unpacking_done')
- code.put_goto(unpacking_done_label)
- code.put_label(unpacking_error_label)
- code.put_decref_clear(iterator_temp, py_object_type)
- code.putln("%s = NULL;" % iternext_func)
- code.putln("if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);")
- code.putln(code.error_goto(self.pos))
- code.put_label(unpacking_done_label)
- code.funcstate.release_temp(iternext_func)
- if terminate:
- code.funcstate.release_temp(iterator_temp)
- iterator_temp = None
- return iterator_temp
- def generate_starred_assignment_code(self, rhs, code):
- for i, arg in enumerate(self.args):
- if arg.is_starred:
- starred_target = self.unpacked_items[i]
- unpacked_fixed_items_left = self.unpacked_items[:i]
- unpacked_fixed_items_right = self.unpacked_items[i+1:]
- break
- else:
- assert False
- iterator_temp = None
- if unpacked_fixed_items_left:
- for item in unpacked_fixed_items_left:
- item.allocate(code)
- code.putln('{')
- iterator_temp = self.generate_generic_parallel_unpacking_code(
- code, rhs, unpacked_fixed_items_left,
- use_loop=True, terminate=False)
- for i, item in enumerate(unpacked_fixed_items_left):
- value_node = self.coerced_unpacked_items[i]
- value_node.generate_evaluation_code(code)
- code.putln('}')
- starred_target.allocate(code)
- target_list = starred_target.result()
- code.putln("%s = PySequence_List(%s); %s" % (
- target_list,
- iterator_temp or rhs.py_result(),
- code.error_goto_if_null(target_list, self.pos)))
- code.put_gotref(target_list)
- if iterator_temp:
- code.put_decref_clear(iterator_temp, py_object_type)
- code.funcstate.release_temp(iterator_temp)
- else:
- rhs.generate_disposal_code(code)
- if unpacked_fixed_items_right:
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- length_temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- code.putln('%s = PyList_GET_SIZE(%s);' % (length_temp, target_list))
- code.putln("if (unlikely(%s < %d)) {" % (length_temp, len(unpacked_fixed_items_right)))
- code.putln("__Pyx_RaiseNeedMoreValuesError(%d+%s); %s" % (
- len(unpacked_fixed_items_left), length_temp,
- code.error_goto(self.pos)))
- code.putln('}')
- for item in unpacked_fixed_items_right[::-1]:
- item.allocate(code)
- for i, (item, coerced_arg) in enumerate(zip(unpacked_fixed_items_right[::-1],
- self.coerced_unpacked_items[::-1])):
- code.putln('#if CYTHON_COMPILING_IN_CPYTHON')
- code.putln("%s = PyList_GET_ITEM(%s, %s-%d); " % (
- item.py_result(), target_list, length_temp, i+1))
- # resize the list the hard way
- code.putln("((PyVarObject*)%s)->ob_size--;" % target_list)
- code.putln('#else')
- code.putln("%s = PySequence_ITEM(%s, %s-%d); " % (
- item.py_result(), target_list, length_temp, i+1))
- code.putln('#endif')
- code.put_gotref(item.py_result())
- coerced_arg.generate_evaluation_code(code)
- code.putln('#if !CYTHON_COMPILING_IN_CPYTHON')
- sublist_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln('%s = PySequence_GetSlice(%s, 0, %s-%d); %s' % (
- sublist_temp, target_list, length_temp, len(unpacked_fixed_items_right),
- code.error_goto_if_null(sublist_temp, self.pos)))
- code.put_gotref(sublist_temp)
- code.funcstate.release_temp(length_temp)
- code.put_decref(target_list, py_object_type)
- code.putln('%s = %s; %s = NULL;' % (target_list, sublist_temp, sublist_temp))
- code.putln('#else')
- code.putln('(void)%s;' % sublist_temp) # avoid warning about unused variable
- code.funcstate.release_temp(sublist_temp)
- code.putln('#endif')
- for i, arg in enumerate(self.args):
- arg.generate_assignment_code(self.coerced_unpacked_items[i], code)
- def annotate(self, code):
- for arg in self.args:
- arg.annotate(code)
- if self.unpacked_items:
- for arg in self.unpacked_items:
- arg.annotate(code)
- for arg in self.coerced_unpacked_items:
- arg.annotate(code)
- class TupleNode(SequenceNode):
- # Tuple constructor.
- type = tuple_type
- is_partly_literal = False
- gil_message = "Constructing Python tuple"
- def infer_type(self, env):
- if self.mult_factor or not self.args:
- return tuple_type
- arg_types = [arg.infer_type(env) for arg in self.args]
- if any(type.is_pyobject or type.is_memoryviewslice or type.is_unspecified or type.is_fused
- for type in arg_types):
- return tuple_type
- return env.declare_tuple_type(self.pos, arg_types).type
- def analyse_types(self, env, skip_children=False):
- if len(self.args) == 0:
- self.is_temp = False
- self.is_literal = True
- return self
- if not skip_children:
- for i, arg in enumerate(self.args):
- if arg.is_starred:
- arg.starred_expr_allowed_here = True
- self.args[i] = arg.analyse_types(env)
- if (not self.mult_factor and
- not any((arg.is_starred or arg.type.is_pyobject or arg.type.is_memoryviewslice or arg.type.is_fused)
- for arg in self.args)):
- self.type = env.declare_tuple_type(self.pos, (arg.type for arg in self.args)).type
- self.is_temp = 1
- return self
- node = SequenceNode.analyse_types(self, env, skip_children=True)
- node = node._create_merge_node_if_necessary(env)
- if not node.is_sequence_constructor:
- return node
- if not all(child.is_literal for child in node.args):
- return node
- if not node.mult_factor or (
- node.mult_factor.is_literal and
- isinstance(node.mult_factor.constant_result, _py_int_types)):
- node.is_temp = False
- node.is_literal = True
- else:
- if not node.mult_factor.type.is_pyobject:
- node.mult_factor = node.mult_factor.coerce_to_pyobject(env)
- node.is_temp = True
- node.is_partly_literal = True
- return node
- def analyse_as_type(self, env):
- # ctuple type
- if not self.args:
- return None
- item_types = [arg.analyse_as_type(env) for arg in self.args]
- if any(t is None for t in item_types):
- return None
- entry = env.declare_tuple_type(self.pos, item_types)
- return entry.type
- def coerce_to(self, dst_type, env):
- if self.type.is_ctuple:
- if dst_type.is_ctuple and self.type.size == dst_type.size:
- return self.coerce_to_ctuple(dst_type, env)
- elif dst_type is tuple_type or dst_type is py_object_type:
- coerced_args = [arg.coerce_to_pyobject(env) for arg in self.args]
- return TupleNode(self.pos, args=coerced_args, type=tuple_type, is_temp=1).analyse_types(env, skip_children=True)
- else:
- return self.coerce_to_pyobject(env).coerce_to(dst_type, env)
- elif dst_type.is_ctuple and not self.mult_factor:
- return self.coerce_to_ctuple(dst_type, env)
- else:
- return SequenceNode.coerce_to(self, dst_type, env)
- def as_list(self):
- t = ListNode(self.pos, args=self.args, mult_factor=self.mult_factor)
- if isinstance(self.constant_result, tuple):
- t.constant_result = list(self.constant_result)
- return t
- def is_simple(self):
- # either temp or constant => always simple
- return True
- def nonlocally_immutable(self):
- # either temp or constant => always safe
- return True
- def calculate_result_code(self):
- if len(self.args) > 0:
- return self.result_code
- else:
- return Naming.empty_tuple
- def calculate_constant_result(self):
- self.constant_result = tuple([
- arg.constant_result for arg in self.args])
- def compile_time_value(self, denv):
- values = self.compile_time_value_list(denv)
- try:
- return tuple(values)
- except Exception as e:
- self.compile_time_value_error(e)
- def generate_operation_code(self, code):
- if len(self.args) == 0:
- # result_code is Naming.empty_tuple
- return
- if self.is_literal or self.is_partly_literal:
- # The "mult_factor" is part of the deduplication if it is also constant, i.e. when
- # we deduplicate the multiplied result. Otherwise, only deduplicate the constant part.
- dedup_key = make_dedup_key(self.type, [self.mult_factor if self.is_literal else None] + self.args)
- tuple_target = code.get_py_const(py_object_type, 'tuple', cleanup_level=2, dedup_key=dedup_key)
- const_code = code.get_cached_constants_writer(tuple_target)
- if const_code is not None:
- # constant is not yet initialised
- const_code.mark_pos(self.pos)
- self.generate_sequence_packing_code(const_code, tuple_target, plain=not self.is_literal)
- const_code.put_giveref(tuple_target)
- if self.is_literal:
- self.result_code = tuple_target
- else:
- code.putln('%s = PyNumber_Multiply(%s, %s); %s' % (
- self.result(), tuple_target, self.mult_factor.py_result(),
- code.error_goto_if_null(self.result(), self.pos)
- ))
- code.put_gotref(self.py_result())
- else:
- self.type.entry.used = True
- self.generate_sequence_packing_code(code)
- class ListNode(SequenceNode):
- # List constructor.
- # obj_conversion_errors [PyrexError] used internally
- # orignial_args [ExprNode] used internally
- obj_conversion_errors = []
- type = list_type
- in_module_scope = False
- gil_message = "Constructing Python list"
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- # TODO: Infer non-object list arrays.
- return list_type
- def analyse_expressions(self, env):
- for arg in self.args:
- if arg.is_starred:
- arg.starred_expr_allowed_here = True
- node = SequenceNode.analyse_expressions(self, env)
- return node.coerce_to_pyobject(env)
- def analyse_types(self, env):
- with local_errors(ignore=True) as errors:
- self.original_args = list(self.args)
- node = SequenceNode.analyse_types(self, env)
- node.obj_conversion_errors = errors
- if env.is_module_scope:
- self.in_module_scope = True
- node = node._create_merge_node_if_necessary(env)
- return node
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject:
- for err in self.obj_conversion_errors:
- report_error(err)
- self.obj_conversion_errors = []
- if not self.type.subtype_of(dst_type):
- error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
- elif (dst_type.is_array or dst_type.is_ptr) and dst_type.base_type is not PyrexTypes.c_void_type:
- array_length = len(self.args)
- if self.mult_factor:
- if isinstance(self.mult_factor.constant_result, _py_int_types):
- if self.mult_factor.constant_result <= 0:
- error(self.pos, "Cannot coerce non-positively multiplied list to '%s'" % dst_type)
- else:
- array_length *= self.mult_factor.constant_result
- else:
- error(self.pos, "Cannot coerce dynamically multiplied list to '%s'" % dst_type)
- base_type = dst_type.base_type
- self.type = PyrexTypes.CArrayType(base_type, array_length)
- for i in range(len(self.original_args)):
- arg = self.args[i]
- if isinstance(arg, CoerceToPyTypeNode):
- arg = arg.arg
- self.args[i] = arg.coerce_to(base_type, env)
- elif dst_type.is_cpp_class:
- # TODO(robertwb): Avoid object conversion for vector/list/set.
- return TypecastNode(self.pos, operand=self, type=PyrexTypes.py_object_type).coerce_to(dst_type, env)
- elif self.mult_factor:
- error(self.pos, "Cannot coerce multiplied list to '%s'" % dst_type)
- elif dst_type.is_struct:
- if len(self.args) > len(dst_type.scope.var_entries):
- error(self.pos, "Too many members for '%s'" % dst_type)
- else:
- if len(self.args) < len(dst_type.scope.var_entries):
- warning(self.pos, "Too few members for '%s'" % dst_type, 1)
- for i, (arg, member) in enumerate(zip(self.original_args, dst_type.scope.var_entries)):
- if isinstance(arg, CoerceToPyTypeNode):
- arg = arg.arg
- self.args[i] = arg.coerce_to(member.type, env)
- self.type = dst_type
- elif dst_type.is_ctuple:
- return self.coerce_to_ctuple(dst_type, env)
- else:
- self.type = error_type
- error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
- return self
- def as_list(self): # dummy for compatibility with TupleNode
- return self
- def as_tuple(self):
- t = TupleNode(self.pos, args=self.args, mult_factor=self.mult_factor)
- if isinstance(self.constant_result, list):
- t.constant_result = tuple(self.constant_result)
- return t
- def allocate_temp_result(self, code):
- if self.type.is_array:
- if self.in_module_scope:
- self.temp_code = code.funcstate.allocate_temp(
- self.type, manage_ref=False, static=True, reusable=False)
- else:
- # To be valid C++, we must allocate the memory on the stack
- # manually and be sure not to reuse it for something else.
- # Yes, this means that we leak a temp array variable.
- self.temp_code = code.funcstate.allocate_temp(
- self.type, manage_ref=False, reusable=False)
- else:
- SequenceNode.allocate_temp_result(self, code)
- def calculate_constant_result(self):
- if self.mult_factor:
- raise ValueError() # may exceed the compile time memory
- self.constant_result = [
- arg.constant_result for arg in self.args]
- def compile_time_value(self, denv):
- l = self.compile_time_value_list(denv)
- if self.mult_factor:
- l *= self.mult_factor.compile_time_value(denv)
- return l
- def generate_operation_code(self, code):
- if self.type.is_pyobject:
- for err in self.obj_conversion_errors:
- report_error(err)
- self.generate_sequence_packing_code(code)
- elif self.type.is_array:
- if self.mult_factor:
- code.putln("{")
- code.putln("Py_ssize_t %s;" % Naming.quick_temp_cname)
- code.putln("for ({i} = 0; {i} < {count}; {i}++) {{".format(
- i=Naming.quick_temp_cname, count=self.mult_factor.result()))
- offset = '+ (%d * %s)' % (len(self.args), Naming.quick_temp_cname)
- else:
- offset = ''
- for i, arg in enumerate(self.args):
- if arg.type.is_array:
- code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStringH", "StringTools.c"))
- code.putln("memcpy(&(%s[%s%s]), %s, sizeof(%s[0]));" % (
- self.result(), i, offset,
- arg.result(), self.result()
- ))
- else:
- code.putln("%s[%s%s] = %s;" % (
- self.result(),
- i,
- offset,
- arg.result()))
- if self.mult_factor:
- code.putln("}")
- code.putln("}")
- elif self.type.is_struct:
- for arg, member in zip(self.args, self.type.scope.var_entries):
- code.putln("%s.%s = %s;" % (
- self.result(),
- member.cname,
- arg.result()))
- else:
- raise InternalError("List type never specified")
- class ScopedExprNode(ExprNode):
- # Abstract base class for ExprNodes that have their own local
- # scope, such as generator expressions.
- #
- # expr_scope Scope the inner scope of the expression
- subexprs = []
- expr_scope = None
- # does this node really have a local scope, e.g. does it leak loop
- # variables or not? non-leaking Py3 behaviour is default, except
- # for list comprehensions where the behaviour differs in Py2 and
- # Py3 (set in Parsing.py based on parser context)
- has_local_scope = True
- def init_scope(self, outer_scope, expr_scope=None):
- if expr_scope is not None:
- self.expr_scope = expr_scope
- elif self.has_local_scope:
- self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
- else:
- self.expr_scope = None
- def analyse_declarations(self, env):
- self.init_scope(env)
- def analyse_scoped_declarations(self, env):
- # this is called with the expr_scope as env
- pass
- def analyse_types(self, env):
- # no recursion here, the children will be analysed separately below
- return self
- def analyse_scoped_expressions(self, env):
- # this is called with the expr_scope as env
- return self
- def generate_evaluation_code(self, code):
- # set up local variables and free their references on exit
- generate_inner_evaluation_code = super(ScopedExprNode, self).generate_evaluation_code
- if not self.has_local_scope or not self.expr_scope.var_entries:
- # no local variables => delegate, done
- generate_inner_evaluation_code(code)
- return
- code.putln('{ /* enter inner scope */')
- py_entries = []
- for _, entry in sorted(item for item in self.expr_scope.entries.items() if item[0]):
- if not entry.in_closure:
- if entry.type.is_pyobject and entry.used:
- py_entries.append(entry)
- if not py_entries:
- # no local Python references => no cleanup required
- generate_inner_evaluation_code(code)
- code.putln('} /* exit inner scope */')
- return
- # must free all local Python references at each exit point
- old_loop_labels = code.new_loop_labels()
- old_error_label = code.new_error_label()
- generate_inner_evaluation_code(code)
- # normal (non-error) exit
- self._generate_vars_cleanup(code, py_entries)
- # error/loop body exit points
- exit_scope = code.new_label('exit_scope')
- code.put_goto(exit_scope)
- for label, old_label in ([(code.error_label, old_error_label)] +
- list(zip(code.get_loop_labels(), old_loop_labels))):
- if code.label_used(label):
- code.put_label(label)
- self._generate_vars_cleanup(code, py_entries)
- code.put_goto(old_label)
- code.put_label(exit_scope)
- code.putln('} /* exit inner scope */')
- code.set_loop_labels(old_loop_labels)
- code.error_label = old_error_label
- def _generate_vars_cleanup(self, code, py_entries):
- for entry in py_entries:
- if entry.is_cglobal:
- code.put_var_gotref(entry)
- code.put_decref_set(entry.cname, "Py_None")
- else:
- code.put_var_xdecref_clear(entry)
- class ComprehensionNode(ScopedExprNode):
- # A list/set/dict comprehension
- child_attrs = ["loop"]
- is_temp = True
- constant_result = not_a_constant
- def infer_type(self, env):
- return self.type
- def analyse_declarations(self, env):
- self.append.target = self # this is used in the PyList_Append of the inner loop
- self.init_scope(env)
- def analyse_scoped_declarations(self, env):
- self.loop.analyse_declarations(env)
- def analyse_types(self, env):
- if not self.has_local_scope:
- self.loop = self.loop.analyse_expressions(env)
- return self
- def analyse_scoped_expressions(self, env):
- if self.has_local_scope:
- self.loop = self.loop.analyse_expressions(env)
- return self
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- self.generate_operation_code(code)
- def generate_operation_code(self, code):
- if self.type is Builtin.list_type:
- create_code = 'PyList_New(0)'
- elif self.type is Builtin.set_type:
- create_code = 'PySet_New(NULL)'
- elif self.type is Builtin.dict_type:
- create_code = 'PyDict_New()'
- else:
- raise InternalError("illegal type for comprehension: %s" % self.type)
- code.putln('%s = %s; %s' % (
- self.result(), create_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- self.loop.generate_execution_code(code)
- def annotate(self, code):
- self.loop.annotate(code)
- class ComprehensionAppendNode(Node):
- # Need to be careful to avoid infinite recursion:
- # target must not be in child_attrs/subexprs
- child_attrs = ['expr']
- target = None
- type = PyrexTypes.c_int_type
- def analyse_expressions(self, env):
- self.expr = self.expr.analyse_expressions(env)
- if not self.expr.type.is_pyobject:
- self.expr = self.expr.coerce_to_pyobject(env)
- return self
- def generate_execution_code(self, code):
- if self.target.type is list_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ListCompAppend", "Optimize.c"))
- function = "__Pyx_ListComp_Append"
- elif self.target.type is set_type:
- function = "PySet_Add"
- else:
- raise InternalError(
- "Invalid type for comprehension node: %s" % self.target.type)
- self.expr.generate_evaluation_code(code)
- code.putln(code.error_goto_if("%s(%s, (PyObject*)%s)" % (
- function,
- self.target.result(),
- self.expr.result()
- ), self.pos))
- self.expr.generate_disposal_code(code)
- self.expr.free_temps(code)
- def generate_function_definitions(self, env, code):
- self.expr.generate_function_definitions(env, code)
- def annotate(self, code):
- self.expr.annotate(code)
- class DictComprehensionAppendNode(ComprehensionAppendNode):
- child_attrs = ['key_expr', 'value_expr']
- def analyse_expressions(self, env):
- self.key_expr = self.key_expr.analyse_expressions(env)
- if not self.key_expr.type.is_pyobject:
- self.key_expr = self.key_expr.coerce_to_pyobject(env)
- self.value_expr = self.value_expr.analyse_expressions(env)
- if not self.value_expr.type.is_pyobject:
- self.value_expr = self.value_expr.coerce_to_pyobject(env)
- return self
- def generate_execution_code(self, code):
- self.key_expr.generate_evaluation_code(code)
- self.value_expr.generate_evaluation_code(code)
- code.putln(code.error_goto_if("PyDict_SetItem(%s, (PyObject*)%s, (PyObject*)%s)" % (
- self.target.result(),
- self.key_expr.result(),
- self.value_expr.result()
- ), self.pos))
- self.key_expr.generate_disposal_code(code)
- self.key_expr.free_temps(code)
- self.value_expr.generate_disposal_code(code)
- self.value_expr.free_temps(code)
- def generate_function_definitions(self, env, code):
- self.key_expr.generate_function_definitions(env, code)
- self.value_expr.generate_function_definitions(env, code)
- def annotate(self, code):
- self.key_expr.annotate(code)
- self.value_expr.annotate(code)
- class InlinedGeneratorExpressionNode(ExprNode):
- # An inlined generator expression for which the result is calculated
- # inside of the loop and returned as a single, first and only Generator
- # return value.
- # This will only be created by transforms when replacing safe builtin
- # calls on generator expressions.
- #
- # gen GeneratorExpressionNode the generator, not containing any YieldExprNodes
- # orig_func String the name of the builtin function this node replaces
- # target ExprNode or None a 'target' for a ComprehensionAppend node
- subexprs = ["gen"]
- orig_func = None
- target = None
- is_temp = True
- type = py_object_type
- def __init__(self, pos, gen, comprehension_type=None, **kwargs):
- gbody = gen.def_node.gbody
- gbody.is_inlined = True
- if comprehension_type is not None:
- assert comprehension_type in (list_type, set_type, dict_type), comprehension_type
- gbody.inlined_comprehension_type = comprehension_type
- kwargs.update(
- target=RawCNameExprNode(pos, comprehension_type, Naming.retval_cname),
- type=comprehension_type,
- )
- super(InlinedGeneratorExpressionNode, self).__init__(pos, gen=gen, **kwargs)
- def may_be_none(self):
- return self.orig_func not in ('any', 'all', 'sorted')
- def infer_type(self, env):
- return self.type
- def analyse_types(self, env):
- self.gen = self.gen.analyse_expressions(env)
- return self
- def generate_result_code(self, code):
- code.putln("%s = __Pyx_Generator_Next(%s); %s" % (
- self.result(), self.gen.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class MergedSequenceNode(ExprNode):
- """
- Merge a sequence of iterables into a set/list/tuple.
- The target collection is determined by self.type, which must be set externally.
- args [ExprNode]
- """
- subexprs = ['args']
- is_temp = True
- gil_message = "Constructing Python collection"
- def __init__(self, pos, args, type):
- if type in (list_type, tuple_type) and args and args[0].is_sequence_constructor:
- # construct a list directly from the first argument that we can then extend
- if args[0].type is not list_type:
- args[0] = ListNode(args[0].pos, args=args[0].args, is_temp=True, mult_factor=args[0].mult_factor)
- ExprNode.__init__(self, pos, args=args, type=type)
- def calculate_constant_result(self):
- result = []
- for item in self.args:
- if item.is_sequence_constructor and item.mult_factor:
- if item.mult_factor.constant_result <= 0:
- continue
- # otherwise, adding each item once should be enough
- if item.is_set_literal or item.is_sequence_constructor:
- # process items in order
- items = (arg.constant_result for arg in item.args)
- else:
- items = item.constant_result
- result.extend(items)
- if self.type is set_type:
- result = set(result)
- elif self.type is tuple_type:
- result = tuple(result)
- else:
- assert self.type is list_type
- self.constant_result = result
- def compile_time_value(self, denv):
- result = []
- for item in self.args:
- if item.is_sequence_constructor and item.mult_factor:
- if item.mult_factor.compile_time_value(denv) <= 0:
- continue
- if item.is_set_literal or item.is_sequence_constructor:
- # process items in order
- items = (arg.compile_time_value(denv) for arg in item.args)
- else:
- items = item.compile_time_value(denv)
- result.extend(items)
- if self.type is set_type:
- try:
- result = set(result)
- except Exception as e:
- self.compile_time_value_error(e)
- elif self.type is tuple_type:
- result = tuple(result)
- else:
- assert self.type is list_type
- return result
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- return self.type
- def analyse_types(self, env):
- args = [
- arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node(
- # FIXME: CPython's error message starts with the runtime function name
- 'argument after * must be an iterable, not NoneType')
- for arg in self.args
- ]
- if len(args) == 1 and args[0].type is self.type:
- # strip this intermediate node and use the bare collection
- return args[0]
- assert self.type in (set_type, list_type, tuple_type)
- self.args = args
- return self
- def may_be_none(self):
- return False
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- is_set = self.type is set_type
- args = iter(self.args)
- item = next(args)
- item.generate_evaluation_code(code)
- if (is_set and item.is_set_literal or
- not is_set and item.is_sequence_constructor and item.type is list_type):
- code.putln("%s = %s;" % (self.result(), item.py_result()))
- item.generate_post_assignment_code(code)
- else:
- code.putln("%s = %s(%s); %s" % (
- self.result(),
- 'PySet_New' if is_set else 'PySequence_List',
- item.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- item.generate_disposal_code(code)
- item.free_temps(code)
- helpers = set()
- if is_set:
- add_func = "PySet_Add"
- extend_func = "__Pyx_PySet_Update"
- else:
- add_func = "__Pyx_ListComp_Append"
- extend_func = "__Pyx_PyList_Extend"
- for item in args:
- if (is_set and (item.is_set_literal or item.is_sequence_constructor) or
- (item.is_sequence_constructor and not item.mult_factor)):
- if not is_set and item.args:
- helpers.add(("ListCompAppend", "Optimize.c"))
- for arg in item.args:
- arg.generate_evaluation_code(code)
- code.put_error_if_neg(arg.pos, "%s(%s, %s)" % (
- add_func,
- self.result(),
- arg.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- continue
- if is_set:
- helpers.add(("PySet_Update", "Builtins.c"))
- else:
- helpers.add(("ListExtend", "Optimize.c"))
- item.generate_evaluation_code(code)
- code.put_error_if_neg(item.pos, "%s(%s, %s)" % (
- extend_func,
- self.result(),
- item.py_result()))
- item.generate_disposal_code(code)
- item.free_temps(code)
- if self.type is tuple_type:
- code.putln("{")
- code.putln("PyObject *%s = PyList_AsTuple(%s);" % (
- Naming.quick_temp_cname,
- self.result()))
- code.put_decref(self.result(), py_object_type)
- code.putln("%s = %s; %s" % (
- self.result(),
- Naming.quick_temp_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- code.putln("}")
- for helper in sorted(helpers):
- code.globalstate.use_utility_code(UtilityCode.load_cached(*helper))
- def annotate(self, code):
- for item in self.args:
- item.annotate(code)
- class SetNode(ExprNode):
- """
- Set constructor.
- """
- subexprs = ['args']
- type = set_type
- is_set_literal = True
- gil_message = "Constructing Python set"
- def analyse_types(self, env):
- for i in range(len(self.args)):
- arg = self.args[i]
- arg = arg.analyse_types(env)
- self.args[i] = arg.coerce_to_pyobject(env)
- self.type = set_type
- self.is_temp = 1
- return self
- def may_be_none(self):
- return False
- def calculate_constant_result(self):
- self.constant_result = set([arg.constant_result for arg in self.args])
- def compile_time_value(self, denv):
- values = [arg.compile_time_value(denv) for arg in self.args]
- try:
- return set(values)
- except Exception as e:
- self.compile_time_value_error(e)
- def generate_evaluation_code(self, code):
- for arg in self.args:
- arg.generate_evaluation_code(code)
- self.allocate_temp_result(code)
- code.putln(
- "%s = PySet_New(0); %s" % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- for arg in self.args:
- code.put_error_if_neg(
- self.pos,
- "PySet_Add(%s, %s)" % (self.result(), arg.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- class DictNode(ExprNode):
- # Dictionary constructor.
- #
- # key_value_pairs [DictItemNode]
- # exclude_null_values [boolean] Do not add NULL values to dict
- #
- # obj_conversion_errors [PyrexError] used internally
- subexprs = ['key_value_pairs']
- is_temp = 1
- exclude_null_values = False
- type = dict_type
- is_dict_literal = True
- reject_duplicates = False
- obj_conversion_errors = []
- @classmethod
- def from_pairs(cls, pos, pairs):
- return cls(pos, key_value_pairs=[
- DictItemNode(pos, key=k, value=v) for k, v in pairs])
- def calculate_constant_result(self):
- self.constant_result = dict([
- item.constant_result for item in self.key_value_pairs])
- def compile_time_value(self, denv):
- pairs = [(item.key.compile_time_value(denv), item.value.compile_time_value(denv))
- for item in self.key_value_pairs]
- try:
- return dict(pairs)
- except Exception as e:
- self.compile_time_value_error(e)
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- # TODO: Infer struct constructors.
- return dict_type
- def analyse_types(self, env):
- with local_errors(ignore=True) as errors:
- self.key_value_pairs = [
- item.analyse_types(env)
- for item in self.key_value_pairs
- ]
- self.obj_conversion_errors = errors
- return self
- def may_be_none(self):
- return False
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject:
- self.release_errors()
- if self.type.is_struct_or_union:
- if not dict_type.subtype_of(dst_type):
- error(self.pos, "Cannot interpret struct as non-dict type '%s'" % dst_type)
- return DictNode(self.pos, key_value_pairs=[
- DictItemNode(item.pos, key=item.key.coerce_to_pyobject(env),
- value=item.value.coerce_to_pyobject(env))
- for item in self.key_value_pairs])
- if not self.type.subtype_of(dst_type):
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
- elif dst_type.is_struct_or_union:
- self.type = dst_type
- if not dst_type.is_struct and len(self.key_value_pairs) != 1:
- error(self.pos, "Exactly one field must be specified to convert to union '%s'" % dst_type)
- elif dst_type.is_struct and len(self.key_value_pairs) < len(dst_type.scope.var_entries):
- warning(self.pos, "Not all members given for struct '%s'" % dst_type, 1)
- for item in self.key_value_pairs:
- if isinstance(item.key, CoerceToPyTypeNode):
- item.key = item.key.arg
- if not item.key.is_string_literal:
- error(item.key.pos, "Invalid struct field identifier")
- item.key = StringNode(item.key.pos, value="<error>")
- else:
- key = str(item.key.value) # converts string literals to unicode in Py3
- member = dst_type.scope.lookup_here(key)
- if not member:
- error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, key))
- else:
- value = item.value
- if isinstance(value, CoerceToPyTypeNode):
- value = value.arg
- item.value = value.coerce_to(member.type, env)
- else:
- self.type = error_type
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
- return self
- def release_errors(self):
- for err in self.obj_conversion_errors:
- report_error(err)
- self.obj_conversion_errors = []
- gil_message = "Constructing Python dict"
- def generate_evaluation_code(self, code):
- # Custom method used here because key-value
- # pairs are evaluated and used one at a time.
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- is_dict = self.type.is_pyobject
- if is_dict:
- self.release_errors()
- code.putln(
- "%s = __Pyx_PyDict_NewPresized(%d); %s" % (
- self.result(),
- len(self.key_value_pairs),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- keys_seen = set()
- key_type = None
- needs_error_helper = False
- for item in self.key_value_pairs:
- item.generate_evaluation_code(code)
- if is_dict:
- if self.exclude_null_values:
- code.putln('if (%s) {' % item.value.py_result())
- key = item.key
- if self.reject_duplicates:
- if keys_seen is not None:
- # avoid runtime 'in' checks for literals that we can do at compile time
- if not key.is_string_literal:
- keys_seen = None
- elif key.value in keys_seen:
- # FIXME: this could be a compile time error, at least in Cython code
- keys_seen = None
- elif key_type is not type(key.value):
- if key_type is None:
- key_type = type(key.value)
- keys_seen.add(key.value)
- else:
- # different types => may not be able to compare at compile time
- keys_seen = None
- else:
- keys_seen.add(key.value)
- if keys_seen is None:
- code.putln('if (unlikely(PyDict_Contains(%s, %s))) {' % (
- self.result(), key.py_result()))
- # currently only used in function calls
- needs_error_helper = True
- code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % (
- key.py_result(),
- code.error_goto(item.pos)))
- code.putln("} else {")
- code.put_error_if_neg(self.pos, "PyDict_SetItem(%s, %s, %s)" % (
- self.result(),
- item.key.py_result(),
- item.value.py_result()))
- if self.reject_duplicates and keys_seen is None:
- code.putln('}')
- if self.exclude_null_values:
- code.putln('}')
- else:
- code.putln("%s.%s = %s;" % (
- self.result(),
- item.key.value,
- item.value.result()))
- item.generate_disposal_code(code)
- item.free_temps(code)
- if needs_error_helper:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("RaiseDoubleKeywords", "FunctionArguments.c"))
- def annotate(self, code):
- for item in self.key_value_pairs:
- item.annotate(code)
- class DictItemNode(ExprNode):
- # Represents a single item in a DictNode
- #
- # key ExprNode
- # value ExprNode
- subexprs = ['key', 'value']
- nogil_check = None # Parent DictNode takes care of it
- def calculate_constant_result(self):
- self.constant_result = (
- self.key.constant_result, self.value.constant_result)
- def analyse_types(self, env):
- self.key = self.key.analyse_types(env)
- self.value = self.value.analyse_types(env)
- self.key = self.key.coerce_to_pyobject(env)
- self.value = self.value.coerce_to_pyobject(env)
- return self
- def generate_evaluation_code(self, code):
- self.key.generate_evaluation_code(code)
- self.value.generate_evaluation_code(code)
- def generate_disposal_code(self, code):
- self.key.generate_disposal_code(code)
- self.value.generate_disposal_code(code)
- def free_temps(self, code):
- self.key.free_temps(code)
- self.value.free_temps(code)
- def __iter__(self):
- return iter([self.key, self.value])
- class SortedDictKeysNode(ExprNode):
- # build sorted list of dict keys, e.g. for dir()
- subexprs = ['arg']
- is_temp = True
- def __init__(self, arg):
- ExprNode.__init__(self, arg.pos, arg=arg)
- self.type = Builtin.list_type
- def analyse_types(self, env):
- arg = self.arg.analyse_types(env)
- if arg.type is Builtin.dict_type:
- arg = arg.as_none_safe_node(
- "'NoneType' object is not iterable")
- self.arg = arg
- return self
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- dict_result = self.arg.py_result()
- if self.arg.type is Builtin.dict_type:
- code.putln('%s = PyDict_Keys(%s); %s' % (
- self.result(), dict_result,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- else:
- # originally used PyMapping_Keys() here, but that may return a tuple
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- 'PyObjectCallMethod0', 'ObjectHandling.c'))
- keys_cname = code.intern_identifier(StringEncoding.EncodedString("keys"))
- code.putln('%s = __Pyx_PyObject_CallMethod0(%s, %s); %s' % (
- self.result(), dict_result, keys_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- code.putln("if (unlikely(!PyList_Check(%s))) {" % self.result())
- code.put_decref_set(self.result(), "PySequence_List(%s)" % self.result())
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- code.putln("}")
- code.put_error_if_neg(
- self.pos, 'PyList_Sort(%s)' % self.py_result())
- class ModuleNameMixin(object):
- def get_py_mod_name(self, code):
- return code.get_py_string_const(
- self.module_name, identifier=True)
- def get_py_qualified_name(self, code):
- return code.get_py_string_const(
- self.qualname, identifier=True)
- class ClassNode(ExprNode, ModuleNameMixin):
- # Helper class used in the implementation of Python
- # class definitions. Constructs a class object given
- # a name, tuple of bases and class dictionary.
- #
- # name EncodedString Name of the class
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- # doc ExprNode or None Doc string
- # module_name EncodedString Name of defining module
- subexprs = ['doc']
- type = py_object_type
- is_temp = True
- def infer_type(self, env):
- # TODO: could return 'type' in some cases
- return py_object_type
- def analyse_types(self, env):
- if self.doc:
- self.doc = self.doc.analyse_types(env)
- self.doc = self.doc.coerce_to_pyobject(env)
- env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
- return self
- def may_be_none(self):
- return True
- gil_message = "Constructing Python class"
- def generate_result_code(self, code):
- class_def_node = self.class_def_node
- cname = code.intern_identifier(self.name)
- if self.doc:
- code.put_error_if_neg(self.pos,
- 'PyDict_SetItem(%s, %s, %s)' % (
- class_def_node.dict.py_result(),
- code.intern_identifier(
- StringEncoding.EncodedString("__doc__")),
- self.doc.py_result()))
- py_mod_name = self.get_py_mod_name(code)
- qualname = self.get_py_qualified_name(code)
- code.putln(
- '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
- self.result(),
- class_def_node.bases.py_result(),
- class_def_node.dict.py_result(),
- cname,
- qualname,
- py_mod_name,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class Py3ClassNode(ExprNode):
- # Helper class used in the implementation of Python3+
- # class definitions. Constructs a class object given
- # a name, tuple of bases and class dictionary.
- #
- # name EncodedString Name of the class
- # module_name EncodedString Name of defining module
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- # calculate_metaclass bool should call CalculateMetaclass()
- # allow_py2_metaclass bool should look for Py2 metaclass
- subexprs = []
- type = py_object_type
- is_temp = True
- def infer_type(self, env):
- # TODO: could return 'type' in some cases
- return py_object_type
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- return True
- gil_message = "Constructing Python class"
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
- cname = code.intern_identifier(self.name)
- class_def_node = self.class_def_node
- mkw = class_def_node.mkw.py_result() if class_def_node.mkw else 'NULL'
- if class_def_node.metaclass:
- metaclass = class_def_node.metaclass.py_result()
- else:
- metaclass = "((PyObject*)&__Pyx_DefaultClassType)"
- code.putln(
- '%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s, %d, %d); %s' % (
- self.result(),
- metaclass,
- cname,
- class_def_node.bases.py_result(),
- class_def_node.dict.py_result(),
- mkw,
- self.calculate_metaclass,
- self.allow_py2_metaclass,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class PyClassMetaclassNode(ExprNode):
- # Helper class holds Python3 metaclass object
- #
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- subexprs = []
- def analyse_types(self, env):
- self.type = py_object_type
- self.is_temp = True
- return self
- def may_be_none(self):
- return True
- def generate_result_code(self, code):
- bases = self.class_def_node.bases
- mkw = self.class_def_node.mkw
- if mkw:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
- call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
- bases.result(),
- mkw.result())
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
- call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
- bases.result())
- code.putln(
- "%s = %s; %s" % (
- self.result(), call,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
- # Helper class holds Python3 namespace object
- #
- # All this are not owned by this node
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- # doc ExprNode or None Doc string (owned)
- subexprs = ['doc']
- def analyse_types(self, env):
- if self.doc:
- self.doc = self.doc.analyse_types(env).coerce_to_pyobject(env)
- self.type = py_object_type
- self.is_temp = 1
- return self
- def may_be_none(self):
- return True
- def generate_result_code(self, code):
- cname = code.intern_identifier(self.name)
- py_mod_name = self.get_py_mod_name(code)
- qualname = self.get_py_qualified_name(code)
- class_def_node = self.class_def_node
- null = "(PyObject *) NULL"
- doc_code = self.doc.result() if self.doc else null
- mkw = class_def_node.mkw.py_result() if class_def_node.mkw else null
- metaclass = class_def_node.metaclass.py_result() if class_def_node.metaclass else null
- code.putln(
- "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
- self.result(),
- metaclass,
- class_def_node.bases.result(),
- cname,
- qualname,
- mkw,
- py_mod_name,
- doc_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class ClassCellInjectorNode(ExprNode):
- # Initialize CyFunction.func_classobj
- is_temp = True
- type = py_object_type
- subexprs = []
- is_active = False
- def analyse_expressions(self, env):
- return self
- def generate_result_code(self, code):
- assert self.is_active
- code.putln(
- '%s = PyList_New(0); %s' % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- def generate_injection_code(self, code, classobj_cname):
- assert self.is_active
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CyFunctionClassCell", "CythonFunction.c"))
- code.put_error_if_neg(self.pos, '__Pyx_CyFunction_InitClassCell(%s, %s)' % (
- self.result(), classobj_cname))
- class ClassCellNode(ExprNode):
- # Class Cell for noargs super()
- subexprs = []
- is_temp = True
- is_generator = False
- type = py_object_type
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- if not self.is_generator:
- code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
- self.result(),
- Naming.self_cname))
- else:
- code.putln('%s = %s->classobj;' % (
- self.result(), Naming.generator_cname))
- code.putln(
- 'if (!%s) { PyErr_SetString(PyExc_SystemError, '
- '"super(): empty __class__ cell"); %s }' % (
- self.result(),
- code.error_goto(self.pos)))
- code.put_incref(self.result(), py_object_type)
- class PyCFunctionNode(ExprNode, ModuleNameMixin):
- # Helper class used in the implementation of Python
- # functions. Constructs a PyCFunction object
- # from a PyMethodDef struct.
- #
- # pymethdef_cname string PyMethodDef structure
- # self_object ExprNode or None
- # binding bool
- # def_node DefNode the Python function node
- # module_name EncodedString Name of defining module
- # code_object CodeObjectNode the PyCodeObject creator node
- subexprs = ['code_object', 'defaults_tuple', 'defaults_kwdict',
- 'annotations_dict']
- self_object = None
- code_object = None
- binding = False
- def_node = None
- defaults = None
- defaults_struct = None
- defaults_pyobjects = 0
- defaults_tuple = None
- defaults_kwdict = None
- annotations_dict = None
- type = py_object_type
- is_temp = 1
- specialized_cpdefs = None
- is_specialization = False
- @classmethod
- def from_defnode(cls, node, binding):
- return cls(node.pos,
- def_node=node,
- pymethdef_cname=node.entry.pymethdef_cname,
- binding=binding or node.specialized_cpdefs,
- specialized_cpdefs=node.specialized_cpdefs,
- code_object=CodeObjectNode(node))
- def analyse_types(self, env):
- if self.binding:
- self.analyse_default_args(env)
- return self
- def analyse_default_args(self, env):
- """
- Handle non-literal function's default arguments.
- """
- nonliteral_objects = []
- nonliteral_other = []
- default_args = []
- default_kwargs = []
- annotations = []
- # For global cpdef functions and def/cpdef methods in cdef classes, we must use global constants
- # for default arguments to avoid the dependency on the CyFunction object as 'self' argument
- # in the underlying C function. Basically, cpdef functions/methods are static C functions,
- # so their optional arguments must be static, too.
- # TODO: change CyFunction implementation to pass both function object and owning object for method calls
- must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope)
- for arg in self.def_node.args:
- if arg.default and not must_use_constants:
- if not arg.default.is_literal:
- arg.is_dynamic = True
- if arg.type.is_pyobject:
- nonliteral_objects.append(arg)
- else:
- nonliteral_other.append(arg)
- else:
- arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
- if arg.kw_only:
- default_kwargs.append(arg)
- else:
- default_args.append(arg)
- if arg.annotation:
- arg.annotation = self.analyse_annotation(env, arg.annotation)
- annotations.append((arg.pos, arg.name, arg.annotation))
- for arg in (self.def_node.star_arg, self.def_node.starstar_arg):
- if arg and arg.annotation:
- arg.annotation = self.analyse_annotation(env, arg.annotation)
- annotations.append((arg.pos, arg.name, arg.annotation))
- annotation = self.def_node.return_type_annotation
- if annotation:
- annotation = self.analyse_annotation(env, annotation)
- self.def_node.return_type_annotation = annotation
- annotations.append((annotation.pos, StringEncoding.EncodedString("return"), annotation))
- if nonliteral_objects or nonliteral_other:
- module_scope = env.global_scope()
- cname = module_scope.next_id(Naming.defaults_struct_prefix)
- scope = Symtab.StructOrUnionScope(cname)
- self.defaults = []
- for arg in nonliteral_objects:
- entry = scope.declare_var(arg.name, arg.type, None,
- Naming.arg_prefix + arg.name,
- allow_pyobject=True)
- self.defaults.append((arg, entry))
- for arg in nonliteral_other:
- entry = scope.declare_var(arg.name, arg.type, None,
- Naming.arg_prefix + arg.name,
- allow_pyobject=False, allow_memoryview=True)
- self.defaults.append((arg, entry))
- entry = module_scope.declare_struct_or_union(
- None, 'struct', scope, 1, None, cname=cname)
- self.defaults_struct = scope
- self.defaults_pyobjects = len(nonliteral_objects)
- for arg, entry in self.defaults:
- arg.default_value = '%s->%s' % (
- Naming.dynamic_args_cname, entry.cname)
- self.def_node.defaults_struct = self.defaults_struct.name
- if default_args or default_kwargs:
- if self.defaults_struct is None:
- if default_args:
- defaults_tuple = TupleNode(self.pos, args=[
- arg.default for arg in default_args])
- self.defaults_tuple = defaults_tuple.analyse_types(env).coerce_to_pyobject(env)
- if default_kwargs:
- defaults_kwdict = DictNode(self.pos, key_value_pairs=[
- DictItemNode(
- arg.pos,
- key=IdentifierStringNode(arg.pos, value=arg.name),
- value=arg.default)
- for arg in default_kwargs])
- self.defaults_kwdict = defaults_kwdict.analyse_types(env)
- else:
- if default_args:
- defaults_tuple = DefaultsTupleNode(
- self.pos, default_args, self.defaults_struct)
- else:
- defaults_tuple = NoneNode(self.pos)
- if default_kwargs:
- defaults_kwdict = DefaultsKwDictNode(
- self.pos, default_kwargs, self.defaults_struct)
- else:
- defaults_kwdict = NoneNode(self.pos)
- defaults_getter = Nodes.DefNode(
- self.pos, args=[], star_arg=None, starstar_arg=None,
- body=Nodes.ReturnStatNode(
- self.pos, return_type=py_object_type,
- value=TupleNode(
- self.pos, args=[defaults_tuple, defaults_kwdict])),
- decorators=None,
- name=StringEncoding.EncodedString("__defaults__"))
- # defaults getter must never live in class scopes, it's always a module function
- module_scope = env.global_scope()
- defaults_getter.analyse_declarations(module_scope)
- defaults_getter = defaults_getter.analyse_expressions(module_scope)
- defaults_getter.body = defaults_getter.body.analyse_expressions(
- defaults_getter.local_scope)
- defaults_getter.py_wrapper_required = False
- defaults_getter.pymethdef_required = False
- self.def_node.defaults_getter = defaults_getter
- if annotations:
- annotations_dict = DictNode(self.pos, key_value_pairs=[
- DictItemNode(
- pos, key=IdentifierStringNode(pos, value=name),
- value=value)
- for pos, name, value in annotations])
- self.annotations_dict = annotations_dict.analyse_types(env)
- def analyse_annotation(self, env, annotation):
- if annotation is None:
- return None
- atype = annotation.analyse_as_type(env)
- if atype is not None:
- # Keep parsed types as strings as they might not be Python representable.
- annotation = UnicodeNode(
- annotation.pos,
- value=StringEncoding.EncodedString(atype.declaration_code('', for_display=True)))
- annotation = annotation.analyse_types(env)
- if not annotation.type.is_pyobject:
- annotation = annotation.coerce_to_pyobject(env)
- return annotation
- def may_be_none(self):
- return False
- gil_message = "Constructing Python function"
- def self_result_code(self):
- if self.self_object is None:
- self_result = "NULL"
- else:
- self_result = self.self_object.py_result()
- return self_result
- def generate_result_code(self, code):
- if self.binding:
- self.generate_cyfunction_code(code)
- else:
- self.generate_pycfunction_code(code)
- def generate_pycfunction_code(self, code):
- py_mod_name = self.get_py_mod_name(code)
- code.putln(
- '%s = PyCFunction_NewEx(&%s, %s, %s); %s' % (
- self.result(),
- self.pymethdef_cname,
- self.self_result_code(),
- py_mod_name,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- def generate_cyfunction_code(self, code):
- if self.specialized_cpdefs:
- def_node = self.specialized_cpdefs[0]
- else:
- def_node = self.def_node
- if self.specialized_cpdefs or self.is_specialization:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
- constructor = "__pyx_FusedFunction_New"
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
- constructor = "__Pyx_CyFunction_New"
- if self.code_object:
- code_object_result = self.code_object.py_result()
- else:
- code_object_result = 'NULL'
- flags = []
- if def_node.is_staticmethod:
- flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
- elif def_node.is_classmethod:
- flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
- if def_node.local_scope.parent_scope.is_c_class_scope and not def_node.entry.is_anonymous:
- flags.append('__Pyx_CYFUNCTION_CCLASS')
- if flags:
- flags = ' | '.join(flags)
- else:
- flags = '0'
- code.putln(
- '%s = %s(&%s, %s, %s, %s, %s, %s, %s); %s' % (
- self.result(),
- constructor,
- self.pymethdef_cname,
- flags,
- self.get_py_qualified_name(code),
- self.self_result_code(),
- self.get_py_mod_name(code),
- Naming.moddict_cname,
- code_object_result,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- if def_node.requires_classobj:
- assert code.pyclass_stack, "pyclass_stack is empty"
- class_node = code.pyclass_stack[-1]
- code.put_incref(self.py_result(), py_object_type)
- code.putln(
- 'PyList_Append(%s, %s);' % (
- class_node.class_cell.result(),
- self.result()))
- code.put_giveref(self.py_result())
- if self.defaults:
- code.putln(
- 'if (!__Pyx_CyFunction_InitDefaults(%s, sizeof(%s), %d)) %s' % (
- self.result(), self.defaults_struct.name,
- self.defaults_pyobjects, code.error_goto(self.pos)))
- defaults = '__Pyx_CyFunction_Defaults(%s, %s)' % (
- self.defaults_struct.name, self.result())
- for arg, entry in self.defaults:
- arg.generate_assignment_code(code, target='%s->%s' % (
- defaults, entry.cname))
- if self.defaults_tuple:
- code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
- self.result(), self.defaults_tuple.py_result()))
- if self.defaults_kwdict:
- code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
- self.result(), self.defaults_kwdict.py_result()))
- if def_node.defaults_getter and not self.specialized_cpdefs:
- # Fused functions do not support dynamic defaults, only their specialisations can have them for now.
- code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
- self.result(), def_node.defaults_getter.entry.pyfunc_cname))
- if self.annotations_dict:
- code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % (
- self.result(), self.annotations_dict.py_result()))
- class InnerFunctionNode(PyCFunctionNode):
- # Special PyCFunctionNode that depends on a closure class
- #
- binding = True
- needs_self_code = True
- def self_result_code(self):
- if self.needs_self_code:
- return "((PyObject*)%s)" % Naming.cur_scope_cname
- return "NULL"
- class CodeObjectNode(ExprNode):
- # Create a PyCodeObject for a CyFunction instance.
- #
- # def_node DefNode the Python function node
- # varnames TupleNode a tuple with all local variable names
- subexprs = ['varnames']
- is_temp = False
- result_code = None
- def __init__(self, def_node):
- ExprNode.__init__(self, def_node.pos, def_node=def_node)
- args = list(def_node.args)
- # if we have args/kwargs, then the first two in var_entries are those
- local_vars = [arg for arg in def_node.local_scope.var_entries if arg.name]
- self.varnames = TupleNode(
- def_node.pos,
- args=[IdentifierStringNode(arg.pos, value=arg.name)
- for arg in args + local_vars],
- is_temp=0,
- is_literal=1)
- def may_be_none(self):
- return False
- def calculate_result_code(self, code=None):
- if self.result_code is None:
- self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
- return self.result_code
- def generate_result_code(self, code):
- if self.result_code is None:
- self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
- code = code.get_cached_constants_writer(self.result_code)
- if code is None:
- return # already initialised
- code.mark_pos(self.pos)
- func = self.def_node
- func_name = code.get_py_string_const(
- func.name, identifier=True, is_str=False, unicode_value=func.name)
- # FIXME: better way to get the module file path at module init time? Encoding to use?
- file_path = StringEncoding.bytes_literal(func.pos[0].get_filenametable_entry().encode('utf8'), 'utf8')
- # XXX Use get_description() to set arcadia root relative filename
- file_path = StringEncoding.bytes_literal(func.pos[0].get_description().encode('utf8'), 'utf8')
- file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
- # This combination makes CPython create a new dict for "frame.f_locals" (see GH #1836).
- flags = ['CO_OPTIMIZED', 'CO_NEWLOCALS']
- if self.def_node.star_arg:
- flags.append('CO_VARARGS')
- if self.def_node.starstar_arg:
- flags.append('CO_VARKEYWORDS')
- code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
- self.result_code,
- len(func.args) - func.num_kwonly_args, # argcount
- func.num_kwonly_args, # kwonlyargcount (Py3 only)
- len(self.varnames.args), # nlocals
- '|'.join(flags) or '0', # flags
- Naming.empty_bytes, # code
- Naming.empty_tuple, # consts
- Naming.empty_tuple, # names (FIXME)
- self.varnames.result(), # varnames
- Naming.empty_tuple, # freevars (FIXME)
- Naming.empty_tuple, # cellvars (FIXME)
- file_path_const, # filename
- func_name, # name
- self.pos[1], # firstlineno
- Naming.empty_bytes, # lnotab
- code.error_goto_if_null(self.result_code, self.pos),
- ))
- class DefaultLiteralArgNode(ExprNode):
- # CyFunction's literal argument default value
- #
- # Evaluate literal only once.
- subexprs = []
- is_literal = True
- is_temp = False
- def __init__(self, pos, arg):
- super(DefaultLiteralArgNode, self).__init__(pos)
- self.arg = arg
- self.type = self.arg.type
- self.evaluated = False
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- pass
- def generate_evaluation_code(self, code):
- if not self.evaluated:
- self.arg.generate_evaluation_code(code)
- self.evaluated = True
- def result(self):
- return self.type.cast_code(self.arg.result())
- class DefaultNonLiteralArgNode(ExprNode):
- # CyFunction's non-literal argument default value
- subexprs = []
- def __init__(self, pos, arg, defaults_struct):
- super(DefaultNonLiteralArgNode, self).__init__(pos)
- self.arg = arg
- self.defaults_struct = defaults_struct
- def analyse_types(self, env):
- self.type = self.arg.type
- self.is_temp = False
- return self
- def generate_result_code(self, code):
- pass
- def result(self):
- return '__Pyx_CyFunction_Defaults(%s, %s)->%s' % (
- self.defaults_struct.name, Naming.self_cname,
- self.defaults_struct.lookup(self.arg.name).cname)
- class DefaultsTupleNode(TupleNode):
- # CyFunction's __defaults__ tuple
- def __init__(self, pos, defaults, defaults_struct):
- args = []
- for arg in defaults:
- if not arg.default.is_literal:
- arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
- else:
- arg = arg.default
- args.append(arg)
- super(DefaultsTupleNode, self).__init__(pos, args=args)
- def analyse_types(self, env, skip_children=False):
- return super(DefaultsTupleNode, self).analyse_types(env, skip_children).coerce_to_pyobject(env)
- class DefaultsKwDictNode(DictNode):
- # CyFunction's __kwdefaults__ dict
- def __init__(self, pos, defaults, defaults_struct):
- items = []
- for arg in defaults:
- name = IdentifierStringNode(arg.pos, value=arg.name)
- if not arg.default.is_literal:
- arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
- else:
- arg = arg.default
- items.append(DictItemNode(arg.pos, key=name, value=arg))
- super(DefaultsKwDictNode, self).__init__(pos, key_value_pairs=items)
- class LambdaNode(InnerFunctionNode):
- # Lambda expression node (only used as a function reference)
- #
- # args [CArgDeclNode] formal arguments
- # star_arg PyArgDeclNode or None * argument
- # starstar_arg PyArgDeclNode or None ** argument
- # lambda_name string a module-globally unique lambda name
- # result_expr ExprNode
- # def_node DefNode the underlying function 'def' node
- child_attrs = ['def_node']
- name = StringEncoding.EncodedString('<lambda>')
- def analyse_declarations(self, env):
- self.lambda_name = self.def_node.lambda_name = env.next_id('lambda')
- self.def_node.no_assignment_synthesis = True
- self.def_node.pymethdef_required = True
- self.def_node.analyse_declarations(env)
- self.def_node.is_cyfunction = True
- self.pymethdef_cname = self.def_node.entry.pymethdef_cname
- env.add_lambda_def(self.def_node)
- def analyse_types(self, env):
- self.def_node = self.def_node.analyse_expressions(env)
- return super(LambdaNode, self).analyse_types(env)
- def generate_result_code(self, code):
- self.def_node.generate_execution_code(code)
- super(LambdaNode, self).generate_result_code(code)
- class GeneratorExpressionNode(LambdaNode):
- # A generator expression, e.g. (i for i in range(10))
- #
- # Result is a generator.
- #
- # loop ForStatNode the for-loop, containing a YieldExprNode
- # def_node DefNode the underlying generator 'def' node
- name = StringEncoding.EncodedString('genexpr')
- binding = False
- def analyse_declarations(self, env):
- self.genexpr_name = env.next_id('genexpr')
- super(GeneratorExpressionNode, self).analyse_declarations(env)
- # No pymethdef required
- self.def_node.pymethdef_required = False
- self.def_node.py_wrapper_required = False
- self.def_node.is_cyfunction = False
- # Force genexpr signature
- self.def_node.entry.signature = TypeSlots.pyfunction_noargs
- def generate_result_code(self, code):
- code.putln(
- '%s = %s(%s); %s' % (
- self.result(),
- self.def_node.entry.pyfunc_cname,
- self.self_result_code(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class YieldExprNode(ExprNode):
- # Yield expression node
- #
- # arg ExprNode the value to return from the generator
- # label_num integer yield label number
- # is_yield_from boolean is a YieldFromExprNode to delegate to another generator
- subexprs = ['arg']
- type = py_object_type
- label_num = 0
- is_yield_from = False
- is_await = False
- in_async_gen = False
- expr_keyword = 'yield'
- def analyse_types(self, env):
- if not self.label_num or (self.is_yield_from and self.in_async_gen):
- error(self.pos, "'%s' not supported here" % self.expr_keyword)
- self.is_temp = 1
- if self.arg is not None:
- self.arg = self.arg.analyse_types(env)
- if not self.arg.type.is_pyobject:
- self.coerce_yield_argument(env)
- return self
- def coerce_yield_argument(self, env):
- self.arg = self.arg.coerce_to_pyobject(env)
- def generate_evaluation_code(self, code):
- if self.arg:
- self.arg.generate_evaluation_code(code)
- self.arg.make_owned_reference(code)
- code.putln(
- "%s = %s;" % (
- Naming.retval_cname,
- self.arg.result_as(py_object_type)))
- self.arg.generate_post_assignment_code(code)
- self.arg.free_temps(code)
- else:
- code.put_init_to_py_none(Naming.retval_cname, py_object_type)
- self.generate_yield_code(code)
- def generate_yield_code(self, code):
- """
- Generate the code to return the argument in 'Naming.retval_cname'
- and to continue at the yield label.
- """
- label_num, label_name = code.new_yield_label(
- self.expr_keyword.replace(' ', '_'))
- code.use_label(label_name)
- saved = []
- code.funcstate.closure_temps.reset()
- for cname, type, manage_ref in code.funcstate.temps_in_use():
- save_cname = code.funcstate.closure_temps.allocate_temp(type)
- saved.append((cname, save_cname, type))
- if type.is_pyobject:
- code.put_xgiveref(cname)
- code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
- code.put_xgiveref(Naming.retval_cname)
- profile = code.globalstate.directives['profile']
- linetrace = code.globalstate.directives['linetrace']
- if profile or linetrace:
- code.put_trace_return(Naming.retval_cname,
- nogil=not code.funcstate.gil_owned)
- code.put_finish_refcount_context()
- if code.funcstate.current_except is not None:
- # inside of an except block => save away currently handled exception
- code.putln("__Pyx_Coroutine_SwapException(%s);" % Naming.generator_cname)
- else:
- # no exceptions being handled => restore exception state of caller
- code.putln("__Pyx_Coroutine_ResetAndClearException(%s);" % Naming.generator_cname)
- code.putln("/* return from %sgenerator, %sing value */" % (
- 'async ' if self.in_async_gen else '',
- 'await' if self.is_await else 'yield'))
- code.putln("%s->resume_label = %d;" % (
- Naming.generator_cname, label_num))
- if self.in_async_gen and not self.is_await:
- # __Pyx__PyAsyncGenValueWrapperNew() steals a reference to the return value
- code.putln("return __Pyx__PyAsyncGenValueWrapperNew(%s);" % Naming.retval_cname)
- else:
- code.putln("return %s;" % Naming.retval_cname)
- code.put_label(label_name)
- for cname, save_cname, type in saved:
- code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
- if type.is_pyobject:
- code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
- code.put_xgotref(cname)
- self.generate_sent_value_handling_code(code, Naming.sent_value_cname)
- if self.result_is_used:
- self.allocate_temp_result(code)
- code.put('%s = %s; ' % (self.result(), Naming.sent_value_cname))
- code.put_incref(self.result(), py_object_type)
- def generate_sent_value_handling_code(self, code, value_cname):
- code.putln(code.error_goto_if_null(value_cname, self.pos))
- class _YieldDelegationExprNode(YieldExprNode):
- def yield_from_func(self, code):
- raise NotImplementedError()
- def generate_evaluation_code(self, code, source_cname=None, decref_source=False):
- if source_cname is None:
- self.arg.generate_evaluation_code(code)
- code.putln("%s = %s(%s, %s);" % (
- Naming.retval_cname,
- self.yield_from_func(code),
- Naming.generator_cname,
- self.arg.py_result() if source_cname is None else source_cname))
- if source_cname is None:
- self.arg.generate_disposal_code(code)
- self.arg.free_temps(code)
- elif decref_source:
- code.put_decref_clear(source_cname, py_object_type)
- code.put_xgotref(Naming.retval_cname)
- code.putln("if (likely(%s)) {" % Naming.retval_cname)
- self.generate_yield_code(code)
- code.putln("} else {")
- # either error or sub-generator has normally terminated: return value => node result
- if self.result_is_used:
- self.fetch_iteration_result(code)
- else:
- self.handle_iteration_exception(code)
- code.putln("}")
- def fetch_iteration_result(self, code):
- # YieldExprNode has allocated the result temp for us
- code.putln("%s = NULL;" % self.result())
- code.put_error_if_neg(self.pos, "__Pyx_PyGen_FetchStopIterationValue(&%s)" % self.result())
- code.put_gotref(self.result())
- def handle_iteration_exception(self, code):
- code.putln("PyObject* exc_type = __Pyx_PyErr_Occurred();")
- code.putln("if (exc_type) {")
- code.putln("if (likely(exc_type == PyExc_StopIteration || (exc_type != PyExc_GeneratorExit &&"
- " __Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)))) PyErr_Clear();")
- code.putln("else %s" % code.error_goto(self.pos))
- code.putln("}")
- class YieldFromExprNode(_YieldDelegationExprNode):
- # "yield from GEN" expression
- is_yield_from = True
- expr_keyword = 'yield from'
- def coerce_yield_argument(self, env):
- if not self.arg.type.is_string:
- # FIXME: support C arrays and C++ iterators?
- error(self.pos, "yielding from non-Python object not supported")
- self.arg = self.arg.coerce_to_pyobject(env)
- def yield_from_func(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("GeneratorYieldFrom", "Coroutine.c"))
- return "__Pyx_Generator_Yield_From"
- class AwaitExprNode(_YieldDelegationExprNode):
- # 'await' expression node
- #
- # arg ExprNode the Awaitable value to await
- # label_num integer yield label number
- is_await = True
- expr_keyword = 'await'
- def coerce_yield_argument(self, env):
- if self.arg is not None:
- # FIXME: use same check as in YieldFromExprNode.coerce_yield_argument() ?
- self.arg = self.arg.coerce_to_pyobject(env)
- def yield_from_func(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("CoroutineYieldFrom", "Coroutine.c"))
- return "__Pyx_Coroutine_Yield_From"
- class AwaitIterNextExprNode(AwaitExprNode):
- # 'await' expression node as part of 'async for' iteration
- #
- # Breaks out of loop on StopAsyncIteration exception.
- def _generate_break(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c"))
- code.putln("PyObject* exc_type = __Pyx_PyErr_Occurred();")
- code.putln("if (unlikely(exc_type && (exc_type == __Pyx_PyExc_StopAsyncIteration || ("
- " exc_type != PyExc_StopIteration && exc_type != PyExc_GeneratorExit &&"
- " __Pyx_PyErr_GivenExceptionMatches(exc_type, __Pyx_PyExc_StopAsyncIteration))))) {")
- code.putln("PyErr_Clear();")
- code.putln("break;")
- code.putln("}")
- def fetch_iteration_result(self, code):
- assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
- self._generate_break(code)
- super(AwaitIterNextExprNode, self).fetch_iteration_result(code)
- def generate_sent_value_handling_code(self, code, value_cname):
- assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
- code.putln("if (unlikely(!%s)) {" % value_cname)
- self._generate_break(code)
- # all non-break exceptions are errors, as in parent class
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- class GlobalsExprNode(AtomicExprNode):
- type = dict_type
- is_temp = 1
- def analyse_types(self, env):
- env.use_utility_code(Builtin.globals_utility_code)
- return self
- gil_message = "Constructing globals dict"
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- code.putln('%s = __Pyx_Globals(); %s' % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class LocalsDictItemNode(DictItemNode):
- def analyse_types(self, env):
- self.key = self.key.analyse_types(env)
- self.value = self.value.analyse_types(env)
- self.key = self.key.coerce_to_pyobject(env)
- if self.value.type.can_coerce_to_pyobject(env):
- self.value = self.value.coerce_to_pyobject(env)
- else:
- self.value = None
- return self
- class FuncLocalsExprNode(DictNode):
- def __init__(self, pos, env):
- local_vars = sorted([
- entry.name for entry in env.entries.values() if entry.name])
- items = [LocalsDictItemNode(
- pos, key=IdentifierStringNode(pos, value=var),
- value=NameNode(pos, name=var, allow_null=True))
- for var in local_vars]
- DictNode.__init__(self, pos, key_value_pairs=items,
- exclude_null_values=True)
- def analyse_types(self, env):
- node = super(FuncLocalsExprNode, self).analyse_types(env)
- node.key_value_pairs = [ i for i in node.key_value_pairs
- if i.value is not None ]
- return node
- class PyClassLocalsExprNode(AtomicExprNode):
- def __init__(self, pos, pyclass_dict):
- AtomicExprNode.__init__(self, pos)
- self.pyclass_dict = pyclass_dict
- def analyse_types(self, env):
- self.type = self.pyclass_dict.type
- self.is_temp = False
- return self
- def may_be_none(self):
- return False
- def result(self):
- return self.pyclass_dict.result()
- def generate_result_code(self, code):
- pass
- def LocalsExprNode(pos, scope_node, env):
- if env.is_module_scope:
- return GlobalsExprNode(pos)
- if env.is_py_class_scope:
- return PyClassLocalsExprNode(pos, scope_node.dict)
- return FuncLocalsExprNode(pos, env)
- #-------------------------------------------------------------------
- #
- # Unary operator nodes
- #
- #-------------------------------------------------------------------
- compile_time_unary_operators = {
- 'not': operator.not_,
- '~': operator.inv,
- '-': operator.neg,
- '+': operator.pos,
- }
- class UnopNode(ExprNode):
- # operator string
- # operand ExprNode
- #
- # Processing during analyse_expressions phase:
- #
- # analyse_c_operation
- # Called when the operand is not a pyobject.
- # - Check operand type and coerce if needed.
- # - Determine result type and result code fragment.
- # - Allocate temporary for result if needed.
- subexprs = ['operand']
- infix = True
- def calculate_constant_result(self):
- func = compile_time_unary_operators[self.operator]
- self.constant_result = func(self.operand.constant_result)
- def compile_time_value(self, denv):
- func = compile_time_unary_operators.get(self.operator)
- if not func:
- error(self.pos,
- "Unary '%s' not supported in compile-time expression"
- % self.operator)
- operand = self.operand.compile_time_value(denv)
- try:
- return func(operand)
- except Exception as e:
- self.compile_time_value_error(e)
- def infer_type(self, env):
- operand_type = self.operand.infer_type(env)
- if operand_type.is_cpp_class or operand_type.is_ptr:
- cpp_type = operand_type.find_cpp_operation_type(self.operator)
- if cpp_type is not None:
- return cpp_type
- return self.infer_unop_type(env, operand_type)
- def infer_unop_type(self, env, operand_type):
- if operand_type.is_pyobject:
- return py_object_type
- else:
- return operand_type
- def may_be_none(self):
- if self.operand.type and self.operand.type.is_builtin_type:
- if self.operand.type is not type_type:
- return False
- return ExprNode.may_be_none(self)
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- if self.is_pythran_operation(env):
- self.type = PythranExpr(pythran_unaryop_type(self.operator, self.operand.type))
- self.is_temp = 1
- elif self.is_py_operation():
- self.coerce_operand_to_pyobject(env)
- self.type = py_object_type
- self.is_temp = 1
- elif self.is_cpp_operation():
- self.analyse_cpp_operation(env)
- else:
- self.analyse_c_operation(env)
- return self
- def check_const(self):
- return self.operand.check_const()
- def is_py_operation(self):
- return self.operand.type.is_pyobject or self.operand.type.is_ctuple
- def is_pythran_operation(self, env):
- np_pythran = has_np_pythran(env)
- op_type = self.operand.type
- return np_pythran and (op_type.is_buffer or op_type.is_pythran_expr)
- def nogil_check(self, env):
- if self.is_py_operation():
- self.gil_error()
- def is_cpp_operation(self):
- type = self.operand.type
- return type.is_cpp_class
- def coerce_operand_to_pyobject(self, env):
- self.operand = self.operand.coerce_to_pyobject(env)
- def generate_result_code(self, code):
- if self.type.is_pythran_expr:
- code.putln("// Pythran unaryop")
- code.putln("__Pyx_call_destructor(%s);" % self.result())
- code.putln("new (&%s) decltype(%s){%s%s};" % (
- self.result(),
- self.result(),
- self.operator,
- self.operand.pythran_result()))
- elif self.operand.type.is_pyobject:
- self.generate_py_operation_code(code)
- elif self.is_temp:
- if self.is_cpp_operation() and self.exception_check == '+':
- translate_cpp_exception(code, self.pos,
- "%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result()))
- def generate_py_operation_code(self, code):
- function = self.py_operation_function(code)
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- function,
- self.operand.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- def type_error(self):
- if not self.operand.type.is_error:
- error(self.pos, "Invalid operand type for '%s' (%s)" %
- (self.operator, self.operand.type))
- self.type = PyrexTypes.error_type
- def analyse_cpp_operation(self, env, overload_check=True):
- entry = env.lookup_operator(self.operator, [self.operand])
- if overload_check and not entry:
- self.type_error()
- return
- if entry:
- self.exception_check = entry.type.exception_check
- self.exception_value = entry.type.exception_value
- if self.exception_check == '+':
- self.is_temp = True
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- else:
- self.exception_check = ''
- self.exception_value = ''
- cpp_type = self.operand.type.find_cpp_operation_type(self.operator)
- if overload_check and cpp_type is None:
- error(self.pos, "'%s' operator not defined for %s" % (
- self.operator, type))
- self.type_error()
- return
- self.type = cpp_type
- class NotNode(UnopNode):
- # 'not' operator
- #
- # operand ExprNode
- operator = '!'
- type = PyrexTypes.c_bint_type
- def calculate_constant_result(self):
- self.constant_result = not self.operand.constant_result
- def compile_time_value(self, denv):
- operand = self.operand.compile_time_value(denv)
- try:
- return not operand
- except Exception as e:
- self.compile_time_value_error(e)
- def infer_unop_type(self, env, operand_type):
- return PyrexTypes.c_bint_type
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- operand_type = self.operand.type
- if operand_type.is_cpp_class:
- self.analyse_cpp_operation(env)
- else:
- self.operand = self.operand.coerce_to_boolean(env)
- return self
- def calculate_result_code(self):
- return "(!%s)" % self.operand.result()
- class UnaryPlusNode(UnopNode):
- # unary '+' operator
- operator = '+'
- def analyse_c_operation(self, env):
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- def py_operation_function(self, code):
- return "PyNumber_Positive"
- def calculate_result_code(self):
- if self.is_cpp_operation():
- return "(+%s)" % self.operand.result()
- else:
- return self.operand.result()
- class UnaryMinusNode(UnopNode):
- # unary '-' operator
- operator = '-'
- def analyse_c_operation(self, env):
- if self.operand.type.is_numeric:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_enum:
- self.type = PyrexTypes.c_int_type
- else:
- self.type_error()
- if self.type.is_complex:
- self.infix = False
- def py_operation_function(self, code):
- return "PyNumber_Negative"
- def calculate_result_code(self):
- if self.infix:
- return "(-%s)" % self.operand.result()
- else:
- return "%s(%s)" % (self.operand.type.unary_op('-'), self.operand.result())
- def get_constant_c_result_code(self):
- value = self.operand.get_constant_c_result_code()
- if value:
- return "(-%s)" % value
- class TildeNode(UnopNode):
- # unary '~' operator
- def analyse_c_operation(self, env):
- if self.operand.type.is_int:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_enum:
- self.type = PyrexTypes.c_int_type
- else:
- self.type_error()
- def py_operation_function(self, code):
- return "PyNumber_Invert"
- def calculate_result_code(self):
- return "(~%s)" % self.operand.result()
- class CUnopNode(UnopNode):
- def is_py_operation(self):
- return False
- class DereferenceNode(CUnopNode):
- # unary * operator
- operator = '*'
- def infer_unop_type(self, env, operand_type):
- if operand_type.is_ptr:
- return operand_type.base_type
- else:
- return PyrexTypes.error_type
- def analyse_c_operation(self, env):
- if self.operand.type.is_ptr:
- self.type = self.operand.type.base_type
- else:
- self.type_error()
- def calculate_result_code(self):
- return "(*%s)" % self.operand.result()
- class DecrementIncrementNode(CUnopNode):
- # unary ++/-- operator
- def analyse_c_operation(self, env):
- if self.operand.type.is_numeric:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_ptr:
- self.type = self.operand.type
- else:
- self.type_error()
- def calculate_result_code(self):
- if self.is_prefix:
- return "(%s%s)" % (self.operator, self.operand.result())
- else:
- return "(%s%s)" % (self.operand.result(), self.operator)
- def inc_dec_constructor(is_prefix, operator):
- return lambda pos, **kwds: DecrementIncrementNode(pos, is_prefix=is_prefix, operator=operator, **kwds)
- class AmpersandNode(CUnopNode):
- # The C address-of operator.
- #
- # operand ExprNode
- operator = '&'
- def infer_unop_type(self, env, operand_type):
- return PyrexTypes.c_ptr_type(operand_type)
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- argtype = self.operand.type
- if argtype.is_cpp_class:
- self.analyse_cpp_operation(env, overload_check=False)
- if not (argtype.is_cfunction or argtype.is_reference or self.operand.is_addressable()):
- if argtype.is_memoryviewslice:
- self.error("Cannot take address of memoryview slice")
- else:
- self.error("Taking address of non-lvalue (type %s)" % argtype)
- return self
- if argtype.is_pyobject:
- self.error("Cannot take address of Python %s" % (
- "variable '%s'" % self.operand.name if self.operand.is_name else
- "object attribute '%s'" % self.operand.attribute if self.operand.is_attribute else
- "object"))
- return self
- if not argtype.is_cpp_class or not self.type:
- self.type = PyrexTypes.c_ptr_type(argtype)
- return self
- def check_const(self):
- return self.operand.check_const_addr()
- def error(self, mess):
- error(self.pos, mess)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- def calculate_result_code(self):
- return "(&%s)" % self.operand.result()
- def generate_result_code(self, code):
- if (self.operand.type.is_cpp_class and self.exception_check == '+'):
- translate_cpp_exception(code, self.pos,
- "%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- unop_node_classes = {
- "+": UnaryPlusNode,
- "-": UnaryMinusNode,
- "~": TildeNode,
- }
- def unop_node(pos, operator, operand):
- # Construct unnop node of appropriate class for
- # given operator.
- if isinstance(operand, IntNode) and operator == '-':
- return IntNode(pos = operand.pos, value = str(-Utils.str_to_number(operand.value)),
- longness=operand.longness, unsigned=operand.unsigned)
- elif isinstance(operand, UnopNode) and operand.operator == operator in '+-':
- warning(pos, "Python has no increment/decrement operator: %s%sx == %s(%sx) == x" % ((operator,)*4), 5)
- return unop_node_classes[operator](pos,
- operator = operator,
- operand = operand)
- class TypecastNode(ExprNode):
- # C type cast
- #
- # operand ExprNode
- # base_type CBaseTypeNode
- # declarator CDeclaratorNode
- # typecheck boolean
- #
- # If used from a transform, one can if wanted specify the attribute
- # "type" directly and leave base_type and declarator to None
- subexprs = ['operand']
- base_type = declarator = type = None
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- if self.type is None:
- base_type = self.base_type.analyse(env)
- _, self.type = self.declarator.analyse(base_type, env)
- return self.type
- def analyse_types(self, env):
- if self.type is None:
- base_type = self.base_type.analyse(env)
- _, self.type = self.declarator.analyse(base_type, env)
- if self.operand.has_constant_result():
- # Must be done after self.type is resolved.
- self.calculate_constant_result()
- if self.type.is_cfunction:
- error(self.pos,
- "Cannot cast to a function type")
- self.type = PyrexTypes.error_type
- self.operand = self.operand.analyse_types(env)
- if self.type is PyrexTypes.c_bint_type:
- # short circuit this to a coercion
- return self.operand.coerce_to_boolean(env)
- to_py = self.type.is_pyobject
- from_py = self.operand.type.is_pyobject
- if from_py and not to_py and self.operand.is_ephemeral():
- if not self.type.is_numeric and not self.type.is_cpp_class:
- error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
- if to_py and not from_py:
- if self.type is bytes_type and self.operand.type.is_int:
- return CoerceIntToBytesNode(self.operand, env)
- elif self.operand.type.can_coerce_to_pyobject(env):
- self.result_ctype = py_object_type
- self.operand = self.operand.coerce_to(self.type, env)
- else:
- if self.operand.type.is_ptr:
- if not (self.operand.type.base_type.is_void or self.operand.type.base_type.is_struct):
- error(self.pos, "Python objects cannot be cast from pointers of primitive types")
- else:
- # Should this be an error?
- warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
- self.operand.type, self.type))
- self.operand = self.operand.coerce_to_simple(env)
- elif from_py and not to_py:
- if self.type.create_from_py_utility_code(env):
- self.operand = self.operand.coerce_to(self.type, env)
- elif self.type.is_ptr:
- if not (self.type.base_type.is_void or self.type.base_type.is_struct):
- error(self.pos, "Python objects cannot be cast to pointers of primitive types")
- else:
- warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
- self.type, self.operand.type))
- elif from_py and to_py:
- if self.typecheck:
- self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True)
- elif isinstance(self.operand, SliceIndexNode):
- # This cast can influence the created type of string slices.
- self.operand = self.operand.coerce_to(self.type, env)
- elif self.type.is_complex and self.operand.type.is_complex:
- self.operand = self.operand.coerce_to_simple(env)
- elif self.operand.type.is_fused:
- self.operand = self.operand.coerce_to(self.type, env)
- #self.type = self.operand.type
- if self.type.is_ptr and self.type.base_type.is_cfunction and self.type.base_type.nogil:
- op_type = self.operand.type
- if op_type.is_ptr:
- op_type = op_type.base_type
- if op_type.is_cfunction and not op_type.nogil:
- warning(self.pos,
- "Casting a GIL-requiring function into a nogil function circumvents GIL validation", 1)
- return self
- def is_simple(self):
- # either temp or a C cast => no side effects other than the operand's
- return self.operand.is_simple()
- def is_ephemeral(self):
- # either temp or a C cast => no side effects other than the operand's
- return self.operand.is_ephemeral()
- def nonlocally_immutable(self):
- return self.is_temp or self.operand.nonlocally_immutable()
- def nogil_check(self, env):
- if self.type and self.type.is_pyobject and self.is_temp:
- self.gil_error()
- def check_const(self):
- return self.operand.check_const()
- def calculate_constant_result(self):
- self.constant_result = self.calculate_result_code(self.operand.constant_result)
- def calculate_result_code(self, operand_result = None):
- if operand_result is None:
- operand_result = self.operand.result()
- if self.type.is_complex:
- operand_result = self.operand.result()
- if self.operand.type.is_complex:
- real_part = self.type.real_type.cast_code("__Pyx_CREAL(%s)" % operand_result)
- imag_part = self.type.real_type.cast_code("__Pyx_CIMAG(%s)" % operand_result)
- else:
- real_part = self.type.real_type.cast_code(operand_result)
- imag_part = "0"
- return "%s(%s, %s)" % (
- self.type.from_parts,
- real_part,
- imag_part)
- else:
- return self.type.cast_code(operand_result)
- def get_constant_c_result_code(self):
- operand_result = self.operand.get_constant_c_result_code()
- if operand_result:
- return self.type.cast_code(operand_result)
- def result_as(self, type):
- if self.type.is_pyobject and not self.is_temp:
- # Optimise away some unnecessary casting
- return self.operand.result_as(type)
- else:
- return ExprNode.result_as(self, type)
- def generate_result_code(self, code):
- if self.is_temp:
- code.putln(
- "%s = (PyObject *)%s;" % (
- self.result(),
- self.operand.result()))
- code.put_incref(self.result(), self.ctype())
- ERR_START = "Start may not be given"
- ERR_NOT_STOP = "Stop must be provided to indicate shape"
- ERR_STEPS = ("Strides may only be given to indicate contiguity. "
- "Consider slicing it after conversion")
- ERR_NOT_POINTER = "Can only create cython.array from pointer or array"
- ERR_BASE_TYPE = "Pointer base type does not match cython.array base type"
- class CythonArrayNode(ExprNode):
- """
- Used when a pointer of base_type is cast to a memoryviewslice with that
- base type. i.e.
- <int[:M:1, :N]> p
- creates a fortran-contiguous cython.array.
- We leave the type set to object so coercions to object are more efficient
- and less work. Acquiring a memoryviewslice from this will be just as
- efficient. ExprNode.coerce_to() will do the additional typecheck on
- self.compile_time_type
- This also handles <int[:, :]> my_c_array
- operand ExprNode the thing we're casting
- base_type_node MemoryViewSliceTypeNode the cast expression node
- """
- subexprs = ['operand', 'shapes']
- shapes = None
- is_temp = True
- mode = "c"
- array_dtype = None
- shape_type = PyrexTypes.c_py_ssize_t_type
- def analyse_types(self, env):
- from . import MemoryView
- self.operand = self.operand.analyse_types(env)
- if self.array_dtype:
- array_dtype = self.array_dtype
- else:
- array_dtype = self.base_type_node.base_type_node.analyse(env)
- axes = self.base_type_node.axes
- self.type = error_type
- self.shapes = []
- ndim = len(axes)
- # Base type of the pointer or C array we are converting
- base_type = self.operand.type
- if not self.operand.type.is_ptr and not self.operand.type.is_array:
- error(self.operand.pos, ERR_NOT_POINTER)
- return self
- # Dimension sizes of C array
- array_dimension_sizes = []
- if base_type.is_array:
- while base_type.is_array:
- array_dimension_sizes.append(base_type.size)
- base_type = base_type.base_type
- elif base_type.is_ptr:
- base_type = base_type.base_type
- else:
- error(self.pos, "unexpected base type %s found" % base_type)
- return self
- if not (base_type.same_as(array_dtype) or base_type.is_void):
- error(self.operand.pos, ERR_BASE_TYPE)
- return self
- elif self.operand.type.is_array and len(array_dimension_sizes) != ndim:
- error(self.operand.pos,
- "Expected %d dimensions, array has %d dimensions" %
- (ndim, len(array_dimension_sizes)))
- return self
- # Verify the start, stop and step values
- # In case of a C array, use the size of C array in each dimension to
- # get an automatic cast
- for axis_no, axis in enumerate(axes):
- if not axis.start.is_none:
- error(axis.start.pos, ERR_START)
- return self
- if axis.stop.is_none:
- if array_dimension_sizes:
- dimsize = array_dimension_sizes[axis_no]
- axis.stop = IntNode(self.pos, value=str(dimsize),
- constant_result=dimsize,
- type=PyrexTypes.c_int_type)
- else:
- error(axis.pos, ERR_NOT_STOP)
- return self
- axis.stop = axis.stop.analyse_types(env)
- shape = axis.stop.coerce_to(self.shape_type, env)
- if not shape.is_literal:
- shape.coerce_to_temp(env)
- self.shapes.append(shape)
- first_or_last = axis_no in (0, ndim - 1)
- if not axis.step.is_none and first_or_last:
- # '1' in the first or last dimension denotes F or C contiguity
- axis.step = axis.step.analyse_types(env)
- if (not axis.step.type.is_int and axis.step.is_literal and not
- axis.step.type.is_error):
- error(axis.step.pos, "Expected an integer literal")
- return self
- if axis.step.compile_time_value(env) != 1:
- error(axis.step.pos, ERR_STEPS)
- return self
- if axis_no == 0:
- self.mode = "fortran"
- elif not axis.step.is_none and not first_or_last:
- # step provided in some other dimension
- error(axis.step.pos, ERR_STEPS)
- return self
- if not self.operand.is_name:
- self.operand = self.operand.coerce_to_temp(env)
- axes = [('direct', 'follow')] * len(axes)
- if self.mode == "fortran":
- axes[0] = ('direct', 'contig')
- else:
- axes[-1] = ('direct', 'contig')
- self.coercion_type = PyrexTypes.MemoryViewSliceType(array_dtype, axes)
- self.coercion_type.validate_memslice_dtype(self.pos)
- self.type = self.get_cython_array_type(env)
- MemoryView.use_cython_array_utility_code(env)
- env.use_utility_code(MemoryView.typeinfo_to_format_code)
- return self
- def allocate_temp_result(self, code):
- if self.temp_code:
- raise RuntimeError("temp allocated multiple times")
- self.temp_code = code.funcstate.allocate_temp(self.type, True)
- def infer_type(self, env):
- return self.get_cython_array_type(env)
- def get_cython_array_type(self, env):
- cython_scope = env.global_scope().context.cython_scope
- cython_scope.load_cythonscope()
- return cython_scope.viewscope.lookup("array").type
- def generate_result_code(self, code):
- from . import Buffer
- shapes = [self.shape_type.cast_code(shape.result())
- for shape in self.shapes]
- dtype = self.coercion_type.dtype
- shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
- format_temp = code.funcstate.allocate_temp(py_object_type, True)
- itemsize = "sizeof(%s)" % dtype.empty_declaration_code()
- type_info = Buffer.get_type_information_cname(code, dtype)
- if self.operand.type.is_ptr:
- code.putln("if (!%s) {" % self.operand.result())
- code.putln( 'PyErr_SetString(PyExc_ValueError,'
- '"Cannot create cython.array from NULL pointer");')
- code.putln(code.error_goto(self.operand.pos))
- code.putln("}")
- code.putln("%s = __pyx_format_from_typeinfo(&%s); %s" % (
- format_temp,
- type_info,
- code.error_goto_if_null(format_temp, self.pos),
- ))
- code.put_gotref(format_temp)
- buildvalue_fmt = " __PYX_BUILD_PY_SSIZE_T " * len(shapes)
- code.putln('%s = Py_BuildValue((char*) "(" %s ")", %s); %s' % (
- shapes_temp,
- buildvalue_fmt,
- ", ".join(shapes),
- code.error_goto_if_null(shapes_temp, self.pos),
- ))
- code.put_gotref(shapes_temp)
- tup = (self.result(), shapes_temp, itemsize, format_temp,
- self.mode, self.operand.result())
- code.putln('%s = __pyx_array_new('
- '%s, %s, PyBytes_AS_STRING(%s), '
- '(char *) "%s", (char *) %s);' % tup)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.result())
- def dispose(temp):
- code.put_decref_clear(temp, py_object_type)
- code.funcstate.release_temp(temp)
- dispose(shapes_temp)
- dispose(format_temp)
- @classmethod
- def from_carray(cls, src_node, env):
- """
- Given a C array type, return a CythonArrayNode
- """
- pos = src_node.pos
- base_type = src_node.type
- none_node = NoneNode(pos)
- axes = []
- while base_type.is_array:
- axes.append(SliceNode(pos, start=none_node, stop=none_node,
- step=none_node))
- base_type = base_type.base_type
- axes[-1].step = IntNode(pos, value="1", is_c_literal=True)
- memslicenode = Nodes.MemoryViewSliceTypeNode(pos, axes=axes,
- base_type_node=base_type)
- result = CythonArrayNode(pos, base_type_node=memslicenode,
- operand=src_node, array_dtype=base_type)
- result = result.analyse_types(env)
- return result
- class SizeofNode(ExprNode):
- # Abstract base class for sizeof(x) expression nodes.
- type = PyrexTypes.c_size_t_type
- def check_const(self):
- return True
- def generate_result_code(self, code):
- pass
- class SizeofTypeNode(SizeofNode):
- # C sizeof function applied to a type
- #
- # base_type CBaseTypeNode
- # declarator CDeclaratorNode
- subexprs = []
- arg_type = None
- def analyse_types(self, env):
- # we may have incorrectly interpreted a dotted name as a type rather than an attribute
- # this could be better handled by more uniformly treating types as runtime-available objects
- if 0 and self.base_type.module_path:
- path = self.base_type.module_path
- obj = env.lookup(path[0])
- if obj.as_module is None:
- operand = NameNode(pos=self.pos, name=path[0])
- for attr in path[1:]:
- operand = AttributeNode(pos=self.pos, obj=operand, attribute=attr)
- operand = AttributeNode(pos=self.pos, obj=operand, attribute=self.base_type.name)
- node = SizeofVarNode(self.pos, operand=operand).analyse_types(env)
- return node
- if self.arg_type is None:
- base_type = self.base_type.analyse(env)
- _, arg_type = self.declarator.analyse(base_type, env)
- self.arg_type = arg_type
- self.check_type()
- return self
- def check_type(self):
- arg_type = self.arg_type
- if not arg_type:
- return
- if arg_type.is_pyobject and not arg_type.is_extension_type:
- error(self.pos, "Cannot take sizeof Python object")
- elif arg_type.is_void:
- error(self.pos, "Cannot take sizeof void")
- elif not arg_type.is_complete():
- error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type)
- def calculate_result_code(self):
- if self.arg_type.is_extension_type:
- # the size of the pointer is boring
- # we want the size of the actual struct
- arg_code = self.arg_type.declaration_code("", deref=1)
- else:
- arg_code = self.arg_type.empty_declaration_code()
- return "(sizeof(%s))" % arg_code
- class SizeofVarNode(SizeofNode):
- # C sizeof function applied to a variable
- #
- # operand ExprNode
- subexprs = ['operand']
- def analyse_types(self, env):
- # We may actually be looking at a type rather than a variable...
- # If we are, traditional analysis would fail...
- operand_as_type = self.operand.analyse_as_type(env)
- if operand_as_type:
- self.arg_type = operand_as_type
- if self.arg_type.is_fused:
- self.arg_type = self.arg_type.specialize(env.fused_to_specific)
- self.__class__ = SizeofTypeNode
- self.check_type()
- else:
- self.operand = self.operand.analyse_types(env)
- return self
- def calculate_result_code(self):
- return "(sizeof(%s))" % self.operand.result()
- def generate_result_code(self, code):
- pass
- class TypeidNode(ExprNode):
- # C++ typeid operator applied to a type or variable
- #
- # operand ExprNode
- # arg_type ExprNode
- # is_variable boolean
- type = PyrexTypes.error_type
- subexprs = ['operand']
- arg_type = None
- is_variable = None
- is_temp = 1
- def get_type_info_type(self, env):
- env_module = env
- while not env_module.is_module_scope:
- env_module = env_module.outer_scope
- typeinfo_module = env_module.find_module('libcpp.typeinfo', self.pos)
- typeinfo_entry = typeinfo_module.lookup('type_info')
- return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_type(typeinfo_entry.type))
- cpp_message = 'typeid operator'
- def analyse_types(self, env):
- self.cpp_check(env)
- type_info = self.get_type_info_type(env)
- if not type_info:
- self.error("The 'libcpp.typeinfo' module must be cimported to use the typeid() operator")
- return self
- self.type = type_info
- as_type = self.operand.analyse_as_type(env)
- if as_type:
- self.arg_type = as_type
- self.is_type = True
- else:
- self.arg_type = self.operand.analyse_types(env)
- self.is_type = False
- if self.arg_type.type.is_pyobject:
- self.error("Cannot use typeid on a Python object")
- return self
- elif self.arg_type.type.is_void:
- self.error("Cannot use typeid on void")
- return self
- elif not self.arg_type.type.is_complete():
- self.error("Cannot use typeid on incomplete type '%s'" % self.arg_type.type)
- return self
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- return self
- def error(self, mess):
- error(self.pos, mess)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- def check_const(self):
- return True
- def calculate_result_code(self):
- return self.temp_code
- def generate_result_code(self, code):
- if self.is_type:
- arg_code = self.arg_type.empty_declaration_code()
- else:
- arg_code = self.arg_type.result()
- translate_cpp_exception(code, self.pos,
- "%s = typeid(%s);" % (self.temp_code, arg_code),
- None, None, self.in_nogil_context)
- class TypeofNode(ExprNode):
- # Compile-time type of an expression, as a string.
- #
- # operand ExprNode
- # literal StringNode # internal
- literal = None
- type = py_object_type
- subexprs = ['literal'] # 'operand' will be ignored after type analysis!
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- value = StringEncoding.EncodedString(str(self.operand.type)) #self.operand.type.typeof_name())
- literal = StringNode(self.pos, value=value)
- literal = literal.analyse_types(env)
- self.literal = literal.coerce_to_pyobject(env)
- return self
- def analyse_as_type(self, env):
- self.operand = self.operand.analyse_types(env)
- return self.operand.type
- def may_be_none(self):
- return False
- def generate_evaluation_code(self, code):
- self.literal.generate_evaluation_code(code)
- def calculate_result_code(self):
- return self.literal.calculate_result_code()
- #-------------------------------------------------------------------
- #
- # Binary operator nodes
- #
- #-------------------------------------------------------------------
- try:
- matmul_operator = operator.matmul
- except AttributeError:
- def matmul_operator(a, b):
- try:
- func = a.__matmul__
- except AttributeError:
- func = b.__rmatmul__
- return func(a, b)
- compile_time_binary_operators = {
- '<': operator.lt,
- '<=': operator.le,
- '==': operator.eq,
- '!=': operator.ne,
- '>=': operator.ge,
- '>': operator.gt,
- 'is': operator.is_,
- 'is_not': operator.is_not,
- '+': operator.add,
- '&': operator.and_,
- '/': operator.truediv,
- '//': operator.floordiv,
- '<<': operator.lshift,
- '%': operator.mod,
- '*': operator.mul,
- '|': operator.or_,
- '**': operator.pow,
- '>>': operator.rshift,
- '-': operator.sub,
- '^': operator.xor,
- '@': matmul_operator,
- 'in': lambda x, seq: x in seq,
- 'not_in': lambda x, seq: x not in seq,
- }
- def get_compile_time_binop(node):
- func = compile_time_binary_operators.get(node.operator)
- if not func:
- error(node.pos,
- "Binary '%s' not supported in compile-time expression"
- % node.operator)
- return func
- class BinopNode(ExprNode):
- # operator string
- # operand1 ExprNode
- # operand2 ExprNode
- #
- # Processing during analyse_expressions phase:
- #
- # analyse_c_operation
- # Called when neither operand is a pyobject.
- # - Check operand types and coerce if needed.
- # - Determine result type and result code fragment.
- # - Allocate temporary for result if needed.
- subexprs = ['operand1', 'operand2']
- inplace = False
- def calculate_constant_result(self):
- func = compile_time_binary_operators[self.operator]
- self.constant_result = func(
- self.operand1.constant_result,
- self.operand2.constant_result)
- def compile_time_value(self, denv):
- func = get_compile_time_binop(self)
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- return func(operand1, operand2)
- except Exception as e:
- self.compile_time_value_error(e)
- def infer_type(self, env):
- return self.result_type(self.operand1.infer_type(env),
- self.operand2.infer_type(env), env)
- def analyse_types(self, env):
- self.operand1 = self.operand1.analyse_types(env)
- self.operand2 = self.operand2.analyse_types(env)
- self.analyse_operation(env)
- return self
- def analyse_operation(self, env):
- if self.is_pythran_operation(env):
- self.type = self.result_type(self.operand1.type,
- self.operand2.type, env)
- assert self.type.is_pythran_expr
- self.is_temp = 1
- elif self.is_py_operation():
- self.coerce_operands_to_pyobjects(env)
- self.type = self.result_type(self.operand1.type,
- self.operand2.type, env)
- assert self.type.is_pyobject
- self.is_temp = 1
- elif self.is_cpp_operation():
- self.analyse_cpp_operation(env)
- else:
- self.analyse_c_operation(env)
- def is_py_operation(self):
- return self.is_py_operation_types(self.operand1.type, self.operand2.type)
- def is_py_operation_types(self, type1, type2):
- return type1.is_pyobject or type2.is_pyobject or type1.is_ctuple or type2.is_ctuple
- def is_pythran_operation(self, env):
- return self.is_pythran_operation_types(self.operand1.type, self.operand2.type, env)
- def is_pythran_operation_types(self, type1, type2, env):
- # Support only expr op supported_type, or supported_type op expr
- return has_np_pythran(env) and \
- (is_pythran_supported_operation_type(type1) and is_pythran_supported_operation_type(type2)) and \
- (is_pythran_expr(type1) or is_pythran_expr(type2))
- def is_cpp_operation(self):
- return (self.operand1.type.is_cpp_class
- or self.operand2.type.is_cpp_class)
- def analyse_cpp_operation(self, env):
- entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
- if not entry:
- self.type_error()
- return
- func_type = entry.type
- self.exception_check = func_type.exception_check
- self.exception_value = func_type.exception_value
- if self.exception_check == '+':
- # Used by NumBinopNodes to break up expressions involving multiple
- # operators so that exceptions can be handled properly.
- self.is_temp = 1
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- if func_type.is_ptr:
- func_type = func_type.base_type
- if len(func_type.args) == 1:
- self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
- else:
- self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
- self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
- self.type = func_type.return_type
- def result_type(self, type1, type2, env):
- if self.is_pythran_operation_types(type1, type2, env):
- return PythranExpr(pythran_binop_type(self.operator, type1, type2))
- if self.is_py_operation_types(type1, type2):
- if type2.is_string:
- type2 = Builtin.bytes_type
- elif type2.is_pyunicode_ptr:
- type2 = Builtin.unicode_type
- if type1.is_string:
- type1 = Builtin.bytes_type
- elif type1.is_pyunicode_ptr:
- type1 = Builtin.unicode_type
- if type1.is_builtin_type or type2.is_builtin_type:
- if type1 is type2 and self.operator in '**%+|&^':
- # FIXME: at least these operators should be safe - others?
- return type1
- result_type = self.infer_builtin_types_operation(type1, type2)
- if result_type is not None:
- return result_type
- return py_object_type
- elif type1.is_error or type2.is_error:
- return PyrexTypes.error_type
- else:
- return self.compute_c_result_type(type1, type2)
- def infer_builtin_types_operation(self, type1, type2):
- return None
- def nogil_check(self, env):
- if self.is_py_operation():
- self.gil_error()
- def coerce_operands_to_pyobjects(self, env):
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- def check_const(self):
- return self.operand1.check_const() and self.operand2.check_const()
- def is_ephemeral(self):
- return (super(BinopNode, self).is_ephemeral() or
- self.operand1.is_ephemeral() or self.operand2.is_ephemeral())
- def generate_result_code(self, code):
- if self.type.is_pythran_expr:
- code.putln("// Pythran binop")
- code.putln("__Pyx_call_destructor(%s);" % self.result())
- if self.operator == '**':
- code.putln("new (&%s) decltype(%s){pythonic::numpy::functor::power{}(%s, %s)};" % (
- self.result(),
- self.result(),
- self.operand1.pythran_result(),
- self.operand2.pythran_result()))
- else:
- code.putln("new (&%s) decltype(%s){%s %s %s};" % (
- self.result(),
- self.result(),
- self.operand1.pythran_result(),
- self.operator,
- self.operand2.pythran_result()))
- elif self.operand1.type.is_pyobject:
- function = self.py_operation_function(code)
- if self.operator == '**':
- extra_args = ", Py_None"
- else:
- extra_args = ""
- code.putln(
- "%s = %s(%s, %s%s); %s" % (
- self.result(),
- function,
- self.operand1.py_result(),
- self.operand2.py_result(),
- extra_args,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif self.is_temp:
- # C++ overloaded operators with exception values are currently all
- # handled through temporaries.
- if self.is_cpp_operation() and self.exception_check == '+':
- translate_cpp_exception(code, self.pos,
- "%s = %s;" % (self.result(), self.calculate_result_code()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
- def type_error(self):
- if not (self.operand1.type.is_error
- or self.operand2.type.is_error):
- error(self.pos, "Invalid operand types for '%s' (%s; %s)" %
- (self.operator, self.operand1.type,
- self.operand2.type))
- self.type = PyrexTypes.error_type
- class CBinopNode(BinopNode):
- def analyse_types(self, env):
- node = BinopNode.analyse_types(self, env)
- if node.is_py_operation():
- node.type = PyrexTypes.error_type
- return node
- def py_operation_function(self, code):
- return ""
- def calculate_result_code(self):
- return "(%s %s %s)" % (
- self.operand1.result(),
- self.operator,
- self.operand2.result())
- def compute_c_result_type(self, type1, type2):
- cpp_type = None
- if type1.is_cpp_class or type1.is_ptr:
- cpp_type = type1.find_cpp_operation_type(self.operator, type2)
- if cpp_type is None and (type2.is_cpp_class or type2.is_ptr):
- cpp_type = type2.find_cpp_operation_type(self.operator, type1)
- # FIXME: do we need to handle other cases here?
- return cpp_type
- def c_binop_constructor(operator):
- def make_binop_node(pos, **operands):
- return CBinopNode(pos, operator=operator, **operands)
- return make_binop_node
- class NumBinopNode(BinopNode):
- # Binary operation taking numeric arguments.
- infix = True
- overflow_check = False
- overflow_bit_node = None
- def analyse_c_operation(self, env):
- type1 = self.operand1.type
- type2 = self.operand2.type
- self.type = self.compute_c_result_type(type1, type2)
- if not self.type:
- self.type_error()
- return
- if self.type.is_complex:
- self.infix = False
- if (self.type.is_int
- and env.directives['overflowcheck']
- and self.operator in self.overflow_op_names):
- if (self.operator in ('+', '*')
- and self.operand1.has_constant_result()
- and not self.operand2.has_constant_result()):
- self.operand1, self.operand2 = self.operand2, self.operand1
- self.overflow_check = True
- self.overflow_fold = env.directives['overflowcheck.fold']
- self.func = self.type.overflow_check_binop(
- self.overflow_op_names[self.operator],
- env,
- const_rhs = self.operand2.has_constant_result())
- self.is_temp = True
- if not self.infix or (type1.is_numeric and type2.is_numeric):
- self.operand1 = self.operand1.coerce_to(self.type, env)
- self.operand2 = self.operand2.coerce_to(self.type, env)
- def compute_c_result_type(self, type1, type2):
- if self.c_types_okay(type1, type2):
- widest_type = PyrexTypes.widest_numeric_type(type1, type2)
- if widest_type is PyrexTypes.c_bint_type:
- if self.operator not in '|^&':
- # False + False == 0 # not False!
- widest_type = PyrexTypes.c_int_type
- else:
- widest_type = PyrexTypes.widest_numeric_type(
- widest_type, PyrexTypes.c_int_type)
- return widest_type
- else:
- return None
- def may_be_none(self):
- if self.type and self.type.is_builtin_type:
- # if we know the result type, we know the operation, so it can't be None
- return False
- type1 = self.operand1.type
- type2 = self.operand2.type
- if type1 and type1.is_builtin_type and type2 and type2.is_builtin_type:
- # XXX: I can't think of any case where a binary operation
- # on builtin types evaluates to None - add a special case
- # here if there is one.
- return False
- return super(NumBinopNode, self).may_be_none()
- def get_constant_c_result_code(self):
- value1 = self.operand1.get_constant_c_result_code()
- value2 = self.operand2.get_constant_c_result_code()
- if value1 and value2:
- return "(%s %s %s)" % (value1, self.operator, value2)
- else:
- return None
- def c_types_okay(self, type1, type2):
- #print "NumBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_numeric or type1.is_enum) \
- and (type2.is_numeric or type2.is_enum)
- def generate_evaluation_code(self, code):
- if self.overflow_check:
- self.overflow_bit_node = self
- self.overflow_bit = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % self.overflow_bit)
- super(NumBinopNode, self).generate_evaluation_code(code)
- if self.overflow_check:
- code.putln("if (unlikely(%s)) {" % self.overflow_bit)
- code.putln('PyErr_SetString(PyExc_OverflowError, "value too large");')
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- code.funcstate.release_temp(self.overflow_bit)
- def calculate_result_code(self):
- if self.overflow_bit_node is not None:
- return "%s(%s, %s, &%s)" % (
- self.func,
- self.operand1.result(),
- self.operand2.result(),
- self.overflow_bit_node.overflow_bit)
- elif self.type.is_cpp_class or self.infix:
- if is_pythran_expr(self.type):
- result1, result2 = self.operand1.pythran_result(), self.operand2.pythran_result()
- else:
- result1, result2 = self.operand1.result(), self.operand2.result()
- return "(%s %s %s)" % (result1, self.operator, result2)
- else:
- func = self.type.binary_op(self.operator)
- if func is None:
- error(self.pos, "binary operator %s not supported for %s" % (self.operator, self.type))
- return "%s(%s, %s)" % (
- func,
- self.operand1.result(),
- self.operand2.result())
- def is_py_operation_types(self, type1, type2):
- return (type1.is_unicode_char or
- type2.is_unicode_char or
- BinopNode.is_py_operation_types(self, type1, type2))
- def py_operation_function(self, code):
- function_name = self.py_functions[self.operator]
- if self.inplace:
- function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace')
- return function_name
- py_functions = {
- "|": "PyNumber_Or",
- "^": "PyNumber_Xor",
- "&": "PyNumber_And",
- "<<": "PyNumber_Lshift",
- ">>": "PyNumber_Rshift",
- "+": "PyNumber_Add",
- "-": "PyNumber_Subtract",
- "*": "PyNumber_Multiply",
- "@": "__Pyx_PyNumber_MatrixMultiply",
- "/": "__Pyx_PyNumber_Divide",
- "//": "PyNumber_FloorDivide",
- "%": "PyNumber_Remainder",
- "**": "PyNumber_Power",
- }
- overflow_op_names = {
- "+": "add",
- "-": "sub",
- "*": "mul",
- "<<": "lshift",
- }
- class IntBinopNode(NumBinopNode):
- # Binary operation taking integer arguments.
- def c_types_okay(self, type1, type2):
- #print "IntBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_int or type1.is_enum) \
- and (type2.is_int or type2.is_enum)
- class AddNode(NumBinopNode):
- # '+' operator.
- def is_py_operation_types(self, type1, type2):
- if type1.is_string and type2.is_string or type1.is_pyunicode_ptr and type2.is_pyunicode_ptr:
- return 1
- else:
- return NumBinopNode.is_py_operation_types(self, type1, type2)
- def infer_builtin_types_operation(self, type1, type2):
- # b'abc' + 'abc' raises an exception in Py3,
- # so we can safely infer the Py2 type for bytes here
- string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
- if type1 in string_types and type2 in string_types:
- return string_types[max(string_types.index(type1),
- string_types.index(type2))]
- return None
- def compute_c_result_type(self, type1, type2):
- #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ###
- if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
- return type1
- elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum):
- return type2
- else:
- return NumBinopNode.compute_c_result_type(
- self, type1, type2)
- def py_operation_function(self, code):
- type1, type2 = self.operand1.type, self.operand2.type
- if type1 is unicode_type or type2 is unicode_type:
- if type1 in (unicode_type, str_type) and type2 in (unicode_type, str_type):
- is_unicode_concat = True
- elif isinstance(self.operand1, FormattedValueNode) or isinstance(self.operand2, FormattedValueNode):
- # Assume that even if we don't know the second type, it's going to be a string.
- is_unicode_concat = True
- else:
- # Operation depends on the second type.
- is_unicode_concat = False
- if is_unicode_concat:
- if self.operand1.may_be_none() or self.operand2.may_be_none():
- return '__Pyx_PyUnicode_ConcatSafe'
- else:
- return '__Pyx_PyUnicode_Concat'
- return super(AddNode, self).py_operation_function(code)
- class SubNode(NumBinopNode):
- # '-' operator.
- def compute_c_result_type(self, type1, type2):
- if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
- return type1
- elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array):
- return PyrexTypes.c_ptrdiff_t_type
- else:
- return NumBinopNode.compute_c_result_type(
- self, type1, type2)
- class MulNode(NumBinopNode):
- # '*' operator.
- def is_py_operation_types(self, type1, type2):
- if ((type1.is_string and type2.is_int) or
- (type2.is_string and type1.is_int)):
- return 1
- else:
- return NumBinopNode.is_py_operation_types(self, type1, type2)
- def infer_builtin_types_operation(self, type1, type2):
- # let's assume that whatever builtin type you multiply a string with
- # will either return a string of the same type or fail with an exception
- string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
- if type1 in string_types and type2.is_builtin_type:
- return type1
- if type2 in string_types and type1.is_builtin_type:
- return type2
- # multiplication of containers/numbers with an integer value
- # always (?) returns the same type
- if type1.is_int:
- return type2
- if type2.is_int:
- return type1
- return None
- class MatMultNode(NumBinopNode):
- # '@' operator.
- def is_py_operation_types(self, type1, type2):
- return True
- def generate_evaluation_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("MatrixMultiply", "ObjectHandling.c"))
- super(MatMultNode, self).generate_evaluation_code(code)
- class DivNode(NumBinopNode):
- # '/' or '//' operator.
- cdivision = None
- truedivision = None # == "unknown" if operator == '/'
- ctruedivision = False
- cdivision_warnings = False
- zerodivision_check = None
- def find_compile_time_binary_operator(self, op1, op2):
- func = compile_time_binary_operators[self.operator]
- if self.operator == '/' and self.truedivision is None:
- # => true div for floats, floor div for integers
- if isinstance(op1, _py_int_types) and isinstance(op2, _py_int_types):
- func = compile_time_binary_operators['//']
- return func
- def calculate_constant_result(self):
- op1 = self.operand1.constant_result
- op2 = self.operand2.constant_result
- func = self.find_compile_time_binary_operator(op1, op2)
- self.constant_result = func(
- self.operand1.constant_result,
- self.operand2.constant_result)
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- func = self.find_compile_time_binary_operator(
- operand1, operand2)
- return func(operand1, operand2)
- except Exception as e:
- self.compile_time_value_error(e)
- def _check_truedivision(self, env):
- if self.cdivision or env.directives['cdivision']:
- self.ctruedivision = False
- else:
- self.ctruedivision = self.truedivision
- def infer_type(self, env):
- self._check_truedivision(env)
- return self.result_type(
- self.operand1.infer_type(env),
- self.operand2.infer_type(env), env)
- def analyse_operation(self, env):
- self._check_truedivision(env)
- NumBinopNode.analyse_operation(self, env)
- if self.is_cpp_operation():
- self.cdivision = True
- if not self.type.is_pyobject:
- self.zerodivision_check = (
- self.cdivision is None and not env.directives['cdivision']
- and (not self.operand2.has_constant_result() or
- self.operand2.constant_result == 0))
- if self.zerodivision_check or env.directives['cdivision_warnings']:
- # Need to check ahead of time to warn or raise zero division error
- self.operand1 = self.operand1.coerce_to_simple(env)
- self.operand2 = self.operand2.coerce_to_simple(env)
- def compute_c_result_type(self, type1, type2):
- if self.operator == '/' and self.ctruedivision and not type1.is_cpp_class and not type2.is_cpp_class:
- if not type1.is_float and not type2.is_float:
- widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
- widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
- return widest_type
- return NumBinopNode.compute_c_result_type(self, type1, type2)
- def zero_division_message(self):
- if self.type.is_int:
- return "integer division or modulo by zero"
- else:
- return "float division"
- def generate_evaluation_code(self, code):
- if not self.type.is_pyobject and not self.type.is_complex:
- if self.cdivision is None:
- self.cdivision = (
- code.globalstate.directives['cdivision']
- or self.type.is_float
- or ((self.type.is_numeric or self.type.is_enum) and not self.type.signed)
- )
- if not self.cdivision:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("DivInt", "CMath.c").specialize(self.type))
- NumBinopNode.generate_evaluation_code(self, code)
- self.generate_div_warning_code(code)
- def generate_div_warning_code(self, code):
- in_nogil = self.in_nogil_context
- if not self.type.is_pyobject:
- if self.zerodivision_check:
- if not self.infix:
- zero_test = "%s(%s)" % (self.type.unary_op('zero'), self.operand2.result())
- else:
- zero_test = "%s == 0" % self.operand2.result()
- code.putln("if (unlikely(%s)) {" % zero_test)
- if in_nogil:
- code.put_ensure_gil()
- code.putln('PyErr_SetString(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message())
- if in_nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- if self.type.is_int and self.type.signed and self.operator != '%':
- code.globalstate.use_utility_code(UtilityCode.load_cached("UnaryNegOverflows", "Overflow.c"))
- if self.operand2.type.signed == 2:
- # explicitly signed, no runtime check needed
- minus1_check = 'unlikely(%s == -1)' % self.operand2.result()
- else:
- type_of_op2 = self.operand2.type.empty_declaration_code()
- minus1_check = '(!(((%s)-1) > 0)) && unlikely(%s == (%s)-1)' % (
- type_of_op2, self.operand2.result(), type_of_op2)
- code.putln("else if (sizeof(%s) == sizeof(long) && %s "
- " && unlikely(UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
- self.type.empty_declaration_code(),
- minus1_check,
- self.operand1.result()))
- if in_nogil:
- code.put_ensure_gil()
- code.putln('PyErr_SetString(PyExc_OverflowError, "value too large to perform division");')
- if in_nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- if code.globalstate.directives['cdivision_warnings'] and self.operator != '/':
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CDivisionWarning", "CMath.c"))
- code.putln("if (unlikely((%s < 0) ^ (%s < 0))) {" % (
- self.operand1.result(),
- self.operand2.result()))
- warning_code = "__Pyx_cdivision_warning(%(FILENAME)s, %(LINENO)s)" % {
- 'FILENAME': Naming.filename_cname,
- 'LINENO': Naming.lineno_cname,
- }
- if in_nogil:
- result_code = 'result'
- code.putln("int %s;" % result_code)
- code.put_ensure_gil()
- code.putln(code.set_error_info(self.pos, used=True))
- code.putln("%s = %s;" % (result_code, warning_code))
- code.put_release_ensured_gil()
- else:
- result_code = warning_code
- code.putln(code.set_error_info(self.pos, used=True))
- code.put("if (unlikely(%s)) " % result_code)
- code.put_goto(code.error_label)
- code.putln("}")
- def calculate_result_code(self):
- if self.type.is_complex or self.is_cpp_operation():
- return NumBinopNode.calculate_result_code(self)
- elif self.type.is_float and self.operator == '//':
- return "floor(%s / %s)" % (
- self.operand1.result(),
- self.operand2.result())
- elif self.truedivision or self.cdivision:
- op1 = self.operand1.result()
- op2 = self.operand2.result()
- if self.truedivision:
- if self.type != self.operand1.type:
- op1 = self.type.cast_code(op1)
- if self.type != self.operand2.type:
- op2 = self.type.cast_code(op2)
- return "(%s / %s)" % (op1, op2)
- else:
- return "__Pyx_div_%s(%s, %s)" % (
- self.type.specialization_name(),
- self.operand1.result(),
- self.operand2.result())
- _find_formatting_types = re.compile(
- br"%"
- br"(?:%|" # %%
- br"(?:\([^)]+\))?" # %(name)
- br"[-+#,0-9 ]*([a-z])" # %.2f etc.
- br")").findall
- # These format conversion types can never trigger a Unicode string conversion in Py2.
- _safe_bytes_formats = set([
- # Excludes 's' and 'r', which can generate non-bytes strings.
- b'd', b'i', b'o', b'u', b'x', b'X', b'e', b'E', b'f', b'F', b'g', b'G', b'c', b'b', b'a',
- ])
- class ModNode(DivNode):
- # '%' operator.
- def is_py_operation_types(self, type1, type2):
- return (type1.is_string
- or type2.is_string
- or NumBinopNode.is_py_operation_types(self, type1, type2))
- def infer_builtin_types_operation(self, type1, type2):
- # b'%s' % xyz raises an exception in Py3<3.5, so it's safe to infer the type for Py2 and later Py3's.
- if type1 is unicode_type:
- # None + xyz may be implemented by RHS
- if type2.is_builtin_type or not self.operand1.may_be_none():
- return type1
- elif type1 in (bytes_type, str_type, basestring_type):
- if type2 is unicode_type:
- return type2
- elif type2.is_numeric:
- return type1
- elif self.operand1.is_string_literal:
- if type1 is str_type or type1 is bytes_type:
- if set(_find_formatting_types(self.operand1.value)) <= _safe_bytes_formats:
- return type1
- return basestring_type
- elif type1 is bytes_type and not type2.is_builtin_type:
- return None # RHS might implement '% operator differently in Py3
- else:
- return basestring_type # either str or unicode, can't tell
- return None
- def zero_division_message(self):
- if self.type.is_int:
- return "integer division or modulo by zero"
- else:
- return "float divmod()"
- def analyse_operation(self, env):
- DivNode.analyse_operation(self, env)
- if not self.type.is_pyobject:
- if self.cdivision is None:
- self.cdivision = env.directives['cdivision'] or not self.type.signed
- if not self.cdivision and not self.type.is_int and not self.type.is_float:
- error(self.pos, "mod operator not supported for type '%s'" % self.type)
- def generate_evaluation_code(self, code):
- if not self.type.is_pyobject and not self.cdivision:
- if self.type.is_int:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ModInt", "CMath.c").specialize(self.type))
- else: # float
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ModFloat", "CMath.c").specialize(
- self.type, math_h_modifier=self.type.math_h_modifier))
- # NOTE: skipping over DivNode here
- NumBinopNode.generate_evaluation_code(self, code)
- self.generate_div_warning_code(code)
- def calculate_result_code(self):
- if self.cdivision:
- if self.type.is_float:
- return "fmod%s(%s, %s)" % (
- self.type.math_h_modifier,
- self.operand1.result(),
- self.operand2.result())
- else:
- return "(%s %% %s)" % (
- self.operand1.result(),
- self.operand2.result())
- else:
- return "__Pyx_mod_%s(%s, %s)" % (
- self.type.specialization_name(),
- self.operand1.result(),
- self.operand2.result())
- def py_operation_function(self, code):
- type1, type2 = self.operand1.type, self.operand2.type
- # ("..." % x) must call "x.__rmod__()" for string subtypes.
- if type1 is unicode_type:
- if self.operand1.may_be_none() or (
- type2.is_extension_type and type2.subtype_of(type1) or
- type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
- return '__Pyx_PyUnicode_FormatSafe'
- else:
- return 'PyUnicode_Format'
- elif type1 is str_type:
- if self.operand1.may_be_none() or (
- type2.is_extension_type and type2.subtype_of(type1) or
- type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
- return '__Pyx_PyString_FormatSafe'
- else:
- return '__Pyx_PyString_Format'
- return super(ModNode, self).py_operation_function(code)
- class PowNode(NumBinopNode):
- # '**' operator.
- def analyse_types(self, env):
- if not env.directives['cpow']:
- # Note - the check here won't catch cpow directives that don't use '**'
- # but that's probably OK for a placeholder forward compatibility directive
- error(self.pos, "The 'cpow' directive is provided for forward compatibility "
- "and must be True")
- return super(PowNode, self).analyse_types(env)
- def analyse_c_operation(self, env):
- NumBinopNode.analyse_c_operation(self, env)
- if self.type.is_complex:
- if self.type.real_type.is_float:
- self.operand1 = self.operand1.coerce_to(self.type, env)
- self.operand2 = self.operand2.coerce_to(self.type, env)
- self.pow_func = self.type.binary_op('**')
- else:
- error(self.pos, "complex int powers not supported")
- self.pow_func = "<error>"
- elif self.type.is_float:
- self.pow_func = "pow" + self.type.math_h_modifier
- elif self.type.is_int:
- self.pow_func = "__Pyx_pow_%s" % self.type.empty_declaration_code().replace(' ', '_')
- env.use_utility_code(
- UtilityCode.load_cached("IntPow", "CMath.c").specialize(
- func_name=self.pow_func,
- type=self.type.empty_declaration_code(),
- signed=self.type.signed and 1 or 0))
- elif not self.type.is_error:
- error(self.pos, "got unexpected types for C power operator: %s, %s" %
- (self.operand1.type, self.operand2.type))
- def calculate_result_code(self):
- # Work around MSVC overloading ambiguity.
- def typecast(operand):
- if self.type == operand.type:
- return operand.result()
- else:
- return self.type.cast_code(operand.result())
- return "%s(%s, %s)" % (
- self.pow_func,
- typecast(self.operand1),
- typecast(self.operand2))
- def py_operation_function(self, code):
- if (self.type.is_pyobject and
- self.operand1.constant_result == 2 and
- isinstance(self.operand1.constant_result, _py_int_types) and
- self.operand2.type is py_object_type):
- code.globalstate.use_utility_code(UtilityCode.load_cached('PyNumberPow2', 'Optimize.c'))
- if self.inplace:
- return '__Pyx_PyNumber_InPlacePowerOf2'
- else:
- return '__Pyx_PyNumber_PowerOf2'
- return super(PowNode, self).py_operation_function(code)
- class BoolBinopNode(ExprNode):
- """
- Short-circuiting boolean operation.
- Note that this node provides the same code generation method as
- BoolBinopResultNode to simplify expression nesting.
- operator string "and"/"or"
- operand1 BoolBinopNode/BoolBinopResultNode left operand
- operand2 BoolBinopNode/BoolBinopResultNode right operand
- """
- subexprs = ['operand1', 'operand2']
- is_temp = True
- operator = None
- operand1 = None
- operand2 = None
- def infer_type(self, env):
- type1 = self.operand1.infer_type(env)
- type2 = self.operand2.infer_type(env)
- return PyrexTypes.independent_spanning_type(type1, type2)
- def may_be_none(self):
- if self.operator == 'or':
- return self.operand2.may_be_none()
- else:
- return self.operand1.may_be_none() or self.operand2.may_be_none()
- def calculate_constant_result(self):
- operand1 = self.operand1.constant_result
- operand2 = self.operand2.constant_result
- if self.operator == 'and':
- self.constant_result = operand1 and operand2
- else:
- self.constant_result = operand1 or operand2
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- if self.operator == 'and':
- return operand1 and operand2
- else:
- return operand1 or operand2
- def is_ephemeral(self):
- return self.operand1.is_ephemeral() or self.operand2.is_ephemeral()
- def analyse_types(self, env):
- # Note: we do not do any coercion here as we most likely do not know the final type anyway.
- # We even accept to set self.type to ErrorType if both operands do not have a spanning type.
- # The coercion to the final type and to a "simple" value is left to coerce_to().
- operand1 = self.operand1.analyse_types(env)
- operand2 = self.operand2.analyse_types(env)
- self.type = PyrexTypes.independent_spanning_type(
- operand1.type, operand2.type)
- self.operand1 = self._wrap_operand(operand1, env)
- self.operand2 = self._wrap_operand(operand2, env)
- return self
- def _wrap_operand(self, operand, env):
- if not isinstance(operand, (BoolBinopNode, BoolBinopResultNode)):
- operand = BoolBinopResultNode(operand, self.type, env)
- return operand
- def wrap_operands(self, env):
- """
- Must get called by transforms that want to create a correct BoolBinopNode
- after the type analysis phase.
- """
- self.operand1 = self._wrap_operand(self.operand1, env)
- self.operand2 = self._wrap_operand(self.operand2, env)
- def coerce_to_boolean(self, env):
- return self.coerce_to(PyrexTypes.c_bint_type, env)
- def coerce_to(self, dst_type, env):
- operand1 = self.operand1.coerce_to(dst_type, env)
- operand2 = self.operand2.coerce_to(dst_type, env)
- return BoolBinopNode.from_node(
- self, type=dst_type,
- operator=self.operator,
- operand1=operand1, operand2=operand2)
- def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
- code.mark_pos(self.pos)
- outer_labels = (and_label, or_label)
- if self.operator == 'and':
- my_label = and_label = code.new_label('next_and')
- else:
- my_label = or_label = code.new_label('next_or')
- self.operand1.generate_bool_evaluation_code(
- code, final_result_temp, final_result_type, and_label, or_label, end_label, my_label)
- and_label, or_label = outer_labels
- code.put_label(my_label)
- self.operand2.generate_bool_evaluation_code(
- code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through)
- def generate_evaluation_code(self, code):
- self.allocate_temp_result(code)
- result_type = PyrexTypes.py_object_type if self.type.is_pyobject else self.type
- or_label = and_label = None
- end_label = code.new_label('bool_binop_done')
- self.generate_bool_evaluation_code(code, self.result(), result_type, and_label, or_label, end_label, end_label)
- code.put_label(end_label)
- gil_message = "Truth-testing Python object"
- def check_const(self):
- return self.operand1.check_const() and self.operand2.check_const()
- def generate_subexpr_disposal_code(self, code):
- pass # nothing to do here, all done in generate_evaluation_code()
- def free_subexpr_temps(self, code):
- pass # nothing to do here, all done in generate_evaluation_code()
- def generate_operand1_test(self, code):
- # Generate code to test the truth of the first operand.
- if self.type.is_pyobject:
- test_result = code.funcstate.allocate_temp(
- PyrexTypes.c_bint_type, manage_ref=False)
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- test_result,
- self.operand1.py_result(),
- code.error_goto_if_neg(test_result, self.pos)))
- else:
- test_result = self.operand1.result()
- return (test_result, self.type.is_pyobject)
- class BoolBinopResultNode(ExprNode):
- """
- Intermediate result of a short-circuiting and/or expression.
- Tests the result for 'truthiness' and takes care of coercing the final result
- of the overall expression to the target type.
- Note that this node provides the same code generation method as
- BoolBinopNode to simplify expression nesting.
- arg ExprNode the argument to test
- value ExprNode the coerced result value node
- """
- subexprs = ['arg', 'value']
- is_temp = True
- arg = None
- value = None
- def __init__(self, arg, result_type, env):
- # using 'arg' multiple times, so it must be a simple/temp value
- arg = arg.coerce_to_simple(env)
- # wrap in ProxyNode, in case a transform wants to replace self.arg later
- arg = ProxyNode(arg)
- super(BoolBinopResultNode, self).__init__(
- arg.pos, arg=arg, type=result_type,
- value=CloneNode(arg).coerce_to(result_type, env))
- def coerce_to_boolean(self, env):
- return self.coerce_to(PyrexTypes.c_bint_type, env)
- def coerce_to(self, dst_type, env):
- # unwrap, coerce, rewrap
- arg = self.arg.arg
- if dst_type is PyrexTypes.c_bint_type:
- arg = arg.coerce_to_boolean(env)
- # TODO: unwrap more coercion nodes?
- return BoolBinopResultNode(arg, dst_type, env)
- def nogil_check(self, env):
- # let's leave all errors to BoolBinopNode
- pass
- def generate_operand_test(self, code):
- # Generate code to test the truth of the first operand.
- if self.arg.type.is_pyobject:
- test_result = code.funcstate.allocate_temp(
- PyrexTypes.c_bint_type, manage_ref=False)
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- test_result,
- self.arg.py_result(),
- code.error_goto_if_neg(test_result, self.pos)))
- else:
- test_result = self.arg.result()
- return (test_result, self.arg.type.is_pyobject)
- def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
- code.mark_pos(self.pos)
- # x => x
- # x and ... or ... => next 'and' / 'or'
- # False ... or x => next 'or'
- # True and x => next 'and'
- # True or x => True (operand)
- self.arg.generate_evaluation_code(code)
- if and_label or or_label:
- test_result, uses_temp = self.generate_operand_test(code)
- if uses_temp and (and_label and or_label):
- # cannot become final result => free early
- # disposal: uses_temp and (and_label and or_label)
- self.arg.generate_disposal_code(code)
- sense = '!' if or_label else ''
- code.putln("if (%s%s) {" % (sense, test_result))
- if uses_temp:
- code.funcstate.release_temp(test_result)
- if not uses_temp or not (and_label and or_label):
- # disposal: (not uses_temp) or {not (and_label and or_label) [if]}
- self.arg.generate_disposal_code(code)
- if or_label and or_label != fall_through:
- # value is false => short-circuit to next 'or'
- code.put_goto(or_label)
- if and_label:
- # value is true => go to next 'and'
- if or_label:
- code.putln("} else {")
- if not uses_temp:
- # disposal: (not uses_temp) and {(and_label and or_label) [else]}
- self.arg.generate_disposal_code(code)
- if and_label != fall_through:
- code.put_goto(and_label)
- if not and_label or not or_label:
- # if no next 'and' or 'or', we provide the result
- if and_label or or_label:
- code.putln("} else {")
- self.value.generate_evaluation_code(code)
- self.value.make_owned_reference(code)
- code.putln("%s = %s;" % (final_result_temp, self.value.result_as(final_result_type)))
- self.value.generate_post_assignment_code(code)
- # disposal: {not (and_label and or_label) [else]}
- self.arg.generate_disposal_code(code)
- self.value.free_temps(code)
- if end_label != fall_through:
- code.put_goto(end_label)
- if and_label or or_label:
- code.putln("}")
- self.arg.free_temps(code)
- class CondExprNode(ExprNode):
- # Short-circuiting conditional expression.
- #
- # test ExprNode
- # true_val ExprNode
- # false_val ExprNode
- true_val = None
- false_val = None
- is_temp = True
- subexprs = ['test', 'true_val', 'false_val']
- def type_dependencies(self, env):
- return self.true_val.type_dependencies(env) + self.false_val.type_dependencies(env)
- def infer_type(self, env):
- return PyrexTypes.independent_spanning_type(
- self.true_val.infer_type(env),
- self.false_val.infer_type(env))
- def calculate_constant_result(self):
- if self.test.constant_result:
- self.constant_result = self.true_val.constant_result
- else:
- self.constant_result = self.false_val.constant_result
- def is_ephemeral(self):
- return self.true_val.is_ephemeral() or self.false_val.is_ephemeral()
- def analyse_types(self, env):
- self.test = self.test.analyse_types(env).coerce_to_boolean(env)
- self.true_val = self.true_val.analyse_types(env)
- self.false_val = self.false_val.analyse_types(env)
- return self.analyse_result_type(env)
- def analyse_result_type(self, env):
- true_val_type = self.true_val.type
- false_val_type = self.false_val.type
- self.type = PyrexTypes.independent_spanning_type(true_val_type, false_val_type)
- if self.type.is_reference:
- self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- elif self.true_val.is_ephemeral() or self.false_val.is_ephemeral():
- error(self.pos, "Unsafe C derivative of temporary Python reference used in conditional expression")
- if true_val_type.is_pyobject or false_val_type.is_pyobject:
- if true_val_type != self.type:
- self.true_val = self.true_val.coerce_to(self.type, env)
- if false_val_type != self.type:
- self.false_val = self.false_val.coerce_to(self.type, env)
- if self.type.is_error:
- self.type_error()
- return self
- def coerce_to_integer(self, env):
- if not self.true_val.type.is_int:
- self.true_val = self.true_val.coerce_to_integer(env)
- if not self.false_val.type.is_int:
- self.false_val = self.false_val.coerce_to_integer(env)
- self.result_ctype = None
- return self.analyse_result_type(env)
- def coerce_to(self, dst_type, env):
- if self.true_val.type != dst_type:
- self.true_val = self.true_val.coerce_to(dst_type, env)
- if self.false_val.type != dst_type:
- self.false_val = self.false_val.coerce_to(dst_type, env)
- self.result_ctype = None
- return self.analyse_result_type(env)
- def type_error(self):
- if not (self.true_val.type.is_error or self.false_val.type.is_error):
- error(self.pos, "Incompatible types in conditional expression (%s; %s)" %
- (self.true_val.type, self.false_val.type))
- self.type = PyrexTypes.error_type
- def check_const(self):
- return (self.test.check_const()
- and self.true_val.check_const()
- and self.false_val.check_const())
- def generate_evaluation_code(self, code):
- # Because subexprs may not be evaluated we can use a more optimal
- # subexpr allocation strategy than the default, so override evaluation_code.
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- self.test.generate_evaluation_code(code)
- code.putln("if (%s) {" % self.test.result())
- self.eval_and_get(code, self.true_val)
- code.putln("} else {")
- self.eval_and_get(code, self.false_val)
- code.putln("}")
- self.test.generate_disposal_code(code)
- self.test.free_temps(code)
- def eval_and_get(self, code, expr):
- expr.generate_evaluation_code(code)
- if self.type.is_memoryviewslice:
- expr.make_owned_memoryviewslice(code)
- else:
- expr.make_owned_reference(code)
- code.putln('%s = %s;' % (self.result(), expr.result_as(self.ctype())))
- expr.generate_post_assignment_code(code)
- expr.free_temps(code)
- def generate_subexpr_disposal_code(self, code):
- pass # done explicitly above (cleanup must separately happen within the if/else blocks)
- def free_subexpr_temps(self, code):
- pass # done explicitly above (cleanup must separately happen within the if/else blocks)
- richcmp_constants = {
- "<" : "Py_LT",
- "<=": "Py_LE",
- "==": "Py_EQ",
- "!=": "Py_NE",
- "<>": "Py_NE",
- ">" : "Py_GT",
- ">=": "Py_GE",
- # the following are faked by special compare functions
- "in" : "Py_EQ",
- "not_in": "Py_NE",
- }
- class CmpNode(object):
- # Mixin class containing code common to PrimaryCmpNodes
- # and CascadedCmpNodes.
- special_bool_cmp_function = None
- special_bool_cmp_utility_code = None
- def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
- return py_object_type
- def calculate_cascaded_constant_result(self, operand1_result):
- func = compile_time_binary_operators[self.operator]
- operand2_result = self.operand2.constant_result
- if (isinstance(operand1_result, any_string_type) and
- isinstance(operand2_result, any_string_type) and
- type(operand1_result) != type(operand2_result)):
- # string comparison of different types isn't portable
- return
- if self.operator in ('in', 'not_in'):
- if isinstance(self.operand2, (ListNode, TupleNode, SetNode)):
- if not self.operand2.args:
- self.constant_result = self.operator == 'not_in'
- return
- elif isinstance(self.operand2, ListNode) and not self.cascade:
- # tuples are more efficient to store than lists
- self.operand2 = self.operand2.as_tuple()
- elif isinstance(self.operand2, DictNode):
- if not self.operand2.key_value_pairs:
- self.constant_result = self.operator == 'not_in'
- return
- self.constant_result = func(operand1_result, operand2_result)
- def cascaded_compile_time_value(self, operand1, denv):
- func = get_compile_time_binop(self)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- result = func(operand1, operand2)
- except Exception as e:
- self.compile_time_value_error(e)
- result = None
- if result:
- cascade = self.cascade
- if cascade:
- result = result and cascade.cascaded_compile_time_value(operand2, denv)
- return result
- def is_cpp_comparison(self):
- return self.operand1.type.is_cpp_class or self.operand2.type.is_cpp_class
- def find_common_int_type(self, env, op, operand1, operand2):
- # type1 != type2 and at least one of the types is not a C int
- type1 = operand1.type
- type2 = operand2.type
- type1_can_be_int = False
- type2_can_be_int = False
- if operand1.is_string_literal and operand1.can_coerce_to_char_literal():
- type1_can_be_int = True
- if operand2.is_string_literal and operand2.can_coerce_to_char_literal():
- type2_can_be_int = True
- if type1.is_int:
- if type2_can_be_int:
- return type1
- elif type2.is_int:
- if type1_can_be_int:
- return type2
- elif type1_can_be_int:
- if type2_can_be_int:
- if Builtin.unicode_type in (type1, type2):
- return PyrexTypes.c_py_ucs4_type
- else:
- return PyrexTypes.c_uchar_type
- return None
- def find_common_type(self, env, op, operand1, common_type=None):
- operand2 = self.operand2
- type1 = operand1.type
- type2 = operand2.type
- new_common_type = None
- # catch general errors
- if (type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or
- type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type))):
- error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
- new_common_type = error_type
- # try to use numeric comparisons where possible
- elif type1.is_complex or type2.is_complex:
- if (op not in ('==', '!=')
- and (type1.is_complex or type1.is_numeric)
- and (type2.is_complex or type2.is_numeric)):
- error(self.pos, "complex types are unordered")
- new_common_type = error_type
- elif type1.is_pyobject:
- new_common_type = Builtin.complex_type if type1.subtype_of(Builtin.complex_type) else py_object_type
- elif type2.is_pyobject:
- new_common_type = Builtin.complex_type if type2.subtype_of(Builtin.complex_type) else py_object_type
- else:
- new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
- elif type1.is_numeric and type2.is_numeric:
- new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
- elif common_type is None or not common_type.is_pyobject:
- new_common_type = self.find_common_int_type(env, op, operand1, operand2)
- if new_common_type is None:
- # fall back to generic type compatibility tests
- if type1.is_ctuple or type2.is_ctuple:
- new_common_type = py_object_type
- elif type1 == type2:
- new_common_type = type1
- elif type1.is_pyobject or type2.is_pyobject:
- if type2.is_numeric or type2.is_string:
- if operand2.check_for_coercion_error(type1, env):
- new_common_type = error_type
- else:
- new_common_type = py_object_type
- elif type1.is_numeric or type1.is_string:
- if operand1.check_for_coercion_error(type2, env):
- new_common_type = error_type
- else:
- new_common_type = py_object_type
- elif py_object_type.assignable_from(type1) and py_object_type.assignable_from(type2):
- new_common_type = py_object_type
- else:
- # one Python type and one non-Python type, not assignable
- self.invalid_types_error(operand1, op, operand2)
- new_common_type = error_type
- elif type1.assignable_from(type2):
- new_common_type = type1
- elif type2.assignable_from(type1):
- new_common_type = type2
- else:
- # C types that we couldn't handle up to here are an error
- self.invalid_types_error(operand1, op, operand2)
- new_common_type = error_type
- if new_common_type.is_string and (isinstance(operand1, BytesNode) or
- isinstance(operand2, BytesNode)):
- # special case when comparing char* to bytes literal: must
- # compare string values!
- new_common_type = bytes_type
- # recursively merge types
- if common_type is None or new_common_type.is_error:
- common_type = new_common_type
- else:
- # we could do a lot better by splitting the comparison
- # into a non-Python part and a Python part, but this is
- # safer for now
- common_type = PyrexTypes.spanning_type(common_type, new_common_type)
- if self.cascade:
- common_type = self.cascade.find_common_type(env, self.operator, operand2, common_type)
- return common_type
- def invalid_types_error(self, operand1, op, operand2):
- error(self.pos, "Invalid types for '%s' (%s, %s)" %
- (op, operand1.type, operand2.type))
- def is_python_comparison(self):
- return (not self.is_ptr_contains()
- and not self.is_c_string_contains()
- and (self.has_python_operands()
- or (self.cascade and self.cascade.is_python_comparison())
- or self.operator in ('in', 'not_in')))
- def coerce_operands_to(self, dst_type, env):
- operand2 = self.operand2
- if operand2.type != dst_type:
- self.operand2 = operand2.coerce_to(dst_type, env)
- if self.cascade:
- self.cascade.coerce_operands_to(dst_type, env)
- def is_python_result(self):
- return ((self.has_python_operands() and
- self.special_bool_cmp_function is None and
- self.operator not in ('is', 'is_not', 'in', 'not_in') and
- not self.is_c_string_contains() and
- not self.is_ptr_contains())
- or (self.cascade and self.cascade.is_python_result()))
- def is_c_string_contains(self):
- return self.operator in ('in', 'not_in') and \
- ((self.operand1.type.is_int
- and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or
- (self.operand1.type.is_unicode_char
- and self.operand2.type is unicode_type))
- def is_ptr_contains(self):
- if self.operator in ('in', 'not_in'):
- container_type = self.operand2.type
- return (container_type.is_ptr or container_type.is_array) \
- and not container_type.is_string
- def find_special_bool_compare_function(self, env, operand1, result_is_bool=False):
- # note: currently operand1 must get coerced to a Python object if we succeed here!
- if self.operator in ('==', '!='):
- type1, type2 = operand1.type, self.operand2.type
- if result_is_bool or (type1.is_builtin_type and type2.is_builtin_type):
- if type1 is Builtin.unicode_type or type2 is Builtin.unicode_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
- return True
- elif type1 is Builtin.bytes_type or type2 is Builtin.bytes_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("BytesEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyBytes_Equals"
- return True
- elif type1 is Builtin.basestring_type or type2 is Builtin.basestring_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
- return True
- elif type1 is Builtin.str_type or type2 is Builtin.str_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("StrEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyString_Equals"
- return True
- elif self.operator in ('in', 'not_in'):
- if self.operand2.type is Builtin.dict_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c")
- self.special_bool_cmp_function = "__Pyx_PyDict_ContainsTF"
- return True
- elif self.operand2.type is Builtin.set_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySetContains", "ObjectHandling.c")
- self.special_bool_cmp_function = "__Pyx_PySet_ContainsTF"
- return True
- elif self.operand2.type is Builtin.unicode_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_ContainsTF"
- return True
- else:
- if not self.operand2.type.is_pyobject:
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySequenceContains", "ObjectHandling.c")
- self.special_bool_cmp_function = "__Pyx_PySequence_ContainsTF"
- return True
- return False
- def generate_operation_code(self, code, result_code,
- operand1, op , operand2):
- if self.type.is_pyobject:
- error_clause = code.error_goto_if_null
- got_ref = "__Pyx_XGOTREF(%s); " % result_code
- if self.special_bool_cmp_function:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyBoolOrNullFromLong", "ObjectHandling.c"))
- coerce_result = "__Pyx_PyBoolOrNull_FromLong"
- else:
- coerce_result = "__Pyx_PyBool_FromLong"
- else:
- error_clause = code.error_goto_if_neg
- got_ref = ""
- coerce_result = ""
- if self.special_bool_cmp_function:
- if operand1.type.is_pyobject:
- result1 = operand1.py_result()
- else:
- result1 = operand1.result()
- if operand2.type.is_pyobject:
- result2 = operand2.py_result()
- else:
- result2 = operand2.result()
- if self.special_bool_cmp_utility_code:
- code.globalstate.use_utility_code(self.special_bool_cmp_utility_code)
- code.putln(
- "%s = %s(%s(%s, %s, %s)); %s%s" % (
- result_code,
- coerce_result,
- self.special_bool_cmp_function,
- result1, result2, richcmp_constants[op],
- got_ref,
- error_clause(result_code, self.pos)))
- elif operand1.type.is_pyobject and op not in ('is', 'is_not'):
- assert op not in ('in', 'not_in'), op
- code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s%s" % (
- result_code,
- operand1.py_result(),
- operand2.py_result(),
- richcmp_constants[op],
- got_ref,
- error_clause(result_code, self.pos)))
- elif operand1.type.is_complex:
- code.putln("%s = %s(%s%s(%s, %s));" % (
- result_code,
- coerce_result,
- op == "!=" and "!" or "",
- operand1.type.unary_op('eq'),
- operand1.result(),
- operand2.result()))
- else:
- type1 = operand1.type
- type2 = operand2.type
- if (type1.is_extension_type or type2.is_extension_type) \
- and not type1.same_as(type2):
- common_type = py_object_type
- elif type1.is_numeric:
- common_type = PyrexTypes.widest_numeric_type(type1, type2)
- else:
- common_type = type1
- code1 = operand1.result_as(common_type)
- code2 = operand2.result_as(common_type)
- statement = "%s = %s(%s %s %s);" % (
- result_code,
- coerce_result,
- code1,
- self.c_operator(op),
- code2)
- if self.is_cpp_comparison() and self.exception_check == '+':
- translate_cpp_exception(
- code,
- self.pos,
- statement,
- result_code if self.type.is_pyobject else None,
- self.exception_value,
- self.in_nogil_context)
- else:
- code.putln(statement)
- def c_operator(self, op):
- if op == 'is':
- return "=="
- elif op == 'is_not':
- return "!="
- else:
- return op
- class PrimaryCmpNode(ExprNode, CmpNode):
- # Non-cascaded comparison or first comparison of
- # a cascaded sequence.
- #
- # operator string
- # operand1 ExprNode
- # operand2 ExprNode
- # cascade CascadedCmpNode
- # We don't use the subexprs mechanism, because
- # things here are too complicated for it to handle.
- # Instead, we override all the framework methods
- # which use it.
- child_attrs = ['operand1', 'operand2', 'coerced_operand2', 'cascade']
- cascade = None
- coerced_operand2 = None
- is_memslice_nonecheck = False
- def infer_type(self, env):
- type1 = self.operand1.infer_type(env)
- type2 = self.operand2.infer_type(env)
- if is_pythran_expr(type1) or is_pythran_expr(type2):
- if is_pythran_supported_type(type1) and is_pythran_supported_type(type2):
- return PythranExpr(pythran_binop_type(self.operator, type1, type2))
- # TODO: implement this for other types.
- return py_object_type
- def type_dependencies(self, env):
- return ()
- def calculate_constant_result(self):
- assert not self.cascade
- self.calculate_cascaded_constant_result(self.operand1.constant_result)
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- return self.cascaded_compile_time_value(operand1, denv)
- def analyse_types(self, env):
- self.operand1 = self.operand1.analyse_types(env)
- self.operand2 = self.operand2.analyse_types(env)
- if self.is_cpp_comparison():
- self.analyse_cpp_comparison(env)
- if self.cascade:
- error(self.pos, "Cascading comparison not yet supported for cpp types.")
- return self
- type1 = self.operand1.type
- type2 = self.operand2.type
- if is_pythran_expr(type1) or is_pythran_expr(type2):
- if is_pythran_supported_type(type1) and is_pythran_supported_type(type2):
- self.type = PythranExpr(pythran_binop_type(self.operator, type1, type2))
- self.is_pycmp = False
- return self
- if self.analyse_memoryviewslice_comparison(env):
- return self
- if self.cascade:
- self.cascade = self.cascade.analyse_types(env)
- if self.operator in ('in', 'not_in'):
- if self.is_c_string_contains():
- self.is_pycmp = False
- common_type = None
- if self.cascade:
- error(self.pos, "Cascading comparison not yet supported for 'int_val in string'.")
- return self
- if self.operand2.type is unicode_type:
- env.use_utility_code(UtilityCode.load_cached("PyUCS4InUnicode", "StringTools.c"))
- else:
- if self.operand1.type is PyrexTypes.c_uchar_type:
- self.operand1 = self.operand1.coerce_to(PyrexTypes.c_char_type, env)
- if self.operand2.type is not bytes_type:
- self.operand2 = self.operand2.coerce_to(bytes_type, env)
- env.use_utility_code(UtilityCode.load_cached("BytesContains", "StringTools.c"))
- self.operand2 = self.operand2.as_none_safe_node(
- "argument of type 'NoneType' is not iterable")
- elif self.is_ptr_contains():
- if self.cascade:
- error(self.pos, "Cascading comparison not supported for 'val in sliced pointer'.")
- self.type = PyrexTypes.c_bint_type
- # Will be transformed by IterationTransform
- return self
- elif self.find_special_bool_compare_function(env, self.operand1):
- if not self.operand1.type.is_pyobject:
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
- else:
- common_type = py_object_type
- self.is_pycmp = True
- elif self.find_special_bool_compare_function(env, self.operand1):
- if not self.operand1.type.is_pyobject:
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
- else:
- common_type = self.find_common_type(env, self.operator, self.operand1)
- self.is_pycmp = common_type.is_pyobject
- if common_type is not None and not common_type.is_error:
- if self.operand1.type != common_type:
- self.operand1 = self.operand1.coerce_to(common_type, env)
- self.coerce_operands_to(common_type, env)
- if self.cascade:
- self.operand2 = self.operand2.coerce_to_simple(env)
- self.cascade.coerce_cascaded_operands_to_temp(env)
- operand2 = self.cascade.optimise_comparison(self.operand2, env)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- if self.is_python_result():
- self.type = PyrexTypes.py_object_type
- else:
- self.type = PyrexTypes.c_bint_type
- cdr = self.cascade
- while cdr:
- cdr.type = self.type
- cdr = cdr.cascade
- if self.is_pycmp or self.cascade or self.special_bool_cmp_function:
- # 1) owned reference, 2) reused value, 3) potential function error return value
- self.is_temp = 1
- return self
- def analyse_cpp_comparison(self, env):
- type1 = self.operand1.type
- type2 = self.operand2.type
- self.is_pycmp = False
- entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
- if entry is None:
- error(self.pos, "Invalid types for '%s' (%s, %s)" %
- (self.operator, type1, type2))
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- func_type = entry.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- self.exception_check = func_type.exception_check
- self.exception_value = func_type.exception_value
- if self.exception_check == '+':
- self.is_temp = True
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- if len(func_type.args) == 1:
- self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
- else:
- self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
- self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
- self.type = func_type.return_type
- def analyse_memoryviewslice_comparison(self, env):
- have_none = self.operand1.is_none or self.operand2.is_none
- have_slice = (self.operand1.type.is_memoryviewslice or
- self.operand2.type.is_memoryviewslice)
- ops = ('==', '!=', 'is', 'is_not')
- if have_slice and have_none and self.operator in ops:
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- self.is_memslice_nonecheck = True
- return True
- return False
- def coerce_to_boolean(self, env):
- if self.is_pycmp:
- # coercing to bool => may allow for more efficient comparison code
- if self.find_special_bool_compare_function(
- env, self.operand1, result_is_bool=True):
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- self.is_temp = 1
- if self.cascade:
- operand2 = self.cascade.optimise_comparison(
- self.operand2, env, result_is_bool=True)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- return self
- # TODO: check if we can optimise parts of the cascade here
- return ExprNode.coerce_to_boolean(self, env)
- def has_python_operands(self):
- return (self.operand1.type.is_pyobject
- or self.operand2.type.is_pyobject)
- def check_const(self):
- if self.cascade:
- self.not_const()
- return False
- else:
- return self.operand1.check_const() and self.operand2.check_const()
- def calculate_result_code(self):
- operand1, operand2 = self.operand1, self.operand2
- if operand1.type.is_complex:
- if self.operator == "!=":
- negation = "!"
- else:
- negation = ""
- return "(%s%s(%s, %s))" % (
- negation,
- operand1.type.binary_op('=='),
- operand1.result(),
- operand2.result())
- elif self.is_c_string_contains():
- if operand2.type is unicode_type:
- method = "__Pyx_UnicodeContainsUCS4"
- else:
- method = "__Pyx_BytesContains"
- if self.operator == "not_in":
- negation = "!"
- else:
- negation = ""
- return "(%s%s(%s, %s))" % (
- negation,
- method,
- operand2.result(),
- operand1.result())
- else:
- if is_pythran_expr(self.type):
- result1, result2 = operand1.pythran_result(), operand2.pythran_result()
- else:
- result1, result2 = operand1.result(), operand2.result()
- if self.is_memslice_nonecheck:
- if operand1.type.is_memoryviewslice:
- result1 = "((PyObject *) %s.memview)" % result1
- else:
- result2 = "((PyObject *) %s.memview)" % result2
- return "(%s %s %s)" % (
- result1,
- self.c_operator(self.operator),
- result2)
- def generate_evaluation_code(self, code):
- self.operand1.generate_evaluation_code(code)
- self.operand2.generate_evaluation_code(code)
- if self.is_temp:
- self.allocate_temp_result(code)
- self.generate_operation_code(code, self.result(),
- self.operand1, self.operator, self.operand2)
- if self.cascade:
- self.cascade.generate_evaluation_code(
- code, self.result(), self.coerced_operand2 or self.operand2,
- needs_evaluation=self.coerced_operand2 is not None)
- self.operand1.generate_disposal_code(code)
- self.operand1.free_temps(code)
- self.operand2.generate_disposal_code(code)
- self.operand2.free_temps(code)
- def generate_subexpr_disposal_code(self, code):
- # If this is called, it is a non-cascaded cmp,
- # so only need to dispose of the two main operands.
- self.operand1.generate_disposal_code(code)
- self.operand2.generate_disposal_code(code)
- def free_subexpr_temps(self, code):
- # If this is called, it is a non-cascaded cmp,
- # so only need to dispose of the two main operands.
- self.operand1.free_temps(code)
- self.operand2.free_temps(code)
- def annotate(self, code):
- self.operand1.annotate(code)
- self.operand2.annotate(code)
- if self.cascade:
- self.cascade.annotate(code)
- class CascadedCmpNode(Node, CmpNode):
- # A CascadedCmpNode is not a complete expression node. It
- # hangs off the side of another comparison node, shares
- # its left operand with that node, and shares its result
- # with the PrimaryCmpNode at the head of the chain.
- #
- # operator string
- # operand2 ExprNode
- # cascade CascadedCmpNode
- child_attrs = ['operand2', 'coerced_operand2', 'cascade']
- cascade = None
- coerced_operand2 = None
- constant_result = constant_value_not_set # FIXME: where to calculate this?
- def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
- return py_object_type
- def type_dependencies(self, env):
- return ()
- def has_constant_result(self):
- return self.constant_result is not constant_value_not_set and \
- self.constant_result is not not_a_constant
- def analyse_types(self, env):
- self.operand2 = self.operand2.analyse_types(env)
- if self.cascade:
- self.cascade = self.cascade.analyse_types(env)
- return self
- def has_python_operands(self):
- return self.operand2.type.is_pyobject
- def is_cpp_comparison(self):
- # cascaded comparisons aren't currently implemented for c++ classes.
- return False
- def optimise_comparison(self, operand1, env, result_is_bool=False):
- if self.find_special_bool_compare_function(env, operand1, result_is_bool):
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- if not operand1.type.is_pyobject:
- operand1 = operand1.coerce_to_pyobject(env)
- if self.cascade:
- operand2 = self.cascade.optimise_comparison(self.operand2, env, result_is_bool)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- return operand1
- def coerce_operands_to_pyobjects(self, env):
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- if self.operand2.type is dict_type and self.operator in ('in', 'not_in'):
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- if self.cascade:
- self.cascade.coerce_operands_to_pyobjects(env)
- def coerce_cascaded_operands_to_temp(self, env):
- if self.cascade:
- #self.operand2 = self.operand2.coerce_to_temp(env) #CTT
- self.operand2 = self.operand2.coerce_to_simple(env)
- self.cascade.coerce_cascaded_operands_to_temp(env)
- def generate_evaluation_code(self, code, result, operand1, needs_evaluation=False):
- if self.type.is_pyobject:
- code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result)
- code.put_decref(result, self.type)
- else:
- code.putln("if (%s) {" % result)
- if needs_evaluation:
- operand1.generate_evaluation_code(code)
- self.operand2.generate_evaluation_code(code)
- self.generate_operation_code(code, result,
- operand1, self.operator, self.operand2)
- if self.cascade:
- self.cascade.generate_evaluation_code(
- code, result, self.coerced_operand2 or self.operand2,
- needs_evaluation=self.coerced_operand2 is not None)
- if needs_evaluation:
- operand1.generate_disposal_code(code)
- operand1.free_temps(code)
- # Cascaded cmp result is always temp
- self.operand2.generate_disposal_code(code)
- self.operand2.free_temps(code)
- code.putln("}")
- def annotate(self, code):
- self.operand2.annotate(code)
- if self.cascade:
- self.cascade.annotate(code)
- binop_node_classes = {
- "or": BoolBinopNode,
- "and": BoolBinopNode,
- "|": IntBinopNode,
- "^": IntBinopNode,
- "&": IntBinopNode,
- "<<": IntBinopNode,
- ">>": IntBinopNode,
- "+": AddNode,
- "-": SubNode,
- "*": MulNode,
- "@": MatMultNode,
- "/": DivNode,
- "//": DivNode,
- "%": ModNode,
- "**": PowNode,
- }
- def binop_node(pos, operator, operand1, operand2, inplace=False, **kwargs):
- # Construct binop node of appropriate class for
- # given operator.
- return binop_node_classes[operator](
- pos,
- operator=operator,
- operand1=operand1,
- operand2=operand2,
- inplace=inplace,
- **kwargs)
- #-------------------------------------------------------------------
- #
- # Coercion nodes
- #
- # Coercion nodes are special in that they are created during
- # the analyse_types phase of parse tree processing.
- # Their __init__ methods consequently incorporate some aspects
- # of that phase.
- #
- #-------------------------------------------------------------------
- class CoercionNode(ExprNode):
- # Abstract base class for coercion nodes.
- #
- # arg ExprNode node being coerced
- subexprs = ['arg']
- constant_result = not_a_constant
- def __init__(self, arg):
- super(CoercionNode, self).__init__(arg.pos)
- self.arg = arg
- if debug_coercion:
- print("%s Coercing %s" % (self, self.arg))
- def calculate_constant_result(self):
- # constant folding can break type coercion, so this is disabled
- pass
- def annotate(self, code):
- self.arg.annotate(code)
- if self.arg.type != self.type:
- file, line, col = self.pos
- code.annotate((file, line, col-1), AnnotationItem(
- style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
- class CoerceToMemViewSliceNode(CoercionNode):
- """
- Coerce an object to a memoryview slice. This holds a new reference in
- a managed temp.
- """
- def __init__(self, arg, dst_type, env):
- assert dst_type.is_memoryviewslice
- assert not arg.type.is_memoryviewslice
- CoercionNode.__init__(self, arg)
- self.type = dst_type
- self.is_temp = 1
- self.use_managed_ref = True
- self.arg = arg
- self.type.create_from_py_utility_code(env)
- def generate_result_code(self, code):
- code.putln(self.type.from_py_call_code(
- self.arg.py_result(),
- self.result(),
- self.pos,
- code
- ))
- class CastNode(CoercionNode):
- # Wrap a node in a C type cast.
- def __init__(self, arg, new_type):
- CoercionNode.__init__(self, arg)
- self.type = new_type
- def may_be_none(self):
- return self.arg.may_be_none()
- def calculate_result_code(self):
- return self.arg.result_as(self.type)
- def generate_result_code(self, code):
- self.arg.generate_result_code(code)
- class PyTypeTestNode(CoercionNode):
- # This node is used to check that a generic Python
- # object is an instance of a particular extension type.
- # This node borrows the result of its argument node.
- exact_builtin_type = True
- def __init__(self, arg, dst_type, env, notnone=False):
- # The arg is know to be a Python object, and
- # the dst_type is known to be an extension type.
- assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
- CoercionNode.__init__(self, arg)
- self.type = dst_type
- self.result_ctype = arg.ctype()
- self.notnone = notnone
- nogil_check = Node.gil_error
- gil_message = "Python type test"
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- if self.notnone:
- return False
- return self.arg.may_be_none()
- def is_simple(self):
- return self.arg.is_simple()
- def result_in_temp(self):
- return self.arg.result_in_temp()
- def is_ephemeral(self):
- return self.arg.is_ephemeral()
- def nonlocally_immutable(self):
- return self.arg.nonlocally_immutable()
- def reanalyse(self):
- if self.type != self.arg.type or not self.arg.is_temp:
- return self
- if not self.type.typeobj_is_available():
- return self
- if self.arg.may_be_none() and self.notnone:
- return self.arg.as_none_safe_node("Cannot convert NoneType to %.200s" % self.type.name)
- return self.arg
- def calculate_constant_result(self):
- # FIXME
- pass
- def calculate_result_code(self):
- return self.arg.result()
- def generate_result_code(self, code):
- if self.type.typeobj_is_available():
- if self.type.is_builtin_type:
- type_test = self.type.type_test_code(
- self.arg.py_result(),
- self.notnone, exact=self.exact_builtin_type)
- else:
- type_test = self.type.type_test_code(
- self.arg.py_result(), self.notnone)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
- code.putln("if (!(%s)) %s" % (
- type_test, code.error_goto(self.pos)))
- else:
- error(self.pos, "Cannot test type of extern C class "
- "without type object name specification")
- def generate_post_assignment_code(self, code):
- self.arg.generate_post_assignment_code(code)
- def allocate_temp_result(self, code):
- pass
- def release_temp_result(self, code):
- pass
- def free_temps(self, code):
- self.arg.free_temps(code)
- def free_subexpr_temps(self, code):
- self.arg.free_subexpr_temps(code)
- class NoneCheckNode(CoercionNode):
- # This node is used to check that a Python object is not None and
- # raises an appropriate exception (as specified by the creating
- # transform).
- is_nonecheck = True
- def __init__(self, arg, exception_type_cname, exception_message,
- exception_format_args=()):
- CoercionNode.__init__(self, arg)
- self.type = arg.type
- self.result_ctype = arg.ctype()
- self.exception_type_cname = exception_type_cname
- self.exception_message = exception_message
- self.exception_format_args = tuple(exception_format_args or ())
- nogil_check = None # this node only guards an operation that would fail already
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- return False
- def is_simple(self):
- return self.arg.is_simple()
- def result_in_temp(self):
- return self.arg.result_in_temp()
- def nonlocally_immutable(self):
- return self.arg.nonlocally_immutable()
- def calculate_result_code(self):
- return self.arg.result()
- def condition(self):
- if self.type.is_pyobject:
- return self.arg.py_result()
- elif self.type.is_memoryviewslice:
- return "((PyObject *) %s.memview)" % self.arg.result()
- else:
- raise Exception("unsupported type")
- @classmethod
- def generate(cls, arg, code, exception_message,
- exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
- node = cls(arg, exception_type_cname, exception_message, exception_format_args)
- node.in_nogil_context = in_nogil_context
- node.put_nonecheck(code)
- @classmethod
- def generate_if_needed(cls, arg, code, exception_message,
- exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
- if arg.may_be_none():
- cls.generate(arg, code, exception_message, exception_type_cname, exception_format_args, in_nogil_context)
- def put_nonecheck(self, code):
- code.putln(
- "if (unlikely(%s == Py_None)) {" % self.condition())
- if self.in_nogil_context:
- code.put_ensure_gil()
- escape = StringEncoding.escape_byte_string
- if self.exception_format_args:
- code.putln('PyErr_Format(%s, "%s", %s);' % (
- self.exception_type_cname,
- StringEncoding.escape_byte_string(
- self.exception_message.encode('UTF-8')),
- ', '.join([ '"%s"' % escape(str(arg).encode('UTF-8'))
- for arg in self.exception_format_args ])))
- else:
- code.putln('PyErr_SetString(%s, "%s");' % (
- self.exception_type_cname,
- escape(self.exception_message.encode('UTF-8'))))
- if self.in_nogil_context:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- def generate_result_code(self, code):
- self.put_nonecheck(code)
- def generate_post_assignment_code(self, code):
- self.arg.generate_post_assignment_code(code)
- def free_temps(self, code):
- self.arg.free_temps(code)
- class CoerceToPyTypeNode(CoercionNode):
- # This node is used to convert a C data type
- # to a Python object.
- type = py_object_type
- target_type = py_object_type
- is_temp = 1
- def __init__(self, arg, env, type=py_object_type):
- if not arg.type.create_to_py_utility_code(env):
- error(arg.pos, "Cannot convert '%s' to Python object" % arg.type)
- elif arg.type.is_complex:
- # special case: complex coercion is so complex that it
- # uses a macro ("__pyx_PyComplex_FromComplex()"), for
- # which the argument must be simple
- arg = arg.coerce_to_simple(env)
- CoercionNode.__init__(self, arg)
- if type is py_object_type:
- # be specific about some known types
- if arg.type.is_string or arg.type.is_cpp_string:
- self.type = default_str_type(env)
- elif arg.type.is_pyunicode_ptr or arg.type.is_unicode_char:
- self.type = unicode_type
- elif arg.type.is_complex:
- self.type = Builtin.complex_type
- self.target_type = self.type
- elif arg.type.is_string or arg.type.is_cpp_string:
- if (type not in (bytes_type, bytearray_type)
- and not env.directives['c_string_encoding']):
- error(arg.pos,
- "default encoding required for conversion from '%s' to '%s'" %
- (arg.type, type))
- self.type = self.target_type = type
- else:
- # FIXME: check that the target type and the resulting type are compatible
- self.target_type = type
- gil_message = "Converting to Python object"
- def may_be_none(self):
- # FIXME: is this always safe?
- return False
- def coerce_to_boolean(self, env):
- arg_type = self.arg.type
- if (arg_type == PyrexTypes.c_bint_type or
- (arg_type.is_pyobject and arg_type.name == 'bool')):
- return self.arg.coerce_to_temp(env)
- else:
- return CoerceToBooleanNode(self, env)
- def coerce_to_integer(self, env):
- # If not already some C integer type, coerce to longint.
- if self.arg.type.is_int:
- return self.arg
- else:
- return self.arg.coerce_to(PyrexTypes.c_long_type, env)
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
- def generate_result_code(self, code):
- code.putln('%s; %s' % (
- self.arg.type.to_py_call_code(
- self.arg.result(),
- self.result(),
- self.target_type),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class CoerceIntToBytesNode(CoerceToPyTypeNode):
- # This node is used to convert a C int type to a Python bytes
- # object.
- is_temp = 1
- def __init__(self, arg, env):
- arg = arg.coerce_to_simple(env)
- CoercionNode.__init__(self, arg)
- self.type = Builtin.bytes_type
- def generate_result_code(self, code):
- arg = self.arg
- arg_result = arg.result()
- if arg.type not in (PyrexTypes.c_char_type,
- PyrexTypes.c_uchar_type,
- PyrexTypes.c_schar_type):
- if arg.type.signed:
- code.putln("if ((%s < 0) || (%s > 255)) {" % (
- arg_result, arg_result))
- else:
- code.putln("if (%s > 255) {" % arg_result)
- code.putln('PyErr_SetString(PyExc_OverflowError, '
- '"value too large to pack into a byte"); %s' % (
- code.error_goto(self.pos)))
- code.putln('}')
- temp = None
- if arg.type is not PyrexTypes.c_char_type:
- temp = code.funcstate.allocate_temp(PyrexTypes.c_char_type, manage_ref=False)
- code.putln("%s = (char)%s;" % (temp, arg_result))
- arg_result = temp
- code.putln('%s = PyBytes_FromStringAndSize(&%s, 1); %s' % (
- self.result(),
- arg_result,
- code.error_goto_if_null(self.result(), self.pos)))
- if temp is not None:
- code.funcstate.release_temp(temp)
- code.put_gotref(self.py_result())
- class CoerceFromPyTypeNode(CoercionNode):
- # This node is used to convert a Python object
- # to a C data type.
- def __init__(self, result_type, arg, env):
- CoercionNode.__init__(self, arg)
- self.type = result_type
- self.is_temp = 1
- if not result_type.create_from_py_utility_code(env):
- error(arg.pos,
- "Cannot convert Python object to '%s'" % result_type)
- if self.type.is_string or self.type.is_pyunicode_ptr:
- if self.arg.is_name and self.arg.entry and self.arg.entry.is_pyglobal:
- warning(arg.pos,
- "Obtaining '%s' from externally modifiable global Python value" % result_type,
- level=1)
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
- def is_ephemeral(self):
- return (self.type.is_ptr and not self.type.is_array) and self.arg.is_ephemeral()
- def generate_result_code(self, code):
- from_py_function = None
- # for certain source types, we can do better than the generic coercion
- if self.type.is_string and self.arg.type is bytes_type:
- if self.type.from_py_function.startswith('__Pyx_PyObject_As'):
- from_py_function = '__Pyx_PyBytes' + self.type.from_py_function[len('__Pyx_PyObject'):]
- NoneCheckNode.generate_if_needed(self.arg, code, "expected bytes, NoneType found")
- code.putln(self.type.from_py_call_code(
- self.arg.py_result(), self.result(), self.pos, code, from_py_function=from_py_function))
- if self.type.is_pyobject:
- code.put_gotref(self.py_result())
- def nogil_check(self, env):
- error(self.pos, "Coercion from Python not allowed without the GIL")
- class CoerceToBooleanNode(CoercionNode):
- # This node is used when a result needs to be used
- # in a boolean context.
- type = PyrexTypes.c_bint_type
- _special_builtins = {
- Builtin.list_type: 'PyList_GET_SIZE',
- Builtin.tuple_type: 'PyTuple_GET_SIZE',
- Builtin.set_type: 'PySet_GET_SIZE',
- Builtin.frozenset_type: 'PySet_GET_SIZE',
- Builtin.bytes_type: 'PyBytes_GET_SIZE',
- Builtin.bytearray_type: 'PyByteArray_GET_SIZE',
- Builtin.unicode_type: '__Pyx_PyUnicode_IS_TRUE',
- }
- def __init__(self, arg, env):
- CoercionNode.__init__(self, arg)
- if arg.type.is_pyobject:
- self.is_temp = 1
- def nogil_check(self, env):
- if self.arg.type.is_pyobject and self._special_builtins.get(self.arg.type) is None:
- self.gil_error()
- gil_message = "Truth-testing Python object"
- def check_const(self):
- if self.is_temp:
- self.not_const()
- return False
- return self.arg.check_const()
- def calculate_result_code(self):
- return "(%s != 0)" % self.arg.result()
- def generate_result_code(self, code):
- if not self.is_temp:
- return
- test_func = self._special_builtins.get(self.arg.type)
- if test_func is not None:
- checks = ["(%s != Py_None)" % self.arg.py_result()] if self.arg.may_be_none() else []
- checks.append("(%s(%s) != 0)" % (test_func, self.arg.py_result()))
- code.putln("%s = %s;" % (self.result(), '&&'.join(checks)))
- else:
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- self.result(),
- self.arg.py_result(),
- code.error_goto_if_neg(self.result(), self.pos)))
- class CoerceToComplexNode(CoercionNode):
- def __init__(self, arg, dst_type, env):
- if arg.type.is_complex:
- arg = arg.coerce_to_simple(env)
- self.type = dst_type
- CoercionNode.__init__(self, arg)
- dst_type.create_declaration_utility_code(env)
- def calculate_result_code(self):
- if self.arg.type.is_complex:
- real_part = "__Pyx_CREAL(%s)" % self.arg.result()
- imag_part = "__Pyx_CIMAG(%s)" % self.arg.result()
- else:
- real_part = self.arg.result()
- imag_part = "0"
- return "%s(%s, %s)" % (
- self.type.from_parts,
- real_part,
- imag_part)
- def generate_result_code(self, code):
- pass
- class CoerceToTempNode(CoercionNode):
- # This node is used to force the result of another node
- # to be stored in a temporary. It is only used if the
- # argument node's result is not already in a temporary.
- def __init__(self, arg, env):
- CoercionNode.__init__(self, arg)
- self.type = self.arg.type.as_argument_type()
- self.constant_result = self.arg.constant_result
- self.is_temp = 1
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- gil_message = "Creating temporary Python reference"
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
- def coerce_to_boolean(self, env):
- self.arg = self.arg.coerce_to_boolean(env)
- if self.arg.is_simple():
- return self.arg
- self.type = self.arg.type
- self.result_ctype = self.type
- return self
- def generate_result_code(self, code):
- #self.arg.generate_evaluation_code(code) # Already done
- # by generic generate_subexpr_evaluation_code!
- code.putln("%s = %s;" % (
- self.result(), self.arg.result_as(self.ctype())))
- if self.use_managed_ref:
- if self.type.is_pyobject:
- code.put_incref(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_incref_memoryviewslice(self.result(),
- not self.in_nogil_context)
- class ProxyNode(CoercionNode):
- """
- A node that should not be replaced by transforms or other means,
- and hence can be useful to wrap the argument to a clone node
- MyNode -> ProxyNode -> ArgNode
- CloneNode -^
- """
- nogil_check = None
- def __init__(self, arg):
- super(ProxyNode, self).__init__(arg)
- self.constant_result = arg.constant_result
- self._proxy_type()
- def analyse_types(self, env):
- self.arg = self.arg.analyse_expressions(env)
- self._proxy_type()
- return self
- def infer_type(self, env):
- return self.arg.infer_type(env)
- def _proxy_type(self):
- if hasattr(self.arg, 'type'):
- self.type = self.arg.type
- self.result_ctype = self.arg.result_ctype
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
- def generate_result_code(self, code):
- self.arg.generate_result_code(code)
- def result(self):
- return self.arg.result()
- def is_simple(self):
- return self.arg.is_simple()
- def may_be_none(self):
- return self.arg.may_be_none()
- def generate_evaluation_code(self, code):
- self.arg.generate_evaluation_code(code)
- def generate_disposal_code(self, code):
- self.arg.generate_disposal_code(code)
- def free_temps(self, code):
- self.arg.free_temps(code)
- class CloneNode(CoercionNode):
- # This node is employed when the result of another node needs
- # to be used multiple times. The argument node's result must
- # be in a temporary. This node "borrows" the result from the
- # argument node, and does not generate any evaluation or
- # disposal code for it. The original owner of the argument
- # node is responsible for doing those things.
- subexprs = [] # Arg is not considered a subexpr
- nogil_check = None
- def __init__(self, arg):
- CoercionNode.__init__(self, arg)
- self.constant_result = arg.constant_result
- if hasattr(arg, 'type'):
- self.type = arg.type
- self.result_ctype = arg.result_ctype
- if hasattr(arg, 'entry'):
- self.entry = arg.entry
- def result(self):
- return self.arg.result()
- def may_be_none(self):
- return self.arg.may_be_none()
- def type_dependencies(self, env):
- return self.arg.type_dependencies(env)
- def infer_type(self, env):
- return self.arg.infer_type(env)
- def analyse_types(self, env):
- self.type = self.arg.type
- self.result_ctype = self.arg.result_ctype
- self.is_temp = 1
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
- return self
- def coerce_to(self, dest_type, env):
- if self.arg.is_literal:
- return self.arg.coerce_to(dest_type, env)
- return super(CloneNode, self).coerce_to(dest_type, env)
- def is_simple(self):
- return True # result is always in a temp (or a name)
- def generate_evaluation_code(self, code):
- pass
- def generate_result_code(self, code):
- pass
- def generate_disposal_code(self, code):
- pass
- def free_temps(self, code):
- pass
- class CMethodSelfCloneNode(CloneNode):
- # Special CloneNode for the self argument of builtin C methods
- # that accepts subtypes of the builtin type. This is safe only
- # for 'final' subtypes, as subtypes of the declared type may
- # override the C method.
- def coerce_to(self, dst_type, env):
- if dst_type.is_builtin_type and self.type.subtype_of(dst_type):
- return self
- return CloneNode.coerce_to(self, dst_type, env)
- class ModuleRefNode(ExprNode):
- # Simple returns the module object
- type = py_object_type
- is_temp = False
- subexprs = []
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- return False
- def calculate_result_code(self):
- return Naming.module_cname
- def generate_result_code(self, code):
- pass
- class DocstringRefNode(ExprNode):
- # Extracts the docstring of the body element
- subexprs = ['body']
- type = py_object_type
- is_temp = True
- def __init__(self, pos, body):
- ExprNode.__init__(self, pos)
- assert body.type.is_pyobject
- self.body = body
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- code.putln('%s = __Pyx_GetAttr(%s, %s); %s' % (
- self.result(), self.body.result(),
- code.intern_identifier(StringEncoding.EncodedString("__doc__")),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- #------------------------------------------------------------------------------------
- #
- # Runtime support code
- #
- #------------------------------------------------------------------------------------
- pyerr_occurred_withgil_utility_code= UtilityCode(
- proto = """
- static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
- """,
- impl = """
- static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
- int err;
- #ifdef WITH_THREAD
- PyGILState_STATE _save = PyGILState_Ensure();
- #endif
- err = !!PyErr_Occurred();
- #ifdef WITH_THREAD
- PyGILState_Release(_save);
- #endif
- return err;
- }
- """
- )
- #------------------------------------------------------------------------------------
- raise_unbound_local_error_utility_code = UtilityCode(
- proto = """
- static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
- """,
- impl = """
- static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
- PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
- }
- """)
- raise_closure_name_error_utility_code = UtilityCode(
- proto = """
- static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname);
- """,
- impl = """
- static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname) {
- PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
- }
- """)
- # Don't inline the function, it should really never be called in production
- raise_unbound_memoryview_utility_code_nogil = UtilityCode(
- proto = """
- static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname);
- """,
- impl = """
- static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
- #ifdef WITH_THREAD
- PyGILState_STATE gilstate = PyGILState_Ensure();
- #endif
- __Pyx_RaiseUnboundLocalError(varname);
- #ifdef WITH_THREAD
- PyGILState_Release(gilstate);
- #endif
- }
- """,
- requires = [raise_unbound_local_error_utility_code])
- #------------------------------------------------------------------------------------
- raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")
- raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")
- tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c")
|