PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/imunify360/venv/lib/python3.11/site-packages/lxml
Viewing File: /opt/imunify360/venv/lib/python3.11/site-packages/lxml/xslt.pxi
# XSLT from lxml.includes cimport xslt cdef class XSLTError(LxmlError): """Base class of all XSLT errors. """ cdef class XSLTParseError(XSLTError): """Error parsing a stylesheet document. """ cdef class XSLTApplyError(XSLTError): """Error running an XSL transformation. """ class XSLTSaveError(XSLTError, SerialisationError): """Error serialising an XSLT result. """ cdef class XSLTExtensionError(XSLTError): """Error registering an XSLT extension. """ # version information LIBXSLT_COMPILED_VERSION = __unpackIntVersion(xslt.LIBXSLT_VERSION) LIBXSLT_VERSION = __unpackIntVersion(xslt.xsltLibxsltVersion) ################################################################################ # Where do we store what? # # xsltStylesheet->doc->_private # == _XSLTResolverContext for XSL stylesheet # # xsltTransformContext->_private # == _XSLTResolverContext for transformed document # ################################################################################ ################################################################################ # XSLT document loaders @cython.final @cython.internal cdef class _XSLTResolverContext(_ResolverContext): cdef xmlDoc* _c_style_doc cdef _BaseParser _parser cdef _XSLTResolverContext _copy(self): cdef _XSLTResolverContext context context = _XSLTResolverContext() _initXSLTResolverContext(context, self._parser) context._c_style_doc = self._c_style_doc return context cdef _initXSLTResolverContext(_XSLTResolverContext context, _BaseParser parser): _initResolverContext(context, parser.resolvers) context._parser = parser context._c_style_doc = NULL cdef xmlDoc* _xslt_resolve_from_python(const_xmlChar* c_uri, void* c_context, int parse_options, int* error) with gil: # call the Python document loaders cdef _XSLTResolverContext context cdef _ResolverRegistry resolvers cdef _InputDocument doc_ref cdef xmlDoc* c_doc cdef xmlDoc* c_return_doc = NULL error[0] = 0 context = <_XSLTResolverContext>c_context # shortcut if we resolve the stylesheet itself c_doc = context._c_style_doc try: if c_doc is not NULL and c_doc.URL is not NULL: if tree.xmlStrcmp(c_uri, c_doc.URL) == 0: c_return_doc = _copyDoc(c_doc, 1) return c_return_doc # 'goto', see 'finally' below # delegate to the Python resolvers resolvers = context._resolvers if tree.xmlStrncmp(<unsigned char*>'string://__STRING__XSLT__/', c_uri, 26) == 0: c_uri += 26 uri = _decodeFilename(c_uri) doc_ref = resolvers.resolve(uri, None, context) if doc_ref is not None: if doc_ref._type == PARSER_DATA_STRING: c_return_doc = _parseDoc( doc_ref._data_bytes, doc_ref._filename, context._parser) elif doc_ref._type == PARSER_DATA_FILENAME: c_return_doc = _parseDocFromFile( doc_ref._filename, context._parser) elif doc_ref._type == PARSER_DATA_FILE: c_return_doc = _parseDocFromFilelike( doc_ref._file, doc_ref._filename, context._parser) elif doc_ref._type == PARSER_DATA_EMPTY: c_return_doc = _newXMLDoc() if c_return_doc is not NULL and c_return_doc.URL is NULL: c_return_doc.URL = tree.xmlStrdup(c_uri) except: error[0] = 1 context._store_raised() finally: return c_return_doc # and swallow any further exceptions cdef void _xslt_store_resolver_exception(const_xmlChar* c_uri, void* context, xslt.xsltLoadType c_type) with gil: try: message = f"Cannot resolve URI {_decodeFilename(c_uri)}" if c_type == xslt.XSLT_LOAD_DOCUMENT: exception = XSLTApplyError(message) else: exception = XSLTParseError(message) (<_XSLTResolverContext>context)._store_exception(exception) except BaseException as e: (<_XSLTResolverContext>context)._store_exception(e) finally: return # and swallow any further exceptions cdef xmlDoc* _xslt_doc_loader(const_xmlChar* c_uri, tree.xmlDict* c_dict, int parse_options, void* c_ctxt, xslt.xsltLoadType c_type) nogil: # nogil => no Python objects here, may be called without thread context ! cdef xmlDoc* c_doc cdef xmlDoc* result cdef void* c_pcontext cdef int error = 0 # find resolver contexts of stylesheet and transformed doc if c_type == xslt.XSLT_LOAD_DOCUMENT: # transformation time c_pcontext = (<xslt.xsltTransformContext*>c_ctxt)._private elif c_type == xslt.XSLT_LOAD_STYLESHEET: # include/import resolution while parsing c_pcontext = (<xslt.xsltStylesheet*>c_ctxt).doc._private else: c_pcontext = NULL if c_pcontext is NULL: # can't call Python without context, fall back to default loader return XSLT_DOC_DEFAULT_LOADER( c_uri, c_dict, parse_options, c_ctxt, c_type) c_doc = _xslt_resolve_from_python(c_uri, c_pcontext, parse_options, &error) if c_doc is NULL and not error: c_doc = XSLT_DOC_DEFAULT_LOADER( c_uri, c_dict, parse_options, c_ctxt, c_type) if c_doc is NULL: _xslt_store_resolver_exception(c_uri, c_pcontext, c_type) if c_doc is not NULL and c_type == xslt.XSLT_LOAD_STYLESHEET: c_doc._private = c_pcontext return c_doc cdef xslt.xsltDocLoaderFunc XSLT_DOC_DEFAULT_LOADER = xslt.xsltDocDefaultLoader xslt.xsltSetLoaderFunc(<xslt.xsltDocLoaderFunc>_xslt_doc_loader) ################################################################################ # XSLT file/network access control cdef class XSLTAccessControl: u"""XSLTAccessControl(self, read_file=True, write_file=True, create_dir=True, read_network=True, write_network=True) Access control for XSLT: reading/writing files, directories and network I/O. Access to a type of resource is granted or denied by passing any of the following boolean keyword arguments. All of them default to True to allow access. - read_file - write_file - create_dir - read_network - write_network For convenience, there is also a class member `DENY_ALL` that provides an XSLTAccessControl instance that is readily configured to deny everything, and a `DENY_WRITE` member that denies all write access but allows read access. See `XSLT`. """ cdef xslt.xsltSecurityPrefs* _prefs def __cinit__(self): self._prefs = xslt.xsltNewSecurityPrefs() if self._prefs is NULL: raise MemoryError() def __init__(self, *, bint read_file=True, bint write_file=True, bint create_dir=True, bint read_network=True, bint write_network=True): self._setAccess(xslt.XSLT_SECPREF_READ_FILE, read_file) self._setAccess(xslt.XSLT_SECPREF_WRITE_FILE, write_file) self._setAccess(xslt.XSLT_SECPREF_CREATE_DIRECTORY, create_dir) self._setAccess(xslt.XSLT_SECPREF_READ_NETWORK, read_network) self._setAccess(xslt.XSLT_SECPREF_WRITE_NETWORK, write_network) DENY_ALL = XSLTAccessControl( read_file=False, write_file=False, create_dir=False, read_network=False, write_network=False) DENY_WRITE = XSLTAccessControl( read_file=True, write_file=False, create_dir=False, read_network=True, write_network=False) def __dealloc__(self): if self._prefs is not NULL: xslt.xsltFreeSecurityPrefs(self._prefs) @cython.final cdef _setAccess(self, xslt.xsltSecurityOption option, bint allow): cdef xslt.xsltSecurityCheck function if allow: function = xslt.xsltSecurityAllow else: function = xslt.xsltSecurityForbid xslt.xsltSetSecurityPrefs(self._prefs, option, function) @cython.final cdef void _register_in_context(self, xslt.xsltTransformContext* ctxt): xslt.xsltSetCtxtSecurityPrefs(self._prefs, ctxt) @property def options(self): """The access control configuration as a map of options.""" return { u'read_file': self._optval(xslt.XSLT_SECPREF_READ_FILE), u'write_file': self._optval(xslt.XSLT_SECPREF_WRITE_FILE), u'create_dir': self._optval(xslt.XSLT_SECPREF_CREATE_DIRECTORY), u'read_network': self._optval(xslt.XSLT_SECPREF_READ_NETWORK), u'write_network': self._optval(xslt.XSLT_SECPREF_WRITE_NETWORK), } @cython.final cdef _optval(self, xslt.xsltSecurityOption option): cdef xslt.xsltSecurityCheck function function = xslt.xsltGetSecurityPrefs(self._prefs, option) if function is <xslt.xsltSecurityCheck>xslt.xsltSecurityAllow: return True elif function is <xslt.xsltSecurityCheck>xslt.xsltSecurityForbid: return False else: return None def __repr__(self): items = sorted(self.options.items()) return u"%s(%s)" % ( python._fqtypename(self).decode('UTF-8').split(u'.')[-1], u', '.join([u"%s=%r" % item for item in items])) ################################################################################ # XSLT cdef int _register_xslt_function(void* ctxt, name_utf, ns_utf): if ns_utf is None: return 0 # libxml2 internalises the strings if ctxt has a dict return xslt.xsltRegisterExtFunction( <xslt.xsltTransformContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf), <xslt.xmlXPathFunction>_xpath_function_call) cdef dict EMPTY_DICT = {} @cython.final @cython.internal cdef class _XSLTContext(_BaseContext): cdef xslt.xsltTransformContext* _xsltCtxt cdef _ReadOnlyElementProxy _extension_element_proxy cdef dict _extension_elements def __cinit__(self): self._xsltCtxt = NULL self._extension_elements = EMPTY_DICT def __init__(self, namespaces, extensions, error_log, enable_regexp, build_smart_strings): if extensions is not None and extensions: for ns_name_tuple, extension in extensions.items(): if ns_name_tuple[0] is None: raise XSLTExtensionError, \ u"extensions must not have empty namespaces" if isinstance(extension, XSLTExtension): if self._extension_elements is EMPTY_DICT: self._extension_elements = {} extensions = extensions.copy() ns_utf = _utf8(ns_name_tuple[0]) name_utf = _utf8(ns_name_tuple[1]) self._extension_elements[(ns_utf, name_utf)] = extension del extensions[ns_name_tuple] _BaseContext.__init__(self, namespaces, extensions, error_log, enable_regexp, build_smart_strings) cdef _BaseContext _copy(self): cdef _XSLTContext context context = <_XSLTContext>_BaseContext._copy(self) context._extension_elements = self._extension_elements return context cdef register_context(self, xslt.xsltTransformContext* xsltCtxt, _Document doc): self._xsltCtxt = xsltCtxt self._set_xpath_context(xsltCtxt.xpathCtxt) self._register_context(doc) self.registerLocalFunctions(xsltCtxt, _register_xslt_function) self.registerGlobalFunctions(xsltCtxt, _register_xslt_function) _registerXSLTExtensions(xsltCtxt, self._extension_elements) cdef free_context(self): self._cleanup_context() self._release_context() if self._xsltCtxt is not NULL: xslt.xsltFreeTransformContext(self._xsltCtxt) self._xsltCtxt = NULL self._release_temp_refs() @cython.final @cython.internal @cython.freelist(8) cdef class _XSLTQuotedStringParam: u"""A wrapper class for literal XSLT string parameters that require quote escaping. """ cdef bytes strval def __cinit__(self, strval): self.strval = _utf8(strval) @cython.no_gc_clear cdef class XSLT: u"""XSLT(self, xslt_input, extensions=None, regexp=True, access_control=None) Turn an XSL document into an XSLT object. Calling this object on a tree or Element will execute the XSLT:: transform = etree.XSLT(xsl_tree) result = transform(xml_tree) Keyword arguments of the constructor: - extensions: a dict mapping ``(namespace, name)`` pairs to extension functions or extension elements - regexp: enable exslt regular expression support in XPath (default: True) - access_control: access restrictions for network or file system (see `XSLTAccessControl`) Keyword arguments of the XSLT call: - profile_run: enable XSLT profiling and make the profile available as XML document in ``result.xslt_profile`` (default: False) Other keyword arguments of the call are passed to the stylesheet as parameters. """ cdef _XSLTContext _context cdef xslt.xsltStylesheet* _c_style cdef _XSLTResolverContext _xslt_resolver_context cdef XSLTAccessControl _access_control cdef _ErrorLog _error_log def __cinit__(self): self._c_style = NULL def __init__(self, xslt_input, *, extensions=None, regexp=True, access_control=None): cdef xslt.xsltStylesheet* c_style = NULL cdef xmlDoc* c_doc cdef _Document doc cdef _Element root_node doc = _documentOrRaise(xslt_input) root_node = _rootNodeOrRaise(xslt_input) # set access control or raise TypeError self._access_control = access_control # make a copy of the document as stylesheet parsing modifies it c_doc = _copyDocRoot(doc._c_doc, root_node._c_node) # make sure we always have a stylesheet URL if c_doc.URL is NULL: doc_url_utf = python.PyUnicode_AsASCIIString( f"string://__STRING__XSLT__/{id(self)}.xslt") c_doc.URL = tree.xmlStrdup(_xcstr(doc_url_utf)) self._error_log = _ErrorLog() self._xslt_resolver_context = _XSLTResolverContext() _initXSLTResolverContext(self._xslt_resolver_context, doc._parser) # keep a copy in case we need to access the stylesheet via 'document()' self._xslt_resolver_context._c_style_doc = _copyDoc(c_doc, 1) c_doc._private = <python.PyObject*>self._xslt_resolver_context with self._error_log: orig_loader = _register_document_loader() c_style = xslt.xsltParseStylesheetDoc(c_doc) _reset_document_loader(orig_loader) if c_style is NULL or c_style.errors: tree.xmlFreeDoc(c_doc) if c_style is not NULL: xslt.xsltFreeStylesheet(c_style) self._xslt_resolver_context._raise_if_stored() # last error seems to be the most accurate here if self._error_log.last_error is not None and \ self._error_log.last_error.message: raise XSLTParseError(self._error_log.last_error.message, self._error_log) else: raise XSLTParseError( self._error_log._buildExceptionMessage( u"Cannot parse stylesheet"), self._error_log) c_doc._private = NULL # no longer used! self._c_style = c_style self._context = _XSLTContext(None, extensions, self._error_log, regexp, True) def __dealloc__(self): if self._xslt_resolver_context is not None and \ self._xslt_resolver_context._c_style_doc is not NULL: tree.xmlFreeDoc(self._xslt_resolver_context._c_style_doc) # this cleans up the doc copy as well if self._c_style is not NULL: xslt.xsltFreeStylesheet(self._c_style) @property def error_log(self): """The log of errors and warnings of an XSLT execution.""" return self._error_log.copy() @staticmethod def strparam(strval): u"""strparam(strval) Mark an XSLT string parameter that requires quote escaping before passing it into the transformation. Use it like this:: result = transform(doc, some_strval = XSLT.strparam( '''it's \"Monty Python's\" ...''')) Escaped string parameters can be reused without restriction. """ return _XSLTQuotedStringParam(strval) @staticmethod def set_global_max_depth(int max_depth): u"""set_global_max_depth(max_depth) The maximum traversal depth that the stylesheet engine will allow. This does not only count the template recursion depth but also takes the number of variables/parameters into account. The required setting for a run depends on both the stylesheet and the input data. Example:: XSLT.set_global_max_depth(5000) Note that this is currently a global, module-wide setting because libxslt does not support it at a per-stylesheet level. """ if max_depth < 0: raise ValueError("cannot set a maximum stylesheet traversal depth < 0") xslt.xsltMaxDepth = max_depth def apply(self, _input, *, profile_run=False, **kw): u"""apply(self, _input, profile_run=False, **kw) :deprecated: call the object, not this method.""" return self(_input, profile_run=profile_run, **kw) def tostring(self, _ElementTree result_tree): u"""tostring(self, result_tree) Save result doc to string based on stylesheet output method. :deprecated: use str(result_tree) instead. """ return str(result_tree) def __deepcopy__(self, memo): return self.__copy__() def __copy__(self): return _copyXSLT(self) def __call__(self, _input, *, profile_run=False, **kw): u"""__call__(self, _input, profile_run=False, **kw) Execute the XSL transformation on a tree or Element. Pass the ``profile_run`` option to get profile information about the XSLT. The result of the XSLT will have a property xslt_profile that holds an XML tree with profiling data. """ cdef _XSLTContext context = None cdef _XSLTResolverContext resolver_context cdef _Document input_doc cdef _Element root_node cdef _Document result_doc cdef _Document profile_doc = None cdef xmlDoc* c_profile_doc cdef xslt.xsltTransformContext* transform_ctxt cdef xmlDoc* c_result = NULL cdef xmlDoc* c_doc cdef tree.xmlDict* c_dict cdef const_char** params = NULL assert self._c_style is not NULL, "XSLT stylesheet not initialised" input_doc = _documentOrRaise(_input) root_node = _rootNodeOrRaise(_input) c_doc = _fakeRootDoc(input_doc._c_doc, root_node._c_node) transform_ctxt = xslt.xsltNewTransformContext(self._c_style, c_doc) if transform_ctxt is NULL: _destroyFakeDoc(input_doc._c_doc, c_doc) raise MemoryError() # using the stylesheet dict is safer than using a possibly # unrelated dict from the current thread. Almost all # non-input tag/attr names will come from the stylesheet # anyway. if transform_ctxt.dict is not NULL: xmlparser.xmlDictFree(transform_ctxt.dict) if kw: # parameter values are stored in the dict # => avoid unnecessarily cluttering the global dict transform_ctxt.dict = xmlparser.xmlDictCreateSub(self._c_style.doc.dict) if transform_ctxt.dict is NULL: xslt.xsltFreeTransformContext(transform_ctxt) raise MemoryError() else: transform_ctxt.dict = self._c_style.doc.dict xmlparser.xmlDictReference(transform_ctxt.dict) xslt.xsltSetCtxtParseOptions( transform_ctxt, input_doc._parser._parse_options) if profile_run: transform_ctxt.profile = 1 try: context = self._context._copy() context.register_context(transform_ctxt, input_doc) resolver_context = self._xslt_resolver_context._copy() transform_ctxt._private = <python.PyObject*>resolver_context _convert_xslt_parameters(transform_ctxt, kw, &params) c_result = self._run_transform( c_doc, params, context, transform_ctxt) if params is not NULL: # deallocate space for parameters python.lxml_free(params) if transform_ctxt.state != xslt.XSLT_STATE_OK: if c_result is not NULL: tree.xmlFreeDoc(c_result) c_result = NULL if transform_ctxt.profile: c_profile_doc = xslt.xsltGetProfileInformation(transform_ctxt) if c_profile_doc is not NULL: profile_doc = _documentFactory( c_profile_doc, input_doc._parser) finally: if context is not None: context.free_context() _destroyFakeDoc(input_doc._c_doc, c_doc) try: if resolver_context is not None and resolver_context._has_raised(): if c_result is not NULL: tree.xmlFreeDoc(c_result) c_result = NULL resolver_context._raise_if_stored() if context._exc._has_raised(): if c_result is not NULL: tree.xmlFreeDoc(c_result) c_result = NULL context._exc._raise_if_stored() if c_result is NULL: # last error seems to be the most accurate here error = self._error_log.last_error if error is not None and error.message: if error.line > 0: message = f"{error.message}, line {error.line}" else: message = error.message elif error is not None and error.line > 0: message = f"Error applying stylesheet, line {error.line}" else: message = u"Error applying stylesheet" raise XSLTApplyError(message, self._error_log) finally: if resolver_context is not None: resolver_context.clear() result_doc = _documentFactory(c_result, input_doc._parser) c_dict = c_result.dict xmlparser.xmlDictReference(c_dict) __GLOBAL_PARSER_CONTEXT.initThreadDictRef(&c_result.dict) if c_dict is not c_result.dict or \ self._c_style.doc.dict is not c_result.dict or \ input_doc._c_doc.dict is not c_result.dict: with nogil: if c_dict is not c_result.dict: fixThreadDictNames(<xmlNode*>c_result, c_dict, c_result.dict) if self._c_style.doc.dict is not c_result.dict: fixThreadDictNames(<xmlNode*>c_result, self._c_style.doc.dict, c_result.dict) if input_doc._c_doc.dict is not c_result.dict: fixThreadDictNames(<xmlNode*>c_result, input_doc._c_doc.dict, c_result.dict) xmlparser.xmlDictFree(c_dict) return _xsltResultTreeFactory(result_doc, self, profile_doc) cdef xmlDoc* _run_transform(self, xmlDoc* c_input_doc, const_char** params, _XSLTContext context, xslt.xsltTransformContext* transform_ctxt): cdef xmlDoc* c_result xslt.xsltSetTransformErrorFunc(transform_ctxt, <void*>self._error_log, <xmlerror.xmlGenericErrorFunc>_receiveXSLTError) if self._access_control is not None: self._access_control._register_in_context(transform_ctxt) with self._error_log, nogil: orig_loader = _register_document_loader() c_result = xslt.xsltApplyStylesheetUser( self._c_style, c_input_doc, params, NULL, NULL, transform_ctxt) _reset_document_loader(orig_loader) return c_result cdef _convert_xslt_parameters(xslt.xsltTransformContext* transform_ctxt, dict parameters, const_char*** params_ptr): cdef Py_ssize_t i, parameter_count cdef const_char** params cdef tree.xmlDict* c_dict = transform_ctxt.dict params_ptr[0] = NULL parameter_count = len(parameters) if parameter_count == 0: return # allocate space for parameters # * 2 as we want an entry for both key and value, # and + 1 as array is NULL terminated params = <const_char**>python.lxml_malloc(parameter_count * 2 + 1, sizeof(const_char*)) if not params: raise MemoryError() try: i = 0 for key, value in parameters.iteritems(): k = _utf8(key) if isinstance(value, _XSLTQuotedStringParam): v = (<_XSLTQuotedStringParam>value).strval xslt.xsltQuoteOneUserParam( transform_ctxt, _xcstr(k), _xcstr(v)) else: if isinstance(value, XPath): v = (<XPath>value)._path else: v = _utf8(value) params[i] = <const_char*>tree.xmlDictLookup(c_dict, _xcstr(k), len(k)) i += 1 params[i] = <const_char*>tree.xmlDictLookup(c_dict, _xcstr(v), len(v)) i += 1 except: python.lxml_free(params) raise params[i] = NULL params_ptr[0] = params cdef XSLT _copyXSLT(XSLT stylesheet): cdef XSLT new_xslt cdef xmlDoc* c_doc assert stylesheet._c_style is not NULL, "XSLT stylesheet not initialised" new_xslt = XSLT.__new__(XSLT) new_xslt._access_control = stylesheet._access_control new_xslt._error_log = _ErrorLog() new_xslt._context = stylesheet._context._copy() new_xslt._xslt_resolver_context = stylesheet._xslt_resolver_context._copy() new_xslt._xslt_resolver_context._c_style_doc = _copyDoc( stylesheet._xslt_resolver_context._c_style_doc, 1) c_doc = _copyDoc(stylesheet._c_style.doc, 1) new_xslt._c_style = xslt.xsltParseStylesheetDoc(c_doc) if new_xslt._c_style is NULL: tree.xmlFreeDoc(c_doc) raise MemoryError() return new_xslt @cython.final cdef class _XSLTResultTree(_ElementTree): """The result of an XSLT evaluation. Use ``str()`` or ``bytes()`` (or ``unicode()`` in Python 2.x) to serialise to a string, and the ``.write_output()`` method to write serialise to a file. """ cdef XSLT _xslt cdef _Document _profile cdef xmlChar* _buffer cdef Py_ssize_t _buffer_len cdef Py_ssize_t _buffer_refcnt def write_output(self, file, *, compression=0): """write_output(self, file, *, compression=0) Serialise the XSLT output to a file or file-like object. As opposed to the generic ``.write()`` method, ``.write_output()`` serialises the result as defined by the ``<xsl:output>`` tag. """ cdef _FilelikeWriter writer = None cdef _Document doc cdef int r, rclose, c_compression cdef const_xmlChar* c_encoding = NULL cdef tree.xmlOutputBuffer* c_buffer if self._context_node is not None: doc = self._context_node._doc else: doc = None if doc is None: doc = self._doc if doc is None: raise XSLTSaveError("No document to serialise") c_compression = compression or 0 xslt.LXML_GET_XSLT_ENCODING(c_encoding, self._xslt._c_style) writer = _create_output_buffer(file, <const_char*>c_encoding, compression, &c_buffer, close=False) if writer is None: with nogil: r = xslt.xsltSaveResultTo(c_buffer, doc._c_doc, self._xslt._c_style) rclose = tree.xmlOutputBufferClose(c_buffer) else: r = xslt.xsltSaveResultTo(c_buffer, doc._c_doc, self._xslt._c_style) rclose = tree.xmlOutputBufferClose(c_buffer) if writer is not None: writer._exc_context._raise_if_stored() if r < 0 or rclose == -1: python.PyErr_SetFromErrno(IOError) # raises IOError cdef _saveToStringAndSize(self, xmlChar** s, int* l): cdef _Document doc cdef int r if self._context_node is not None: doc = self._context_node._doc else: doc = None if doc is None: doc = self._doc if doc is None: s[0] = NULL return with nogil: r = xslt.xsltSaveResultToString(s, l, doc._c_doc, self._xslt._c_style) if r == -1: raise MemoryError() def __str__(self): cdef xmlChar* s = NULL cdef int l = 0 if not python.IS_PYTHON2: return self.__unicode__() self._saveToStringAndSize(&s, &l) if s is NULL: return '' # we must not use 'funicode()' here as this is not always UTF-8 try: result = <bytes>s[:l] finally: tree.xmlFree(s) return result def __unicode__(self): cdef xmlChar* encoding cdef xmlChar* s = NULL cdef int l = 0 self._saveToStringAndSize(&s, &l) if s is NULL: return u'' encoding = self._xslt._c_style.encoding try: if encoding is NULL: result = s[:l].decode('UTF-8') else: result = s[:l].decode(encoding) finally: tree.xmlFree(s) return _stripEncodingDeclaration(result) def __getbuffer__(self, Py_buffer* buffer, int flags): cdef int l = 0 if buffer is NULL: return if self._buffer is NULL or flags & python.PyBUF_WRITABLE: self._saveToStringAndSize(<xmlChar**>&buffer.buf, &l) buffer.len = l if self._buffer is NULL and not flags & python.PyBUF_WRITABLE: self._buffer = <xmlChar*>buffer.buf self._buffer_len = l self._buffer_refcnt = 1 else: buffer.buf = self._buffer buffer.len = self._buffer_len self._buffer_refcnt += 1 if flags & python.PyBUF_WRITABLE: buffer.readonly = 0 else: buffer.readonly = 1 if flags & python.PyBUF_FORMAT: buffer.format = "B" else: buffer.format = NULL buffer.ndim = 0 buffer.shape = NULL buffer.strides = NULL buffer.suboffsets = NULL buffer.itemsize = 1 buffer.internal = NULL if buffer.obj is not self: # set by Cython? buffer.obj = self def __releasebuffer__(self, Py_buffer* buffer): if buffer is NULL: return if <xmlChar*>buffer.buf is self._buffer: self._buffer_refcnt -= 1 if self._buffer_refcnt == 0: tree.xmlFree(<char*>self._buffer) self._buffer = NULL else: tree.xmlFree(<char*>buffer.buf) buffer.buf = NULL property xslt_profile: """Return an ElementTree with profiling data for the stylesheet run. """ def __get__(self): cdef object root if self._profile is None: return None root = self._profile.getroot() if root is None: return None return ElementTree(root) def __del__(self): self._profile = None cdef _xsltResultTreeFactory(_Document doc, XSLT xslt, _Document profile): cdef _XSLTResultTree result result = <_XSLTResultTree>_newElementTree(doc, None, _XSLTResultTree) result._xslt = xslt result._profile = profile return result # functions like "output" and "write" are a potential security risk, but we # rely on the user to configure XSLTAccessControl as needed xslt.xsltRegisterAllExtras() # enable EXSLT support for XSLT xslt.exsltRegisterAll() ################################################################################ # XSLT PI support cdef object _RE_PI_HREF = re.compile(ur'\s+href\s*=\s*(?:\'([^\']*)\'|"([^"]*)")') cdef object _FIND_PI_HREF = _RE_PI_HREF.findall cdef object _REPLACE_PI_HREF = _RE_PI_HREF.sub cdef XPath __findStylesheetByID = None cdef _findStylesheetByID(_Document doc, id): global __findStylesheetByID if __findStylesheetByID is None: __findStylesheetByID = XPath( u"//xsl:stylesheet[@xml:id = $id]", namespaces={u"xsl" : u"http://www.w3.org/1999/XSL/Transform"}) return __findStylesheetByID(doc, id=id) cdef class _XSLTProcessingInstruction(PIBase): def parseXSL(self, parser=None): u"""parseXSL(self, parser=None) Try to parse the stylesheet referenced by this PI and return an ElementTree for it. If the stylesheet is embedded in the same document (referenced via xml:id), find and return an ElementTree for the stylesheet Element. The optional ``parser`` keyword argument can be passed to specify the parser used to read from external stylesheet URLs. """ cdef _Document result_doc cdef _Element result_node cdef bytes href_utf cdef const_xmlChar* c_href cdef xmlAttr* c_attr _assertValidNode(self) if self._c_node.content is NULL: raise ValueError, u"PI lacks content" hrefs = _FIND_PI_HREF(u' ' + (<unsigned char*>self._c_node.content).decode('UTF-8')) if len(hrefs) != 1: raise ValueError, u"malformed PI attributes" hrefs = hrefs[0] href_utf = utf8(hrefs[0] or hrefs[1]) c_href = _xcstr(href_utf) if c_href[0] != c'#': # normal URL, try to parse from it c_href = tree.xmlBuildURI( c_href, tree.xmlNodeGetBase(self._c_node.doc, self._c_node)) if c_href is not NULL: try: href_utf = <unsigned char*>c_href finally: tree.xmlFree(<char*>c_href) result_doc = _parseDocumentFromURL(href_utf, parser) return _elementTreeFactory(result_doc, None) # ID reference to embedded stylesheet # try XML:ID lookup _assertValidDoc(self._doc) c_href += 1 # skip leading '#' c_attr = tree.xmlGetID(self._c_node.doc, c_href) if c_attr is not NULL and c_attr.doc is self._c_node.doc: result_node = _elementFactory(self._doc, c_attr.parent) return _elementTreeFactory(result_node._doc, result_node) # try XPath search root = _findStylesheetByID(self._doc, funicode(c_href)) if not root: raise ValueError, u"reference to non-existing embedded stylesheet" elif len(root) > 1: raise ValueError, u"ambiguous reference to embedded stylesheet" result_node = root[0] return _elementTreeFactory(result_node._doc, result_node) def set(self, key, value): u"""set(self, key, value) Supports setting the 'href' pseudo-attribute in the text of the processing instruction. """ if key != u"href": raise AttributeError, \ u"only setting the 'href' attribute is supported on XSLT-PIs" if value is None: attrib = u"" elif u'"' in value or u'>' in value: raise ValueError, u"Invalid URL, must not contain '\"' or '>'" else: attrib = f' href="{value}"' text = u' ' + self.text if _FIND_PI_HREF(text): self.text = _REPLACE_PI_HREF(attrib, text) else: self.text = text + attrib