pytils-0.2.3/0000755000175000017500000000000011051306250010743 5ustar j2aj2apytils-0.2.3/doc/0000755000175000017500000000000011051306250011510 5ustar j2aj2apytils-0.2.3/doc/examples/0000755000175000017500000000000011051306250013326 5ustar j2aj2apytils-0.2.3/doc/examples/dt.distance_of_time_in_words.py0000755000175000017500000000404711047726655021542 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- import datetime import time from pytils import dt # поддерживаются оба модуля работы со временем: # time current_time = time.time() in_past = current_time - 100000 in_future = current_time + 100000 # и datetime.datetime dt_current_time = datetime.datetime.now() dt_in_past = dt_current_time - datetime.timedelta(0, 100000) dt_in_future = dt_current_time + datetime.timedelta(0, 100000) # # У distance_of_time_in_words три параметра: # 1) from_time, время от которого считать # 2) accuracy, точность, по умолчанию -- 1 # 3) to_time, до которого времени считать, по умолчанию - сейчас # # если to_time не передано, считается от "сейчас", # и тогда -1 день -> "вчера", а +1 день -> "завтра" print dt.distance_of_time_in_words(in_past) #-> вчера print dt.distance_of_time_in_words(dt_in_future) #-> завтра # а вот если передано to_time, то нельзя говорить "вчера", # потому что to_time не обязательно "сейчас", # поэтому -1 день -> "1 день назад" print dt.distance_of_time_in_words(in_past, to_time=current_time) #-> 1 день назад # увеличение точности отражается на результате print dt.distance_of_time_in_words(in_past, accuracy=2) #-> 1 день 3 часа назад print dt.distance_of_time_in_words(in_past, accuracy=3) #-> 1 день 3 часа 46 минут назад # аналогично и с будущим временем: print dt.distance_of_time_in_words(in_future) #-> завтра print dt.distance_of_time_in_words(in_future, to_time=current_time) #-> через 1 день print dt.distance_of_time_in_words(in_future, accuracy=2) #-> через 1 день 3 часа print dt.distance_of_time_in_words(in_future, accuracy=3) #-> через 1 день 3 часа 46 минут pytils-0.2.3/doc/examples/dt.ru_strftime.py0000755000175000017500000000314211047726655016700 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- import datetime from pytils import dt # действие ru_strftime аналогично оригинальному strftime # только в %a, %A, %b и %B вместо английских названий будут русские d = datetime.date(2006, 9, 15) # оригинал print d.strftime("%d.%m.%Y (%a)") # -> 15.09.2006 (Fri) # теперь на русском # (единственно, что нужно формат строки передавать в unicode # в то время, как в оригинальном strftime это обязательно str) print dt.ru_strftime(u"%d.%m.%Y (%a)", d) # -> 15.09.2006 (пт) # %A дает полное название дня недели print dt.ru_strftime(u"%d.%m.%Y (%A)", d) # -> 15.09.2006 (пятница) # %B -- название месяца print dt.ru_strftime(u"%d %B %Y", d) # -> 15 сентябрь 2006 # ru_strftime умеет правильно склонять месяц (опция inflected) print dt.ru_strftime(u"%d %B %Y", d, inflected=True) # -> 15 сентября 2006 # ... и день (опция inflected_day) print dt.ru_strftime(u"%d.%m.%Y, в %A", d, inflected_day=True) # -> 15.09.2006, в пятницу # ... и добавлять правильный предлог (опция preposition) print dt.ru_strftime(u"%d.%m.%Y, %A", d, preposition=True) # -> 15.09.2006, в пятницу # второй параметр можно не передавать, будет использована текущая дата print dt.ru_strftime(u"%d %B %Y", inflected=True) pytils-0.2.3/doc/examples/numeral.choose_plural.py0000755000175000017500000000422611047726655020234 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- from pytils import numeral # choose_plural нужен для выбора правильной формы # существительного # у choose_plural два параметра: # 1) amount, количество # 2) variants, варианты # варианты - это кортеж из вариантов склонения # его легко составить по мнемоническому правилу: # (один, два, пять) # т.е. для 1, 2 и 5 объектов, например для слова "пример" # (пример, примера, примеров) print numeral.choose_plural(21, (u"пример", u"примера", u"примеров")) #-> пример print numeral.choose_plural(12, (u"пример", u"примера", u"примеров")) #-> примеров print numeral.choose_plural(32, (u"пример", u"примера", u"примеров")) #-> примера # также можно задавать варианты в одну строку, разделенные запятой print numeral.choose_plural(32, u"пример,примера, примеров") #-> примера # если в варианте используется запятая, она экранируется слешем print numeral.choose_plural(35, u"гвоздь, гвоздя, гвоздей\, шпунтов") #-> гвоздей, шпунтов # зачастую требуется не просто вариант, а вместе с числительным # в этом случае следует использовать get_plural print numeral.get_plural(32, u"пример,примера, примеров") #-> 32 примера # часто хочется, чтобы в случае отсутсвия значения (т.е. количество равно нулю) # выводилось не "0 примеров", а "примеров нет" # в этом случае используйте третий параметр get_plural: print numeral.get_plural(0, u"пример,примера, примеров", u"без примеров") # -> без примеровpytils-0.2.3/doc/examples/numeral.in_words.py0000755000175000017500000000211111047726655017210 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- from pytils import numeral # in_words нужен для представления цифр словами print numeral.in_words(12) #-> двенадцать # вторым параметром можно задать пол: # мужской=numeral.MALE, женский=numeral.FEMALE, срелний=numeral.NEUTER (по умолчанию -- мужской) print numeral.in_words(21) #-> двадцать один # можно передавать неименованным параметром: print numeral.in_words(21, numeral.FEMALE) #-> двадцать одна # можно именованным print numeral.in_words(21, gender=numeral.FEMALE) #-> двадцать одна print numeral.in_words(21, gender=numeral.NEUTER) #-> двадцать одно # можно и дробные print numeral.in_words(12.5) #-> двенадцать целых пять десятых # причем "пишутся" только значимые цифры print numeral.in_words(5.30000) #-> пять целых три десятых pytils-0.2.3/doc/examples/numeral.rubles.py0000755000175000017500000000123111047726655016662 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- from pytils import numeral # rubles служит для формирования строк с деньгами :) print numeral.rubles(10) #-> десять рублей # если нужно, то даже 0 копеек можно записать словами print numeral.rubles(10, zero_for_kopeck=True) #-> десять рублей ноль копеек print numeral.rubles(2.35) #-> два рубля тридцать пять копеек # в случае чего, копейки округляются print numeral.rubles(3.95754) #-> три рубля девяносто шесть копеек pytils-0.2.3/doc/examples/numeral.sum_string.py0000755000175000017500000000240511047726655017564 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- from pytils import numeral # sum_string объединяет в себе choose_plural и in_words # т.е. передаются и количество, и варианты названия объекта # а на выходе получаем количество объектов в правильной форме # параметры: # 1) amount, количество (только целое) # 2) gender, пол (1=мужской, 2=женский, 3=средний) # 3) items, варианты названий объекта (необязательно), # правила аналогичны таковым у choose_plural print numeral.sum_string(3, numeral.MALE, (u"носок", u"носка", u"носков")) #-> три носка print numeral.sum_string(5, numeral.FEMALE, (u"коробка", u"коробки", u"коробок")) #-> пять коробок print numeral.sum_string(21, numeral.NEUTER, (u"очко", u"очка", u"очков")) #-> двадцать одно очко # если варианты не указывать, то действие функции аналогично дейтсвию in_words print numeral.sum_string(21, gender=numeral.NEUTER) #-> двадцать одно pytils-0.2.3/doc/examples/translit.py0000755000175000017500000000311011047726655015562 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- from pytils import translit # простая траслитерация/детранслитерация # обратите внимание на то, что при транслитерации вход - unicode, # выход - str, а в детранслитерации -- наоборот # print translit.translify(u"Это тест и ничего более") #-> Eto test i nichego bolee print translit.translify(u"Традиционно сложные для транслитерации буквы - подъезд, щука") #-> Traditsionno slozhnyie dlya transliteratsii bukvyi - pod`ezd, schuka # и теперь пытаемся вернуть назад... (понятно, что Э и Е получаются одинаково) print translit.detranslify("Eto test i nichego bolee") #-> Ето тест и ничего более print translit.detranslify("Traditsionno slozhnyie dlya transliteratsii bukvyi - pod`ezd, schuka") #-> Традиционно сложные для транслитерации буквы - подъезд, щука # и пригодные для url и названий каталогов/файлов транслиты # dirify и slugify -- синонимы, действия абсолютно идентичны print translit.slugify(u"Традиционно сложные для транслитерации буквы - подъезд, щука") #-> traditsionno-slozhnyie-dlya-transliteratsii-bukvyi-podezd-schuka # обратного преобразования, понятно, нет :) pytils-0.2.3/doc/examples-django/0000755000175000017500000000000011051306250014566 5ustar j2aj2apytils-0.2.3/doc/examples-django/pytilsex/0000755000175000017500000000000011051306250016447 5ustar j2aj2apytils-0.2.3/doc/examples-django/pytilsex/static/0000755000175000017500000000000011051306250017736 5ustar j2aj2apytils-0.2.3/doc/examples-django/pytilsex/static/css/0000755000175000017500000000000011051306250020526 5ustar j2aj2apytils-0.2.3/doc/examples-django/pytilsex/static/css/style.css0000644000175000017500000000242411047726655022426 0ustar j2aj2a/* * Quick mash-up of CSS for the TG quick start page with Django colors. */ html, body, th, td { color: black; background-color: #ddd; font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif; margin: 0; padding: 0; } #header { height: 80px; width: 777px; background: green URL('../images/header_inner.png') no-repeat; border-left: 1px solid #aaa; border-right: 1px solid #aaa; margin: 0 auto 0 auto; } a.link, a, a.active { color: #ab5603; } a:hover { background-color: #e0ffb8; color: #234f32; } #main_content { color: black; font-size: 127%; background-color: white; width: 757px; margin: 0 auto 0 auto; border-left: 1px solid #aaa; border-right: 1px solid #aaa; padding: 10px; } h1,h2,h3,h4,h5,h6,#getting_started_steps { font-family: "Century Schoolbook L", Georgia, serif; font-weight: bold; } h2 { font-size:150% } #footer { border: 1px solid #aaa; border-top: 0px none; color: #999; background-color: white; padding: 10px; font-size: 90%; text-align: center; width: 757px; margin: 0 auto 1em auto; } .code { font-family: monospace; } pre { background-color: #e0ffb8; border: 1px solid #94da3a; border-width: 1px 0; } span.code { font-weight: bold; background: #eee; } pytils-0.2.3/doc/examples-django/pytilsex/static/images/0000755000175000017500000000000011051306250021203 5ustar j2aj2apytils-0.2.3/doc/examples-django/pytilsex/static/images/header_inner.png0000644000175000017500000011574411047726655024374 0ustar j2aj2aPNG  IHDR PݐybKGD pHYs  tIME *9Ϟx IDATx̽ieu&:w^^sΔH-ٲ̤Jm4%V}Ҹc&!*S *YTtXj^7%sLڳ' 8mwS3yfe*x.){~y^ԜoeckGQQ>g^5_w%w̝wyw"Vgx4K%g3Ey/˟武G=(5ʜbh*Y.D)]}2V>0J$>2Z5N?29PWW̧3 Iz&|C}d!31'2G[jYf`$*>hػ4jQ p%I>~{#*8b*}F)1{2W$Gcb3M3OX ]ٙOvC{nϙSiF{6`{vG&Q[rm!xfh1h_+BPsaj h! .a$b~h!YLdn`Ӥe)E/s({5[x?+F()ݛ;Q8.=UI&K yZ{8cFJKr5]-+`C7izOW (=ETHSDQ(-V:XH=FC$ /UtTk 3$e!w]դfS /L2,a_]@k7>S1 A{Fx35)_=8?!@#&͊{ro;\V>?QLkd*1uI.}rHp@3̊/-u'muį0*ʛհ k!@2S>ҥ٨ͦA+,K91 \iJn@-4O IwKH)D/:9?Op{2%ICx 2{@|=xDs&k!j 3i$|ָ"}-o*yyL=̶B'$cT+٠h) Gsz쩦7K7eve۶\&cC.]Snp_ikMBj DB^ ))%3O|҂fT&^e AqGdnI {.H(dЏ_UK 1ﲈH3E}/R`ٙd(T,Ad5INi!K|,jZEN<52{cM%P훍i׹?* O] !-KԆDsř=yjĔ 1{7Cr]kffC)0llww* XΦwQV0Ð̎P_VA:00*WLM?C2t n,/& Y*D>x Ij\>#A52'PY&l̉PٍrC$EgONTRɐ(EHyBUep"_Yh/H'GXⲥɜK_N2( 9xj_&;QdPQ $4OPu\1qрPcst꺰e4вGZ X qѓpEFD9 LQ(sxwkȕ(|!sNŻ1dk6u^YP731;51UΎ4Q򸿐zL0%ٷuVoooК1et{WjJybʨe"c/+?;3ƾM$euNAVJ'b 7Ȇ^"g1JzQ[^tb ݊G)!E^r -tP Z\SRŘ:ΘXjy._A*8>xؕeݸNp F~(QvRj:Z&.(h(hAEEmpJF8K Bo;f{@c`a,\J  :9 $nt|;-#<.h:q[ NΈd܏m )R('@\ئGPO=' h ?(`/{ VA-?*ɻ߮uPKBUպL+sA*΂qB@n{{.d wtw5c(!jnFQ))pVEY {En )dY\T=sx`9(f*xǷu()CP# wy*g2:ÊVn 1LF{;@me3o!F aKk^3AGR2/dR@й轎]n#|wCU\ٲ+'FTi G/6VkX*mقDQU&ĉ.qپ>4mV?AtV-y>`יUv9,5kjlJzNU2.5ԿKQR;jF2tt]]VsĿ3~jf QIeJFY,JXD0꠭N-A*S(p(NJcxyj^9 Mm÷2)p.Ȫeg!i=ĴAy^8 3G`6OFLR?eUWUw%u'4`:~w?՜lÅCaz k'7 Ul+&?/VeU? M8S'LyLQ43o3(t$%M=ۛlBa\upJk E`DnÑQ1uctăP~W)\h#i/%B$Ŏ3Ēm[c%_9Z9bձH/l^FWu?Ժ}jӶurARطB,jàQe͢|2#Z˓%a*]2Ds]6?. HhFȬ%i2EB @mj+v{40\Q[pL_Xԙ9! @,8NdGrp'|I(i+ۃX}3E!N]#^R/7*בZV͒+N|̝dDu`S*w- Xe= kZ;`Ob87ߋ]I4:F>#;D6R0Ţ U)8;&_5Neǹeq@$`[bvTPQm1y@EJ eSJ$ # J* F-v}a@ގ9l Nwo)>{Y+XJvNR!R X,SDj`#@AI2׏:1s@9}=gEDP4V-+tP& Ks[N0F宻7!8>?3LF5v> }wF*)+KTy4wח[.4 ,F4LsךaKr$Oo$'Q.}߷_D%sŎZvu3t+1Ŵ;2R!TVY*ϐ?XJ==%X&2 ss3@^2hg2v(Yщ&7Z?z^$hte-6w^jQUD551HG`fwk"qN61()I@վyr|bfb "knw;wnvvl0 ݴ[L JjUR sc8bխ;۝֣K +D=SvžX\v߹psփ;[BD q=(. zՎO:K_s?gT<.9tA$GA*W++݋|5\+s|zw2ﴒyY55H93 ]yQ50w[!uÒt'@m3{g<;l;K7>7psMQUcF:39}pe<ʞٹF:m8F~tʅ+'ܾ1]ڞYJv`1wnB={ίLML./,0hPF;[ݽq֥k\ᅏ޿ܽkIP)(R&puʡ,]=gi~znnzf5933ap'>X{y`{76m>ūÓU#]sgO|cG?8Olߛn_{ݏ޿^x#e!Dp]٪2 rch)ĈReM1 +,g!VX!(|s;@|5Z8{S{`9nS,X}LQAd4(]JoUy:F 锔 ۪|vg0rweD6М$d TuBgO>ŧ^zccH8y-"1hafeD@g/t7Ko>Zg"%6F5W9}'?zdTaWu//,- =KyG_v31Q'lNWyeJnZJB$$ /W"2qY&[W,l`vQfee;xs <{􉹩Yk'Q$U>0xʡ>ADD+K7-촻x[u2kä^ELb[Af7/EӉ^:?,0ax;`JSLFܩqPj֌3#|AldZbk:AxKryƶZڢ2Qz_ZNRGB9!oVNmҭB6SßLn fC" 3lD 8Sq;nl;;[7~vჭم:u5՘ȅ~NRGd Dvf@9M!JS6%eNy'VUj'u?V!U$P# 'sۙ4p/Uӏ'd]lj1dc귙Q8Pn4 Qejd}!QO} WdB1ƃo~xo{;Z+K~U ǀYL]ӽΣ{/|tl͟.?ycVJCCDLsTIG ⵭?㻯Ƿo}utfϜienYIzNH Qא?W^:|#ly" ֶ/o^7??96_|_<<3+$[xpKۛ|k^½^5K/Ww~t]ϝ<_GCzh'C郧=ƅ O>+39g8Cʋϝ&LtyAO 4GR!Th} unWGQeo7ZlQ@ɽDa=⒈Ty bCniup  ^?IJ4?>a)y# 2¬y{DqljAc3Gf5,k>ʤЮD ׾spz}ct}̳~KK=pڥ^x}gHU 8F=Ԙ.204~ǧOJE GwovF N{i5&]u'ejqJ!  jP0$3(^cJOqS %肙T7I܂gyK~Z`̘&On9sg^DW41b 8k W5"oxU쒫b6 sӳYw}!TQ rUEn1F$[c؉vH1;EDS8V;$_^ 5u$Ҧ Ȱ4)GʤB y#ayٙ(`Q%Elܐ+l Kφ j119M`Ą٨=BȨN$7ғIRYi|f( 'bf*3 bunRT/BU. L3^5BalAoB1E,@dq%e"Q !4qƹs{"͐z-46jmb)˩+#bRR~g TD1"be[[ȠETx#q‘*HP6he SV|uٚ!0 H-S<{٣܌Weq"S߃W hmˉƜmrE ǛF;6LDƪ+*6$d2`WBO M t%b6jZ p Ql/RAb3\" (r)d\n E2w!3aBŖ2Zd'2 3*j6BqQʹ6›]"oܞM|t)+K}Ԓ x*|x!b5eUj1E䗵&b: ^ ˲i-k2!c,AfqzrLlGN3,ַ~Sqx YvFxG"PI4fA"H%lF${ _, oB AW A\1bB>)s. !Lةf]~D[t_ ;3%:s6}#&aG'Λ ~W!+:DFGEoPQO@օHB'ϚMwiXC^h߽Aۛ~@ANԓT Ly2/ȩC6CL#-dv^OǕK2#rP ^//~%.?KQ'*GQ>_h%6yכ uwl]s6lI㫼K;zoɒp0D5bjW+OJ92ꩤ{uT-ބA}+Kت`ɩ3=dy FT'q?>Vk!%,qDpGDptȭ띝m"koͿ̑c]&@[s|H6{[&Nm}tfcϻ"oӹsN;'a!B«DZ#_RۉHg>s/Ź(8i@Dnuۛ;[;v{᳍X@0U8. {z &VXX{K{c+) 6n!kBBNnBS v)Fa&befY[M-Fh1gfg`.?dl3u_G9gA$*35j+CChӳ [7|]{xȀB"Y%a2 / IDATs]i4@ރ 33kΎ  ^އߺQwekցIQq$bgxG=:E.7υ6c*VxWNeRJgg+cVتs_fzgh iimW`+0TȊDhf2AXu=ƙc->~5~W/#^|61UHԏSD<$x7$6W_Y!EإZpl8=K?n;֠t7ba0#*Z*DcFg9|,uHDŽmp}N .HLb0i9z"xx7>[C Bt Rg.N1Ǘ/PA$bgA eeo^kv;!sԵ)"4$ PŠ IC͕Y6s>D5ynr>[J,)T`?F@Im]%@-8G(8Lg>OJnAP;(Y>):s5 ~ލ:J30hGOxŽ+LmDb-6(#Km8v'[Wb~ڥ}|{ݿuo[.S Nj69y ivzzfvN80 kK cI2ozzb|%߹A-SB,k{ST @fLMLmV]jݾqvO#S0` `i?bVM0GSh *[Da[f !( r9Fs <9Mkhlgs# hy?1l 9%&@]Pb)299W$l徭0/4Q/WKW "Zbɹ!x/m|JBon \85sr e"B +Ǡ%֛H0IN4-kԱ&/X2G6崃]F|R N .;z%ζ,6h~Q~$I|cʼnBVp|FÖV)d|N7Uݽq-thJ u*R+@&'uOyUB&O^|K|(aUZ'ߢm#fҰ 0C9)#(S@}I>ckK`a r#~5M ZM-xSѢ^oHc  0kx;Q')G־x(bCLWi\lEn~#Pb?|Az'x +Ui3!3?6m[{g}>75l \ P8Ǧ2$Ӈk@Cd5T`6; xpmcg1f &jM9tGoz&[Dn%٪됽0͏@3hpq9i6ƞ9~b_0bmv 0"SD=m QxfR0X!Y%&a8ta^QCt1='qlz`~R7of~ܾ0eF|&nJ@&&90|FEA;;Pc#S/ 4Ĕ"hrۖndZ WIC:YIVnG"CD46FAQbv%qnɘH)hu;=ָ0V/i50m%c׻ DZ *bs1fP]vb'#lps.wwl1|DĆ'w6d3E`t0gN͎bsGوMU%e*aSM2vvzX)&nѠd|!6᱂dm66GQ6lIN NLgWݒwGݶ] ¦TTțr 4DŽj4c.K%lV6G=yr7FcAIAނH $9g&&nSԱcUv\1ѠGzjR-ĥ<11,6l!>8c-mPK'N4Bj[Ĝ2 q BaZIJQ X DrĶM L6Rr&Xn KnBǃaN^} EH5;s,;O%uF(/KAa g?Q}/_/OpTp B3#3GOy9Նƣ?1Q#lJ=NJD$,kۛJX33s?ɏ?GB3!(81(7ݼpg!3-/޼s@NʓSt)#? u q N!%!>lͫtDX@zOA7 Z(;MooW1 =25-woiC8xf76E^{ԓ4qKAq\8P􉛫S"S`6NK'⻷E+i~H,Skwxs}:֢IQE{[69l,@.>&robV^hЍW? :vAa ϼD&ҷVvv<삾nXg &5X߁Q4 'xvZX.kl At \yw|=C4=ç[zuvhv:Lϳ7q޺ƝѯwK~5!s{)o6T omНut|6|xrЌۡ;7ڴy*sh­ lF;EiQQp t̵ig S|XgÆn+tˤ5.{ciU'暾.-"g뭛twޤA #!mm5ViqEƧl0 '(`ax2nt#"0cUCl5,2ApT_ff`f^@6՞zW7 X7F߶!3{|?ƽ;x݉ >x shS{Iy`|B4:iaBa^(&npe?ƚAͷ#c@D,Hk8}>8\89ho9FI ti[G贱z{ql0ֲ <+O31>{mF28}:kAs,1=x -χ' 3mciϾ 0<'O}Fvwh BݳDAB/GN0">LN9< :Aj֦Y^ }y]2lёrI jo#'{OLGO]Frt<>lmRk9 eyU;,GOIkZ8" nV5 "8{9:;~* C£"U4[z/ c-{PgyraȶHlYjJ83+n{KyyϽ> #u}i]cO?xfaby9m(V1{>sgiSd2'__"cG-eK1Mt)"w_W~9YhZ{A mI7+o _?m;[o|j{Vvނ)cMJ #bQoQgԓ:wDt.yp7 փ_ܛX\gb眸׾{n.")H#1g0gÿ a۰5 =F "Ž%Q셽TwWu{V/_f.qD]޻-YB~y߽q#8[^cQcSc$ Qć"X^Ġ-U8~f栶Jc"LkK##|M͏am5:}n\'AO[@K/Kf Dخʍ+0f$U52 ~E5ž|lz2;Ke[zlLJߺƫK$/ǭT7^^CD8tNMM_-2-=fۛp'&x"kE)xxqrZ&xm+o$z'-x \~N-l$U6i ! k_¹~p ^ K8ṗ1|y yyu8s6V)4> FZ${@@>V׮)=@Ͽ}fkB-З_sz8)/94 )FIDP^OgM.З_%B ju14@&&T%oץ^7paʣܳ43׮Xf/ԣF@GFUw>%@%/8hӦ4z/\'>}(A_\tYهwdz=[Z~}YFVg4: fPԌ,PˏôpP0)V%Бĸ{蛟x'x#nozM>Jr9olo3\MLKjf~>yQOgJGKWCMcW9v2.=MܪG[02G7qtB?S^8?09ؐN$Phyg 65)nӱSѹg)b(<¿s/©)jȵM)f]3V(;G[o}&`xөg>HDE[?ǿn'G|Kg.'K-P"s/LnthOŊ‰G,C<~؋^xwM\ ":bNa_P?@jd"kn}9?BxC7fm{Tc L'1h^pq ژ|d??x΃Ga=wW_~g^tjs$^Z[5sy(K +sȵ9up $T*b9ݔBR|_շs j|\l˱LW1 ec=GS <1%  D!fE+0VZm6%HB`"dV۾Hct@Jfߧ<>22 x4iƝ*oUy@=: R<] i6`fGFps@9wW-}$9?mE#-SrP _\yO?alMھ! )ˋn{ANOK/nFHȀjgGigf@֞D!>3ߖYy %:z'i}<ْ<>>/",X6l1z{  jq $RYU"Ց 288Ges-J 1Kk=h<"Oj[n66.=/YYGq%mDT@AxAm\FZj՜Pȗ5wPv{i79OPCN OmU#ՆPQEY* D"w"۸]%qdq ID4ǽ{ |12/<c<9v$%4Ϋͨ@<giX[wt(XM_SZlz[˒0VmU&,85hQgPGʄ jq0ʺnmYsy Bz  \Sˋk9՚N,ۙ{""uKʓUI4|āS%$)+¡مGO2sLW5tT`42JE&;ݖ^?Wn}zuG/λI+qzae4f'{Ua @~ rtqո 6Dv$`Y1FQ@֟ZCs2 !ʁ/Hk(y^TVNؾpQھ~[#9J:&mՑ bJ/8fĿ (t:-B é{fA[QBrIKşV0U'y|X.y~u :ppCDT/ TJ+e,P@1C}K4/ YBBtQڌF p23#D;Ci3LL8_|E<"҈QNZ =Iѝ[zl{Kx-?yש^h L nAۧs^žNBwY(7fIdݩ  & )b `e6OK^L1 8.,hL&AvrڈIULw1m! BD0"05EcS0{lyʀ`mK fo~m3 IDAT/eJ=Y o3$ʾSE1l(am~ɕi﬷djQ;22H)CG"RT(ꈈD@m1DD!\| =H< 879}# -#cGh`Am,E H_#K\Aw8bMc%Jc (vNt۬DP[?_|sϝ|FAػ[1)S^^S'(镟q2y%\HَwUݴUGzKTAt^!ggDO|%' 02}Y^uCN_i;W4K8l5?c#\S"7>'3RD1FHShȑkn @dTkFcFqy|Ko~JsUpHRg%S_2Jl!|D(nՌ*뀈Pz{:|L->gV x2Jf$EX!"( B@)ߏDm Mj-*d4)[6Y2m5-LadDhRT$ hnaCa@dX4Bpn@Y-B㏄ 0bQ 5F /v H_; ?0kA" 6hd5 mFdHdW6{yA}k##ͫ ,,X% 'ry55auJAOPçGi5=60`NZ掅q6:-X0h!!-`EIʻlm$IK^ .Z~ru] *_ $(FT"5,D -?4ok9de8>2@hJl󍫑5& !^.^#?,? n#NY@WGQm;fԥza=oW7:-u@bb?or\"m0hI}&qtL".*ul =s3r5=7''N+"0h}dfd5 Ü4nTY ϩg^ YUb ,D SVSc 썌դm)(+=>Ƈ*@mau]E G@y@dJ@ JhPV8NNJDaWĔ?H%4 DX hrHelɈ'Ff)(eU$"X"v{2+E~?~AW#e0P S+kWd~6"&oa<,E VW^T*V7ݒgF륐- Ϧ lh xf&Д"߂ZM8)D#^I 9JK/~ھ!/?" |mLibC2,ȹC8aŹ(B2"D`v%XլsS\e%Ȥ+6vUVE"2±q){ַ9Ʈ"Ѕ9=o_&U,(4?y6v*N rj:$ sdB%OAY08O(M/DfNuyw~SoAs۟ Z٧3;E)ŸT(]o}we-d\;eXt k?nlV,aEaCmp2Za#gQ$ H $&@DFF.8 ܼ9}-k(+S->rRfc |SDp N,=gdKx{VԔxzޜ/,>'\vr"2w$"`R*bye<3䠵`EXMD|7dlR\C85pw< nUoʹg+#XP<9|@&zfN'nG \mlԡ sN)/OHeli ݪտ?;3VK(Х7;1h[a#eC'% wҕK_+##eIQ2c-Is2(h۰?;t$viJɭ_m+OZkaH|%tVt064g_Gɔsy,XtE%}?^3b"BEs9HEĖ-يK)Ì~KB?6&~xOV/~-2Ns`,noab,Y  eju'oታpn7f׏zqSts6V9!@:׀.A+/Q^xZMQZZGw(9[-j͖C$l/:duЪ 5[Usϩ#'Yymլ1E͖ f)y|\ Jm>} jDx%KewjPK aj-!`eNx8-(߽! W}%nh*OVv@Vy0<2>50x^=D}3gCEkw=[[6[ U0c ~!#l4%ϗ$ج{:]zIlo8zä`l<⍵HGvGG$fБgW'tDQN89zuI44* 5 cHeMȈ<"lV8!"DE4K&T5T%xe:{01E`uIԿ:pk6@kPZGlڲ@/Vj4dt öuu糨<.\N%-nq"݆(@F-j0C`4[َAT-nD#ckjCҟpdy^MLQ삪7quLb `+nu6}.XWI"'Sg߼ԳCW6HՍY~[~|oaY/yʹ3نZ1/ޚ84'}m #КO5Z*#vl͍0~yghA^m棍vJZ.g'gFJƱcJGju+o֣{~f[03DI,F rLlf0Vm_ \fCҘ7nAb6뺺zcE-" خP z:*AЧ6WVqaR c TqlWEkAb%ln@:(M|p붵[!½MEYYܙD#ceua!Ҩom&UbB}Y_ehAu[UKZVC6סQ'aD(eVA7eg ta:-=GEkӼ@]Y7Px4긾"kFhє(Մu",>Qw7 aחi{+ p5n4Z تKRat}/KR߱߂:46, NtUA8~W#ƒ67b5AuX}"s&FLk\_ u5ީlK}Hk,ޏ*] (>~ [a(lJm _XNU*kZM6,ʭ\  ahn ׶w4jloDGܽ-=FMYڶnƪpKAOk튀 A-BCdV uXz,ukjJT ةJ%mhԙYE,/[ɑ|g[Wui`DqFPߑkPۖGwxe Q 00f{78 1 ^ӭ"`Gweﰈ4jPY!>4j,J} ݒ ٩BW +緥;UaEZu\[fِj#Xn2 "QkOOm20CWtm qc|>3>Tjу#L _~jP:6adcO@0^pg.xó,$iԷ;Vc{W샨@_?9p??[A0B:4 ^yKϜsF!vg%˷>9G:Ѹ}aN=|s/yvjl&Y/WV?{{WܹUˆY`u-}_[n>w44X&-x0ntSb@'/㉆ DInq$F0TBe " զLCӺ93 isx[!gc?sm{H!FG9q SScdIϋs1 ;ppnzfvl6dkGfQzuc5Bn~o7[/o݄7{g.>@'k} 1O\'$U4+˘8022W޾fg'y|_zD-\/Qr&:_)+.ѭO.;sęN>8Yz; W+Kkמ<^_zxs{3 c?IaEV0̎-U"_і8BKVX v 21=*qL)rCl+Ȼ&3N rJQ̾.IKʳ !6 q'$ '1sBLvvtʐSW3՘EQ ҄/%lpF,wM,^I9ְp?5Ƃ*:oblnui0b/!IW.5MdnHK)HVHLS4V$"@`LRcrrN{nIY$AQwbr؄UٰӵS\ SQ6!+Q5}jh i%&IƿV2kMzPiO 5M7"r鐟vUuhإ8>G/q5LxRZIȔš2պTߗmXt^._W^DP*d 'ەJƊ"#%U*eK4C~a`;aʚ0s;=sſimvQRYQMGʥr5dѬ[ZGVY$b0UZ{?%K0Ыx7uc0S(vƵ7 %٧lY@Qۆqt VV\F#@A6GHB씩lJM -=|h곏f&J}k x %dbyeJ8H_)nK:#̟{I Υ倱qqTKw+v[s:z|FI6]bno6̏ayUjgG}\VEI0%fN8/c>b?UϠާsqp{Л;+̘[I]JR_Gʛz^w;tM7Oj]$~_nlЅ#マt'slq>H qcȚpflK>pBl4k D d1<0rzdf@BɗL !w#J*W>swl,BDׄdq!F³{AIDAT̑>sXK2legsSĸ2fi`m؁НcTe=;ܹKqwiJ[$Us =Z}!f,ڎ`rZco-xBC"$Ul7vx\t3d`pYjӋ+\}XY ȈynP]liP4{JtX;fQ3sxJ%_޻Ɂrop 礤Rмo_te1anLtN5-Ԇ R{'k'mq}E d]/0)+j\9 7 x XJ3@+h}ze%uU%]_'*Bg[Ek&G鞿%yt :+VߢCaE%D]mqe*;5 zbd^>~ ;CR v*'y ]tz7Mݾ';8OCoWI9#\ əC۴3:L>%>I3_Y  ogLB챂'wgv-4¨* Y?rJ2Y|Rl.K^ʩ)zA(u[*L$a#g7?f}'|r/dfZˁ/'GA H(+֞ohGP bp8X{DƘ&=šdUwRSzA~n3Urvj<@u抓Á+J"Y pgN)bBW⒪ {f1 Vm)렍[9_ʎAOcFrJ#EE֍iQ.O Uk>5gwؿTQ|{}_MQq ۽ens0ml7H[''aNg&8XuqF珞7_YD1T5Xޓ鷬ꬅ@FH'l>E-cƻy+4q-Ӏ 4%8]:̥jukW>w}Vݮo7ZM?&LUij|o>_z}njVR}X?Xypy1FE8gwmVt:U(-b$9Fd< qd'Dx.W,dUj$˹;`7K~:HZ#;jN1kZkɬ=I?TBIm;8JL t x[HV'&f+bUЍ #8P0;J=^ /;3|#Xڻ:o8|I*b_U4%~j웉|q.9҉YHkS7+0ko.snHr 𚪽Pl> \o~_u}LDZk^'v8KM*N6t}ґUIvHt cƿ8VG4ELD+u!0Ed"9O"l&q7֒neIj`mV4-Wʍr{> rMWfD^coTIur'{aQX+AGTf:pj9wǡ+I" xmH:r;ܯ,,BeX߽_}vtE#LޭBQ2Xՙ`NE7jzlrJaѢ\@0+w ¯A@x (¹ rX }J;sNunKP; s@gME7}j sD /i𵖷_w%dP]B$WA rOƃ Qz>5n2%2 ?H&fZ~]\AF1ծ j}1{:CLբh`rߞPk6`k)$CݭQAPG>Yo[3m3="`\?Z2%zή^qw0k Wr8EDڨP>;?noqY~_q8X\8͍kMJ0Ny"S\hoW-iiu<=>`ҥvsMɤb8N(^"4E {['۰ggr˦*CbR 2CP08X}XE!k藕caxac< 7ۋw[_{_ ;ooƲboWȵu'YCLY~1İ(Q¿Ru鿿e~mC'xB'L9ICt(Ց\t~͍?He{ƸRxpdž!s[݄@2&4͌fT$8{z_̭$H`cvҖޤwްwxZJ+8 ;wZ+ [EY<Zʋ q 9zZYxۯϿ>9:io\搁ܮUCD)1rtsW8!j}οm- Xz 2SJ1%'Gguڱ\3tH]'e\9!`j8 f]EgÎ*J6#鳡ǍAc+''/cz QCfGCsx2\". a;<s3{ϓL}a  O=]gplvR 9 "݀/~A(7"*j}{?{燿Ư}GF'֪Aj4xH ET{NAN&Hbڙ޿Pòqұ,YO7xc02&^[OqqeWQzgcgesG*Mxdr*ږAs8= u]'"AtL}k䮐%q)3e|ur 7J/5Rg#ZJMނ's]G\)y؍8βn- ԫs+e: Ꙑ.RICڞ.*ػ7[e.pW-,y:ûUebqՠ@H#'ܪ޾~?_ f3hd&Qܹq73o94@3 E8 iu;GJԩN.90 F;ghj~1FX[C,a /Tô{690VOZnDdMQ^} ;5CLAMg~7G i&I,(SLcm %t2=֍}bN׍oJ;-RIX97Ή9m R(y̴{P;.OM|w g'xc-Q-:y? R:Hϯ v~!9" fLhL؈@X6֕R"lɲ%]vřC<>+Rvϲ YEYEb!R{v_]\-^9NTHB ڕZ7?ds5 CE@`T9J Wf0*a]IyyR&[ޖ91$m Ps޺`l(!EG ]FAhU\ ן4 l.fu֒ܛT#C5dͨth;(2@ٮP_$sLNJ;FVꥱE)q~EԩB i;׉<4NDIX+ {rΉ!Q+r[un`F+a"W;lxZ N-OÀƱ]b([v?r(S˰ % ={ؐ4rz¤Dz߼G8-/ OKt+M:MVԻƓ{Vl6!Q۽x/[ʣW߽pԅO>~౲*/HGVccx'w-/n7v5!3 j'+F~{r C8ogh7o=E]rg_lg)Ru}rS3IRP:d44IM$1J)qǿ;x"8mȁ=n]qn25H/x Xrˁ BbàJ4= NwtǒPDzВڟߡ^&XmRKˉFE u ާhݬ|zO_GDjbt|~vs3 }(̢"n7 phw2:p~rqB8Yfhʽ7d2-$vxPg\:;͕g1q{&@3R<$  !d Ymw9v*i6Wzw雺e{\# Kc;Cn'47 H{}.\-}8a2 ZKрv'{ߎ pą5dN[!KMBFffa[U2o23 g)DwhOǔHc٤3c-\4!1כ=v#"*< Rf>7I:xHX7ܝe@?T,*cXwOEkiF#{ϼ7һ0=onN z{;lUWP, ǖ~e-{wD|KuKq}g'qI}PPvd &'(Q=)J+{(DC)ptQtK9InOm7aD4KoR :d[05:[eGT!yc6w|."%,f\OcOB 7EV|/= a{Æ]7m;CIQ7w > U4%28x">QbPqmF\3Pʌ0#ޚ# 34RViZy?xn[EhM04Ô0dqt?^qvb|wЭ%"+1S&I|miMe)RAaQ@V ュ<>J^dy׌]Oػ4=]=g S.i;R-S/s=z@>&aAK-o";Mk, WIQYs5FTs7A^"Mzuh0N~YvfΒGH̜ؒdf9EW칋ƒgtڵsng4*SKONNY(YauScYL~wLb#[¢~pat8Lq6L"2hD`AȻSf: 0qKqx! 8kWy@M>7SH('V!yН3XJ\,*Ww{z=+,zRFO do1WۘC MRwz~ P} YWHJEb]p͝'sq@g Q\ l T\vιlpy ;N!/0󟨨4A `ux~ u?.YL#s%ʆZDYql%7-*>ps7|sx:~n%e'E]5!UcsujZ}fa(@= @EIENDB`pytils-0.2.3/doc/examples-django/pytilsex/templates/0000755000175000017500000000000011051306250020445 5ustar j2aj2apytils-0.2.3/doc/examples-django/pytilsex/templates/base.html0000644000175000017500000000520611051303736022256 0ustar j2aj2a pytils demo

pytils demo

{% block content %}

Для того, чтобы воспользоваться pytils в Django, нужно:

  1. Установить pytils (как это сделать написано в INSTALL, в архиве с pytils)
  2. Добавить 'pytils' в INSTALLED_APPS
  3. В шаблоне загрузить соответствующий компонент pytils (подробности см. в примерах к компонентам)
  4. Вставить в нужном месте искомый тег/фильтр.

Компоненты pytils, доступные для загрузки:

Замечание: В зависимости от обстановки pytils по разному реагирует на ошибки при обработке тега/фильтра:

  • Если в режиме отладки (в settings указано DEBUG = True), то подставляется "unknown: краткое описание ошибки"
  • Если не в режиме отладки, то вместо значений подставляется пустая строка
  • Если не в режиме отладки, но в settings указано PYTILS_SHOW_VALUES_ON_ERROR = True, то вместо значений подставляются переданные параметры. Подробности см. в описании тегов/фильтров.

Протестировано с Django 0.95, 0.96 и 1.0

Данный пример работает на Django {{ params.django_version }} с использованием pytils {{ params.pytils_version }}.

{% endblock %}
pytils-0.2.3/doc/examples-django/pytilsex/templates/dt.html0000644000175000017500000001377411047726655022002 0ustar j2aj2a{% extends "base.html" %} {% block content %} {% load pytils_dt %}

pytils_dt filters demo

Для загрузки компоненты, в шаблон вставьте код:

{% templatetag openblock %} load pytils_dt {% templatetag closeblock %}

Фильтры

Для наглядности, текст подставленный фильтром выделен курсивом.

distance_of_time

Например, тест прошлого времени был {{ params.otime|distance_of_time }}. Если более точно, то {{ params.otime|distance_of_time:2 }}. Нужно ли еще более точно? Пожалуйста - это было {{ params.otime|distance_of_time:3 }}.

Точно так же (т.е. для Вас абсолютно прозрачно) и с будущим временем - следующий тест будет {{ params.ftime|distance_of_time }}.

distance_of_time умеет работать с обеими типами времени, представленных в Python: с datetime.datetime и time.time. Например, {{ params.fdate }} будет {{ params.fdate|distance_of_time }}.

Сделано это так:

<p>Например, тест прошлого времени был <em>{% templatetag openvariable %} params.otime|distance_of_time {% templatetag closevariable %}</em>.
Если более точно, то <em>{% templatetag openvariable %} params.otime|distance_of_time:2 {% templatetag closevariable %}</em>. Нужно ли еще
более точно? Пожалуйста - это было <em>{% templatetag openvariable %} params.otime|distance_of_time:3 {% templatetag closevariable %}</em>.
</p>

<p>Точно так же (т.е. для Вас абсолютно прозрачно) и с будущим временем -
следующий тест будет <em>{% templatetag openvariable %} params.ftime|distance_of_time {% templatetag closevariable %}</em>.</p>

<p><code>distance_of_time</code> умеет работать с обеими типами времени, 
представленных в Python: с <code>datetime.datetime</code> и <code>time.time</code>.
Например, {% templatetag openvariable %} params.fdate {% templatetag closevariable %} будет  <em>{% templatetag openvariable %} params.fdate|distance_of_time {% templatetag closevariable %}</em>.</p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке будет отображена разница во времени в секундах, либо пустая строка (если получить разницу не удалось).

ru_strftime

Тоже всё просто - используем обычный формат strftime, в котором %a, %A, %b, %B заменены на русские.

К примеру, текущая дата: {{ params.cdate|ru_strftime:"%d %B %Y, %A" }}.

Код таков:

<p>К примеру, текущая дата: <em>{% templatetag openvariable %} params.cdate|ru_strftime:"%d %B %Y, %A" {% templatetag closevariable %}</em>.</p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке будет отображена дата с использованием оригинального strftime (т.е. с английскими днями/месяцами), либо пустая строка (если strftime не получилось выполнить).

ru_strftime_inflected

Аналогично ru_strftime, только день склоняется. Т.е. текущий тест был выполнен в {{ params.cdate|ru_strftime_inflected:"%A, %d %B %Y" }}

В шаблоне запись такова:


<p>Аналогично <code>ru_strftime</code>, только день склоняется. Т.е. текущий тест был 
выполнен в <em>{% templatetag openvariable %} params.cdate|ru_strftime_inflected:"%A, %d %B %Y" {% templatetag closevariable %}</em>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке будет отображена дата с использованием оригинального strftime (т.е. с английскими днями/месяцами), либо пустая строка (если strftime не получилось выполнить).

ru_strftime_preposition

Аналогично ru_strftime_inflected, только добавляется правильный предлог. Т.е. текущий тест был выполнен {{ params.cdate|ru_strftime_preposition:"%A, %d %B %Y" }}

В шаблоне запись такова:


<p>Аналогично <code>ru_strftime</code>, только добавляется правильный предлог. Т.е. текущий тест был 
выполнен <em>{% templatetag openvariable %} params.cdate|ru_strftime_preposition:"%A, %d %B %Y" {% templatetag closevariable %}</em>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке будет отображена дата с использованием оригинального strftime (т.е. с английскими днями/месяцами), либо пустая строка (если strftime не получилось выполнить).

{% endblock %} pytils-0.2.3/doc/examples-django/pytilsex/templates/numeral.html0000644000175000017500000002475511047726655023037 0ustar j2aj2a{% extends "base.html" %} {% block content %} {% load pytils_numeral %}

pytils_numeral filters/tags demo

Для загрузки компоненты, в шаблон вставьте код:

{% templatetag openblock %} load pytils_numeral {% templatetag closeblock %}

Фильтры

Для наглядности, текст подставленный фильтром выделен курсивом.

choose_plural и get_plural

Выбор нужной формы множественного числа. Классический пример с количеством комментариев: {{ params.comment_number }} {{ params.comment_number|choose_plural:params.comment_variants }}

В качестве аргумента можно передавать не только список вариантов, но и варианты в одну строку, например: так {{ params.comment_number }} комментари{{ params.comment_number|choose_plural:"й,я,ев" }} или так {{ params.comment_number }} {{ params.comment_number|choose_plural:"комментарий, комментария, комментариев" }} - как Вам больше нравится.

Сделано это так:

<p>Выбор нужной формы множественного числа. Классический пример с количеством 
комментариев: {% templatetag openvariable %} params.comment_number {% templatetag closevariable %} 
<em>{% templatetag openvariable %} params.comment_number|choose_plural:params.comment_variants {% templatetag closevariable %}</em>
</p>

<p>В качестве аргумента можно передавать не только список вариантов, но и 
варианты в одну строку, например: так {% templatetag openvariable %} params.comment_number {% templatetag closevariable %} 
комментари<em>{% templatetag openvariable %} params.comment_number|choose_plural:"й,я,ев" {% templatetag closevariable %}</em> или так 
{% templatetag openvariable %} params.comment_number {% templatetag closevariable %} 
<em>{% templatetag openvariable %} params.comment_number|choose_plural:"комментарий, комментария, комментариев" {% templatetag closevariable %}</em>
- как Вам больше нравится.</p>

Зачастую нужно показывать и число, и название объекта в правильной форме, а не только название объекта. В этом случае следует воспользоваться фильтром get_plural. Пример с теми же комментариями можно записать проще: {{ params.comment_number|get_plural:"комментарий,комментария,комментариев" }}. get_plural удобен еще и тем, что можно указать вариант, когда значение равно нулю. Например, гораздо симпатичней "без комментариев", чем "0 комментариев". В этом случае к вариантам нужно просто добавить еще один - нуль-вариант. Пример: {{ params.zero|get_plural:"пример,примера,примеров,без примеров" }}.

Сделано это так:

<p>Зачастую нужно показывать и число, и название объекта в правильной форме, а не только название
объекта. В этом случае следует воспользоваться фильтром <code>get_plural</em>. Пример с теми же
комментариями можно записать проще: 
<em>{% templatetag openvariable %} params.comment_number|get_plural:"комментарий,комментария,комментариев" {% templatetag closevariable %}</em>.
<code>get_plural</code> удобен еще и тем, что можно указать вариант, когда значение равно нулю.
Например, гораздо симпатичней "без комментариев", чем "0 комментариев". В этом случае к вариантам
нужно просто добавить еще один - нуль-вариант. Пример: 
<em>{% templatetag openvariable %} params.zero|get_plural:"пример,примера,примеров,без примеров" {% templatetag closevariable %}.</em></p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается первый вариант либо пустая строка (если получить первый вариант не получилось).

rubles

Рубли словами. К примеру, {{ params.rubles_value }} р. словами будет {{ params.rubles_value|rubles }}. У этого фильтра есть один параметр, определяющий, нужно ли нулевые копейки "проговаривать". Если нужно - то True, по умолчанию rubles этого не делает. Пример: {{ params.rubles_value2 }} р. словами будет {{ params.rubles_value2|rubles }}, а с копейками - {{ params.rubles_value2|rubles:"True" }}.

В шаблоне этот фрагмент записан так:

<p>Рубли словами. К примеру, {% templatetag openvariable %} params.rubles_value {% templatetag closevariable %} р. словами будет 
<em>{% templatetag openvariable %} params.rubles_value|rubles {% templatetag closevariable %}</em>. 
У этого фильтра есть один параметр, определяющий, нужно ли нулевые копейки "проговаривать". Если нужно - 
то True, по умолчанию <code>rubles</code> этого не делает. Пример: {% templatetag openvariable %} params.rubles_value2 {% templatetag closevariable %} р. 
словами будет <em>{% templatetag openvariable %} params.rubles_value2|rubles {% templatetag closevariable %}</em>, а с копейками - 
<em>{% templatetag openvariable %} params.rubles_value2|rubles:"True" {% templatetag closevariable %}</em>.</p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается просто число.

in_words

Число словами. Можно целые, можно дробные. Примеры: {{ params.int_value }} - {{ params.int_value|in_words }}. У целых можно менять пол (по умолчанию - мужской, MALE): {{ params.int_value|in_words:"FEMALE" }} (женский), {{ params.int_value|in_words:"NEUTER" }} (средний).

У дробных почти то же самое, только пол всегда женский и не меняется (т.е. параметр передавать можно, но он не будет влиять). {{ params.float_value }} словами будет {{ params.float_value|in_words }}.

В коде это так:

<p>Число словами. Можно целые, можно дробные. Примеры: {% templatetag openvariable %} params.int_value {% templatetag closevariable %} - 
<em>{% templatetag openvariable %} params.int_value|in_words {% templatetag closevariable %}</em>. У целых можно менять пол (по умолчанию - 
мужской, MALE): <em>{% templatetag openvariable %} params.int_value|in_words:"FEMALE" {% templatetag closevariable %}</em> (женский),
<em>{% templatetag openvariable %} params.int_value|in_words:"NEUTER" {% templatetag closevariable %}</em> (средний).</p>

<p>У дробных почти то же самое, только пол всегда женский и не меняется (т.е. 
параметр передавать можно, но он не будет влиять). {% templatetag openvariable %} params.float_value {% templatetag closevariable %} 
словами будет <em>{% templatetag openvariable %} params.float_value|in_words {% templatetag closevariable %}</em>.</p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается просто число.

Теги

Пока только один.

sum_string

Наиболее общая функция для работы с числами. Умеет "проговаривать" числа и одновременно подставлять название объекта в нужной форме. Например, вместо {{ params.comment_number }} комментарий(ев) можно смело писать {% sum_string params.comment_number params.comment_gender params.comment_variants %}

В коде это реализовано так:

<p>Наиболее общая функция для работы с числами. Умеет "проговаривать" числа и 
одновременно подставлять название объекта в нужной форме. Например, вместо 
{% templatetag openvariable %} params.comment_number {% templatetag closevariable %} комментарий(ев) можно смело писать 
<em>{% templatetag openblock %} sum_string params.comment_number params.comment_gender params.comment_variants {% templatetag closeblock %}</em>
</p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается просто число (без названия объекта).

{% endblock %} pytils-0.2.3/doc/examples-django/pytilsex/templates/translit.html0000644000175000017500000000716411047726655023227 0ustar j2aj2a{% extends "base.html" %} {% block content %} {% load pytils_translit %}

pytils_translit filters demo

Для загрузки компоненты, в шаблон вставьте код:

 
{% templatetag openblock %} load pytils_translit {% templatetag closeblock %}

Фильтры

Для наглядности, текст подставленный фильтром выделен курсивом.

translify

Простая транслитерация, из текста

{{ params.text }}
получается
{{ params.text|translify }}

В шаблоне записано так:

<p>Простая транслитерация, из текста <blockquote>{% templatetag openvariable %} params.text {% templatetag closevariable %}</blockquote> 
получается <blockquote><em>{% templatetag openvariable %} params.text|translify {% templatetag closevariable %}</em></blockquote></p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается оригинальный текст.

detranslify

Простая детранслитерация, из текста

{{ params.translit }}
получается
{{ params.translit|detranslify }}

В шаблоне записано так:

<p>Простая детранслитерация, из текста <blockquote>{% templatetag openvariable %} params.translit {% templatetag closevariable %}</blockquote> 
получается <blockquote><em>{% templatetag openvariable %} params.translit|detranslify {% templatetag closevariable %}</em></blockquote></p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается оригинальный текст.

slugify

Подготовка текста для URL. Из текста

{{ params.text }}
получается slug
{{ params.text|slugify }}
Также возможна обработка и английского текста: например из
{{ params.translit }}
получается slug
{{ params.translit|slugify }}

В шаблоне это всё записано так:

<p>Подготовка текста для URL. Из текста <blockquote>{% templatetag openvariable %} params.text {% templatetag closevariable %}</blockquote> 
получается slug <blockquote><em>{% templatetag openvariable %} params.text|slugify {% templatetag closevariable %}</em></blockquote>
Также возможна обработка и английского текста: например из <blockquote>{% templatetag openvariable %} params.translit {% templatetag closevariable %}</blockquote>
получается slug <blockquote><em>{% templatetag openvariable %} params.translit|slugify {% templatetag closevariable %}</em></blockquote></p>

Если включен режим PYTILS_SHOW_VALUES_ON_ERROR, то при ошибке отображается оригинальный текст.

{% endblock %} pytils-0.2.3/doc/examples-django/pytilsex/__init__.py0000644000175000017500000000005411047726655020603 0ustar j2aj2a""" Example of usage pytils with Django """ pytils-0.2.3/doc/examples-django/pytilsex/__init__.pyc0000644000175000017500000000035311051303537020731 0ustar j2aj2a Hc@s dZdS(s% Example of usage pytils with Django N(t__doc__(((sX/home/j2a/projects/mycode/hg_pytils/doc/examples-django/pytilsex/../pytilsex/__init__.pysspytils-0.2.3/doc/examples-django/pytilsex/manage.py0000755000175000017500000000104211047726655020275 0ustar j2aj2a#!/usr/bin/env python from django.core.management import execute_manager try: import settings # Assumed to be in the same directory. except ImportError: import sys sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) sys.exit(1) if __name__ == "__main__": execute_manager(settings) pytils-0.2.3/doc/examples-django/pytilsex/settings.py0000644000175000017500000000460711047726655020714 0ustar j2aj2a# Django settings for pytilsex project. # find current path import os CURRPATH = os.path.abspath('.') DEBUG = True TEMPLATE_DEBUG = DEBUG DEFAULT_CHARSET='utf-8' ADMINS = ( ('Pythy', 'the.pythy@gmail.com'), ) MANAGERS = ADMINS DATABASE_ENGINE = '' # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'. DATABASE_NAME = '' # Or path to database file if using sqlite3. DATABASE_USER = '' # Not used with sqlite3. DATABASE_PASSWORD = '' # Not used with sqlite3. DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. # Local time zone for this installation. All choices can be found here: # http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE TIME_ZONE = 'Asia/Omsk' # Language code for this installation. All choices can be found here: # http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes # http://blogs.law.harvard.edu/tech/stories/storyReader$15 LANGUAGE_CODE = 'ru-ru' SITE_ID = 1 # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = CURRPATH+'/static/' # URL that handles the media served from MEDIA_ROOT. # Example: "http://media.lawrence.com" MEDIA_URL = '' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = '/media/' # Make this unique, and don't share it with anybody. SECRET_KEY = '-)^ay7gz76#9!j=ssycphb7*(gg74zhx9h-(j_1k7!wfr7j(o^' # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', 'django.template.loaders.app_directories.load_template_source', # 'django.template.loaders.eggs.load_template_source', ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.middleware.doc.XViewMiddleware', ) ROOT_URLCONF = 'pytilsex.urls' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates". # Always use forward slashes, even on Windows. CURRPATH+'/templates', ) TEMPLATE_CONTEXT_PROCESSORS = [] INSTALLED_APPS = ( # -- install pytils 'pytils', ) # is value will shown at error in pytils (default - False) # PYTILS_SHOW_VALUES_ON_ERROR = True pytils-0.2.3/doc/examples-django/pytilsex/settings.pyc0000644000175000017500000000311411051303537021030 0ustar j2aj2a Hc@sddkZeiidZeZeZdZdZeZ dZ dZ dZ dZ dZdZdZdZd Zed ZdZd Zd ZdZdZdZedfZgZdZdS(iNt.sutf-8tPythysthe.pythy@gmail.comts Asia/Omsksru-ruis/static/s/media/s2-)^ay7gz76#9!j=ssycphb7*(gg74zhx9h-(j_1k7!wfr7j(o^s7django.template.loaders.filesystem.load_template_sources<django.template.loaders.app_directories.load_template_sources)django.middleware.common.CommonMiddlewares%django.middleware.doc.XViewMiddlewares pytilsex.urlss /templatestpytils(Rsthe.pythy@gmail.com((Rsthe.pythy@gmail.com(s7django.template.loaders.filesystem.load_template_sources<django.template.loaders.app_directories.load_template_source(s)django.middleware.common.CommonMiddlewares%django.middleware.doc.XViewMiddleware(R(tostpathtabspathtCURRPATHtTruetDEBUGtTEMPLATE_DEBUGtDEFAULT_CHARSETtADMINStMANAGERStDATABASE_ENGINEt DATABASE_NAMEt DATABASE_USERtDATABASE_PASSWORDt DATABASE_HOSTt DATABASE_PORTt TIME_ZONEt LANGUAGE_CODEtSITE_IDt MEDIA_ROOTt MEDIA_URLtADMIN_MEDIA_PREFIXt SECRET_KEYtTEMPLATE_LOADERStMIDDLEWARE_CLASSESt ROOT_URLCONFt TEMPLATE_DIRStTEMPLATE_CONTEXT_PROCESSORStINSTALLED_APPS(((sL/home/j2a/projects/mycode/hg_pytils/doc/examples-django/pytilsex/settings.pyss6   pytils-0.2.3/doc/examples-django/pytilsex/urls.py0000644000175000017500000000347411047726655020042 0ustar j2aj2a# -*- coding: utf-8 -*- import time import datetime from django.conf.urls.defaults import * import settings from pytils import VERSION as pytils_version from django import VERSION as _django_version def get_django_version(_ver): suffix = _ver[-1] ver = '.'.join([str(x) for x in _ver[:-1]]) if suffix is not None: ver += suffix return ver urlpatterns = patterns('django.views', (r'^dt/', 'generic.simple.direct_to_template', {'template': 'dt.html', 'ctime': time.time(), 'otime': time.time() - 100000, 'ftime': time.time() + 100000, 'cdate': datetime.datetime.now(), 'odate': datetime.datetime.now() - datetime.timedelta(0, 100000), 'fdate': datetime.datetime.now() + datetime.timedelta(0, 100000), } ), (r'^numeral/', 'generic.simple.direct_to_template', {'template': 'numeral.html', 'comment_variants': ('комментарий', 'комментария', 'комментариев'), 'comment_number': 21, 'zero': 0, 'comment_gender': 'MALE', 'rubles_value': 23.152, 'rubles_value2': 12, 'int_value': 21, 'float_value': 31.385, } ), (r'^translit/', 'generic.simple.direct_to_template', {'template': 'translit.html', 'text': 'Пример траслитерации средствами pytils', 'translit': 'Primer obratnoj transliteratsii', } ), (r'^static/(?P.*)$', 'static.serve', {'document_root': settings.MEDIA_ROOT, } ), (r'^$', 'generic.simple.direct_to_template', {'template': 'base.html', 'pytils_version': pytils_version, 'django_version': get_django_version(_django_version)} ), ) pytils-0.2.3/doc/examples-django/pytilsex/urls.pyc0000644000175000017500000000405011051303552020152 0ustar j2aj2a Hc@sddkZddkZddkTddkZddklZddklZdZ e dddhdd <eid <eid d <eid d <eii d<eii ei dd d<eii ei dd d.*)$s static.servet document_roots^$s base.htmltpytils_versiontdjango_version(sкомментарийsкомментарияsкомментариев(ttimetdatetimetdjango.conf.urls.defaultstsettingstpytilsRRtdjangot_django_versionR tpatternstnowt timedeltat MEDIA_ROOTt urlpatterns(((sT/home/j2a/projects/mycode/hg_pytils/doc/examples-django/pytilsex/../pytilsex/urls.pyssF      "$              pytils-0.2.3/doc/examples-django/README0000644000175000017500000000160611047726655015475 0ustar j2aj2aExamples of usage pytils with Django. ===================================== You need installed Django (0.95 and higher) and pytils for run it. It's a regular Django-project, so just start it python manage.py runserver go to http://127.0.0.1:8000/ and look it... Warn: tested on Django 0.95, 0.95.1, 0.96, 0.97-pre ------------------------------------ Пример использования pytils с Django. ==================================== Чтобы запустить примеры, Вам понадобятся установленные Django (версии 0.95 и выше) и pytils. Это обычный Django-проект. Запускайте python manage.py runserver заходите браузером на http://127.0.0.1:8000/ и смотрите... Внимание: протестировано на Django версий 0.95, 0.95.1, 0.96, 0.97-pre pytils-0.2.3/doc/examples-turbogears/0000755000175000017500000000000011051306250015501 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/0000755000175000017500000000000011051306250017362 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/config/0000755000175000017500000000000011051306250020627 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/config/__init__.py0000644000175000017500000000000011047726655022752 0ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/config/app.cfg0000644000175000017500000000224511047726655022117 0ustar j2aj2a[global] # The settings in this file should not vary depending on the deployment # environment. dev.cfg and prod.cfg are the locations for # the different deployment settings. Settings in this file will # be overridden by settings in those other files. # The commented out values below are the defaults # VIEW # which view (template engine) to use if one is not specified in the # template name # tg.defaultview = "kid" # The following kid settings determine the settings used by the kid serializer. # One of (html|xml|json) kid.outputformat="html" kid.encoding="utf-8" # The sitetemplate is used for overall styling of a site that # includes multiple TurboGears applications # tg.sitetemplate="" # Allow every exposed function to be called as json, # tg.allow_json = False # List of Widgets to include on every page. # for exemple ['turbogears.mochikit'] # tg.include_widgets = [] # Set to True if the scheduler should be started # tg.scheduler = False [/static] static_filter.on = True static_filter.dir = "%(top_level_dir)s/static" [/favicon.ico] static_filter.on = True static_filter.file = "%(top_level_dir)s/static/images/favicon.ico" pytils-0.2.3/doc/examples-turbogears/pytilsex/config/log.cfg0000644000175000017500000000102511047726655022113 0ustar j2aj2a# LOGGING # Logging is often deployment specific, but some handlers and # formatters can be defined here. [logging] [[formatters]] [[[message_only]]] format='*(message)s' [[[full_content]]] format='*(asctime)s *(name)s *(levelname)s *(message)s' [[handlers]] [[[debug_out]]] class='StreamHandler' level='DEBUG' args='(sys.stdout,)' formatter='full_content' [[[access_out]]] class='StreamHandler' level='INFO' args='(sys.stdout,)' formatter='message_only' [[[error_out]]] class='StreamHandler' level='ERROR' args='(sys.stdout,)' pytils-0.2.3/doc/examples-turbogears/pytilsex/static/0000755000175000017500000000000011051306250020651 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/static/css/0000755000175000017500000000000011051306250021441 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/static/css/style.css0000644000175000017500000000227711047726655023347 0ustar j2aj2a/* * Quick mash-up of CSS for the TG quick start page. */ html, body, th, td { color: black; background-color: #ddd; font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif; margin: 0; padding: 0; } #header { height: 80px; width: 777px; background: blue URL('../images/header_inner.png') no-repeat; border-left: 1px solid #aaa; border-right: 1px solid #aaa; margin: 0 auto 0 auto; } a.link, a, a.active { color: #369; } #main_content { color: black; font-size: 127%; background-color: white; width: 757px; margin: 0 auto 0 auto; border-left: 1px solid #aaa; border-right: 1px solid #aaa; padding: 10px; } h1,h2,h3,h4,h5,h6,#getting_started_steps { font-family: "Century Schoolbook L", Georgia, serif; font-weight: bold; } h2 { font-size:150% } #footer { border: 1px solid #aaa; border-top: 0px none; color: #999; background-color: white; padding: 10px; font-size: 90%; text-align: center; width: 757px; margin: 0 auto 1em auto; } code { font-family: monospace; } pre { background-color: #dfedee; border: 1px solid #7df6ff; border-width: 1px 0; } span.code { font-weight: bold; background: #eee; } pytils-0.2.3/doc/examples-turbogears/pytilsex/static/images/0000755000175000017500000000000011051306250022116 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/static/images/favicon.ico0000644000175000017500000000207111047726655024263 0ustar j2aj2aGIF89aM]]]Cqqq|||ӑan/-.ͣӴ򓓓̾ίuRƴZ0T%`ԇmbn01onniiiؐiV}QQQGGGC&ÛT*fff```sA.{-M"WWWSfAZ2wgc[Hp$>l1l:pPI򞾜jlqL̞沶䌍beiۅ=ߏ썐nqvwzľHuuvՂLMMSUYzCt6\_cxxx󅉏NPS]`d2.on:::QSWֶ˦PHfhmABBöƻ՝ISSSbcéssgggXXXZZ[ȿ|$##ͺ!Created with The GIMP,4a!y3PeHfD$ <͊HXMc ʁp0:Hctq wbE'Ę4iRx{oXBQUGZP#UR]  t-( ϗ.5Q:TPiŚ(ьlj16ls"@˹kexK#F 2>}\bD*$h5 碆1cb6q#U0$#_NH D蛬`-a@;pytils-0.2.3/doc/examples-turbogears/pytilsex/static/images/header_inner.png0000644000175000017500000011124111047726655025273 0ustar j2aj2aPNG  IHDR PR9.bKGDC pHYs  tIME /D8e IDATxwu}?|?gq7;!, B kEUqTVۯmVuj-jk]((30!d'7#ws޿?z9=q|{}#|<,UgsT/@̏_Ps-.%r}5xO{_d+SsG5k}/!C%<^OFvx !b P%H2{ ?/kɇU` \怳qdLO=̘s Ȼp'.< ;npAR`N87\jQ ¼ipV@(4q+>#\h]G QeAEë)7ʦ1+|rԔur8F&=* r|2PQdYgR vjnB>9(p #hϺT`"X5x KW 7t7sW$wdK&AUjmR8.$~O~ &9}>Yj(|᱊D~G ."px\:r(Ψe>8N~)a$(e1C#T,B9(^vR8 S~)ԩnL?WRE *P8’&$qq2dW3Dn4 .g cUʎxEn8?/:(PRhPEXT;y/~xW!lgg׻&q xp |WBRx9U<% "?X$xcx̂C+9 @bdf&LZΙoJ΂"Cu^3#% ɾ(1d eZ]?Wj gizH P‡k7gHm G`}(U=SpZ RHŁUO"P x^U)E Xd)FqUE@2j> aB;>'@vbŖO:VƶIavoa[@æ MƐ?gt#{dS3'_@›Bqs|&o7y W}_D$ B`Bq_5n3YA.S12Dv 0ikӠ9&n Dx V'F TcsBds˿@$PeD|#AmyoO]@!Fǡ}JMMf"EjB Jxf<衑($$23{NF7*A2fIR\Mk_vG,%'o$0 E1Q}B.ĭQĹ-Up͚QӐNe n"= ¶.ypwF'b@ Q1J.Ap9;97yA8rc% $_xa"쥞kJx%, % (t)Ť/0?{6'f-ʓ0X'PV0aW Rg?x-T(U_,RvfN **"OTj / O@% Q3f3IوqW^c%9QP,>!d1#yH0!}yK[,HF2ڐ 5H.$]PcZnƵ$2&DWyd!ZWȉ(Rǁ?7 9EfFK, bXD%]4+Pʓ,!i1l!~x4w^Ǽxc2rdt?kn/gxl0U i hȎԬFVO"OJy0qʉÊ&Uf B@Ԙ=-€#Zklvs0n U:XѪ_QC"7q|Y0e cA\bqܳECgnU5\iwjrD%iz507 vA=0 1vus˓j,߆B6҉"{8C~>YԜdj+({@J3!ikH6xMhu<] T)nd lY1YčFPr@6hD6Z5* Tr+RBN%(u:W#xΖwvQ(d< &է?IH0#|%qc=~'Hk]I*+,R5W5 d~aC34CcuPr6"MŢBB&6mnD Ѝ'"4n"\kSY\{E1q-4f@rń-̉L)۸(R !;ud@j)4d1%Q iD=$Q'pa#C=)%oC Ԉ[ &'4B5#8.J+*5jI('={5 @̌djO5*uFTƧ)P_Z@w6 @t\$'oڔ5 y$2+U|75¤eeGD@)ƒ6pCc+zɜx%2 &LH33~ӢٴL91h72P/*.e\)/Y&L]&(*bH3T}BJ[# 4C"L'"-mꕐأkk<Qj ;x)b+ sV.Hpj2%LJcV28*q{2F(= n>[$ETŽ3+̹ɾ],4xP2p d"+4^YU┸IrN6) MJ#1"fQq0 1CrUmI'ؙi~P4F871,T<:M1ư |~]xL&+Xaϼ1<'F4mp63 DtvV4="&n[8AF.ȕ@ *b`.Gٳ:1*I$4Pwބ& #&Y1)f6\WW:;,nu[ ̆@x6a$gpMzn97h`yj ﹤̬2%B79JRee.Ju1ї I@`dMf/ +dOG(T!;M%[Uag @t?^2 [Mr #SA .B]ϕ@>V$@yv'YX:eOeIwH_@ʍ級AAJbPvFLglo?f)#]Rd"kHiZb*t×HM͓`ȭՌdc"kUL:Ȇ*H#)mGۈmBHgt)N#iq>q CD#ytsJq}850 PpѴHhYzR ebn,e7\cL,Mח%6JDZ }$K%$5lK5`W®E $ I#`dh+Lwk7Yp0 DAB'yp(O~81ij1& +_a>,5QZI aFȗ2c.d1H>-/ 4w2,-4iy {x e-~f}$0pk2_fTd@̥ mYg9Xؙ2;"6^8oFs?{I+>T-l?<^Z^J[vs-1EL3¨]В>)s8K,AZHz}Z?nd¤Dƿ1~3"r2 Wxρ\Z ,+!0DOߊ_9A4hb:$Ԑ{$,\gS'B&j,#v5/9F18?'lS%TI)2IFX`5zt#3369PDIlL\ZLVpot+Nfu/ĝOo<,IH0ß8W̎|JJmAZ9ɥ h0|7u`^}azw%a<vdݒ bIܽcQ`!'q3O/oco=Pk1dC~G ςsڅ4]"Jr4"2OVq5􄷩'Bqk%j D%A:@"ФQ"Rp@Pq"emb"C΃ zsS{.4Q\s 5Ϝѧљgci(1=3E#GY4ddJϫWUq?st3xҭ0˙oUV|i}lc <(6o,Xؑ/^?\m-{x߸cs~Nƍ^,!@I(jPz賀GPSwRI,Ŕm'Y;L.Gf2j=0$ԹI] 8F^aM=~jNm<0\@A}|u^otfx>lÙަTu@9pbGK&B!3YL3-1mnMҷGfӧ'F11WBN*W9zsiX%:n9|wH Z$B+sxi=tE[:7OwǎArD5*1[/Z碔Iqr` IDAT|MCӓ{v~/$iFyʬ+QW8" 55dGf3KL;qv5 qk@j=CNIR0rc~Z@٦Xgk0wpO\]>ӎn;Q#ع(IdoS ~R7.]ǟݲ*&'!.T_\D}xDu+p6|]15WظprrP,i  Rj}CY:bxfPLJR$ q *Oq݈K t  KvbL^urh߈w'rO_>=?9epa4}X ˧q[OU?܎=r؃'׉\BMgfX[]{f+g^ɼ}LKH@:,g6"#':6e,Dk{GA#Fo 9 7ED"w.C'.6 _?7BZg{#>Hnq֓ k&6k!\c] a IH}9j cZ`N|fFd13"&8m,d~8^ s%3A{ X 6K[AIic=+2֋aAKDbb2\[ؚ/_+A<o< {2'`\Lsk3#]Ƨv'|s u\c@;_?鄨h*K3͋V>-~pq6/5o1oF+PN.d1UD2BYaߍ} C岤BN'!D^#"~փd9<=SYz5 M7ecHT !R<ȦɈٹ14 5ӟa =%0kjCE.:J@*"tВ_lis ;n%nVy7 +9I8|Sh˥ ۛb75[ׄ^oq7:0߄1ƕV= }ͥUIe&"5Q"NSf0<:E|]ن5 t+_Uh, Bמڃ#Ms 7ŒO]Gy0`aW .C7?mop̂E[KlC'#%=X֡nC'welA$=-x~_׆SuujeOoƅMigu=9|ŋƦfqƧp{q3CB3ipxә+іӝKƶSqpg"ù z4~y~`xb| ;~y/.^Kk{sh?~`'(k1 Ov7> } P,gB[p]sks׹ [Qy(w`dՃeR8aQVt{sД}GS 8\;{}82>-}QW + lp棺D1m0|&7XV)[.[̘*5ϋ y<h\^tl%Z; ~y{R9)fx/'c> ^!׏m )0;6fI,,F;M\':Y~o\_vM_27Ò$fh,l3Gqdk\rV'yx4.Njvbjhr8`eh<}E밠Uv4^U|Ql2S(֩ߛvL,}+P\9`,eλŌsޜ(,_sO&)3eܔ?>'uMUF,QIא 2C.R4̄MՕx^Bơ1)%ᘉOp q{P(`@̔,=x ^+QʼXE+CMأzrzrʟt\ W}~ҨQgoɞ|f (u^;?9-irq ~{mceR VS– O_~Tv,va_1'lDM*ٜb Y3sxo_*E]`m%pu8$&G|<_}zdg On]ҤMJ7Vӗt9mf %B)@7]r8֡i\/b`,hM{A] 75[G{`܁(WbTf܏oL|wVqn,& %22w"m2b?Jqj1vӘ)@ao6Cc,\Jp}#~Ar_tZ/#V%OY<;тcÚ$ ,Q5'9:PZk)Y3j[2{cˀDdH٤]@,( Im Si\v:1};3GwRG5nնlNl3#X.oZ/F:OZ.[Ö b`m@8@&D&ܺ^[7&3[qqaŽ&[aݖBW}gAҼBRLh#XQ|}s:J%O3Zxl)J2M&UJsX,Ƿ;r&:;ƒYa+Oqn#_/{w>z`=#kBԾbRl>7UrͶ j_a_#|PHb085NNXffdN)c k>[c@3-;ЋVphp&du] R+kL5 Т¤2~K'&lL|HZ5h3 <93dgV86Z? }3. BoĂ$&35b9s+yȉ|a&M!KՌEg&qB1~n9J\ ֒/&TܩB}<6Nᓿ٢єƻִhKLS>0j73כ7(Bo %8XkW. "X-h>\Ť13CZ[DH|5d"-[-ʸ2~˹T*}XݣsZ&`vZ }ېA8eEɜL PRAE(#H-XU2!XH@><V.abT2ic>Tz6\4bTf>ʞKjr>:oimCe}/ϱ:۞VJ [j8W+Z1' ^ ƍ[r?!ĖrVos FܓsuBrx"Xb cљCB$ €eĜAk}7dg4199/䌐Đ#ׁ“WUaxGqxt&N mO1Rf~|v(J."&X`i͹gY1\U+O@H/d O-\g"!]k¿?9#zO+9MZ/>4L8xQ|FH-BV^!h2V&|+DYYs#xg5O&ƪ_Y 0#bޯa13z5G9|gtෞ$ XpOK.eqi}Q7%]9y@.>KXk)=1d02Jz;b_z`-QY&fJokeZÚikc_+z @{~ F᭍.' –'LG)ZEo(C^&ta+4:wϳ"͛OB2w."ظ0tq1؆Hu€!|L3>{6=|h=2"Q{/XS;*c㝶x@pE>9|}ڻ彭xѲTsʜ \afבqi [Gp`lZ+L_+nxӱxiHHg`dafbTXQwR4Le #MD7ՅJ]YuWb ]WI9ȩNXEF!V$Lu)<|M8pqqs&뉷K [=e('>UU!ڴ(%.Bm94"U3)o B+WYV}/ɾܸQѣy,'t5j9 BYhs|W>v"~|!C141 XnɃʃfw8$oKHؖR|PdY$?9qBh8n#Ue;W.}/TKj@z#5m/H41: BCpbNRl CZ'r8zό r͸xP'x}_ikҩpxAGo\ǻӝ?-%fi}Nie HwjaNr+8p1\T ҎYc;2hNȁTG?@DuN#S4>}!1Fibak4bY{4SǭHgmŻH~WRp zvik"o?pXcN_֍SoZѡgeWzB{:#zp~m c.ȧsg'[FaIñ ^1 fЩ+␃eurQ!s+;g:( '`)kʻBDӳfds(vFs[#1H,f] bD i$Y|(A }"FDfH~@(21ZD0ce_ 5*qɒ7Sxhm1sNȢ+Kg.橃`fr8J9"71? JWW"=ѓ$力 @IIllFa|s!P& Vw.t^2]983t!}RR9 1ؚ±]Y`ak=EHaF$8 @Qà>]ۅl+ b|~} F_>+n?\+&'B\U'#7~@#<>-]+^^Ӂ:y[L5b]d [dB\cd+\BF+a&i+7a"ZVup&GI~E2 %Wq 3ik̅g&jɾuP=fd:P2#L Dp R`RP}P.B/GqMt?NH46^!,tq |xvȅ:őQ}lW+xU| VH=5BK=Hui nHu{W)I Y3cxl̵eV[񞉢cJaNyA&")l}dCA+PդnЇ팜{Ƨ{1{xй¶Qcޱ^"ܼy3ʔ9[}kKt >ugapZ)wca[]udqRN3i3!81[(%i#r .ofEمГ+Nƹ+z- -5͍¨,mK)K`xr?z98yQ'r)މaço~t ?UmU'ʭ[|w7O=K|Jw9O>2w=-&{f~_O\wM>^y\;7?-^/U NZ񻪈0WM˦Y;nd?%S )D8e?u!pEھ BՆprSa4F u]&r; a"<%snfٖkbI1x:.]d $7^`VyuDWR6<'Ma\k\!Ȅۈ(N"1hֱIp0Qklj$J^|Q3񏛎 LΙ;g[x ^kyHnh_L R.?]9TOi9siD@zdSsa@;qå'^{Ŧ=Sd[~~dž1eYkzsaBW巨Og'Ziku'/ݲ,Eo=rW&iɦpMڣz3-$d0>=k؎O1L@ eY3.LϨ9a}û Wtc)|E{Oa|+ L &I#1خ8WGi5P!AC`?sPBYphCsSx: Q! Ix#LcPmiTם'NiPAl0Q^#5HdGu0uʼnW5.֤Nc?!ܺinf 2Ў%0ĭY߼EkёKԁA_-ppmyq{30ZtcL&JB'fu~~"cYьW5{*r|x X3(տi>|:2(JgA,R [sÅ_==gFgGH6R%ĨZd_ua~3`٦0mÝ;d^ܼy`P8f=(1!,Ew ( h[nDm](NaF|鶭+OuYܖ'smMɃ mE>҈^m?m Ĵ!:tjkLZ@1<;*mdYO OO-J&J)T<2qj`qHI2%$Sx'@sdU,#J P&Xׄ'%{(#pk Is"(AQ[P)^u>X۝{gwR-4f9wbbv`!)VJ:B#(#羜 O,Pzv—E7M /+p7~ B{ɂM-hB! 㙽|R]u >MřY3'P@ǙkLB_>KgpBlT{`l o]D_^n 7aQLQ(߉-X4KFZgu^.]7םɯXӉ/>4B9WP3sL‰F%" ,}q%SX{#a' &VO[t8_fֻa!}f U)8Ah=YR;)rȞY4x/#%n#ƅwIAi7i^)'k"!<y%I1H%<0v9 P}׆Sl?t̖=y:9<9߃{1+:Z o> ֜#u3.=N85 ^CqjW߷:q*|ug36 hLY{k$!})[30>M)yF@D`Q t5ذʸۏ; H&G-ჷ7~g.AK&uvah),&k΃;as`=n|=q"v%dGNf[on I"yZql :ϼMY{ëB<.?|g &fP!X@;@Rgbyw =OfZ8yM59 8B݂(r_A!c| He Xʑ=&鶃"R.m7Q k 46m#,חkJܩZ8Pn@X5jd^x!_] ϛt˕LLuӦK6ㆣdNw{^,o4%ɃL`Mbf~)iѤ)=>ݵ)guɡvں2kiGspJe"jnuvad$!|aO8y0X֑ǘ}6VՅ cÂdцkS)6Ҍkف7Lz}J$hmVIQr|޽! yXQ/ 5B2#waq,̃Zn ơqӲGeaA!aWjcT y[ˤBÛR]\jfC gaT)&]#Hpj?f$\b~)hPXFԎ (l\TB f͍6gA3nN¦Ϛ]O͂(ٖN]ދW!$a욜 ґf^^8#LGa]= g>qӼs n DQ!xɤnN޾Z'` `|Υ{06WDa ""n| T2}ҽ Aqj(@- +5 ^w|5WCӖ+`&\ dsq^C laywɝ1*[O:^@||068=2|q)\mȩeȧppb2T8Aҙ|hbI(<]Mi4K9$%+gfU~,VZS\Fn?+ .m߸XI8\F / ܰ*W5*/"A nI| 796'OG2 #EH4ܘa93n1-pIɨ0ܬ!]42ML1&FqꔬY5RSl*vٵ|&cE< eShyƽ)d6$7P coKslw+x'l0 3J~ϧ#||yP=/S$ХL ũq)ݣv͋3 V~WHSƨMHʟj3fz=CZo;e!~8o7AjKC_& QG/X*]_T1A0O᪓9/"Fx+!m**R4P$3*"D 4hu;9ϕE~[ `0n{ ~?|vVv@RMxɝppkc`#cOi?k 1BㆨSfuY}Xeb#P1ʓPt;XɕUzJu"N,RGz@ LItI bR:!W[cRaiEP BHaVΌJUeTǩJ7 T\KGHS>crt-##UX$=p?1L>#]+Nfd;a$)u,0i)HBI% ͐bn9⌕S`bds,'nXIOUBbbl7aȱCÒ0/J}:1fEP3c paI9&?#꩚^CۑY`vD/l@Hk{qZWF^9:22\m`f"8W6> b6ii^tgS5 Y1k>ECG֟h}(jp>e$4"E`b?aMG5|>oY&3LaLP|'"-WۍLI{CBS݋w㢥-X\bWue&J*-āGf^]2 %ԩV :L_O1ZV^.16 ;`?fwyy z0)PYp ;L}#&h@R HR8w܁;h1#4tOl)]YM ;{⵵5wJ6x s>y4ebp9ydNhat :C=cSŒn~?~?_*f _> WnW,⾝16=XEG=▃)D+͉0ӄ=gTon| ?yg,+-~61m^?Бuy!T` yҤ~$&A`k# R {&cu`^q G$5>vbtĆcw;!Wm<#3ERs~;v "d,:r2OGf}>05!wceByf|NŻ1BG@rx`vh8k9]9|~C6:+0\sce+g+g&rM7m~%]JXfX\: ۆM#_6IHG W3_eq5/~kt-}Huj58px2ɥZ·ǬV12oV=81"[YO_JsbE>gc;Y0r`ڛemxf3ut o^Lt!6iNǬaٹǴg K;OabV URG\bK[NDW6rݜ' ڧ&ZP@*6RoEX\tDxӉCGqrHw&bDZ=rOZnyӈ@yπI xg?ߋSmЙ8wqhɁ$vc>wahbl!2 f#i 22Id3,A\/1EK"\i\0_+4% %%ktw!0\tra84(ԠְA:ɾ!V ӗO%;HԸ1EiD_γE;u} vQݒyڳL:,]X~ד(HwZÊk6#hѫ]~Ta)" p_=q(!I]xmCݰkNrmX҉k`ȬtWiִ ǚ@QZr.~$hA A(9a~ D"2*@wЇ1DSYf5t)k!D], $ MFrZ\.nKGM֑X-aFfn ) fڢ^G]uǙǘ<@z$?ݱ>GWEL8Ö1*ִOߵ FԊ%xҗ\;>pS:W B.\>wE)DM2K4nz`እ-bi ws(pt",iN9(i3sCxh(Fvp5YΗ\zۏbMGSsEܻ06,qўax mx m8<]މ9.lKZShN+.CնhJA=\|@3>ۍ5"M_GMπY|l"YՔN>hjb|Ψzddw RҀ8YN' ੱY<84"Gs9ir>ܷtʾyCWol;" K%vn[W_gk\ L_^c>HE`4<v_d\Ź㳙m)=*${ jx:%>&(@O?s}B7j,U(P&8!$>p,r2cam{kAkͻvDHw7l*{ӌ͸QLT·Bg7<"GGXs+~12݈Ͽ$,jwf:3 'cx)0Ɛ\Z%jK Cqhhܸ{&7;^"Ϯ,CW6xn~~qѪx(&G&YKjr(pfc8ܻEtWo'),.?rW,Ku60H$f7mW_F'uótxFjGg⧇aey>o$ND}գYfW^, -0yȶeK旅,,Kd6BڀW w.;3=ۯ8j?\ťsw ['fMJh l2mu=տΑ&8 ( pmI ؒ#dd0Iv3tN #ŊZrh0uP9i8y6 /Ч ǹMւ>v{ g >qPG,upbUL;V}VN{c`Aٰ8tC :Ģ^9xNԀ7*🷢5Ojγ+?|`wrQ?wߝC 6!~]㚝Т>#_'p0]@'.܏2BPI]|5?ƇzTe+_x?Odctd*UK IDATpDX=BHSAWFP 's Ci]#d|Cڃ>'md7kR Icn&9|M}^7.Bm5~_~w3RoYjAZ_"SAJs= ]Ӣ<&1Uԛ}o2M))Dy4͆ފ kTkev}cs M_EʶIsk Q=0 .p=+HTc|ŧv>9w#w񫷦_J'iϼ^\>Nm6~M|ߕSGgd 1[d,`ʭ_??o=8S?kw fb8wCL6@iGPo _O|lU|:-+E}o~+71.מC+a%I-mHϽ]ZS LTȿ|`[u[FU+W_<çKol 3߼G?W˟{~;xt1!l@j8z}N 5 md@3!9NA mcPY/(zUCl1yÅ<?b 5'_7FrŎSV\.w7Z{V=|pc3&4Kq\8̀s :5Ouzybii0utvyZXoԝo¢E?rۏ2"Gg-.9/qd[S|=oap*?h,. ދw?]y /_˗> W"u< qb1})1ܿQ.Gw>z w}}>z~^w>><|?L Q /bbEg&xp^ΝӧnD~Rx?YS.38n\$8c|0SxSp01m]S}?\ߺ㳇+gמÿW} wc٨. | _~x@Y-;5]Swmr/Y>Y2"7 A$L 7=0̚]0pNQl+o3?G"gϨ+Pϸ>9iS nZE~^sy5! A_tRNgj&Yc {PW%)vUME}Rij饈qG켠a&VKƹkCNPb~)B-K6K+'҃8tU`SD ͼ8a&'{q%{xqwA 5->ÃFGϿp?ٛ_ '.bpflaVG m7vʼn6L,w)gdsK٩NwryY92kh°?4v6 9>N`#a!*2 "lcK~#00@|G?9skAnlBnh܎ɴ|lb0=,=9`pGQ?DYUyY9$-@!p iBz:p41F7^OQ  b8܆ځ #Km97+7P ȍM|^ /'ggv Vmϼ}K?E~:^{hl2]˟QNKVQ3owgb$HB7t<㆑@!3v;s90q?.VĽ#|Σ) &[@ FP2ubkIN{hܮr*A=ShN3QK8G$,YiUxSL'|CoYCAD fH.'ೣs j*j%P#i,Q:ū{Wy~z9>MEk<9 vNkF@zhvBcF J:rF|)R,0XǨY c1aIF32 1B GAËqug0'W1WJWB)Y^9?vQq\=>Duόs77>}W_ CW#?vNϟ,6mio=FN>%;:(q>N߹2S>ib)fTՕh0D6pY@ׂ<%e8>? cnWjڴa FGҵ!66 pF/GsK?qY;dI޽s}]+_ %N,ϱS rsǨYݗTruH8#81M'."֬ A|gyb'X׀yfJQڰz$-!9zW[ZihYqdUD]u~K~AQ< p 7^D+LR`s>Q$$ |o^svhg'' 2<f`G9|x EAы/2W2Mb0Dq$sCkuGC(CF '^?$ɪڡ^CmPD mI.{6+H=g_!%vs֫$,_XgI:܈sg*,)d3b0S[:ZJS'Pa '[B5kx(beE*;N ';NRC~w!-QfexJJ9UM]N%0tP3s׳ L>6A((d:G[!ĄHA8N ײ P]qc#O%Ł(y]8>9碟sD뎕_! |FZSų-+Y {|#)_} d1X-GoAN&xy+"N!.Ii 7_ #c h;#R y@ ^BG3P?3)ǀb[ usqyjcf[ӑN~#+d/76|Bz} pSŘ Hy]Qga|GnI脽"p:La#9mfSȎEdEs$,uNhr u43^(ArԴCצ\g r3dX/\Us'azFY~eqy!9Bv2epg\$5U 饣=7>عM\]Ў"Km^w~r,yK0jx~T#xu\RX⹍ 7lswo n.D+lJ0$oðyQP){"zy#&>b~9qv^JWBQKTTN 9]F!5ת*9UWpp-3E _dUi2NFC(vIT9 U;%I+FLCP$<7`cN  A@֩sz>u g#9EL *%R@>YK8P$:%!-6fDI#{6wovu/&;VDWJ)!0pxmhbl\$ٚ_-=%d!Ea%8L>b"[S~_A)$"@ވ" $7HjvW91* uA[y40J Db?%0SW_u%YX#0(H*^r=%{,}πeOV8m8 j0slÆ5%Ak8H L]`(qDr_]/Zcs9i2Ag"O3 u9`Agj ):"hmվ6Jm1w:K2Dڇ-6mWuJL$&Y:-pPx4/W;P1 L`o$7I8\ӂ`<8\* v+f}!xޛ twaH@&:AJ p}$Z`ƴbi `Kay'L'T%Aؐ݁a$uCi9'KGKhS4[= @?8#c=YK6z0^FR?2Axq#È_}´VɷsC:EfW]8\(/U Cib3XyZ`=n#aB 8+LU##=ƒ;kDvfF ޙN  %c+S םbޣ +Eq Kݱ~mb{\vwq͚!~BQuSEؾ /b+&B6ԏ@C-0uov'ewS6e~1O$.d$! dJr1dI3wK A 772et5RK`9V̸3-poZtU:CWlBvr{$ Ui8X<'7'S0T`Tv2,T*H\T̘*b D+`ţeK~Xű>y3#kКK;S)\w`+,<xi3@T m}h>ҋ'j\P`ܞ8X(A SkSo-WOJD}N* %\!h|H sYeVnVv؆1/D>>_NrA} 1. Z} s`"ت:2,CTTmǔMpb)iZ鑱 ^].Q1z =W`P3 Uð3WjehrmAU^a #¡V5f (qݮ$(-5H[S᱌0b˝-G5ۧA/|v1qP Y,9xq688'R2g\UM|i8w:h,."Y~W"]׼B6SrA(Cjߟq& fE#cbIJc7bP|cpP>y7ׇcB/Q <35r=jomK1wvgVh0NX ʹ,}_D u^q, ︿'*$_.QrQRvdI IZA$XGV||bˉ@LpLfXj/&(/]C7١Q&ϲ|?n"6y'GԴ="mZKz{*Ǝ$m `*cBvz~xܙ0t]}OVvq_Yn/B!|Oi8Pl{s ]FJdӌ3:rZF{J#o B{yi&V%\qT (#G(ců2`^Xxid$s#X'>^q++`c`ʌL6_αY1h{qR_ P@ۜ&<^L>mF5[a ]&Ћ4HG2AOe#6MYUwV Of" `g +;B9RLnՖ!\`Kn18?+-DZZKw[2 gCף)AL; 88ugb"Pb-p玾u]SE)[b &Bz +YUW*ü)b%Q{lCRmKضI캓8VѫEl^oʵ(W]M?&E3m15yCAFR2ݦFJ!U-} {Q8 X 3xy7C11 ;܌yj}j&n{MOY,"tYi8 ٽ\@ b%:^*L ]A3?VS 㬎]1a"h%։hslUWFU^LFC|Jo37T y7S<̰[|;#.lg{ymM x`y6.KH+Y\qF><xk^ [d8Z*\u>-uܹ@w3xɘKC=1-RBiUN(Q1qen` %U LHe*~3bnsRJ:R2!qSv_ 5bgJ+R4dS2a1xxTNzNJ+–7Y'0N(m9{80w5@4v?` B&<]6bl+f@J qGÖF q<ROv $,áb+7dNw\oנCQїx/J/;EF-&$ [8[@S9mn1Is-\gNM(g孈tfZlOvLuDtu(Ɉֿ7Qs[-DŽg58`aS䜦(nD:LސMȂAnzgIU>6+C@ߪ:Lx0-p}R+rlX&sleX[ѸM' r+ס76S1a IFL0sc}},0>όHdh ag@0S ejaQ+\T2)p$2"$*iU flԚApe~Dtn}xk^`w )E^]Sanh J^wD+Հ`s CǹPCesEU;u)$ KC*Ŭ3[a&%rX&[%a'W- )+fL"wN M)O0#}lflj#hrַVf0#DE_0ދQo68ό*.)Hm02)Y% CUVGȨfiO<rD(S9,pX v̞OV򽓗ilOk9Q!]iU[U EA'a-W^DHg#HDg(IރSحQ5v%~.N2u4!&3oï1(1P7ZZf+R P4:`q`XT"pdQ7nJJa3X0UQ56FVuW64\1R9b2nt"hQ8!Jy$*E |gKš6bJA#!CmdfYRns34H ܛ} E)HUQADt]%!vXT+lJ{5ɀ܌[seq(Fp] :=ay(pbpɄu㞾V'wdƜޕKP6+v2dhD 4HWֶ4` `(]y,B= L+5mP} Ʌ)@fcGkfLbcpWBnqOU9)SlM!CYXB֟* *7`U7-YFPU[PY $gfT5IX@ ]V!͢dcĢI( v-繹 3+'CPCRͯBPTP;&qQBp6?v:s^[՚-a)v53f%\b$$Wg~ `Ej TIC c߮`QqvN=*Ue4!\olmŪ yJa+#'YJ0k@vΕr-X_GfrՆWW!P:U e(aTdo⚧T}$e'XuFFhLWC5r\Ժ@$9A\>Uzle+zmIL'V"FBp#J7!Ӯueep=pM8ZysU4Wc#X:0Rɪ*r:}VNfJ'p:DA+[&rbd$l{Ƚ҂Inŕad9dJ}p︈d'o  \Cd"[ J~IENDB`pytils-0.2.3/doc/examples-turbogears/pytilsex/templates/0000755000175000017500000000000011051306250021360 5ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/templates/__init__.py0000644000175000017500000000000011047726655023503 0ustar j2aj2apytils-0.2.3/doc/examples-turbogears/pytilsex/templates/dt.kid0000644000175000017500000001175211047726655022512 0ustar j2aj2a pytils demo

pytils demo

pytils.dt

pytils.dt.distance_of_time_in_words

Например, тест прошлого времени был ${pytils.dt.distance_of_time_in_words(otime)}. Если более точно, то ${pytils.dt.distance_of_time_in_words(otime, 2)}. Нужно еще более точно? Пожалуйста - это было ${pytils.dt.distance_of_time_in_words(otime, 3)}.

Точно так же (т.е. для Вас абсолютно прозрачно) и с будущим временем - следующий тест будет ${pytils.dt.distance_of_time_in_words(ftime)}

distance_of_time умеет работать с обеими типами времени, представленных в Python: с datetime.datetime и time.time. Например, ${fdate} будет ${pytils.dt.distance_of_time_in_words(fdate)}

В шаблоне это выглядит так:

<p>Например, тест прошлого времени был <em>$${pytils.dt.distance_of_time_in_words(otime)}</em>.
Если более точно, то <em>$${pytils.dt.distance_of_time_in_words(otime, 2)}</em>. Нужно
еще более точно? Пожалуйста - это было <em>$${pytils.dt.distance_of_time_in_words(otime, 3)}</em>.
</p>
<p>
Точно так же (т.е. для Вас абсолютно прозрачно) и с будущим временем - следующий тест
будет <em>$${pytils.dt.distance_of_time_in_words(ftime)}</em>
</p>
<p>
<code>distance_of_time</code> умеет работать с обеими типами времени, представленных 
в Python: с <code>datetime.datetime</code> и <code>time.time</code>. Например,
$${fdate} будет <em>$${pytils.dt.distance_of_time_in_words(fdate)}</em>
</p>

pytils.dt.ru_strftime

Тоже всё просто - используем обычный формат strftime, в котором %a, %A, %b и %B заменены на русские. К примеру, текущая дата: ${pytils.dt.ru_strftime(u"%d %B %Y, %A")}. Согласитесь, выглядит не по-русски. Чтобы месяц склонялся, используйте опцию inflected: ${pytils.dt.ru_strftime(u"%d %B %Y, %A", inflected=True)}. Если же нужно чтобы и день склонялся, используйте опцию inflected_day: текущий тест был выполнен в ${pytils.dt.ru_strftime(u"%A, %d %B %Y", inflected=True, inflected_day=True)}. Лучше же чтобы и предлог добавлялся автоматически: текущий тест выполнен ${pytils.dt.ru_strftime(u"%A, %d %B %Y", inflected=True, preposition=True)} (опция preposition).

Не забудьте, что формат нужно передавать в unicode, а не в строке (как в обычном strftime).

Приведенный выше текст в шаблоне записан следующим образом:

<p>Тоже всё просто - используем обычный формат strftime, в котором %a, %A, %b и %B
заменены на русские. К примеру, текущая дата: <em>$${pytils.dt.ru_strftime(u"%d %B %Y, %A")}</em>.
Согласитесь, выглядит не по-русски. Чтобы месяц склонялся, используйте опцию 
<code>inflected</code>: <em>$${pytils.dt.ru_strftime(u"%d %B %Y, %A", inflected=True)}</em></p>. 
Если же нужно чтобы и день склонялся, используйте опцию <code>inflected_day</code>:
текущий тест был выполнен в 
<em>$${pytils.dt.ru_strftime(u"%A, %d %B %Y", inflected=True, inflected_day=True)}</em>.
Лучше же чтобы и предлог добавлялся автоматически: текущий тест выполнен 
<em>$${pytils.dt.ru_strftime(u"%A, %d %B %Y", inflected=True, preposition=True)}</em>
(опция <code>preposition</code>).
</p>

pytils-0.2.3/doc/examples-turbogears/pytilsex/templates/master.kid0000644000175000017500000000220311047726655023365 0ustar j2aj2a Your title goes here
pytils-0.2.3/doc/examples-turbogears/pytilsex/templates/numeral.kid0000644000175000017500000002011311047726655023535 0ustar j2aj2a pytils demo

pytils demo

pytils.numeral

pytils.numeral.choose_plural и pytils.numera.get_plural

Выбор нужной формы множественного числа. Классический пример с количеством комментариев: ${comment_number} ${pytils.numeral.choose_plural(comment_number, comment_variants)}, а можно передавать только окончания: ${comment_number} комментари${pytils.numeral.choose_plural(comment_number, u"й,я,ев")}

Код примера:

<p>Выбор нужной формы множественного числа. Классический пример с количеством 
комментариев: $${comment_number} 
<em>$${pytils.numeral.choose_plural(comment_number, comment_variants)}</em>, 
а можно передавать только окончания: $${comment_number} 
комментари<em>$${pytils.numeral.choose_plural(comment_number, u"й,я,ев")}</em>
</p>

Зачастую нужно показывать и число, и название объекта в правильной форме, а не только название объекта. В этом случае следует воспользоваться фильтром get_plural. Пример с теми же комментариями можно записать проще: ${pytils.numeral.get_plural(comment_number, u"комментарий,комментария,комментариев")}. get_plural удобен еще и тем, что можно указать вариант, когда значение равно нулю. Например, гораздо симпатичней "без комментариев", чем "0 комментариев". В этом случае третим параметром передается нуль-вариант. Пример: ${pytils.numeral.get_plural(zero, u"пример,примера,примеров", u"без примеров")}.

Сделано это так:

<p>Зачастую нужно показывать и число, и название объекта в правильной форме, а не только название
объекта. В этом случае следует воспользоваться фильтром <code>get_plural</code>. Пример с теми же
комментариями можно записать проще: 
<em>$${pytils.numeral.get_plural(comment_number, u"комментарий,комментария,комментариев")}</em>.
<code>get_plural</code> удобен еще и тем, что можно указать вариант, когда значение равно нулю.
Например, гораздо симпатичней "без комментариев", чем "0 комментариев". В этом случае третим 
параметром передается нуль-вариант. Пример: 
<em>$${pytils.numeral.get_plural(zero, u"пример,примера,примеров", u"без примеров")}</em>.</p>

pytils.numeral.rubles

Рубли словами. К примеру, ${rubles_value} р. словами будет ${pytils.numeral.rubles(rubles_value)}. У этой функции есть один дополнительный параметр zero_for_kopeck, определяющий, нужно ли нулевые копейки "проговаривать". Если нужно - то True, по умолчанию rubles этого не делает. Пример: ${rubles_value2} р. словами будет ${pytils.numeral.rubles(rubles_value2)}, а с копейками - ${pytils.numeral.rubles(rubles_value2, zero_for_kopeck=True)}.

Данный пример в шаблоне записан так:

<p>Рубли словами. К примеру, $${rubles_value} р. словами будет
<em>$${pytils.numeral.rubles(rubles_value)}</em>. У этой функции есть один 
дополнительный параметр <code>zero_for_kopeck</code>, определяющий, нужно ли 
нулевые копейки "проговаривать". Если нужно - то True, по умолчанию 
<code>rubles</code> этого не делает.
Пример: $${rubles_value2} р. словами будет <em>$${pytils.numeral.rubles(rubles_value2)}</em>,
а с копейками - <em>$${pytils.numeral.rubles(rubles_value2, zero_for_kopeck=True)}</em>.</p>

pytils.numeral.in_words

Число словами. Можно целые, можно дробные. Примеры: ${int_value} - ${pytils.numeral.in_words(int_value)}. У целых можно менять пол (по умолчанию - мужской, pytils.numeral.MALE): ${pytils.numeral.in_words(int_value, gender=pytils.numeral.FEMALE)} (женский), ${pytils.numeral.in_words(int_value, gender=pytils.numeral.NEUTER)} (средний).

У дробных почти то же самое, только пол женский и не меняется (т.е. параметр передавать можно, но он не будет влиять). ${float_value} словами будет ${pytils.numeral.in_words(float_value)}.

В шаблоне этот текст выглядит следующим образом:

<p>Число словами. Можно целые, можно дробные. Примеры: $${int_value} - 
<em>$${pytils.numeral.in_words(int_value)}</em>. У целых можно менять пол 
(по умолчанию - мужской, pytils.numeral.MALE): 
<em>$${pytils.numeral.in_words(int_value, gender=pytils.numeral.FEMALE)}</em> (женский),
<em>$${pytils.numeral.in_words(int_value, gender=pytils.numeral.NEUTER)}</em> (средний).</p>

<p>У дробных почти то же самое, только пол женский и не меняется (т.е. параметр
передавать можно, но он не будет влиять). $${float_value} словами будет
<em>$${pytils.numeral.in_words(float_value)}</em>.</p>

pytils.numeral.sum_string

Наиболее общая функция работы с числами. Умеет "проговаривать" числа и одновременно представлять название объекта в нужной форме. Например, вместо ${comment_number} комментарий(ев) можно смело писать ${pytils.numeral.sum_string(comment_number, comment_gender, comment_variants)}.

В коде рализовано так:

<p>Наиболее общая функция работы с числами. Умеет "проговаривать" числа и 
одновременно представлять название объекта в нужной форме. Например, вместо 
$${comment_number} комментарий(ев) можно смело писать 
<em>$${pytils.numeral.sum_string(comment_number, comment_gender, comment_variants)}</em>.
</p>

pytils-0.2.3/doc/examples-turbogears/pytilsex/templates/root.kid0000644000175000017500000000405611047726655023065 0ustar j2aj2a pytils demo

pytils demo

Для того, чтобы воспользоваться pytils в TurboGears, достаточно просто поставить pytils (см. файл INSTALL внутри архива с pytils), никаких дополнительных манипуляций не требуется - интеграция pytils с TurboGears целиком на стороне шаблонов - Kid (или, например, Cheetah). Достигается это простым способом: в указанных шаблонах можно выполнять Python-код, так что примеры в Kid фактически повторяют Python-примеры pytils (которые всё же стоит глянуть). Еще стоит помнить, что все передаваемые строки должны быть unicode.

В шаблоне не забудьте сделать импорт модуля pytils таким образом:

<?python
import pytils
?>

Примеры использования pytils в TurboGears/Kid:

Протестировано с TurboGears 1.0 и 1.0.2

Данный пример работает на TurboGears ${tg_version} с использованием pytils ${pytils_version}

pytils-0.2.3/doc/examples-turbogears/pytilsex/templates/translit.kid0000644000175000017500000000532311047726655023740 0ustar j2aj2a pytils demo

pytils demo

pytils.translit

pytils.translit.translify

Простая транслитерация, из текста

${text}
получается
${pytils.translit.translify(text)}

В шаблоне записано так:

<p>Простая транслитерация, из текста <blockquote>$${text}</blockquote> 
получается <blockquote><em>$${pytils.translit.translify(text)}</em></blockquote>
</p>

pytils.translit.detranslify

Простая детранслитерация, из текста

${translit}
получается
${pytils.translit.detranslify(translit)}

В шаблоне записано так:

<p>Простая детранслитерация, из текста <blockquote>$${translit}</blockquote> 
получается <blockquote><em>$${pytils.translit.detranslify(translit)}</em></blockquote>
</p>

pytils.translit.slugify

Подготовка текста для URL. Из текста

${text}
получается slug
${pytils.translit.slugify(text)}
Также возможна обработка и английского текста: например из
${translit}
получается slug
${pytils.translit.slugify(translit)}

В шаблоне это всё записано так:

<p>Подготовка текста для URL. Из текста <blockquote>$${text}</blockquote> 
получается slug <blockquote><em>$${pytils.translit.slugify(text)}</em></blockquote>
Также возможна обработка и английского текста: например из 
<blockquote>$${translit}</blockquote> получается slug 
<blockquote><em>$${pytils.translit.slugify(translit)}</em></blockquote></p>

pytils-0.2.3/doc/examples-turbogears/pytilsex/__init__.py0000644000175000017500000000006011047726655021513 0ustar j2aj2a""" Example of usage pytils with TurboGears """ pytils-0.2.3/doc/examples-turbogears/pytilsex/controllers.py0000644000175000017500000000401611047726655022327 0ustar j2aj2a# -*- coding: utf-8 -*- import logging import time import datetime import cherrypy import turbogears import pkg_resources from turbogears import controllers, expose, validate, redirect from pytils import numeral, VERSION as pytils_version log = logging.getLogger("pytilsex.controllers") def get_tg_version(): try: ver = pkg_resources.get_distribution('TurboGears')._version except AttributeError: # setuptools 0.6c5 ver = pkg_resources.get_distribution('TurboGears').version return ver class Root(controllers.RootController): @expose(template="pytilsex.templates.root") def index(self): log.debug("pytilsex root controller ready to go") return { 'tg_version': get_tg_version(), 'pytils_version': pytils_version, } @expose(template="pytilsex.templates.dt") def dt(self): log.debug("pytilsex root/dt controller ready to go") return { 'otime': time.time()-100000, 'ftime': time.time()+100000, 'odate': datetime.datetime.now() - datetime.timedelta(0,100000), 'fdate': datetime.datetime.now() + datetime.timedelta(0,100000), } @expose(template="pytilsex.templates.numeral") def numeral(self): log.debug("pytilsex root/numeral controller ready to go") return { 'comment_variants': (u"комментарий", u"комментария", u"комментариев"), 'comment_number': 21, 'zero': 0, 'comment_gender': numeral.MALE, 'rubles_value': 23.152, 'rubles_value2': 12, 'int_value': 21, 'float_value': 31.385, } @expose(template="pytilsex.templates.translit") def translit(self): log.debug("pytilsex root/translit controller ready to go") return { 'text': u'Пример транслитерации средствами pytils', 'translit': 'Primer obratnoj transliteratsii', } pytils-0.2.3/doc/examples-turbogears/pytilsex/model.py0000644000175000017500000000024611047726655021062 0ustar j2aj2afrom sqlobject import * from turbogears.database import PackageHub hub = PackageHub("pytilsex") __connection__ = hub # class YourDataClass(SQLObject): # pass pytils-0.2.3/doc/examples-turbogears/README0000644000175000017500000000137011047726655016406 0ustar j2aj2aExamples of usage pytils with TurboGears. ======================================== You need installed TurboGears (1.0b1 and higher) and pytils for run it. It's a regular TG-project, so just start it python start-pytilsex.py go to http://127.0.0.1:8080/ and look it... ------------------------------------ Пример использования pytils с TurboGears. ======================================== Чтобы запустить примеры, Вам понадобятся установленные TurboGears (версии 1.0b1 и выше) и pytils. Это обычный TG-проект. Запускайте python start-pytilsex.py заходите бразуером на http://127.0.0.1:8080/ и смотрите... pytils-0.2.3/doc/examples-turbogears/dev.cfg0000644000175000017500000000353511047726655016772 0ustar j2aj2a[global] # This is where all of your settings go for your development environment # Settings that are the same for both development and production # (such as template engine, encodings, etc.) all go in # pytilsex/config/app.cfg # DATABASE # pick the form for your database # sqlobject.dburi="postgres://username@hostname/databasename" # sqlobject.dburi="mysql://username:password@hostname:port/databasename" # sqlobject.dburi="sqlite:///file_name_and_path" # If you have sqlite, here's a simple default to get you started # in development sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" # if you are using a database or table type without transactions # (MySQL default, for example), you should turn off transactions # by prepending notrans_ on the uri # sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" # for Windows users, sqlite URIs look like: # sqlobject.dburi="sqlite:///drive_letter:/path/to/file" # SERVER # Some server parameters that you may want to tweak # server.socket_port=8080 # Enable the debug output at the end on pages. # log_debug_info_filter.on = False server.environment="development" autoreload.package="pytilsex" # session_filter.on = True # Set to True if you'd like to abort execution if a controller gets an # unexpected parameter. False by default tg.strict_parameters = True # LOGGING # Logging configuration generally follows the style of the standard # Python logging module configuration. Note that when specifying # log format messages, you need to use *() for formatting variables. # Deployment independent log configuration is in pytilsex/config/log.cfg [logging] [[loggers]] [[[pytilsex]]] level='DEBUG' qualname='pytilsex' handlers=['debug_out'] [[[allinfo]]] level='INFO' handlers=['debug_out'] [[[access]]] level='INFO' qualname='turbogears.access' handlers=['access_out'] propagate=0 pytils-0.2.3/doc/examples-turbogears/setup.py0000644000175000017500000000373011047726655017242 0ustar j2aj2afrom setuptools import setup, find_packages from turbogears.finddata import find_package_data import os execfile(os.path.join("pytilsex", "release.py")) setup( name="PyTilsEx", version=version, # uncomment the following lines if you fill them out in release.py #description=description, #author=author, #author_email=email, #url=url, #download_url=download_url, #license=license, install_requires = [ "TurboGears >= 1.0b1", ], scripts = ["start-pytilsex.py"], zip_safe=False, packages=find_packages(), package_data = find_package_data(where='pytilsex', package='pytilsex'), keywords = [ # Use keywords if you'll be adding your package to the # Python Cheeseshop # if this has widgets, uncomment the next line # 'turbogears.widgets', # if this has a tg-admin command, uncomment the next line # 'turbogears.command', # if this has identity providers, uncomment the next line # 'turbogears.identity.provider', # If this is a template plugin, uncomment the next line # 'python.templating.engines', # If this is a full application, uncomment the next line # 'turbogears.app', ], classifiers = [ 'Development Status :: 3 - Alpha', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules', 'Framework :: TurboGears', # if this is an application that you'll distribute through # the Cheeseshop, uncomment the next line # 'Framework :: TurboGears :: Applications', # if this is a package that includes widgets that you'll distribute # through the Cheeseshop, uncomment the next line # 'Framework :: TurboGears :: Widgets', ], test_suite = 'nose.collector', ) pytils-0.2.3/doc/examples-turbogears/start-pytilsex.py0000755000175000017500000000142611047726655021121 0ustar j2aj2a#!/usr/bin/python import pkg_resources pkg_resources.require("TurboGears") import turbogears import cherrypy cherrypy.lowercase_api = True from os.path import * import sys # first look on the command line for a desired config file, # if it's not on the command line, then # look for setup.py in this directory. If it's not there, this script is # probably installed if len(sys.argv) > 1: turbogears.update_config(configfile=sys.argv[1], modulename="pytilsex.config") elif exists(join(dirname(__file__), "setup.py")): turbogears.update_config(configfile="dev.cfg", modulename="pytilsex.config") else: turbogears.update_config(configfile="prod.cfg", modulename="pytilsex.config") from pytilsex.controllers import Root turbogears.start_server(Root()) pytils-0.2.3/doc/INSTALL0000644000175000017500000000127111047726655012566 0ustar j2aj2apytils installation =================== Install via easy_install ------------------------ You can install the most recent pytils version using [easy_install](http://peak.telecommunity.com/DevCenter/EasyInstall) easy_install pytils Install from tarball ----------------------------- 1. Download the recent tarball from http://cheeseshop.python.org/pypi/pytils/ 2. Unpack the tarball 3. Run `python setup.py install` Install the development version -------------------------------- If you want to be on a edge, you can install development version via easy_install: easy_install pytils==dev or get pytils snapshot from repository http://hg.pyobject.ru/pytils/archive/tip.zip pytils-0.2.3/doc/INSTALL.rus.txt0000644000175000017500000000237311047726655014220 0ustar j2aj2aУстановка pytils ================ Установка с использованием easy_install --------------------------------------- Рекомендуемый способ установки pytils -- при помощи [easy_install](http://peak.telecommunity.com/DevCenter/EasyInstall) easy_install pytils Установка из исходных текстов ----------------------------- 1. Скачайте последнюю версию с http://cheeseshop.python.org/pypi/pytils/ 2. Разархивируйте архив 3. Выполните `python setup.py install` Установка разрабатываемой версии -------------------------------- Если у Вас есть желание как можно быстрее получать исправления ошибок или использовать последние улучшения pytils, то Вы можете установить свежий срез репозитория pytils при помощи easy_install: easy_install pytils==dev Если Вы не используете по каким-либо причинам easy_install, то срез доступен по адресу: http://hg.pyobject.ru/pytils/archive/tip.zip pytils-0.2.3/doc/README.rus.txt0000644000175000017500000001214011047726655014040 0ustar j2aj2apytils - простой обработчик русского текста. ========================================================== Кратко ------ pytils - простой обработчик русского текста, реализован на Python. Идея позаимствована у [Julik](http://live.julik.nl) и его [RuTils](http://rutils.rubyforge.org/). Автор pytils - [Юревич Юрий](mailto:the.pythy@gmail.com). Ссылки ------ * [Пост о pytils в блоге](http://www.pyobject.ru/blog/post//pytils/) * [Страница pytils](http://www.pyobject.ru/projects/pytils/) * [Mercurial-репозиторий](http://hg.pyobject.ru/pytils/) Как установить -------------- Смотрите INSTALL (или INSTALL.rus.txt) Как использовать ---------------- Во-первых, **все** входящие строки - unicode. И выходящие - тоже (за малыми исключениями, о них ниже). В случае, если Вы передадите str, получите AssertionError. pytils содержит следующие модули: 1. `numeral` - для обработки числительных 2. `dt` - русские даты без локалей 3. `translit` - транслитерация API модулей смотрите в директории [api](api/index.html). pytils легко интегрируется с популярными Web-фреймворками (Django, TurboGears), подробнее об этом смотрите в WEBFRAMEWORKS.rus.txt. Примеры смотрите в каталоге examples. Числительные ------------ pytils умеет выбирать правильный падеж в зависимости от числа >>> pytils.numeral.choose_plural(15, (u"гвоздь", u"гвоздя", u"гвоздей")) u'гвоздей' В качестве второго параметра передается кортеж с вариантами (либо строка, где варианты перечисляются через запятую). Чтобы легко запомнить, в каком порядке указывать варианты, пользуйтесь мнемоническим правилом: один-два-пять - один гвоздь, два гвоздя, пять гвоздей. Часто нужен не просто вариант, а число вместе с текстом >>> pytils.numeral.get_prural(15, u"гвоздь, гвоздя, гвоздей") u'15 гвоздей' В get_plural можно еще передать вариант, когда число -- ноль. Т.е. чтобы было не '0 гвоздей', а 'гвоздей нет': >>> pytils.numeral.get_plural(0, u"гвоздь, гвоздя, гвоздей") u'0 гвоздей' >>> pytils.numeral.get_plural(0, u"гвоздь, гвоздя, гвоздей", absence=u"гвоздей нет") u'гвоздей нет' Также pytils реализует числа прописью >>> pytils.numeral.in_words(254) u'двести пятьдесят четыре' >>> pytils.numeral.in_words(2.01) u'две целых одна сотая' >>> pytils.numeral.rubles(2.01) u'два рубля одна копейка' >>> pytils.numeral.sum_string(32, pytils.numeral.MALE, (u"гвоздь", u"гвоздя", u"гвоздей")) u'тридцать два гвоздя' >>> pytils.numeral.sum_string(21, pytils.numeral.FEMALE, u"белка, белки, белок") u'двадцать одна белка' Даты ---- В pytils можно получить русские даты без использования локалей. >>> pytils.dt.ru_strftime(u"сегодня - %d %B %Y, %A", inflected=True, date=datetime.date(2006, 9, 2)) u'сегодня - 2 сентября 2006, суббота' >>> pytils.dt.ru_strftime(u"сделано %A, %d %B %Y", inflected=True, preposition=True) u'сделано во вторник, 10 июля 2007' Есть возможность получить величину периода: >>> pytils.dt.distance_of_time_in_words(time.time()-10000) u'2 часа назад' >>> pytils.dt.distance_of_time_in_words(datetime.datetime.now()+datetime.timedelta(0,10000), accuracy=2) u'через 2 часа 46 минут' Транслитерация -------------- При помощи pytils можно сделать транслитерацию: >>> print pytils.translit.translify(u"Проверка связи") 'Proverka svyazi' >>> pytils.translit.detranslify("Proverka svyazi") u'Проверка связи' В translify вывод - str, а не unicode. В detranslify вход может быть как unicode, так и str. И сделать строку для URL (удаляются лишние символы, пробелы заменяются на дефисы): >>> pytils.translit.slugify(u"тест и еще раз тест") 'test-i-esche-raz-test' pytils-0.2.3/doc/WEBFRAMEWORKS.rus.txt0000644000175000017500000000023411047726655015122 0ustar j2aj2aПолнофункциональные примеры смотрите: * для Django в examples-django * для TurboGears в examples-turbogears pytils-0.2.3/pytils/0000755000175000017500000000000011051306250012267 5ustar j2aj2apytils-0.2.3/pytils/templatetags/0000755000175000017500000000000011051306250014761 5ustar j2aj2apytils-0.2.3/pytils/templatetags/__init__.py0000644000175000017500000000625311050343106017100 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/pythy/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Pytils templatetags for Django web-framework """ import warnings import django from pytils import utils def is_django_unicode_aware(): if django.VERSION[:2] > (0, 96): return True else: return False text_version = '.'.join(str(v) for v in django.VERSION if v is not None) unicode_aware = is_django_unicode_aware() if not unicode_aware: warnings.warn('Django %s is not unicode-aware, please upgrade your Django to unicode-branch' % text_version, DeprecationWarning) # Если отладка, то показываем 'unknown+сообщение об ошибке'. # Если отладка выключена, то можно чтобы при ошибках показывалось # значение, переданное фильтру (PYTILS_SHOW_VALUES_ON_ERROR=True) # либо пустая строка. def init_defaults(debug, show_value): if debug: default_value = "unknown: %(error)s" default_uvalue = u"unknown: %(error)s" elif show_value: default_value = "%(value)s" default_uvalue = u"%(value)s" else: default_value = "" default_uvalue = u"" return default_value, default_uvalue def pseudo_unicode(stext, encoding, default_value=u''): """ Return (unicode) stext if Django is unicode-aware, decode from encoding otherwise. It raises UnicodeDecodeError when such error occures and default_value is None. Otherwise (i.e. default_value is not None), it return default_value. """ if unicode_aware and isinstance(stext, unicode): utext = stext elif isinstance(stext, unicode): utext = stext else: try: utext = unicode(stext, encoding) except UnicodeDecodeError, err: if default_value is None: raise UnicodeDecodeError, err utext = default_value % {'error': err, 'value': u""} return utext def pseudo_str(utext, encoding, default_value=''): """ Return (unicode) utext if Django is unicode-aware, encode to encoding otherwise It raises UnicodeEncodeError when such error occures and default_value is None. Otherwise (i.e. default_value is not None), it return default_value. """ if unicode_aware and isinstance(utext, unicode): stext = utext else: try: stext = unicode(utext).encode(encoding) except (UnicodeEncodeError, UnicodeDecodeError), err: if default_value is None: raise UnicodeEncodeError, err stext = default_value % {'error': err, 'value': ""} return stext pytils-0.2.3/pytils/templatetags/pytils_dt.py0000644000175000017500000001027211050343130017345 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.templatetags.test_dt -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ pytils.dt templatetags for Django web-framework """ import time from django import template, conf from pytils import dt, utils from pytils.templatetags import pseudo_str, pseudo_unicode, init_defaults register = template.Library() #: Django template tag/filter registrator encoding = conf.settings.DEFAULT_CHARSET #: Current charset (sets in Django project's settings) debug = conf.settings.DEBUG #: Debug mode (sets in Django project's settings) show_value = getattr(conf.settings, 'PYTILS_SHOW_VALUES_ON_ERROR', False) #: Show values on errors (sets in Django project's settings) default_value, default_uvalue = init_defaults(debug, show_value) # -- filters -- def distance_of_time(from_time, accuracy=1): """ Display distance of time from current time. Parameter is an accuracy level (deafult is 1). Value must be numeral (i.e. time.time() result) or datetime.datetime (i.e. datetime.datetime.now() result). Examples:: {{ some_time|distance_of_time }} {{ some_dtime|distance_of_time:2 }} """ try: ures = dt.distance_of_time_in_words(from_time, accuracy) res = pseudo_str( ures, encoding, default_value) except Exception, err: # because filter must die silently try: default_distance = "%s seconds" % str(int(time.time() - from_time)) except Exception: default_distance = "" res = default_value % {'error': err, 'value': default_distance} return res def ru_strftime(date, format="%d.%m.%Y", inflected_day=False, preposition=False): """ Russian strftime, formats date with given format. Value is a date (supports datetime.date and datetime.datetime), parameter is a format (string). For explainings about format, see documentation for original strftime: http://docs.python.org/lib/module-time.html Examples:: {{ some_date|ru_strftime:"%d %B %Y, %A" }} """ try: uformat = pseudo_unicode(format, encoding, u"%d.%m.%Y") ures = dt.ru_strftime(uformat, date, inflected=True, inflected_day=inflected_day, preposition=preposition) res = pseudo_str(ures, encoding) except Exception, err: # because filter must die silently try: default_date = date.strftime(format) except Exception: default_date = str(date) res = default_value % {'error': err, 'value': default_date} return res def ru_strftime_inflected(date, format="%d.%m.%Y"): """ Russian strftime with inflected day, formats date with given format (similar to ru_strftime), also inflects day in proper form. Examples:: {{ some_date|ru_strftime_inflected:"in %A (%d %B %Y)" """ return ru_strftime(date, format, inflected_day=True) def ru_strftime_preposition(date, format="%d.%m.%Y"): """ Russian strftime with inflected day and correct preposition, formats date with given format (similar to ru_strftime), also inflects day in proper form and inserts correct preposition. Examples:: {{ some_date|ru_strftime_prepoisiton:"%A (%d %B %Y)" """ return ru_strftime(date, format, preposition=True) # -- register filters register.filter('distance_of_time', distance_of_time) register.filter('ru_strftime', ru_strftime) register.filter('ru_strftime_inflected', ru_strftime_inflected) register.filter('ru_strftime_preposition', ru_strftime_preposition) pytils-0.2.3/pytils/templatetags/pytils_numeral.py0000644000175000017500000001337611050343151020414 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.templatetags.test_numeral -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ pytils.numeral templatetags for Django web-framework """ from django import template, conf from pytils import numeral, utils from pytils.templatetags import pseudo_str, pseudo_unicode, init_defaults register = template.Library() #: Django template tag/filter registrator encoding = conf.settings.DEFAULT_CHARSET #: Current charset (sets in Django project's settings) debug = conf.settings.DEBUG #: Debug mode (sets in Django project's settings) show_value = getattr(conf.settings, 'PYTILS_SHOW_VALUES_ON_ERROR', False) #: Show values on errors (sets in Django project's settings) default_value, default_uvalue = init_defaults(debug, show_value) # -- filters def choose_plural(amount, variants): """ Choose proper form for plural. Value is a amount, parameters are forms of noun. Forms are variants for 1, 2, 5 nouns. It may be tuple of elements, or string where variants separates each other by comma. Examples:: {{ some_int|choose_plural:"пример,примера,примеров" }} """ try: if isinstance(variants, basestring): uvariants = pseudo_unicode(variants, encoding, default_value) else: uvariants = [pseudo_unicode(v, encoding, default_uvalue) for v in variants] ures = numeral.choose_plural(amount, uvariants) res = pseudo_str( ures, encoding, default_value ) except Exception, err: # because filter must die silently try: default_variant = variants except Exception: default_variant = "" res = default_value % {'error': err, 'value': default_variant} return res def get_plural(amount, variants): """ Get proper form for plural and it value. Value is a amount, parameters are forms of noun. Forms are variants for 1, 2, 5 nouns. It may be tuple of elements, or string where variants separates each other by comma. You can append 'absence variant' after all over variants Examples:: {{ some_int|get_plural:"пример,примера,примеров,нет примеров" }} """ try: if isinstance(variants, basestring): uvariants = pseudo_unicode(variants, encoding, default_value) else: uvariants = [pseudo_unicode(v, encoding, default_uvalue) for v in variants] ures = numeral._get_plural_legacy(amount, uvariants) res = pseudo_str( ures, encoding, default_value ) except Exception, err: # because filter must die silently try: default_variant = variants except Exception: default_variant = "" res = default_value % {'error': err, 'value': default_variant} return res def rubles(amount, zero_for_kopeck=False): """Converts float value to in-words representation (for money)""" try: ures = numeral.rubles(amount, zero_for_kopeck) res = pseudo_str( ures, encoding, default_value ) except Exception, err: # because filter must die silently res = default_value % {'error': err, 'value': str(amount)} return res def in_words(amount, gender=None): """ In-words representation of amount. Parameter is a gender: MALE, FEMALE or NEUTER Examples:: {{ some_int|in_words }} {{ some_other_int|in_words:FEMALE }} """ try: ures = numeral.in_words(amount, getattr(numeral, str(gender), None)) res = pseudo_str( ures, encoding, default_value ) except Exception, err: # because filter must die silently res = default_value % {'error': err, 'value': str(amount)} return res # -- register filters register.filter('choose_plural', choose_plural) register.filter('get_plural', get_plural) register.filter('rubles', rubles) register.filter('in_words', in_words) # -- tags def sum_string(amount, gender, items): """ in_words and choose_plural in a one flask Makes in-words representation of value with choosing correct form of noun. First parameter is an amount of objects. Second is a gender (MALE, FEMALE, NEUTER). Third is a variants of forms for object name. Examples:: {% sum_string some_int MALE "пример,примера,примеров" %} {% sum_string some_other_int FEMALE "задача,задачи,задач" %} """ try: if isinstance(items, basestring): uitems = pseudo_unicode(items, encoding, default_uvalue) else: uitems = [pseudo_unicode(i, encoding, default_uvalue) for i in items] ures = numeral.sum_string(amount, getattr(numeral, str(gender), None), uitems) res = pseudo_str( ures, encoding, default_value ) except Exception, err: # because tag's renderer must die silently res = default_value % {'error': err, 'value': str(amount)} return res # -- register tags register.simple_tag(sum_string) pytils-0.2.3/pytils/templatetags/pytils_translit.py0000644000175000017500000000513211050343172020603 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.templatetags.test_translit -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ pytils.translit templatetags for Django web-framework """ from django import template, conf from pytils import translit, utils from pytils.templatetags import pseudo_str, pseudo_unicode, init_defaults register = template.Library() #: Django template tag/filter registrator encoding = conf.settings.DEFAULT_CHARSET #: Current charset (sets in Django project's settings) debug = conf.settings.DEBUG #: Debug mode (sets in Django project's settings) show_value = getattr(conf.settings, 'PYTILS_SHOW_VALUES_ON_ERROR', False) #: Show values on errors (sets in Django project's settings) default_value, default_uvalue = init_defaults(debug, show_value) # -- filters -- def translify(stext): """Translify russian text""" try: utext = pseudo_unicode( stext, encoding, default_value) res = translit.translify(utext) except Exception, err: # because filter must die silently res = default_value % {'error': err, 'value': stext} return res def detranslify(stext): """Detranslify russian text""" try: ures = translit.detranslify(stext) res = pseudo_str( ures, encoding, default_uvalue) except Exception, err: # because filter must die silently res = default_value % {'error': err, 'value': stext} return res def slugify(stext): """Make slug from (russian) text""" try: utext = pseudo_unicode( stext, encoding, default_value) res = translit.slugify(utext) except Exception, err: print err # because filter must die silently res = default_value % {'error': err, 'value': stext} print "so res = %r" % res return res # -- register filters register.filter('translify', translify) register.filter('detranslify', detranslify) register.filter('slugify', slugify) pytils-0.2.3/pytils/test/0000755000175000017500000000000011051306250013246 5ustar j2aj2apytils-0.2.3/pytils/test/templatetags/0000755000175000017500000000000011051306250015740 5ustar j2aj2apytils-0.2.3/pytils/test/templatetags/__init__.py0000644000175000017500000000311711050343406020056 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit tests for pytils' templatetags for Django web framework """ __all__ = ["test_common", "test_numeral", "test_dt", "test_translit"] import unittest def get_suite(): """Return TestSuite for all unit-test of pytils' templatetags""" suite = unittest.TestSuite() for module_name in __all__: imported_module = __import__("pytils.test.templatetags."+module_name, globals(), locals(), ["pytils.test.templatetags"]) getter = getattr(imported_module, 'get_suite', False) if getter: suite.addTest(getter()) loader = unittest.defaultTestLoader suite.addTest(loader.loadTestsFromModule(imported_module)) return suite def run(verbosity=1): """Run all unit-test of pytils' templatetags""" suite = get_suite() unittest.TextTestRunner(verbosity=verbosity).run(suite) if __name__ == '__main__': run(2) pytils-0.2.3/pytils/test/templatetags/helpers.py0000644000175000017500000000413511050343435017764 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Helpers for templatetags' unit tests in Django webframework """ from django.conf import settings encoding = 'utf-8' settings.configure( TEMPLATE_DIRS=(), TEMPLATE_CONTEXT_PROCESSORS=(), TEMPLATE_LOADERS=(), INSTALLED_APPS=('pytils',), DEFAULT_CHARSET=encoding, ) from django import template from django.template import loader from pytils.templatetags import pseudo_str import unittest def pstr(ustr): """ Provide/Pseudo unicode """ return pseudo_str(ustr, encoding, None) class TemplateTagTestCase(unittest.TestCase): """ TestCase for testing template tags and filters """ def check_template_tag(self, template_name, template_string, context, result_string): """ Method validates output of template tag or filter @param template_name: name of template @type template_name: C{str} @param template_string: contents of template @type template_string: C{str} or C{unicode} @param context: rendering context @type context: C{dict} @param result_string: reference output @type result_string: C{str} or C{unicode} """ def test_template_loader(template_name, template_dirs=None): return pstr(template_string), template_name loader.template_source_loaders = [test_template_loader,] output = loader.get_template(template_name).render(template.Context(context)) self.assertEquals(output, pstr(result_string)) pytils-0.2.3/pytils/test/templatetags/test_common.py0000644000175000017500000000576111050343455020661 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit tests for pytils' templatetags common things """ import unittest from pytils import templatetags as tt class TemplateTagsCommonsTestCase(unittest.TestCase): def testInitDefaults(self): """ Unit-tests for pytils.templatetags.init_defaults """ self.assertEquals(tt.init_defaults(debug=False, show_value=False), ('', u'')) self.assertEquals(tt.init_defaults(debug=False, show_value=True), ('%(value)s', u'%(value)s')) self.assertEquals(tt.init_defaults(debug=True, show_value=False), ('unknown: %(error)s', u'unknown: %(error)s')) self.assertEquals(tt.init_defaults(debug=True, show_value=True), ('unknown: %(error)s', u'unknown: %(error)s')) def testPseudoUnicode(self): """ Unit-tests for pytils.templatetags.pseudo_unicode """ self.assertEquals(tt.pseudo_unicode(u'тест', 'utf-8'), u'тест') self.assertEquals(tt.pseudo_unicode('тест', 'utf-8'), u'тест') self.assertEquals(tt.pseudo_unicode('тест', 'ascii'), u'') self.assertEquals(tt.pseudo_unicode('тест', 'ascii', u'опа'), u'опа') self.assertRaises(UnicodeDecodeError, tt.pseudo_unicode, 'тест', 'ascii', None) def testPseudoStr(self): """ Unit-tests for pytils.templatetags.pseudo_str """ # in django unicode-branch either str() must return unicode # this test depends on Django unicode awareness if tt.unicode_aware: self.assertEquals(tt.pseudo_str(u'тест', 'utf-8'), u'тест') self.assertEquals(tt.pseudo_str(u'тест', 'utf-8'), u'тест') self.assertEquals(tt.pseudo_str('тест', 'utf-8'), '') self.assertEquals(tt.pseudo_str('тест', 'utf-8', u'опа'), u'опа') self.assertEquals(tt.pseudo_str(u'тест', 'ascii'), u'тест') self.assertEquals(tt.pseudo_str(u'тест', 'ascii', 'опа'), u'тест') else: self.assertEquals(tt.pseudo_str(u'тест', 'utf-8'), 'тест') self.assertEquals(tt.pseudo_str('тест', 'utf-8'), '') self.assertEquals(tt.pseudo_str(u'тест', 'ascii'), '') self.assertEquals(tt.pseudo_str(u'тест', 'ascii', 'опа'), 'опа') self.assertRaises(UnicodeEncodeError, tt.pseudo_str, u'тест', 'ascii', None) if __name__ == '__main__': unittest.main() pytils-0.2.3/pytils/test/templatetags/test_dt.py0000644000175000017500000000525511050343475020000 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit tests for pytils' dt templatetags for Django web framework """ import datetime from pytils.test.templatetags import helpers class DtDefaultTestCase(helpers.TemplateTagTestCase): def setUp(self): self.date = datetime.datetime(2007, 1, 26, 15, 50) self.date_before = datetime.datetime.now() - datetime.timedelta(1, 2000) def testLoad(self): self.check_template_tag('load_tag', u'{% load pytils_dt %}', {}, u'') def testRuStrftimeFilter(self): self.check_template_tag('ru_strftime_filter', u'{% load pytils_dt %}{{ val|ru_strftime:"%d %B %Y, %A" }}', {'val': self.date}, u'26 января 2007, пятница') def testRuStrftimeInflectedFilter(self): self.check_template_tag('ru_strftime_inflected_filter', u'{% load pytils_dt %}{{ val|ru_strftime_inflected:"в %A, %d %B %Y" }}', {'val': self.date}, u'в пятницу, 26 января 2007') def testRuStrftimePrepositionFilter(self): self.check_template_tag('ru_strftime_preposition_filter', u'{% load pytils_dt %}{{ val|ru_strftime_preposition:"%A, %d %B %Y" }}', {'val': self.date}, u'в\xa0пятницу, 26 января 2007') def testDistanceFilter(self): self.check_template_tag('distance_filter', u'{% load pytils_dt %}{{ val|distance_of_time }}', {'val': self.date_before}, u'вчера') self.check_template_tag('distance_filter', u'{% load pytils_dt %}{{ val|distance_of_time:3 }}', {'val': self.date_before}, u'1 день 0 часов 33 минуты назад') # без отладки, если ошибка -- по умолчанию пустая строка def testRuStrftimeError(self): self.check_template_tag('ru_strftime_error', u'{% load pytils_dt %}{{ val|ru_strftime:"%d %B %Y" }}', {'val': 1}, u'') if __name__ == '__main__': import unittest unittest.main() pytils-0.2.3/pytils/test/templatetags/test_numeral.py0000644000175000017500000000665011050343516021030 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit tests for pytils' numeral templatetags for Django web framework """ from pytils.test.templatetags import helpers class NumeralDefaultTestCase(helpers.TemplateTagTestCase): def testLoad(self): self.check_template_tag('load_tag', u'{% load pytils_numeral %}', {}, u'') def testChoosePluralFilter(self): self.check_template_tag('choose_plural', u'{% load pytils_numeral %}{{ val|choose_plural:"гвоздь,гвоздя,гвоздей" }}', {'val': 10}, u'гвоздей') def testGetPluralFilter(self): self.check_template_tag('get_plural', u'{% load pytils_numeral %}{{ val|get_plural:"гвоздь,гвоздя,гвоздей" }}', {'val': 10}, u'10 гвоздей') self.check_template_tag('get_plural', u'{% load pytils_numeral %}{{ val|get_plural:"гвоздь,гвоздя,гвоздей" }}', {'val': 0}, u'0 гвоздей') self.check_template_tag('get_plural', u'{% load pytils_numeral %}{{ val|get_plural:"гвоздь,гвоздя,гвоздей,нет гвоздей" }}', {'val': 0}, u'нет гвоздей') def testRublesFilter(self): self.check_template_tag('rubles', u'{% load pytils_numeral %}{{ val|rubles }}', {'val': 10.1}, u'десять рублей десять копеек') def testInWordsFilter(self): self.check_template_tag('in_words', u'{% load pytils_numeral %}{{ val|in_words }}', {'val': 21}, u'двадцать один') self.check_template_tag('in_words', u'{% load pytils_numeral %}{{ val|in_words:"NEUTER" }}', {'val': 21}, u'двадцать одно') def testSumStringTag(self): self.check_template_tag('sum_string', u'{% load pytils_numeral %}{% sum_string val "MALE" "пример,пример,примеров" %}', {'val': 21}, u'двадцать один пример') self.check_template_tag('sum_string_w_gender', u'{% load pytils_numeral %}{% sum_string val male variants %}', { 'val': 21, 'male':'MALE', 'variants': ('пример','пример','примеров') }, u'двадцать один пример') # без отладки, если ошибка -- по умолчанию пустая строка def testChoosePluralError(self): self.check_template_tag('choose_plural_error', u'{% load pytils_numeral %}{{ val|choose_plural:"вариант" }}', {'val': 1}, u'') if __name__ == '__main__': import unittest unittest.main() pytils-0.2.3/pytils/test/templatetags/test_translit.py0000644000175000017500000000414211050343541021215 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit tests for pytils' translit templatetags for Django web framework """ from pytils.test.templatetags import helpers class TranslitDefaultTestCase(helpers.TemplateTagTestCase): def testLoad(self): self.check_template_tag('load_tag', u'{% load pytils_translit %}', {}, u'') def testTranslifyFilter(self): self.check_template_tag('translify_filter', u'{% load pytils_translit %}{{ val|translify }}', {'val': 'проверка'}, u'proverka') def testDetranslifyFilter(self): self.check_template_tag('detranslify_filter', u'{% load pytils_translit %}{{ val|detranslify }}', {'val': 'proverka'}, u'проверка') def testSlugifyFilter(self): self.check_template_tag('slugify_filter', u'{% load pytils_translit %}{{ val|slugify }}', {'val': 'Проверка связи'}, u'proverka-svyazi') # без отладки, если ошибка -- по умолчанию пустая строка def testDetranslifyError(self): # в юникод-режиме это не ошибка from pytils.templatetags import unicode_aware if not unicode_aware: self.check_template_tag('detranslify_error', u'{% load pytils_translit %}{{ val|detranslify }}', {'val': 'Проверка связи'}, u'') if __name__ == '__main__': import unittest unittest.main() pytils-0.2.3/pytils/test/__init__.py0000644000175000017500000000360111051304632015361 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit tests for pytils """ __all__ = ["test_numeral", "test_dt", "test_translit", "test_utils"] import unittest def get_django_suite(): try: import django except ImportError: return unittest.TestSuite() import pytils.test.templatetags return pytils.test.templatetags.get_suite() def get_suite(): """Return TestSuite for all unit-test of pytils""" suite = unittest.TestSuite() for module_name in __all__: imported_module = __import__("pytils.test."+module_name, globals(), locals(), ["pytils.test"]) loader = unittest.defaultTestLoader suite.addTest(loader.loadTestsFromModule(imported_module)) suite.addTest(get_django_suite()) return suite def run_tests_from_module(module, verbosity=1): """Run unit-tests for single module""" suite = unittest.TestSuite() loader = unittest.defaultTestLoader suite.addTest(loader.loadTestsFromModule(module)) unittest.TextTestRunner(verbosity=verbosity).run(suite) def run(verbosity=1): """Run all unit-test of pytils""" suite = get_suite() unittest.TextTestRunner(verbosity=verbosity).run(suite) if __name__ == '__main__': run(2) pytils-0.2.3/pytils/test/test_dt.py0000644000175000017500000003354711050343244015305 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit-tests for pytils.dt """ import datetime import time import unittest import pytils class DistanceOfTimeInWordsTestCase(unittest.TestCase): """ Test case for pytils.dt.distance_of_time_in_words """ def setUp(self): """ Setting up environment for tests """ self.time = 1156862275.7711999 self.dtime = {} self.updateTime(self.time) def updateTime(self, _time): """Update all time-related values for current time """ self.dtime['10sec_ago'] = _time - 10 self.dtime['1min_ago'] = _time - 60 self.dtime['10min_ago'] = _time - 600 self.dtime['1hr_ago'] = _time - 3720 self.dtime['10hr_ago'] = _time - 36600 self.dtime['1day_ago'] = _time - 87600 self.dtime['1day1hr_ago'] = _time - 90600 self.dtime['2day_ago'] = _time - 87600*2 self.dtime['in_10sec'] = _time + 10 self.dtime['in_1min'] = _time + 61 self.dtime['in_10min'] = _time + 601 self.dtime['in_1hr'] = _time + 3721 self.dtime['in_10hr'] = _time + 36601 self.dtime['in_1day'] = _time + 87601 self.dtime['in_1day1hr'] = _time + 90601 self.dtime['in_2day'] = _time + 87600*2 + 1 def ckDefaultAccuracy(self, typ, estimated): """ Checks with default value for accuracy """ t0 = time.time() # --- change state !!! attention self.updateTime(t0) # --- t1 = self.dtime[typ] res = pytils.dt.distance_of_time_in_words(from_time=t1, to_time=t0) # --- revert state to original value self.updateTime(self.time) # --- self.assertEquals(res, estimated) def ckDefaultToTime(self, typ, accuracy, estimated): """ Checks with default value of time """ t0 = time.time() # --- change state !!! attention self.updateTime(t0) # --- t1 = self.dtime[typ] res = pytils.dt.distance_of_time_in_words(t1, accuracy) # --- revert state to original value self.updateTime(self.time) # --- self.assertEquals(res, estimated) def testDOTIWDefaultAccuracy(self): """ Unit-test for distance_of_time_in_words with default accuracy """ self.ckDefaultAccuracy("10sec_ago", u"менее минуты назад") self.ckDefaultAccuracy("1min_ago", u"1 минуту назад") self.ckDefaultAccuracy("10min_ago", u"10 минут назад") self.ckDefaultAccuracy("1hr_ago", u"1 час назад") self.ckDefaultAccuracy("10hr_ago", u"10 часов назад") self.ckDefaultAccuracy("1day_ago", u"1 день назад") self.ckDefaultAccuracy("1day1hr_ago", u"1 день назад") self.ckDefaultAccuracy("2day_ago", u"2 дня назад") self.ckDefaultAccuracy("in_10sec", u"менее чем через минуту") self.ckDefaultAccuracy("in_1min", u"через 1 минуту") self.ckDefaultAccuracy("in_10min", u"через 10 минут") self.ckDefaultAccuracy("in_1hr", u"через 1 час") self.ckDefaultAccuracy("in_10hr", u"через 10 часов") self.ckDefaultAccuracy("in_1day", u"через 1 день") self.ckDefaultAccuracy("in_1day1hr", u"через 1 день") self.ckDefaultAccuracy("in_2day", u"через 2 дня") def testDOTIWDefaultToTimeAcc1(self): """ Unit-tests for distance_of_time_in_words with default to_time and accuracy=1 """ # accuracy = 1 self.ckDefaultToTime("10sec_ago", 1, u"менее минуты назад") self.ckDefaultToTime("1min_ago", 1, u"минуту назад") self.ckDefaultToTime("10min_ago", 1, u"10 минут назад") self.ckDefaultToTime("1hr_ago", 1, u"час назад") self.ckDefaultToTime("10hr_ago", 1, u"10 часов назад") self.ckDefaultToTime("1day_ago", 1, u"вчера") self.ckDefaultToTime("1day1hr_ago", 1, u"вчера") self.ckDefaultToTime("2day_ago", 1, u"позавчера") self.ckDefaultToTime("in_10sec", 1, u"менее чем через минуту") self.ckDefaultToTime("in_1min", 1, u"через минуту") self.ckDefaultToTime("in_10min", 1, u"через 10 минут") self.ckDefaultToTime("in_1hr", 1, u"через час") self.ckDefaultToTime("in_10hr", 1, u"через 10 часов") self.ckDefaultToTime("in_1day", 1, u"завтра") self.ckDefaultToTime("in_1day1hr", 1, u"завтра") self.ckDefaultToTime("in_2day", 1, u"послезавтра") def testDOTIWDefaultToTimeAcc2(self): """ Unit-tests for distance_of_time_in_words with default to_time and accuracy=2 """ # accuracy = 2 self.ckDefaultToTime("10sec_ago", 2, u"менее минуты назад") self.ckDefaultToTime("1min_ago", 2, u"минуту назад") self.ckDefaultToTime("10min_ago", 2, u"10 минут назад") self.ckDefaultToTime("1hr_ago", 2, u"1 час 2 минуты назад") self.ckDefaultToTime("10hr_ago", 2, u"10 часов 10 минут назад") self.ckDefaultToTime("1day_ago", 2, u"вчера") self.ckDefaultToTime("1day1hr_ago", 2, u"1 день 1 час назад") self.ckDefaultToTime("2day_ago", 2, u"позавчера") self.ckDefaultToTime("in_10sec", 2, u"менее чем через минуту") self.ckDefaultToTime("in_1min", 2, u"через минуту") self.ckDefaultToTime("in_10min", 2, u"через 10 минут") self.ckDefaultToTime("in_1hr", 2, u"через 1 час 2 минуты") self.ckDefaultToTime("in_10hr", 2, u"через 10 часов 10 минут") self.ckDefaultToTime("in_1day", 2, u"завтра") self.ckDefaultToTime("in_1day1hr", 2, u"через 1 день 1 час") self.ckDefaultToTime("in_2day", 2, u"послезавтра") def testDOTIWDefaultToTimeAcc3(self): """ Unit-tests for distance_of_time_in_words with default to_time and accuracy=3 """ # accuracy = 3 self.ckDefaultToTime("10sec_ago", 3, u"менее минуты назад") self.ckDefaultToTime("1min_ago", 3, u"минуту назад") self.ckDefaultToTime("10min_ago", 3, u"10 минут назад") self.ckDefaultToTime("1hr_ago", 3, u"1 час 2 минуты назад") self.ckDefaultToTime("10hr_ago", 3, u"10 часов 10 минут назад") self.ckDefaultToTime("1day_ago", 3, u"1 день 0 часов 20 минут назад") self.ckDefaultToTime("1day1hr_ago", 3, u"1 день 1 час 10 минут назад") self.ckDefaultToTime("2day_ago", 3, u"2 дня 0 часов 40 минут назад") self.ckDefaultToTime("in_10sec", 3, u"менее чем через минуту") self.ckDefaultToTime("in_1min", 3, u"через минуту") self.ckDefaultToTime("in_10min", 3, u"через 10 минут") self.ckDefaultToTime("in_1hr", 3, u"через 1 час 2 минуты") self.ckDefaultToTime("in_10hr", 3, u"через 10 часов 10 минут") self.ckDefaultToTime("in_1day", 3, u"через 1 день 0 часов 20 минут") self.ckDefaultToTime("in_1day1hr", 3, u"через 1 день 1 час 10 минут") self.ckDefaultToTime("in_2day", 3, u"через 2 дня 0 часов 40 минут") def testDOTWDatetimeType(self): """ Unit-tests for testing datetime.datetime as input values """ first_time = datetime.datetime.now() second_time = first_time + datetime.timedelta(0, 1000) self.assertEquals(pytils.dt.distance_of_time_in_words( from_time=first_time, accuracy=1, to_time=second_time), u"16 минут назад") def testDOTIWExceptions(self): """ Unit-tests for testings distance_of_time_in_words' exceptions """ self.assertRaises(TypeError, pytils.dt.distance_of_time_in_words, "test") self.assertRaises(TypeError, pytils.dt.distance_of_time_in_words, time.time(), "test") self.assertRaises(TypeError, pytils.dt.distance_of_time_in_words, time.time(), 2, "test") self.assertRaises(pytils.err.InputParameterError, pytils.dt.distance_of_time_in_words, "test") self.assertRaises(pytils.err.InputParameterError, pytils.dt.distance_of_time_in_words, time.time(), "test") self.assertRaises(pytils.err.InputParameterError, pytils.dt.distance_of_time_in_words, time.time(), 2, "test") self.assertRaises(ValueError, pytils.dt.distance_of_time_in_words, time.time(), 0) def testIssue25DaysFixed(self): """ Unit-test for testing that Issue#25 is fixed (err when accuracy==1, days<>0, hours==1) """ d_days = datetime.datetime.now() - datetime.timedelta(13, 3620) self.assertEquals(pytils.dt.distance_of_time_in_words(d_days), u"13 дней назад") def testIssue25HoursFixed(self): """ Unit-test for testing that Issue#25 is fixed (err when accuracy==1, hours<>0, minutes==1) """ d_hours = datetime.datetime.now() - datetime.timedelta(0, 46865) self.assertEquals(pytils.dt.distance_of_time_in_words(d_hours), u"13 часов назад") class RuStrftimeTestCase(unittest.TestCase): """ Test case for pytils.dt.ru_strftime """ def setUp(self): """ Setting up environment for tests """ self.date = datetime.date(2006, 8, 25) def ck(self, format, estimates, date=None): """ Checks w/o inflected """ if date is None: date = self.date res = pytils.dt.ru_strftime(format, date) self.assertEquals(res, estimates) def ckInflected(self, format, estimates, date=None): """ Checks with inflected """ if date is None: date = self.date res = pytils.dt.ru_strftime(format, date, True) self.assertEquals(res, estimates) def ckInflectedDay(self, format, estimates, date=None): """ Checks with inflected day """ if date is None: date = self.date res = pytils.dt.ru_strftime(format, date, inflected_day=True) self.assertEquals(res, estimates) def ckPreposition(self, format, estimates, date=None): """ Checks with inflected day """ if date is None: date = self.date res = pytils.dt.ru_strftime(format, date, preposition=True) self.assertEquals(res, estimates) def testRuStrftime(self): """ Unit-tests for pytils.dt.ru_strftime """ self.ck(u"тест %a", u"тест пт") self.ck(u"тест %A", u"тест пятница") self.ck(u"тест %b", u"тест авг") self.ck(u"тест %B", u"тест август") self.ckInflected(u"тест %B", u"тест августа") self.ckInflected(u"тест выполнен %d %B %Y года", u"тест выполнен 25 августа 2006 года") self.ckInflectedDay(u"тест выполнен в %A", u"тест выполнен в пятницу") def testRuStrftimeWithPreposition(self): """ Unit-tests for pytils.dt.ru_strftime with preposition option """ self.ckPreposition(u"тест %a", u"тест в\xa0пт") self.ckPreposition(u"тест %A", u"тест в\xa0пятницу") self.ckPreposition(u"тест %A", u"тест во\xa0вторник", datetime.date(2007, 6, 5)) def testRuStrftimeZeros(self): """ Unit-test for testing that Issue#24 is correctly implemented It means, 1 April 2007, but 01.04.2007 """ self.ck(u"%d.%m.%Y", u"01.04.2007", datetime.date(2007, 4, 1)) self.ckInflected(u"%d %B %Y", u"1 апреля 2007", datetime.date(2007, 4, 1)) def testRuStrftimeExceptions(self): """ Unit-tests for testing pytils.dt.ru_strftime's exceptions """ self.assertRaises(TypeError, pytils.dt.ru_strftime, time.time()) self.assertRaises(TypeError, pytils.dt.ru_strftime, u"%Y.%m.%d%", time.time()) self.assertRaises(pytils.err.InputParameterError, pytils.dt.ru_strftime, time.time()) self.assertRaises(pytils.err.InputParameterError, pytils.dt.ru_strftime, u"%Y.%m.%d%", time.time()) def testIssue20Fixed(self): """ Unit-test for testing that Issue#20 is fixed (typo) """ self.assertEquals(u"воскресенье", pytils.dt.ru_strftime( u"%A", datetime.date(2007,3,18), inflected_day=True) ) if __name__ == '__main__': unittest.main() pytils-0.2.3/pytils/test/test_numeral.py0000644000175000017500000004700311050343264016333 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit-tests for pytils.numeral """ import unittest import pytils class ChoosePluralTestCase(unittest.TestCase): """ Test case for pytils.numeral.choose_plural """ def setUp(self): """ Setting up environment for tests """ self.variants = (u"гвоздь", u"гвоздя", u"гвоздей") def checkChoosePlural(self, amount, estimated): """ Checks choose_plural """ self.assertEquals(pytils.numeral.choose_plural(amount, self.variants), estimated) def testChoosePlural(self): """ Unit-test for choose_plural """ self.checkChoosePlural(1, u"гвоздь") self.checkChoosePlural(2, u"гвоздя") self.checkChoosePlural(3, u"гвоздя") self.checkChoosePlural(5, u"гвоздей") self.checkChoosePlural(11, u"гвоздей") self.checkChoosePlural(109, u"гвоздей") self.checkChoosePlural(109l, u"гвоздей") def testChoosePluralExceptions(self): """ Unit-test for testing choos_plural's exceptions """ self.assertRaises(TypeError, pytils.numeral.choose_plural, "25", u"any,bene,raba") self.assertRaises(TypeError, pytils.numeral.choose_plural, 25, 30) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.choose_plural, "25", u"any,bene,raba") self.assertRaises(pytils.err.InputParameterError, pytils.numeral.choose_plural, 25, 30) self.assertRaises(ValueError, pytils.numeral.choose_plural, 25, u"any,bene") self.assertRaises(ValueError, pytils.numeral.choose_plural, -25, u"any,bene,raba") def testChoosePluralVariantsInStr(self): """ Tests new-style variants """ self.assertEquals( pytils.numeral.choose_plural(1,u"гвоздь,гвоздя, гвоздей"), u"гвоздь") self.assertEquals( pytils.numeral.choose_plural(5,u"гвоздь, гвоздя, гвоздей\, шпунтов"), u"гвоздей, шпунтов") class GetPluralTestCase(unittest.TestCase): """ Test case for get_plural """ def testGetPlural(self): """ Test regular get_plural """ self.assertEquals( pytils.numeral.get_plural(1, u"комментарий, комментария, комментариев"), u"1 комментарий") self.assertEquals( pytils.numeral.get_plural(0, u"комментарий, комментария, комментариев"), u"0 комментариев") def testGetPluralAbsence(self): """ Test get_plural with absence """ self.assertEquals( pytils.numeral.get_plural(1, u"комментарий, комментария, комментариев", u"без комментариев"), u"1 комментарий") self.assertEquals( pytils.numeral.get_plural(0, u"комментарий, комментария, комментариев", u"без комментариев"), u"без комментариев") def testGetPluralLegacy(self): """ Test _get_plural_legacy """ self.assertEquals( pytils.numeral._get_plural_legacy(1, u"комментарий, комментария, комментариев"), u"1 комментарий") self.assertEquals( pytils.numeral._get_plural_legacy(0, u"комментарий, комментария, комментариев"), u"0 комментариев") self.assertEquals( pytils.numeral._get_plural_legacy(1, u"комментарий, комментария, комментариев, без комментариев"), u"1 комментарий") self.assertEquals( pytils.numeral._get_plural_legacy(0, u"комментарий, комментария, комментариев, без комментариев"), u"без комментариев") class GetFloatRemainderTestCase(unittest.TestCase): """ Test case for pytils.numeral._get_float_remainder """ def testFloatRemainder(self): """ Unit-test for _get_float_remainder """ self.assertEquals(pytils.numeral._get_float_remainder(1.3), '3') self.assertEquals(pytils.numeral._get_float_remainder(2.35, 1), '4') self.assertEquals(pytils.numeral._get_float_remainder(123.1234567891), '123456789') self.assertEquals(pytils.numeral._get_float_remainder(2.353, 2), '35') self.assertEquals(pytils.numeral._get_float_remainder(0.01), '01') self.assertEquals(pytils.numeral._get_float_remainder(5), '0') def testFloatRemainderExceptions(self): """ Unit-test for testing _get_float_remainder's exceptions """ self.assertRaises(ValueError, pytils.numeral._get_float_remainder, 2.998, 2) self.assertRaises(TypeError, pytils.numeral._get_float_remainder, "1.23") self.assertRaises(pytils.err.InputParameterError, pytils.numeral._get_float_remainder, "1.23") self.assertRaises(ValueError, pytils.numeral._get_float_remainder, -1.23) class RublesTestCase(unittest.TestCase): """ Test case for pytils.numeral.rubles """ def testRubles(self): """ Unit-test for rubles """ self.assertEquals(pytils.numeral.rubles(10.01), u"десять рублей одна копейка") self.assertEquals(pytils.numeral.rubles(10.10), u"десять рублей десять копеек") self.assertEquals(pytils.numeral.rubles(2.353), u"два рубля тридцать пять копеек") self.assertEquals(pytils.numeral.rubles(2.998), u"три рубля") self.assertEquals(pytils.numeral.rubles(3), u"три рубля") self.assertEquals(pytils.numeral.rubles(3, True), u"три рубля ноль копеек") self.assertEquals(pytils.numeral.rubles(3l), u"три рубля") def testRublesExceptions(self): """ Unit-test for testing rubles' exceptions """ self.assertRaises(TypeError, pytils.numeral.rubles, "3") self.assertRaises(pytils.err.InputParameterError, pytils.numeral.rubles, "3") self.assertRaises(ValueError, pytils.numeral.rubles, -15) class InWordsTestCase(unittest.TestCase): """ Test case for pytils.numeral.in_words """ def testInt(self): """ Unit-test for in_words_int """ self.assertEquals(pytils.numeral.in_words_int(10), u"десять") self.assertEquals(pytils.numeral.in_words_int(5), u"пять") self.assertEquals(pytils.numeral.in_words_int(102), u"сто два") self.assertEquals(pytils.numeral.in_words_int(3521), u"три тысячи пятьсот двадцать один") self.assertEquals(pytils.numeral.in_words_int(3500), u"три тысячи пятьсот") self.assertEquals(pytils.numeral.in_words_int(5231000), u"пять миллионов двести тридцать одна тысяча") self.assertEquals(pytils.numeral.in_words_int(10l), u"десять") def testIntExceptions(self): """ Unit-test for testing in_words_int's exceptions """ self.assertRaises(TypeError, pytils.numeral.in_words_int, 2.5) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.in_words_int, 2.5) self.assertRaises(ValueError, pytils.numeral.in_words_int, -3) def testFloat(self): """ Unit-test for in_words_float """ self.assertEquals(pytils.numeral.in_words_float(10.0), u"десять целых ноль десятых") self.assertEquals(pytils.numeral.in_words_float(2.25), u"две целых двадцать пять сотых") self.assertEquals(pytils.numeral.in_words_float(0.01), u"ноль целых одна сотая") self.assertEquals(pytils.numeral.in_words_float(0.10), u"ноль целых одна десятая") def testFloatExceptions(self): """ Unit-test for testing in_words_float's exceptions """ self.assertRaises(TypeError, pytils.numeral.in_words_float, '2') self.assertRaises(TypeError, pytils.numeral.in_words_float, 2) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.in_words_float, '2') self.assertRaises(pytils.err.InputParameterError, pytils.numeral.in_words_float, 2) self.assertRaises(ValueError, pytils.numeral.in_words_float, -2.3) def testWithGenderOldStyle(self): """ Unit-test for in_words_float with gender (old-style, i.e. ints) """ self.assertEquals(pytils.numeral.in_words(21, 1), u"двадцать один") self.assertEquals(pytils.numeral.in_words(21, 2), u"двадцать одна") self.assertEquals(pytils.numeral.in_words(21, 3), u"двадцать одно") # на дробные пол не должен влиять - всегда в женском роде self.assertEquals(pytils.numeral.in_words(21.0, 1), u"двадцать одна целая ноль десятых") self.assertEquals(pytils.numeral.in_words(21.0, 2), u"двадцать одна целая ноль десятых") self.assertEquals(pytils.numeral.in_words(21.0, 3), u"двадцать одна целая ноль десятых") self.assertEquals(pytils.numeral.in_words(21l, 1), u"двадцать один") def testWithGender(self): """ Unit-test for in_words_float with gender (old-style, i.e. ints) """ self.assertEquals(pytils.numeral.in_words(21, pytils.numeral.MALE), u"двадцать один") self.assertEquals(pytils.numeral.in_words(21, pytils.numeral.FEMALE), u"двадцать одна") self.assertEquals(pytils.numeral.in_words(21, pytils.numeral.NEUTER), u"двадцать одно") # на дробные пол не должен влиять - всегда в женском роде self.assertEquals(pytils.numeral.in_words(21.0, pytils.numeral.MALE), u"двадцать одна целая ноль десятых") self.assertEquals(pytils.numeral.in_words(21.0, pytils.numeral.FEMALE), u"двадцать одна целая ноль десятых") self.assertEquals(pytils.numeral.in_words(21.0, pytils.numeral.NEUTER), u"двадцать одна целая ноль десятых") self.assertEquals(pytils.numeral.in_words(21l, pytils.numeral.MALE), u"двадцать один") def testCommon(self): """ Unit-test for general in_words """ self.assertEquals(pytils.numeral.in_words(10), u"десять") self.assertEquals(pytils.numeral.in_words(5), u"пять") self.assertEquals(pytils.numeral.in_words(102), u"сто два") self.assertEquals(pytils.numeral.in_words(3521), u"три тысячи пятьсот двадцать один") self.assertEquals(pytils.numeral.in_words(3500), u"три тысячи пятьсот") self.assertEquals(pytils.numeral.in_words(5231000), u"пять миллионов двести тридцать одна тысяча") self.assertEquals(pytils.numeral.in_words(10.0), u"десять целых ноль десятых") self.assertEquals(pytils.numeral.in_words(2.25), u"две целых двадцать пять сотых") self.assertEquals(pytils.numeral.in_words(0.01), u"ноль целых одна сотая") self.assertEquals(pytils.numeral.in_words(0.10), u"ноль целых одна десятая") self.assertEquals(pytils.numeral.in_words(10l), u"десять") def testCommonExceptions(self): """ Unit-test for testing in_words' exceptions """ self.assertRaises(TypeError, pytils.numeral.in_words, "0.2") self.assertRaises(TypeError, pytils.numeral.in_words, 0.2, "1") self.assertRaises(TypeError, pytils.numeral.in_words, 0.2, 5) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.in_words, "0.2") self.assertRaises(pytils.err.InputParameterError, pytils.numeral.in_words, 0.2, "1") self.assertRaises(pytils.err.InputParameterError, pytils.numeral.in_words, 0.2, 5) self.assertRaises(ValueError, pytils.numeral.in_words, -2) class SumStringTestCase(unittest.TestCase): """ Test case for pytils.numeral.sum_string """ def setUp(self): """ Setting up environment for tests """ self.variants_male = (u"гвоздь", u"гвоздя", u"гвоздей") self.variants_female = (u"шляпка", u"шляпки", u"шляпок") def ckMaleOldStyle(self, amount, estimated): """ Checks sum_string with male gender with old-style genders (i.e. ints) """ self.assertEquals(pytils.numeral.sum_string(amount, 1, self.variants_male), estimated) def ckMale(self, amount, estimated): """ Checks sum_string with male gender """ self.assertEquals(pytils.numeral.sum_string(amount, pytils.numeral.MALE, self.variants_male), estimated) def ckFemaleOldStyle(self, amount, estimated): """ Checks sum_string with female gender wuth old-style genders (i.e. ints) """ self.assertEquals(pytils.numeral.sum_string(amount, 2, self.variants_female), estimated) def ckFemale(self, amount, estimated): """ Checks sum_string with female gender """ self.assertEquals(pytils.numeral.sum_string(amount, pytils.numeral.FEMALE, self.variants_female), estimated) def testSumStringOldStyleGender(self): """ Unit-test for sum_string with old-style genders """ self.ckMaleOldStyle(10, u"десять гвоздей") self.ckMaleOldStyle(2, u"два гвоздя") self.ckMaleOldStyle(31, u"тридцать один гвоздь") self.ckFemaleOldStyle(10, u"десять шляпок") self.ckFemaleOldStyle(2, u"две шляпки") self.ckFemaleOldStyle(31, u"тридцать одна шляпка") self.ckFemaleOldStyle(31l, u"тридцать одна шляпка") self.assertEquals(u"одиннадцать негритят", pytils.numeral.sum_string( 11, 1, u"негритенок,негритенка,негритят" )) def testSumString(self): """ Unit-test for sum_string """ self.ckMale(10, u"десять гвоздей") self.ckMale(2, u"два гвоздя") self.ckMale(31, u"тридцать один гвоздь") self.ckFemale(10, u"десять шляпок") self.ckFemale(2, u"две шляпки") self.ckFemale(31, u"тридцать одна шляпка") self.ckFemale(31l, u"тридцать одна шляпка") self.assertEquals(u"одиннадцать негритят", pytils.numeral.sum_string( 11, pytils.numeral.MALE, u"негритенок,негритенка,негритят" )) def testSumStringExceptions(self): """ Unit-test for testing sum_string's exceptions """ self.assertRaises(TypeError, pytils.numeral.sum_string, "1", 1) self.assertRaises(TypeError, pytils.numeral.sum_string, 1, "1") self.assertRaises(TypeError, pytils.numeral.sum_string, 1, "1", 23) self.assertRaises(TypeError, pytils.numeral.sum_string, 1, pytils.numeral.MALE, (23,24,25)) self.assertRaises(TypeError, pytils.numeral.sum_string, 1, pytils.numeral.MALE, (23,)) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.sum_string, "1", 1) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.sum_string, 1, "1") self.assertRaises(pytils.err.InputParameterError, pytils.numeral.sum_string, 1, "1", 23) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.sum_string, 1, pytils.numeral.MALE, (23,24,25)) self.assertRaises(pytils.err.InputParameterError, pytils.numeral.sum_string, 1, pytils.numeral.MALE, (23,)) self.assertRaises(ValueError, pytils.numeral.sum_string, -1, pytils.numeral.MALE, u"any,bene,raba") if __name__ == '__main__': unittest.main() pytils-0.2.3/pytils/test/test_translit.py0000644000175000017500000001014311050343313016516 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit-tests for pytils.translit """ import unittest import pytils class TranslitTestCase(unittest.TestCase): """ Test case for pytils.translit """ def ckTransl(self, in_, out_): """ Checks translify """ self.assertEquals(pytils.translit.translify(in_), out_) def ckDetransl(self, in_, out_): """ Checks detranslify """ self.assertEquals(pytils.translit.detranslify(in_), out_) def ckSlug(self, in_, out_): """ Checks slugify """ self.assertEquals(pytils.translit.slugify(in_), out_) def testTransliteration(self): """ Unit-test for transliterations """ self.ckTransl(u"тест", 'test') self.ckTransl(u"проверка", 'proverka') self.ckTransl(u"транслит", 'translit') self.ckTransl(u"правда ли это", 'pravda li eto') self.ckTransl(u"Щука", 'Schuka') def testTransliterationExceptions(self): """ Unit-test for testing translify's exceptions """ self.assertRaises(TypeError, pytils.translit.translify, 25) self.assertRaises(pytils.err.InputParameterError, pytils.translit.translify, 25) self.assertRaises(ValueError, pytils.translit.translify, u'\u00bfHabla espa\u00f1ol?') def testDetransliteration(self): """ Unit-test for detransliterations """ self.ckDetransl('test', u"тест") self.ckDetransl('proverka', u"проверка") self.ckDetransl('translit', u"транслит") self.ckDetransl('SCHuka', u"Щука") self.ckDetransl('Schuka', u"Щука") def testDetransliterationExceptions(self): """ Unit-test for testing detranslify's exceptions """ self.assertRaises(TypeError, pytils.translit.detranslify, 25) self.assertRaises(pytils.err.InputParameterError, pytils.translit.detranslify, 25) self.assertRaises(ValueError, pytils.translit.detranslify, "тест") def testSlug(self): """ Unit-test for slugs """ self.ckSlug(u"ТеСт", 'test') self.ckSlug(u"Проверка связи", 'proverka-svyazi') self.ckSlug(u"me&you", 'me-and-you') self.ckSlug(u"и еще один тест", 'i-esche-odin-test') def testSlugExceptions(self): """ Unit-test for testing slugify's exceptions """ self.assertRaises(TypeError, pytils.translit.slugify, 25) self.assertRaises(pytils.err.InputParameterError, pytils.translit.slugify, 25) self.assertRaises(ValueError, pytils.translit.slugify, "тест") def testTranslifyAdditionalUnicodeSymbols(self): """ Unit-test for testing additional unicode symbols """ self.ckTransl(u"«Вот так вот»", '"Vot tak vot"') self.ckTransl(u"‘Или вот так’", "'Ili vot tak'") self.ckTransl(u"– Да…", "- Da...") def testSlugifyIssue10(self): """ Unit-test for testing that bug#10 fixed """ self.ckSlug(u"Проверка связи…", 'proverka-svyazi') self.ckSlug(u"Проверка\x0aсвязи 2", 'proverka-svyazi-2') self.ckSlug(u"Проверка\201связи 3", 'proverkasvyazi-3') def testSlugifyIssue15(self): """ Unit-test for testing that bug#15 fixed """ self.ckSlug(u"World of Warcraft", "world-of-warcraft") if __name__ == '__main__': unittest.main() pytils-0.2.3/pytils/test/test_utils.py0000644000175000017500000001271311050343355016031 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Unit-tests for pytils.utils """ import unittest import pytils class ASPN426123TestCase(unittest.TestCase): """ Test case for third-party library from ASPN cookbook recipe #426123 This unit-test don't cover all code from recipe """ def testTakesPositional(self): @pytils.utils.takes(int, basestring) def func(i, s): return i + len(s) self.assertEquals(func(2, 'var'), 5) self.assertEquals(func(2, u'var'), 5) self.assertRaises(pytils.err.InputParameterError, func, 2, 5) self.assertRaises(pytils.err.InputParameterError, func, 2, ('var',)) self.assertRaises(pytils.err.InputParameterError, func, 'var', 5) def testTakesNamed(self): @pytils.utils.takes(int, s=basestring) def func(i, s): return i + len(s) self.assertEquals(func(2, s='var'), 5) self.assertEquals(func(2, s=u'var'), 5) self.assertRaises(pytils.err.InputParameterError, func, 2, 'var') self.assertRaises(pytils.err.InputParameterError, func, 2, 5) self.assertRaises(pytils.err.InputParameterError, func, 2, ('var',)) self.assertRaises(pytils.err.InputParameterError, func, 'var', 5) def testTakesOptional(self): @pytils.utils.takes(int, pytils.utils.optional(basestring), s=pytils.utils.optional(basestring)) def func(i, s=''): return i + len(s) self.assertEquals(func(2, 'var'), 5) self.assertEquals(func(2, s='var'), 5) self.assertEquals(func(2, s=u'var'), 5) self.assertRaises(pytils.err.InputParameterError, func, 2, 5) self.assertRaises(pytils.err.InputParameterError, func, 2, ('var',)) self.assertRaises(pytils.err.InputParameterError, func, 'var', 5) def testTakesMultiplyTypesAndTupleOf(self): @pytils.utils.takes((int, long), pytils.utils.tuple_of(basestring)) def func(i, t=tuple()): return i + sum(len(s) for s in t) self.assertEquals(func(2, ('var', 'var2')), 9) self.assertEquals(func(2L, (u'var', 'var2')), 9) self.assertEquals(func(2, t=('var', 'var2')), 9) self.assertEquals(func(2, t=(u'var', u'var2')), 9) self.assertRaises(pytils.err.InputParameterError, func, 2, (2, 5)) class ChecksTestCase(unittest.TestCase): """ Test case for check_* utils """ def testCheckLength(self): """ Unit-test for pytils.utils.check_length """ self.assertEquals(pytils.utils.check_length("var", 3), None) self.assertRaises(ValueError, pytils.utils.check_length, "var", 4) self.assertRaises(ValueError, pytils.utils.check_length, "var", 2) self.assertRaises(ValueError, pytils.utils.check_length, (1,2), 3) self.assertRaises(TypeError, pytils.utils.check_length, 5) def testCheckPositive(self): """ Unit-test for pytils.utils.check_positive """ self.assertEquals(pytils.utils.check_positive(0), None) self.assertEquals(pytils.utils.check_positive(1), None) self.assertEquals(pytils.utils.check_positive(1, False), None) self.assertEquals(pytils.utils.check_positive(1, strict=False), None) self.assertEquals(pytils.utils.check_positive(1, True), None) self.assertEquals(pytils.utils.check_positive(1, strict=True), None) self.assertEquals(pytils.utils.check_positive(2.0), None) self.assertRaises(ValueError, pytils.utils.check_positive, -2) self.assertRaises(ValueError, pytils.utils.check_positive, -2.0) self.assertRaises(ValueError, pytils.utils.check_positive, 0, True) class SplitValuesTestCase(unittest.TestCase): def testClassicSplit(self): """ Unit-test for pytils.utils.split_values, classic split """ self.assertEquals((u"Раз", u"Два", u"Три"), pytils.utils.split_values(u"Раз,Два,Три")) self.assertEquals((u"Раз", u"Два", u"Три"), pytils.utils.split_values(u"Раз, Два,Три")) self.assertEquals((u"Раз", u"Два", u"Три"), pytils.utils.split_values(u" Раз, Два, Три ")) self.assertEquals((u"Раз", u"Два", u"Три"), pytils.utils.split_values(u" Раз, \nДва,\n Три ")) def testEscapedSplit(self): """ Unit-test for pytils.utils.split_values, split with escaping """ self.assertEquals((u"Раз,Два", u"Три,Четыре", u"Пять,Шесть"), pytils.utils.split_values(u"Раз\,Два,Три\,Четыре,Пять\,Шесть")) self.assertEquals((u"Раз, Два", u"Три", u"Четыре"), pytils.utils.split_values(u"Раз\, Два, Три, Четыре")) if __name__ == '__main__': unittest.main() pytils-0.2.3/pytils/third/0000755000175000017500000000000011051306250013401 5ustar j2aj2apytils-0.2.3/pytils/third/__init__.py0000644000175000017500000000003411047726655015533 0ustar j2aj2a""" Third-party modules """ pytils-0.2.3/pytils/third/aspn426123.py0000644000175000017500000003366411047726655015436 0ustar j2aj2a#!/usr/bin/env python # -*- coding: iso-8859-1 -*- ################################################################################ # # Method call parameters/return value type checking decorators. # (c) 2006-2007, Dmitry Dvoinikov # Distributed under BSD license. # # Samples: # # from typecheck import * # # @takes(int, str) # takes int, str, upon a problem throws InputParameterError # @returns(int) # returns int, upon a problem throws ReturnValueError # def foo(i, s): # return i + len(s) # # @takes((int, long), by_regex("^[0-9]+$")) # int or long, numerical string # def foo(i, s, anything): # and the third parameter is not checked # ... # # @takes(int, int, foo = int, bar = optional(int)) # keyword argument foo must be int # def foo(a, b, **kwargs): # bar may be int or missing # ... # # Note: @takes for positional arguments, @takes for keyword arguments and @returns # all support the same checker syntax, for example for the following declaration # # @takes(C) # def foo(x): # ... # # then C may be one of the simple checkers: # # --------- C --------- ------------- semantics ------------- # typename ==> ok if x is is an instance of typename # "typename" ==> ok if x is is an instance of typename # with_attr("a", "b") ==> ok if x has specific attributes # some_callable ==> ok if some_callable(x) is True # one_of(1, "2") ==> ok if x is one of the literal values # by_regex("^foo$") ==> ok if x is a matching basestring # nothing ==> ok if x is None # anything ==> always ok # # simple checkers can further be combined with OR semantics using tuples: # # --------- C --------- ------------- semantics ------------- # (checker1, checker2) ==> ok if x conforms with either checker # # be optional: # # --------- C --------- ------------- semantics ------------- # optional(checker) ==> ok if x is checker-conformant or None # # or nested recursively into one of the following checkers # # --------- C --------- ------------- semantics ------------- # list_of(checker) ==> ok if x is a list of checker-conformant values # tuple_of(checker) ==> ok if x is a tuple of checker-conformant values # dict_of(key_checker, value_checker) ==> ok if x is a dict mapping key_checker- # conformant keys to value_checker-conformant values # # More samples: # # class foo(object): # @takes("foo", optional(int)) # foo, maybe int, but foo is yet incomplete # def __init__(self, i = None): # and is thus specified by name # ... # @takes("foo", int) # foo, and int if presents in args, # def bar(self, *args): # if args is empty, the check passes ok # ... # @takes("foo") # @returns(object) # returns foo which is fine, because # def biz(self): # foo is an object # return self # @classmethod # classmethod's and staticmethod's # @takes(type) # go same way # def baz(cls): # ... # # @takes(int) # @returns(optional("int", foo)) # returns either int, foo or NoneType # def bar(i): # "int" (rather than just int) is for fun # if i > 0: # return i # elif i == 0: # return foo() # otherwise returns NoneType # # @takes(callable) # built-in functions are treated as predicates # @returns(lambda x: x == 123) # and so do user-defined functions or lambdas # def execute(f, *args, **kwargs): # return f(*args, **kwargs) # # assert execute(execute, execute, execute, lambda x: x, 123) == 123 # # def readable(x): # user-defined type-checking predicate # return hasattr(x, "read") # # anything is an alias for predicate lambda: True, # nothing is an alias for NoneType, as in: # # @takes(callable, readable, optional(anything), optional(int)) # @returns(nothing) # def foo(f, r, x = None, i = None): # ... # # @takes(with_attr("read", "write")) # another way of protocol checking # def foo(pipe): # ... # # @takes(list_of(int)) # list of ints # def foo(x): # print x[0] # # @takes(tuple_of(callable)) # tuple of callables # def foo(x): # print x[0]() # # @takes(dict_of(str, list_of(int))) # dict mapping strs to lists of int # def foo(x): # print sum(x["foo"]) # # @takes(by_regex("^[0-9]{1,8}$")) # integer-as-a-string regex # def foo(x): # i = int(x) # # @takes(one_of(1, 2)) # must be equal to either one # def set_version(version): # ... # # The (3 times longer) source code with self-tests is available from: # http://www.targeted.org/python/recipes/typecheck.py # ################################################################################ __all__ = [ "takes", "InputParameterError", "returns", "ReturnValueError", "optional", "nothing", "anything", "list_of", "tuple_of", "dict_of", "by_regex", "with_attr", "one_of" ] no_check = False # set this to True to turn all checks off ################################################################################ from inspect import getargspec, isfunction, isbuiltin, isclass from types import NoneType from re import compile as regex ################################################################################ def base_names(C): "Returns list of base class names for a given class" return [ x.__name__ for x in C.__mro__ ] ################################################################################ def type_name(v): "Returns the name of the passed value's type" return type(v).__name__ ################################################################################ class Checker(object): def __init__(self, reference): self.reference = reference def check(self, value): # abstract pass _registered = [] # a list of registered descendant class factories @staticmethod def create(value): # static factory method for f, t in Checker._registered: if f(value): return t(value) else: return None ################################################################################ class TypeChecker(Checker): def check(self, value): return isinstance(value, self.reference) Checker._registered.append((isclass, TypeChecker)) nothing = NoneType ################################################################################ class StrChecker(Checker): def check(self, value): value_base_names = base_names(type(value)) return self.reference in value_base_names or "instance" in value_base_names Checker._registered.append((lambda x: isinstance(x, str), StrChecker)) ################################################################################ class TupleChecker(Checker): def __init__(self, reference): self.reference = map(Checker.create, reference) def check(self, value): return reduce(lambda r, c: r or c.check(value), self.reference, False) Checker._registered.append((lambda x: isinstance(x, tuple) and not filter(lambda y: Checker.create(y) is None, x), TupleChecker)) optional = lambda *args: args + (NoneType, ) ################################################################################ class FunctionChecker(Checker): def check(self, value): return self.reference(value) Checker._registered.append((lambda x: isfunction(x) or isbuiltin(x), FunctionChecker)) anything = lambda *args: True ################################################################################ class ListOfChecker(Checker): def __init__(self, reference): self.reference = Checker.create(reference) def check(self, value): return isinstance(value, list) and \ not filter(lambda e: not self.reference.check(e), value) list_of = lambda *args: lambda value: ListOfChecker(*args).check(value) ################################################################################ class TupleOfChecker(Checker): def __init__(self, reference): self.reference = Checker.create(reference) def check(self, value): return isinstance(value, tuple) and \ not filter(lambda e: not self.reference.check(e), value) tuple_of = lambda *args: lambda value: TupleOfChecker(*args).check(value) ################################################################################ class DictOfChecker(Checker): def __init__(self, key_reference, value_reference): self.key_reference = Checker.create(key_reference) self.value_reference = Checker.create(value_reference) def check(self, value): return isinstance(value, dict) and \ not filter(lambda e: not self.key_reference.check(e), value.iterkeys()) and \ not filter(lambda e: not self.value_reference.check(e), value.itervalues()) dict_of = lambda *args: lambda value: DictOfChecker(*args).check(value) ################################################################################ class RegexChecker(Checker): def __init__(self, reference): self.reference = regex(reference) def check(self, value): return isinstance(value, basestring) and self.reference.match(value) by_regex = lambda *args: lambda value: RegexChecker(*args).check(value) ################################################################################ class AttrChecker(Checker): def __init__(self, *attrs): self.attrs = attrs def check(self, value): return reduce(lambda r, c: r and c, map(lambda a: hasattr(value, a), self.attrs), True) with_attr = lambda *args: lambda value: AttrChecker(*args).check(value) ################################################################################ class OneOfChecker(Checker): def __init__(self, *values): self.values = values def check(self, value): return value in self.values one_of = lambda *args: lambda value: OneOfChecker(*args).check(value) ################################################################################ def takes(*args, **kwargs): "Method signature checking decorator" # convert decorator arguments into a list of checkers checkers = [] for i, arg in enumerate(args): checker = Checker.create(arg) if checker is None: raise TypeError("@takes decorator got parameter %d of unsupported " "type %s" % (i + 1, type_name(arg))) checkers.append(checker) kwcheckers = {} for kwname, kwarg in kwargs.iteritems(): checker = Checker.create(kwarg) if checker is None: raise TypeError("@takes decorator got parameter %s of unsupported " "type %s" % (kwname, type_name(kwarg))) kwcheckers[kwname] = checker if no_check: # no type checking is performed, return decorated method itself def takes_proxy(method): return method else: def takes_proxy(method): method_args, method_defaults = getargspec(method)[0::3] def takes_invocation_proxy(*args, **kwargs): ## do not append the default parameters // 'DONT' by Pythy # if method_defaults is not None and len(method_defaults) > 0 \ # and len(method_args) - len(method_defaults) <= len(args) < len(method_args): # args += method_defaults[len(args) - len(method_args):] # check the types of the actual call parameters for i, (arg, checker) in enumerate(zip(args, checkers)): if not checker.check(arg): raise InputParameterError("%s() got invalid parameter " "%d of type %s" % (method.__name__, i + 1, type_name(arg))) for kwname, checker in kwcheckers.iteritems(): if not checker.check(kwargs.get(kwname, None)): raise InputParameterError("%s() got invalid parameter " "%s of type %s" % (method.__name__, kwname, type_name(kwargs.get(kwname, None)))) return method(*args, **kwargs) takes_invocation_proxy.__name__ = method.__name__ return takes_invocation_proxy return takes_proxy class InputParameterError(TypeError): pass ################################################################################ def returns(sometype): "Return type checking decorator" # convert decorator argument into a checker checker = Checker.create(sometype) if checker is None: raise TypeError("@returns decorator got parameter of unsupported " "type %s" % type_name(sometype)) if no_check: # no type checking is performed, return decorated method itself def returns_proxy(method): return method else: def returns_proxy(method): def returns_invocation_proxy(*args, **kwargs): result = method(*args, **kwargs) if not checker.check(result): raise ReturnValueError("%s() has returned an invalid " "value of type %s" % (method.__name__, type_name(result))) return result returns_invocation_proxy.__name__ = method.__name__ return returns_invocation_proxy return returns_proxy class ReturnValueError(TypeError): pass ################################################################################ # EOF pytils-0.2.3/pytils/__init__.py0000644000175000017500000000130611051304242014377 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Simple processing for russian strings """ VERSION = '0.2.3' from pytils import numeral, dt, translit, err pytils-0.2.3/pytils/dt.py0000644000175000017500000002176011050343017013257 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.test_dt -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Russian dates without locales """ import datetime from pytils import numeral from pytils.utils import takes, returns, optional, check_positive import utils DAY_ALTERNATIVES = { 1: (u"вчера", u"завтра"), 2: (u"позавчера", u"послезавтра") } #: Day alternatives (i.e. one day ago -> yesterday) DAY_VARIANTS = ( u"день", u"дня", u"дней", ) #: Forms (1, 2, 5) for noun 'day' HOUR_VARIANTS = ( u"час", u"часа", u"часов", ) #: Forms (1, 2, 5) for noun 'hour' MINUTE_VARIANTS = ( u"минуту", u"минуты", u"минут", ) #: Forms (1, 2, 5) for noun 'minute' PREFIX_IN = u"через" #: Prefix 'in' (i.e. B{in} three hours) SUFFIX_AGO = u"назад" #: Prefix 'ago' (i.e. three hours B{ago}) MONTH_NAMES = ( (u"янв", u"январь", u"января"), (u"фев", u"февраль", u"февраля"), (u"мар", u"март", u"марта"), (u"апр", u"апрель", u"апреля"), (u"май", u"май", u"мая"), (u"июн", u"июнь", u"июня"), (u"июл", u"июль", u"июля"), (u"авг", u"август", u"августа"), (u"сен", u"сентябрь", u"сентября"), (u"окт", u"октябрь", u"октября"), (u"ноя", u"ноябрь", u"ноября"), (u"дек", u"декабрь", u"декабря"), ) #: Month names (abbreviated, full, inflected) DAY_NAMES = ( (u"пн", u"понедельник", u"понедельник", u"в\xa0"), (u"вт", u"вторник", u"вторник", u"во\xa0"), (u"ср", u"среда", u"среду", u"в\xa0"), (u"чт", u"четверг", u"четверг", u"в\xa0"), (u"пт", u"пятница", u"пятницу", u"в\xa0"), (u"сб", u"суббота", u"субботу", u"в\xa0"), (u"вск", u"воскресенье", u"воскресенье", u"в\xa0") ) #: Day names (abbreviated, full, inflected, preposition) @takes((int, float, datetime.datetime), optional(int), optional((int, float, datetime.datetime)), accuracy=optional(int), to_time=optional((int, float, datetime.datetime))) @returns(unicode) def distance_of_time_in_words(from_time, accuracy=1, to_time=None): """ Represents distance of time in words @param from_time: source time (in seconds from epoch) @type from_time: C{int}, C{float} or C{datetime.datetime} @param accuracy: level of accuracy (1..3), default=1 @type accuracy: C{int} @param to_time: target time (in seconds from epoch), default=None translates to current time @type to_time: C{int}, C{float} or C{datetime.datetime} @return: distance of time in words @rtype: unicode @raise L{pytils.err.InputParameterError}: input parameters' check failed @raise ValueError: accuracy is lesser or equal zero """ current = False if to_time is None: current = True to_time = datetime.datetime.now() check_positive(accuracy, strict=True) if not isinstance(from_time, datetime.datetime): from_time = datetime.datetime.fromtimestamp(from_time) if not isinstance(to_time, datetime.datetime): to_time = datetime.datetime.fromtimestamp(to_time) dt_delta = to_time - from_time difference = dt_delta.days*86400 + dt_delta.seconds seconds_orig = int(abs(difference)) minutes_orig = int(abs(difference)/60.0) hours_orig = int(abs(difference)/3600.0) days_orig = int(abs(difference)/86400.0) in_future = from_time > to_time words = [] values = [] alternatives = [] days = days_orig hours = hours_orig - days_orig*24 words.append(u"%d %s" % (days, numeral.choose_plural(days, DAY_VARIANTS))) values.append(days) words.append(u"%d %s" % \ (hours, numeral.choose_plural(hours, HOUR_VARIANTS))) values.append(hours) hours == 1 and current and alternatives.append(u"час") minutes = minutes_orig - hours_orig*60 words.append(u"%d %s" % (minutes, numeral.choose_plural(minutes, MINUTE_VARIANTS))) values.append(minutes) minutes == 1 and current and alternatives.append(u"минуту") real_words = words # убираем из values и words конечные нули while values and not values[-1]: values.pop() words.pop() # убираем из values и words начальные нули while values and not values[0]: values.pop(0) words.pop(0) limit = min(accuracy, len(words)) real_words = words[:limit] real_values = values[:limit] # снова убираем конечные нули while real_values and not real_values[-1]: real_values.pop() real_words.pop() limit -= 1 real_str = u" ".join(real_words) # альтернативные варианты нужны только если в real_words одно значение # и, вдобавок, если используется текущее время alter_str = limit == 1 and current and alternatives and \ not (days and hours==1) and \ not (hours and minutes==1) and \ alternatives[0] _result_str = alter_str or real_str result_str = in_future and u"%s %s" % (PREFIX_IN, _result_str) \ or u"%s %s" % (_result_str, SUFFIX_AGO) # если же прошло менее минуты, то real_words -- пустой, и поэтому # нужно брать alternatives[0], а не result_str zero_str = minutes == 0 and not real_words and \ (in_future and u"менее чем через минуту" \ or u"менее минуты назад") # нужно использовать вчера/позавчера/завтра/послезавтра # если days 1..2 и в real_words одно значение day_alternatives = DAY_ALTERNATIVES.get(days, False) alternate_day = day_alternatives and current and limit == 1 and \ ((in_future and day_alternatives[1]) \ or day_alternatives[0]) final_str = not real_words and zero_str or alternate_day or result_str return final_str @takes(optional(unicode), optional((datetime.date, datetime.datetime)), optional(bool), optional(bool), optional(bool), format=optional(unicode), date=optional((datetime.date, datetime.datetime)), inflected=optional(bool), inflected_day=optional(bool), preposition=optional(bool)) @returns(unicode) def ru_strftime(format=u"%d.%m.%Y", date=None, inflected=False, inflected_day=False, preposition=False): """ Russian strftime without locale @param format: strftime format, default=u'%d.%m.%Y' @type format: C{unicode} @param date: date value, default=None translates to today @type date: C{datetime.date} or C{datetime.datetime} @param inflected: is month inflected, default False @type inflected: C{bool} @param inflected_day: is day inflected, default False @type inflected: C{bool} @param preposition: is preposition used, default False preposition=True automatically implies inflected_day=True @type preposition: C{bool} @return: strftime string @rtype: unicode @raise L{pytils.err.InputParameterError}: input parameters' check failed """ if date is None: date = datetime.datetime.today() weekday = date.weekday() prepos = preposition and DAY_NAMES[weekday][3] or u"" month_idx = inflected and 2 or 1 day_idx = (inflected_day or preposition) and 2 or 1 # for russian typography standard, # 1 April 2007, but 01.04.2007 if u'%b' in format or u'%B' in format: format = format.replace(u'%d', unicode(date.day)) format = format.replace(u'%a', prepos+DAY_NAMES[weekday][0]) format = format.replace(u'%A', prepos+DAY_NAMES[weekday][day_idx]) format = format.replace(u'%b', MONTH_NAMES[date.month-1][0]) format = format.replace(u'%B', MONTH_NAMES[date.month-1][month_idx]) # strftime must be str, so encode it to utf8: s_format = format.encode("utf-8") s_res = date.strftime(s_format) # and back to unicode u_res = s_res.decode("utf-8") return u_res pytils-0.2.3/pytils/err.py0000644000175000017500000000131211050342767013442 0ustar j2aj2a# -*- coding: utf-8 -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Custom exceptions for pytils """ # TypeError children from pytils.third.aspn426123 import InputParameterError pytils-0.2.3/pytils/numeral.py0000644000175000017500000003672611050342745014332 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.test_numeral -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Plural forms and in-word representation for numerals. """ from pytils import utils from pytils.utils import takes, returns, optional, list_of, tuple_of, \ nothing, one_of, check_positive, check_length FRACTIONS = ( (u"десятая", u"десятых", u"десятых"), (u"сотая", u"сотых", u"сотых"), (u"тысячная", u"тысячных", u"тысячных"), (u"десятитысячная", u"десятитысячных", u"десятитысячных"), (u"стотысячная", u"стотысячных", u"стотысячных"), (u"миллионная", u"милллионных", u"милллионных"), (u"десятимиллионная", u"десятимилллионных", u"десятимиллионных"), (u"стомиллионная", u"стомилллионных", u"стомиллионных"), (u"миллиардная", u"миллиардных", u"миллиардных"), ) #: Forms (1, 2, 5) for fractions ONES = { 0: (u"", u"", u""), 1: (u"один", u"одна", u"одно"), 2: (u"два", u"две", u"два"), 3: (u"три", u"три", u"три"), 4: (u"четыре", u"четыре", u"четыре"), 5: (u"пять", u"пять", u"пять"), 6: (u"шесть", u"шесть", u"шесть"), 7: (u"семь", u"семь", u"семь"), 8: (u"восемь", u"восемь", u"восемь"), 9: (u"девять", u"девять", u"девять"), } #: Forms (MALE, FEMALE, NEUTER) for ones TENS = { 0: u"", # 1 - особый случай 10: u"десять", 11: u"одиннадцать", 12: u"двенадцать", 13: u"тринадцать", 14: u"четырнадцать", 15: u"пятнадцать", 16: u"шестнадцать", 17: u"семнадцать", 18: u"восемнадцать", 19: u"девятнадцать", 2: u"двадцать", 3: u"тридцать", 4: u"сорок", 5: u"пятьдесят", 6: u"шестьдесят", 7: u"семьдесят", 8: u"восемьдесят", 9: u"девяносто", } #: Tens HUNDREDS = { 0: u"", 1: u"сто", 2: u"двести", 3: u"триста", 4: u"четыреста", 5: u"пятьсот", 6: u"шестьсот", 7: u"семьсот", 8: u"восемьсот", 9: u"девятьсот", } #: Hundreds MALE = 1 #: sex - male FEMALE = 2 #: sex - female NEUTER = 3 #: sex - neuter @takes((int, long, float), optional(int), signs=optional(int)) def _get_float_remainder(fvalue, signs=9): """ Get remainder of float, i.e. 2.05 -> '05' @param fvalue: input value @type fvalue: C{int}, C{long} or C{float} @param signs: maximum number of signs @type signs: C{int} or C{long} @return: remainder @rtype: C{str} @raise L{pytils.err.InputParameterError}: input parameters' check failed (fvalue neither C{int}, no C{float}) @raise ValueError: fvalue is negative @raise ValueError: signs overflow """ check_positive(fvalue) if isinstance(fvalue, (int, long)): return "0" signs = min(signs, len(FRACTIONS)) # нужно remainder в строке, потому что дробные X.0Y # будут "ломаться" до X.Y remainder = str(fvalue).split('.')[1] iremainder = int(remainder) orig_remainder = remainder factor = len(str(remainder)) - signs if factor > 0: # после запятой цифр больше чем signs, округляем iremainder = int(round(iremainder / (10.0**factor))) format = "%%0%dd" % min(len(remainder), signs) remainder = format % iremainder if len(remainder) > signs: # при округлении цифр вида 0.998 ругаться raise ValueError("Signs overflow: I can't round only fractional part \ of %s to fit %s in %d signs" % \ (str(fvalue), orig_remainder, signs)) return remainder @takes((int,long), (unicode, list_of(unicode), tuple_of(unicode))) def choose_plural(amount, variants): """ Choose proper case depending on amount @param amount: amount of objects @type amount: C{int} or C{long} @param variants: variants (forms) of object in such form: (1 object, 2 objects, 5 objects). @type variants: 3-element C{sequence} of C{unicode} or C{unicode} (three variants with delimeter ',') @return: proper variant @rtype: C{unicode} @raise L{pytils.err.InputParameterError}: input parameters' check failed (amount isn't C{int}, variants isn't C{sequence}) @raise ValueError: amount is negative @raise ValueError: variants' length lesser than 3 """ if isinstance(variants, unicode): variants = utils.split_values(variants) check_length(variants, 3) check_positive(amount) if amount % 10 == 1 and amount % 100 != 11: variant = 0 elif amount % 10 >= 2 and amount % 10 <= 4 and \ (amount % 100 < 10 or amount % 100 >= 20): variant = 1 else: variant = 2 return variants[variant] @takes((int,long), (unicode, list_of(unicode), tuple_of(unicode)), optional((nothing, unicode)), absence=optional((nothing, unicode))) def get_plural(amount, variants, absence=None): """ Get proper case with value @param amount: amount of objects @type amount: C{int} or C{long} @param variants: variants (forms) of object in such form: (1 object, 2 objects, 5 objects). @type variants: 3-element C{sequence} of C{unicode} or C{unicode} (three variants with delimeter ',') @param absence: if amount is zero will return it @type absence: C{unicode} @return: amount with proper variant @rtype: C{unicode} """ if amount or absence is None: return u"%d %s" % (amount, choose_plural(amount, variants)) else: return absence @takes((int,long), (unicode, list_of(unicode), tuple_of(unicode))) def _get_plural_legacy(amount, extra_variants): """ Get proper case with value (legacy variant, without absence) @param amount: amount of objects @type amount: C{int} or C{long} @param variants: variants (forms) of object in such form: (1 object, 2 objects, 5 objects, 0-object variant). 0-object variant is similar to C{absence} in C{get_plural} @type variants: 3-element C{sequence} of C{unicode} or C{unicode} (three variants with delimeter ',') @return: amount with proper variant @rtype: C{unicode} """ absence = None if isinstance(extra_variants, unicode): extra_variants = utils.split_values(extra_variants) if len(extra_variants) == 4: variants = extra_variants[:3] absence = extra_variants[3] else: variants = extra_variants return get_plural(amount, variants, absence) @takes((int, long, float), optional(bool), zero_for_kopeck=optional(bool)) def rubles(amount, zero_for_kopeck=False): """ Get string for money @param amount: amount of money @type amount: C{int}, C{long} or C{float} @param zero_for_kopeck: If false, then zero kopecks ignored @type zero_for_kopeck: C{bool} @return: in-words representation of money's amount @rtype: C{unicode} @raise L{pytils.err.InputParameterError}: input parameters' check failed (amount neither C{int}, no C{float}) @raise ValueError: amount is negative """ check_positive(amount) pts = [] amount = round(amount, 2) pts.append(sum_string(int(amount), 1, (u"рубль", u"рубля", u"рублей"))) remainder = _get_float_remainder(amount, 2) iremainder = int(remainder) if iremainder != 0 or zero_for_kopeck: # если 3.1, то это 10 копеек, а не одна if iremainder < 10 and len(remainder) == 1: iremainder *= 10 pts.append(sum_string(iremainder, 2, (u"копейка", u"копейки", u"копеек"))) return u" ".join(pts) @takes((int,long), optional(one_of(1,2,3)), gender=optional(one_of(1,2,3))) def in_words_int(amount, gender=MALE): """ Integer in words @param amount: numeral @type amount: C{int} or C{long} @param gender: gender (MALE, FEMALE or NEUTER) @type gender: C{int} @return: in-words reprsentation of numeral @rtype: C{unicode} @raise L{pytils.err.InputParameterError}: input parameters' check failed (when amount is not C{int}) @raise ValueError: amount is negative """ check_positive(amount) return sum_string(amount, gender) @takes(float, optional(one_of(1,2,3)), _gender=optional(one_of(1,2,3))) def in_words_float(amount, _gender=FEMALE): """ Float in words @param amount: float numeral @type amount: C{float} @return: in-words reprsentation of float numeral @rtype: C{unicode} @raise L{pytils.err.InputParameterError}: input parameters' check failed (when amount is not C{float}) @raise ValueError: when ammount is negative """ utils.check_positive(amount) pts = [] # преобразуем целую часть pts.append(sum_string(int(amount), 2, (u"целая", u"целых", u"целых"))) # теперь то, что после запятой remainder = _get_float_remainder(amount) signs = len(str(remainder)) - 1 pts.append(sum_string(int(remainder), 2, FRACTIONS[signs])) return u" ".join(pts) @takes((int,long,float), optional(one_of(None,1,2,3)), gender=optional(one_of(None,1,2,3))) def in_words(amount, gender=None): """ Numeral in words @param amount: numeral @type amount: C{int}, C{long} or C{float} @param gender: gender (MALE, FEMALE or NEUTER) @type gender: C{int} @return: in-words reprsentation of numeral @rtype: C{unicode} raise L{pytils.err.InputParameterError}: input parameters' check failed (when amount not C{int} or C{float}, gender is not C{int} (and not None), gender isn't in (MALE, FEMALE, NEUTER)) raise ValueError: when amount is negative """ check_positive(amount) if gender is None: args = (amount,) else: args = (amount, gender) # если целое if isinstance(amount, (int, long)): return in_words_int(*args) # если дробное elif isinstance(amount, float): return in_words_float(*args) # ни float, ни int else: # до сюда не должно дойти raise RuntimeError() @takes((int, long), one_of(1, 2, 3), optional((unicode, nothing, list_of(unicode), tuple_of(unicode))), items=optional((unicode, nothing, list_of(unicode), tuple_of(unicode)))) def sum_string(amount, gender, items=None): """ Get sum in words @param amount: amount of objects @type amount: C{int} or C{long} @param gender: gender of object (MALE, FEMALE or NEUTER) @type gender: C{int} @param items: variants of object in three forms: for one object, for two objects and for five objects @type items: 3-element C{sequence} of C{unicode} or just C{unicode} (three variants with delimeter ',') @return: in-words representation objects' amount @rtype: C{unicode} @raise L{pytils.err.InputParameterError}: input parameters' check failed @raise ValueError: items isn't 3-element C{sequence} or C{unicode} @raise ValueError: amount bigger than 10**11 @raise ValueError: amount is negative """ if isinstance(items, unicode): items = utils.split_values(items) if items is None: items = (u"", u"", u"") try: one_item, two_items, five_items = items except ValueError: raise ValueError("Items must be 3-element sequence") check_positive(amount) if amount == 0: return u"ноль %s" % five_items into = u'' tmp_val = amount # единицы into, tmp_val = _sum_string_fn(into, tmp_val, gender, items) # тысячи into, tmp_val = _sum_string_fn(into, tmp_val, FEMALE, (u"тысяча", u"тысячи", u"тысяч")) # миллионы into, tmp_val = _sum_string_fn(into, tmp_val, MALE, (u"миллион", u"миллиона", u"миллионов")) # миллиарды into, tmp_val = _sum_string_fn(into, tmp_val, MALE, (u"миллиард", u"миллиарда", u"миллиардов")) if tmp_val == 0: return into else: raise ValueError("Cannot operand with numbers bigger than 10**11") @takes(unicode, (int,long), one_of(1,2,3), optional((unicode, nothing, list_of(unicode), tuple_of(unicode))), items=optional((unicode, nothing, list_of(unicode), tuple_of(unicode)))) def _sum_string_fn(into, tmp_val, gender, items=None): """ Make in-words representation of single order @param into: in-words representation of lower orders @type into: C{unicode} @param tmp_val: temporary value without lower orders @type tmp_val: C{int} or C{long} @param gender: gender (MALE, FEMALE or NEUTER) @type gender: C{int} @param items: variants of objects @type items: 3-element C{sequence} of C{unicode} @return: new into and tmp_val @rtype: C{tuple} @raise L{pytils.err.InputParameterError}: input parameters' check failed @raise ValueError: tmp_val is negative """ if items is None: items = (u"", u"", u"") one_item, two_items, five_items = items check_positive(tmp_val) if tmp_val == 0: return into, tmp_val rest = rest1 = end_word = None words = [] rest = tmp_val % 1000 tmp_val = tmp_val / 1000 if rest == 0: # последние три знака нулевые if into == u"": into = u"%s " % five_items return into, tmp_val # начинаем подсчет с rest end_word = five_items # сотни words.append(HUNDREDS[rest / 100]) # десятки rest = rest % 100 rest1 = rest / 10 # особый случай -- tens=1 tens = rest1 == 1 and TENS[rest] or TENS[rest1] words.append(tens) # единицы if rest1 < 1 or rest1 > 1: amount = rest % 10 end_word = choose_plural(amount, items) words.append(ONES[amount][gender-1]) words.append(end_word) # добавляем то, что уже было words.append(into) # убираем пустые подстроки words = filter(lambda x: len(x) > 0, words) # склеиваем и отдаем return u" ".join(words).strip(), tmp_val pytils-0.2.3/pytils/translit.py0000644000175000017500000001552211050342725014514 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.test_translit -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Simple transliteration """ import re from pytils.utils import takes, returns TRANSTABLE = ( (u"'", u"'"), (u'"', u'"'), (u"‘", u"'"), (u"’", u"'"), (u"«", u'"'), (u"»", u'"'), (u"–", u"-"), (u"…", u"..."), (u"№", u"#"), ## верхний регистр # трехбуквенные замены (u"Щ", u"Sch"), # при замене русский->английский будет первая замена, # т.е. Sch # а вот если английский->русский, то вариант SCH и Sch -- # оба пройдут (u"Щ", u"SCH"), # двухбуквенные замены (u"Ё", u"Yo"), (u"Ё", u"YO"), (u"Ж", u"Zh"), (u"Ж", u"ZH"), (u"Ц", u"Ts"), (u"Ц", u"TS"), (u"Ч", u"Ch"), (u"Ч", u"CH"), (u"Ш", u"Sh"), (u"Ш", u"SH"), (u"Ы", u"Yi"), (u"Ы", u"YI"), (u"Ю", u"Yu"), (u"Ю", u"YU"), (u"Я", u"Ya"), (u"Я", u"YA"), # однобуквенные замены (u"А", u"A"), (u"Б", u"B"), (u"В", u"V"), (u"Г", u"G"), (u"Д", u"D"), (u"Е", u"E"), (u"З", u"Z"), (u"И", u"I"), (u"Й", u"J"), (u"К", u"K"), (u"Л", u"L"), (u"М", u"M"), (u"Н", u"N"), (u"О", u"O"), (u"П", u"P"), (u"Р", u"R"), (u"С", u"S"), (u"Т", u"T"), (u"У", u"U"), (u"Ф", u"F"), (u"Х", u"H"), (u"Э", u"E"), (u"Ъ", u"`"), (u"Ь", u"'"), ## нижний регистр # трехбуквенные замены (u"щ", u"sch"), # двухбуквенные замены (u"ё", u"yo"), (u"ж", u"zh"), (u"ц", u"ts"), (u"ч", u"ch"), (u"ш", u"sh"), (u"ы", u"yi"), (u"ю", u"yu"), (u"я", u"ya"), # однобуквенные замены (u"а", u"a"), (u"б", u"b"), (u"в", u"v"), (u"г", u"g"), (u"д", u"d"), (u"е", u"e"), (u"з", u"z"), (u"и", u"i"), (u"й", u"j"), (u"к", u"k"), (u"л", u"l"), (u"м", u"m"), (u"н", u"n"), (u"о", u"o"), (u"п", u"p"), (u"р", u"r"), (u"с", u"s"), (u"т", u"t"), (u"у", u"u"), (u"ф", u"f"), (u"х", u"h"), (u"э", u"e"), (u"ъ", u"`"), (u"ь", u"'"), # для полноты английского алфавит (в slugify) # дополняем английскими буквами, которых # не в парах (u"c", u"c"), (u"q", u"q"), (u"y", u"y"), (u"x", u"x"), (u"w", u"w"), (u"1", u"1"), (u"2", u"2"), (u"3", u"3"), (u"4", u"4"), (u"5", u"5"), (u"6", u"6"), (u"7", u"7"), (u"8", u"8"), (u"9", u"9"), (u"0", u"0"), ) #: Translation table RU_ALPHABET = [x[0] for x in TRANSTABLE] #: Russian alphabet that we can translate EN_ALPHABET = [x[1] for x in TRANSTABLE] #: English alphabet that we can detransliterate ALPHABET = RU_ALPHABET + EN_ALPHABET #: Alphabet that we can (de)transliterate @takes(unicode) @returns(str) def translify(in_string): """ Translify russian text @param in_string: input string @type in_string: C{unicode} @return: transliterated string @rtype: C{str} @raise L{pytils.err.InputParameterError}: input parameters' check failed (in_string is not C{unicode}) @raise ValueError: when string doesn't transliterate completely """ translit = in_string for symb_in, symb_out in TRANSTABLE: translit = translit.replace(symb_in, symb_out) try: translit = str(translit) except UnicodeEncodeError: raise ValueError("Unicode string doesn't transliterate completely, " + \ "is it russian?") return translit @takes(basestring) @returns(unicode) def detranslify(in_string): """ Detranslify @param in_string: input string @type in_string: C{basestring} @return: detransliterated string @rtype: C{unicode} @raise L{pytils.err.InputParameterError}: input parameters' check failed (when in_string not C{basestring}) @raise ValueError: if in_string is C{str}, but it isn't ascii """ # в unicode try: russian = unicode(in_string) except UnicodeDecodeError: raise ValueError("We expects if in_string is 8-bit string," + \ "then it consists only ASCII chars, but now it doesn't. " + \ "Use unicode in this case.") for symb_out, symb_in in TRANSTABLE: russian = russian.replace(symb_in, symb_out) return russian @takes(basestring) @returns(str) def slugify(in_string): """ Prepare string for slug (i.e. URL or file/dir name) @param in_string: input string @type in_string: C{basestring} @return: slug-string @rtype: C{str} @raise L{pytils.err.InputParameterError}: input parameters' check failed (when in_string isn't C{unicode} or C{str}) @raise ValueError: if in_string is C{str}, but it isn't ascii """ try: u_in_string = unicode(in_string).lower() except UnicodeDecodeError: raise ValueError("We expects when in_string is str type," + \ "it is an ascii, but now it isn't. Use unicode " + \ "in this case.") # convert & to "and" u_in_string = re.sub('\&\;|\&', ' and ', u_in_string) # replace spaces by hyphen u_in_string = re.sub('[-\s]+', '-', u_in_string) # remove symbols that not in alphabet u_in_string = u''.join([symb for symb in u_in_string if symb in ALPHABET]) # translify it out_string = translify(u_in_string) # remove non-alpha return re.sub('[^\w\s-]', '', out_string).strip().lower() def dirify(in_string): """ Alias for L{slugify} """ slugify(in_string) pytils-0.2.3/pytils/utils.py0000644000175000017500000000536411050342645014020 0ustar j2aj2a# -*- coding: utf-8 -*- # -*- test-case-name: pytils.test.test_utils -*- # pytils - russian-specific string utils # Copyright (C) 2006-2008 Yury Yurevich # # http://www.pyobject.ru/projects/pytils/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. """ Misc utils for internal use """ import sys import warnings from pytils.third.aspn426123 import takes, returns, optional, list_of, tuple_of, \ nothing, one_of @takes((basestring, tuple, list), (int, long)) def check_length(value, length): """ Checks length of value @param value: value to check @type value: C{str} @param length: length checking for @type length: C{int} @return: None when check successful @raise ValueError: check failed """ _length = len(value) if _length != length: raise ValueError("length must be %d, not %d" % \ (length, _length)) @takes((int,long,float), optional(bool), strict=optional(bool)) def check_positive(value, strict=False): """ Checks if variable is positive @param value: value to check @type value: C{int}, C{long} or C{float} @return: None when check successful @raise ValueError: check failed """ if not strict and value < 0: raise ValueError("Value must be positive or zero, not %s" % str(value)) if strict and value <= 0: raise ValueError("Value must be positive, not %s" % str(value)) @takes(unicode, optional(unicode), sep=optional(unicode)) def split_values(ustring, sep=u','): """ Splits unicode string with separator C{sep}, but skips escaped separator. @param ustring: string to split @type ustring: C{unicode} @param sep: separator (default to u',') @type sep: C{unicode} @return: tuple of splitted elements """ assert isinstance(ustring, unicode), "uvalue must be unicode, not %s" % type(ustring) # в юникоде есть специальный символ, который в нормальном тексте не должен встречаться # это маркер 0xffff # им и будем помечать места, где есть экранированная запятая ustring_marked = ustring.replace(u'\,', u'\uffff') items = tuple([i.strip().replace(u'\uffff', u',') for i in ustring_marked.split(sep)]) return items pytils-0.2.3/pytils.egg-info/0000755000175000017500000000000011051306250013761 5ustar j2aj2apytils-0.2.3/pytils.egg-info/PKG-INFO0000644000175000017500000000150611051306250015060 0ustar j2aj2aMetadata-Version: 1.0 Name: pytils Version: 0.2.3 Summary: Russian-specific string utils Home-page: http://www.pyobject.ru/projects/pytils/ Author: Yury Yurevich Author-email: the.pythy@gmail.com License: GPL Description: Simple tools for processing strings in russian (choose proper form for plurals, in-words representation of numerals, dates in russian without locales, transliteration, etc) Platform: All Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Linguistic Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Natural Language :: Russian Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers pytils-0.2.3/pytils.egg-info/SOURCES.txt0000644000175000017500000000524111051306250015647 0ustar j2aj2aAUTHORS Changelog LICENSE MANIFEST.in README TODO setup.cfg setup.py doc/INSTALL doc/INSTALL.rus.txt doc/README.rus.txt doc/WEBFRAMEWORKS.rus.txt doc/examples/dt.distance_of_time_in_words.py doc/examples/dt.ru_strftime.py doc/examples/numeral.choose_plural.py doc/examples/numeral.in_words.py doc/examples/numeral.rubles.py doc/examples/numeral.sum_string.py doc/examples/translit.py doc/examples-django/README doc/examples-django/pytilsex/__init__.py doc/examples-django/pytilsex/__init__.pyc doc/examples-django/pytilsex/manage.py doc/examples-django/pytilsex/settings.py doc/examples-django/pytilsex/settings.pyc doc/examples-django/pytilsex/urls.py doc/examples-django/pytilsex/urls.pyc doc/examples-django/pytilsex/static/css/style.css doc/examples-django/pytilsex/static/images/header_inner.png doc/examples-django/pytilsex/templates/base.html doc/examples-django/pytilsex/templates/dt.html doc/examples-django/pytilsex/templates/numeral.html doc/examples-django/pytilsex/templates/translit.html doc/examples-turbogears/README doc/examples-turbogears/dev.cfg doc/examples-turbogears/setup.py doc/examples-turbogears/start-pytilsex.py doc/examples-turbogears/pytilsex/__init__.py doc/examples-turbogears/pytilsex/controllers.py doc/examples-turbogears/pytilsex/model.py doc/examples-turbogears/pytilsex/config/__init__.py doc/examples-turbogears/pytilsex/config/app.cfg doc/examples-turbogears/pytilsex/config/log.cfg doc/examples-turbogears/pytilsex/static/css/style.css doc/examples-turbogears/pytilsex/static/images/favicon.ico doc/examples-turbogears/pytilsex/static/images/header_inner.png doc/examples-turbogears/pytilsex/templates/__init__.py doc/examples-turbogears/pytilsex/templates/dt.kid doc/examples-turbogears/pytilsex/templates/master.kid doc/examples-turbogears/pytilsex/templates/numeral.kid doc/examples-turbogears/pytilsex/templates/root.kid doc/examples-turbogears/pytilsex/templates/translit.kid pytils/__init__.py pytils/dt.py pytils/err.py pytils/numeral.py pytils/translit.py pytils/utils.py pytils.egg-info/PKG-INFO pytils.egg-info/SOURCES.txt pytils.egg-info/dependency_links.txt pytils.egg-info/top_level.txt pytils.egg-info/zip-safe pytils/templatetags/__init__.py pytils/templatetags/pytils_dt.py pytils/templatetags/pytils_numeral.py pytils/templatetags/pytils_translit.py pytils/test/__init__.py pytils/test/test_dt.py pytils/test/test_numeral.py pytils/test/test_translit.py pytils/test/test_utils.py pytils/test/templatetags/__init__.py pytils/test/templatetags/helpers.py pytils/test/templatetags/test_common.py pytils/test/templatetags/test_dt.py pytils/test/templatetags/test_numeral.py pytils/test/templatetags/test_translit.py pytils/third/__init__.py pytils/third/aspn426123.pypytils-0.2.3/pytils.egg-info/dependency_links.txt0000644000175000017500000000000111051306250020027 0ustar j2aj2a pytils-0.2.3/pytils.egg-info/top_level.txt0000644000175000017500000000000711051306250016510 0ustar j2aj2apytils pytils-0.2.3/pytils.egg-info/zip-safe0000644000175000017500000000000111051304421015407 0ustar j2aj2a pytils-0.2.3/AUTHORS0000644000175000017500000000004311047726655012034 0ustar j2aj2aYury Yurevich pytils-0.2.3/Changelog0000644000175000017500000000242711051304205012560 0ustar j2aj2apytils (0.2.3) * Add support for Django unicode branch (#27) * Make pytils.test to be optional package, not required (#26) * Make checks of input parameters by decorators (#18) * Python-2.3 is not supported now (decorators are used) * Add custom exception pytils.err.InputParameterError, inherited from TypeError -- Yury Yurevich Fri, 15 Aug 2008 20:45:12 +0700 pytils (0.2.2) * fix zeros in dates (#24) * add 'preposition' option to dt.ru_strftime (#23) * fix bugs (#20, #22, #25) * add get_plural to pytils.numeral (#19) * add escaping in variants (#21) -- Yury Yurevich Thu, 12 Jul 2007 17:31:12 +0700 pytils (0.2.1) * fix bugs (#10, #14, #15) * improve gender manipulation in numeral (issue#13) * add GPL header * add unit-tests for templatetags -- Yury Yurevich Tue, 27 Feb 2007 21:08:31 +0600 pytils (0.2.0) * integration with Django (templatetags) * examples for Django and TurboGears * remove asserts * add datatime.datetime type for distance_of_time_in_words * improve docs * make eggs -- Yury Yurevich Sun, 29 Oct 2006 23:30:17 +0600 pytils (0.1.0) * initial release -- Yury Yurevich Sat, 2 Sep 2006 22:52:37 +0600 pytils-0.2.3/LICENSE0000644000175000017500000003542211047726655012002 0ustar j2aj2a GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS pytils-0.2.3/MANIFEST.in0000644000175000017500000000017111047726655012524 0ustar j2aj2arecursive-include doc * include AUTHORS include LICENSE include TODO include README include setup.cfg include Changelog pytils-0.2.3/README0000644000175000017500000000063111047741476011645 0ustar j2aj2aPytils is a Russian-specific string utils (transliteration, numeral is words, russian dates, etc) See additional docs in doc subdir. ----- Pytils это инструменты для работы с русскими строками (транслитерация, числительные слоами, русские даты и т.д.) Документацию смотрите в каталоге doc pytils-0.2.3/TODO0000644000175000017500000000011211047741265011443 0ustar j2aj2asee also http://www.bitbucket.org/j2a/pytils/issues/ * add Typografica pytils-0.2.3/setup.py0000755000175000017500000000322411050342527012467 0ustar j2aj2a#!/usr/bin/env python # -*- coding: utf-8 -*- use_setuptools = True if use_setuptools: try: from setuptools import setup except ImportError: print "Cannot load setuptool, revert to distutils" use_setuptools = False from distutils.core import setup else: from distutils.core import setup version = "0.2.3" setup_data = { 'name': 'pytils', 'version': version, 'author': 'Yury Yurevich', 'author_email': 'the.pythy@gmail.com', 'url': 'http://www.pyobject.ru/projects/pytils/', 'description': 'Russian-specific string utils', 'long_description': """Simple tools for processing strings in russian (choose proper form for plurals, in-words representation of numerals, dates in russian without locales, transliteration, etc)""", 'packages': ['pytils', 'pytils.templatetags', 'pytils.test', 'pytils.test.templatetags', 'pytils.third'], 'license': "GPL", 'platforms': "All", 'classifiers': [ 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Text Processing :: Linguistic', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Natural Language :: Russian', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', ], } setuptools_extensions = { 'zip_safe': True, 'test_suite': 'pytils.test.get_suite', 'include_package_data': True, } if use_setuptools: setup_data.update(setuptools_extensions) args = () kwargs = setup_data setup(*args, **kwargs) pytils-0.2.3/PKG-INFO0000644000175000017500000000150611051306250012042 0ustar j2aj2aMetadata-Version: 1.0 Name: pytils Version: 0.2.3 Summary: Russian-specific string utils Home-page: http://www.pyobject.ru/projects/pytils/ Author: Yury Yurevich Author-email: the.pythy@gmail.com License: GPL Description: Simple tools for processing strings in russian (choose proper form for plurals, in-words representation of numerals, dates in russian without locales, transliteration, etc) Platform: All Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Linguistic Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Natural Language :: Russian Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers pytils-0.2.3/setup.cfg0000644000175000017500000000007311051306250012564 0ustar j2aj2a[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0