python-debianbts-trusty/0000775000000000000000000000000012607726630012601 5ustar python-debianbts-trusty/setup.py0000775000000000000000000000160112576513637014322 0ustar from distutils.core import setup import sys sys.path.insert(0, 'src') import debianbts setup( name='python-debianbts', version=debianbts.__version__, description="Python interface to Debian's Bug Tracking System", keywords='debian, soap, bts', author='Bastian Venthur', author_email='venthur@debian.org', url='https://github.com/venthur/python-debianbts', license='GPL2', package_dir = {'': 'src'}, py_modules = ['debianbts'], classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Communications", "Topic :: Software Development :: Bug Tracking", ], ) python-debianbts-trusty/dist/0000775000000000000000000000000012607136506013541 5ustar python-debianbts-trusty/dist/python-debianbts-2.4.tar.gz0000664000000000000000000001765712606450141020454 0ustar aPVdist/python-debianbts-2.4.tar=v۶?H,_Zg4qs]_!5EeGsS @Jv]H `0YvĻ~nu>mxaW?:ݣasyڏ?O{tv.[U;;y&Uݳ >b--ݿTIcVvyeÀ.㱟zXg<β:`ϧ#v0˙]2MfDF! n5H{jZ?‡Ǟ"OP}c?zLUJQM8ీ־xӭb:w?׾糇$ DzM&z0IE CLAxT=qiƷ ("Ļ3v:=To?oyS?b~rhƥQďRHi|*Q s ?pm/3./hlßwZr}{l?Ha{v]6HP=6͆OK rP@@,` +%e5^$Yn3hnpnC&rqasr΄Zfd'A(4O3ŒqOSXDS݂}ӱ`ɐ^>_-,}D^#:v}81ĽXW75Yϐ%d4#lUt0[߂?!!"l*p5ή{ኝ~8}ǯ JLxq6W_|5N9gϮ_]^޳SvqŇ7Ň._HKvH܁ #!#SeQn{l q2[gQ`~!0##u7`w Z82p@:QM<Vx{Now؇VVɘypMSy8I3CA͠܋(h2/I&lk@9Yi 3Y8 `2[~ɪߣd4B)[)[|3y`ەFvz2(U-iiKo߼8y&ՐB'ֈgoy1lUw6w`{6-ԉa?GN KKN!-P)xWI6+[as- 1drR0/ZP9G(b-ln[(|RR7`S?r&6Qv9&{:<=Te/XMI6p7h4!;eQ5=s},\WQ睅n1_ H p_lz1:|2u)y @Y_;W锻 1yyv:ktЬ|i nXS\Ժ3Qi^N-# 4`<tU}nZ[;V{sxw6h"imNlPy-u6msST@e4^VāWZ-9L*YE@>wlELDMOEt܇N1qLy`pNNX{*Tn 5OfF њ>LBݴ!(?@sT_0whʙKi2g~0^Z̀D2y)ߢ(!QJbn^ 1Rgz{:-Pw?=p9i1{rAwOH{+ )W$0kR'p{5HR^ŧA+2_0vVqx9gUu?-WD ױB>#ae$hl@TF58+[~ vT Zyun ?ԌzBƃhpfʖTD$sMr"(6)bdr!z mz#?{w h_Z < \[AAg,uL"ہ2\73-P>.,AV(4‰si.@Qrc=h(',JYZ-٢ 2v@R ,.3Kq[$\NpNvmJtʵDcfBAKdzwJC0g8y'I<9viȤ:V6kv'MvDdm{-޹"2!L{uw T LB6% r}h괆5Jw&m]^iT,ݧ6lO-+8 EYݱ;&S79щ?q[o=}*Ȭ0`:)+Cˣ( )i%8Q(( [D =%mOEN;;ⱘ}?sFTՋ˳{ž%.!Y~`5aBREWc0*ߺY$ Úe ~K&fOtnc'ו؄-u9*YƻǴqNvCs pHW'9(/(M/1PB3 )4 CvKR'_apQw-GhTY^)iՠ'Kan,n&k0{ a~bdc=#?E ^aj"D<$E7I_jR<)]oaI h3ώy!у?cA#GGbVadB:8c [K4 i Ř< yoБ?ybĿ<)fD3kTnFVKImTF%ǿIJ Ae{nWt̸PTqxn 5+&c"RջzJʀ%i<;(jHTV-]Q%)(d:[N#J^fW#O-6QGC9$R^-qIS.Tnez)n#_]m4j=[:>6HtֻZ#j TSg: ԯЗc2B (' $45) (U5o*ՊiW&Q F0d<@*Y>1ٍEVLbki@LP-SR9;$2TAZߘדVPE\+ "o'OD!z1*{%M_G}ppfg+}95_?܂}$wF9*;҆,!&JJtEs1WT;MahTO&*fWy/>a0BbxUJUV2uҼb4_g5& 斊B6V R j2,,¹PYj)jLThbUwHiƼ$C^A\@*%Er2ZiT0:*d?x>L|Jc ؈Q:.QJѹK6HZEY ӣKVHgKMFs`ˍ&8Y hu 07Ytb) kO,8<#rgxzYthADnI{<>1d7{֋un#*= cb\܍N`oۦ$B16:Cq䇛F Z:_Y~"u .,y`B DLD蠂Q0r4cȫ `ʡ ܹ|ZuvL_@~pqhҍB6A7J~%<JX2yUJgxz PU<uD~U`.lbIp$Q,4ӁU{z.„q'I%;˯s{<ؔ@rLm2uAL=3kbjT9 C#ḛw[5bM5 *鷖:4*{`^a)F9Z]>FAA:0@sw(9ayJ4*x/AoO4輩^Nʔ|/XjQ,]SBXc/QB*\q(Jpjd*Do* 7@ESL_*DVYcX$P-o0TgH) J*1. Ztl`=;zߔ96dϨ lOegrM?L~aɮD Z[zM_ړa"\*4*N /Ki@#2`  ^Т|j6{\V) ^:vNeڍʨϗt dZ>}304Ȇ 5-Ԅ&arގK6!e+QU!̂R!ٷkP+hMCH(Ib~RXd"LX(JGgT\>F#*l]&GN!f%W4CJyFեfJ} O~4a &;?706cW+Skු&j$[t,mܘɐL0ec-+xY* lU%`W3Pesj"rZ5Y9 v,y)p1";r sQkmIPO,vN7SDU2tCT [51koH x8)3PNi: 6"XWRӚ#An";NMOc#4q-?jЁa|C3iNtv < 8eް`*0_E+ [xuT̑YSZ7+W/:M`KL&  @/\gT*mK(Ĕ}yMŋv"wkY7wW$+AXZ wY(<Λi豬pH$F Kք-`̀}pPE(I_Sr% 3I~kAEg4PIt0UbS8 tB n-D ˸ F{v;ynQRU?sK/&91\muX`źMxwGfO CKz*ڣ<*BF%}F LePVؐV H\M񈴵FM ^!#oj?-A2@p\pϡ~[Nq[}c\.[.p-jh]_[HBYHMy塖5~ֽ/ ?02]?jݡgl}>gl}>gl}>glw/#Ȍpython-debianbts-trusty/dist/python-debianbts-2.2.tar.gz0000664000000000000000000001703012576731643020452 0ustar Udist/python-debianbts-2.2.tar=w6YNT*˒qno;yJ$)R%(;73ڴN|mB3 tx?ЏﶺOӆ=>=9=>iwNΎ Ld~ؓgYn]V_\rtNOE=iujXY6j9 Iz6Ǯqӡ?,Kkr6b7?Oj?$?G8˦wp0 apA29P} >=|b>ac_dVjջn54fԑOe|1Y)V"?&|!aȁGtfz4 fl /{?iS=#۟ 0qv> BvXSp}Χ4yO{ԏլڅqiԟLix4f!J5 7(ϯdJT\'Oya-\'k+ۯã?[wNv{ޭʳ3jɯXykڧ8`"2[6ZPU2h1UuoYyf=|3E0 cd'A(42ŒqpL  |bl vO'%Cbe"ưT Vx+`@b]݄dI 8\?CSZ SUsl*B<^!1"l&p5/n~醝_~< lJLdq67_5_^ go/n.\_>svuէwէWߴH+vH܁ #!SeQx E,J`ߣX8dq5cfdfA G@~|_g(ކC@6J^&" Y;t}ުj40βY=:I3`C(q{ME= ۄ (b2k5d& '\&CWXA2ѿfj20k RAk Q2ӌ]̛4KۮD0ӹ@1h"I&dD˺V|^9KW#b>dgѰ!)v[`ue1P'ޏLw-n%B${R4Go&l^^x$ )鉁PDgIY >QKq6ܶr(PZ:·PHhng~Mjm4ucrjbM}tl?@R/`5ڬ'|ݠdEv>oTA DV5{ r; o H Plz9:|2mf[)9<L}@ج_;7錻 1[ĺ[ 5:R3ކf`wItREy&(Ѐ[ginw-m|E[&Hۂ$2%GP᷵ٶ]R*V)FxUZ^iEXc7#dU 28n}يrN &qe'6(٤Su #X>HͼVFOj'Dh3sJG}a )Ц.e_9@16xiG3[9D dWJZ`D2߀nDҰ]GsT;Bv|!1=V Q껟&s$bD"UYQ;.-^WZAI > Z|#=<<:ZVT,cE 71BHzm$ilg@TN58k[s?ͻu;-Mȃ:wg ?7#І e[%,`3iq=WQAQev2]{.,!C9 0wc<}? yl޹N9޸D6Ewe*͡!ZfSMU}\*XoHPV(,©׳?@Qrc3h(iJ0WkQ!!Qz˴Lf$ %GJmݤCjat$BUt1E(rnKKqM3Uy7߇K:1vb9g^*eLˆ'}L1ԺbWXX߶moQ&f5tdžv@`yNYPHtA^?)t SY%OʃD -E; Ph7BDAq 4=n\hIt3[&dlaCu/|rx)Qq o^]_ ,v BA#**,ܞܱ)N" ֔b{21#~f"l2_WFbZs߃Ox\ng1xCs+pH7g=((M x(H 䅴(Nt^X,+|6փX m`-KWol4qI`6-. z٦%)I*tǯǔWIRxKb@^";煘7G\XRP'$p/Ym'iW,u-0-["(9M<7~Gg]`r۾ې8+)==xJ%(L@;~ Mf F(|aTÎh^)[ eyIyQgݶ7O>I\9iz:iطaEnjnj/ 98[= fC{ԬLa|K7^(5K>>yvr|007 Jc;<9li?1qI($1K(0b48+QGgb ^çt~zKpA3\(h2A@J8֤E23Jꛊ(yA_'WU<0,$:,Gi٭}[Yl}%ŕ%wWlt\i 6J UROڿy7"?L?9tOtns|[5ȣ?9Tyӹ\W9?xwhw p45Q|i8\oHCP8"#~){OOjyIyjbg Caî?_IXyU<>M,@_P([?<$JDWY<$w|sJ}ml*]H60S{)0&߄{< |:'ڳ` ^,KSֹ~z{}ѹ `yM,| sӥ@[ҚiY j2VW5(c%j>Cڻ`u`0 q!= 5Ø  :N d[Aa2Y>LV7kPm֬m3vn3jp.g^΀&9.~vy-D/_U'0YJ-dܪ#Q[tuA㈌ o r]U$!IXD}ȕJYҴ̡BՖAoT$AexQS`'Z:iJC_;n=v!4# p'IAjw%~j ډz[QBQPyN(?Wu L D(D@՚]+tH{.E 3|(LP$cñH׋5t+=]]&,ކ0`0!aq9)䢊1(xdH# _F(*,557g엺;][*2в %2l\44hteLIC \`>U:;{`b _e\ptPoYx ;fJ~<XQ2y>ݪևfg3T=q1g=Tz$A]>/̥p2}^C"@1 N$ښ" \cU?|P&˿1&ɖ 8HicSnc(rpe J{ rOn[AtNXFUI04r[&  ުk6R'c&bs  \t ׁZhAɹ+Qq 0rixݖ}OA@]{;i*S)|,-4d+EpYF'i ޼DQ Z V@Wo5%csH^QQP.ʭ+bv3v TRA h%5Rq@~QAM{*3LXdFx+]Saf."fL8Ğ3)UU/$Q':pBT~gTi\hpAc?Ҋ*Mwy ?Z:Zuv![Wep)#k D% \o 1Bi[q WPϲgH*L]-& )0eAI%feSA o̪!{F!w*? [M0ś,%!B4pjm5}\Oz31 vnUhީuE^b1X3 xD&xpE:X- V jBQNYlUw}8]SGw#4:=@_<>>ixAKG$,,Es1IzK3|VIdFSe~HwĹFЙ=2Q@^|۾[dƆjBKڰҵ\MHJTU|/|#QZl" tKmd%PH+mr%MFx*]UjNO}5LPmJ4>r {:3.ˆmN_k#ښY6uLPw%~8;.{qcW+S[H9K)B鉃pPEƝ*V„ixIǴemZVbJa󵪒Pekjvtj@=r=xrhF81,j 9~Xqn"e^؀P-mzCjIrN74ۈf`j r9rڀh,o$&{^H@~O=?3Uϲ^3'Zuaʇ߿}|#g^CDެ^^2i<>Z^{D)4./ .gq4(ܪ2(zP>0_YpGl>yˆXfqtUo?oyc/`ASn'7⠽ qh_E3^?y=d(ɃsʣEŌ^{[ܴ9[&a簫v{ ;n]?E|0ޫ%a4gdT V7`d4hmjj=e}S\O+{Pl&1Lklxb>Ey™0/)SxhИ%`x*X4/+VEZ7 t=z^ZfLh^ZIP,GKEdԁ#,yXZ~gsp~ݻW쇳^50C),?> >^,t@Agϯ~٫///٫w8{uw/]r$CG`}Sezu}4_<b1 "kY%  FVװ\ `e?(Y$挵:vk}jgJ2)sK9c0J#;?[.tS~4 4%7_ ߴwZ`hW*H!̒Mjs“!lY\^eOQ8']˫: v*q| ^K݈ɇYM`\]6m X1^yZ@ꄰ'YS%m N˧d'Z0z5dTtid#ą)҄)V󅇤: _4ڨA=٪DX = ޗk⟱hn֗d1Q٭ ]ݣ5aOErժyg[}]if v<ebܱ&[ U a~Rty d35-%`ԔE _֘IVj񅰆5ޤV& 0>rtu}nF{ xXAnЀ6u&\AL\*|^Km[jߔjTl+$i2툼WV-:>2Eb f*U%h0Kyv(O0_P*).1Z4I~ ' 䄵̠֩T]QQH_=MeYh.1N.yC4@UʋA{ INO"?^Z3\}F}%Юw߇@NμEy77i#82Zr}Ŏˍp!φ^ qzv=ngQulVqG.9DI:]2eJḳIƗ _ya+#^5~'{!l03/06_tֿD )l|%RXƒ<#-Ot%"/Mh_ j'+a7=rm` ˸ruڳíSLȉ .E?x=gQNYb G»|P 9Y?O0i ag:cF¤g"+99`&J7va-,PfZqNX,TKzM$4zSZgk6 Ky!~#GEJӐrF.2MZB=3 H*4tDasHDBZu@1#m9NY9%y;~v^E^n6$9EJJ{GO (DLă ~|3^YEZ^DSڟ &17X=J6>)e-k?(^W a:uVwܶ+txPS8bRu]Dm7q_E{i-+3QRSR?G|:ANaQA,RK\(Y: u,,U} Tޔ"'$idWad/Fũ1̡Z`AWv c/VwX>˕֞aa-th` a*ʯ޽xϠ'.n+X~?z2~xΓ}C\:`ΥNl9ؽ'LJ50'o#ǽq +U(?\Uh0PG'/Imx̟zbGPe1(|p'fEzOv;O/RD.[-S EBlת7i)lto@:PZ`A tWP?) jdU]/ Fpc U3Џ; 5r՘1y` tW*h]\u]B׭J\x mwsw>V?ǭv;Aghwx^-'x kW }G,D -L:+DnCiIulUN]?Q6h2B*R@ʴ̕Y@y)U$eJȕT(%+ۣ% he&ȒBL(i.=c\a]VFY6ʫ ˒lsV 迆No֜鳟qp2LM5^_vÏl`tQ\:햑I^+jXR u-aN9Ms ]9LS]ҰZQU[CsyNj YED  }0_Y#Mۗ5y$DΏPE&1|0j>d;j; {Q,‡/nq 1HV!1AJLKǙA;uCGhaB℮ { O}1 #Y/N=/&LV]t)Bi[H/ݔCC^җfz(1}BT]2V;TL-%;Hï^P./@H`RM<^L> >5F̻=܋ZM<6}T;J7*@C$n~H,7.J˾ԛL='yg` ڷ[?Id8@'x D;+֭y` [7q00\AP~)1Ls࣏ >,SOөL'Qڳ8Oƃe9_\ߒqW%*-W 6Ȱ7TMq>ҵҚ5cb(dHr E‘zztx0a88mo 1"G %P-4FJ{LyBS-7ZW(+Td}M3;@Z_kY 1݈!QTO'R6T2G"-ԙ5,:HPڞPpwTsVZ\CLwJn_DQ,`Ei.' WGr q-^ &dhVJO2ƶ{&[5C}3OaX Y`T;Px,-Dj4ɩ"7x2ˊ\kDR̎DƉTP:"&}(g Nݹ/^TJ4{D*KyPK[HSj29TR9ͅUtu/XeQ^5lȗRPh^*9Yf WWW>:[svP(!-UHh{LHHtEIƐV.lc5y(AzJJfj0L4椉GY#vKRD&̌Ps K: j '(Z.$^Rbds%ёcK#F|ZPǩ8jv/ r%_Uhgu X 瓯@"3"+ؗUC%:(O\ت"\ky|YE'OOڪɪ`Vg ^uأظ}SY4.M/qLB>G6*Y V'F cRY6F*NA"]ƭg,?Ze) H"e.ZtT+#Rg]rzz[j/N}BWnw ]`D[~SϼcoH dS9mb>ej{ʆOik"\$1<]i5f2NdZs6 --s6 [XȔ/¡weLBŅ"9aZt5 U`5a OFþԕyw`kHs2n|ݯSM- V5;+RXΈWU$#Ҙʱji 䛿ꥥ-KހO傾"5' ^!RG@CCB!t{_=kZn @*E.hF>|"Q?TfI=iRy2DvmD^;z;YcV8ktBg)M@B TlR"Z&됌QȼxeDb!SV)0ea*joH hiUN Ldl0}VIᰇ#PiN4|K?%u367}Fw!JWjZju펩w瓂4:8#D4{ xVT{dVHȠn”!mfѻGw<2x!jA}YW/?SAtAmkt?Tf)  ^u]*Vo d´bvob8Z [g%1xGY2# e%H=Vr݄B̹88l o rF?diNZL",AD[V^uqL]Ć$da5yZNè9zO?*TD Mzy?f% 3IkzKsF%FS%}E$c6bx˧hB% ^HYڎ>;!;hkh(ꕪI_nҀȺhяwaqHlݤdJ.C6&g+zȲQئ%ЦQIѦ'G%6F MTM񈴵zI ғpZ-W"S/@$8J-$9);28ACFxbeqsp|4[\p t}9U BH9W?sF9}S:yN':+@c2~@EQyҌIAIG\jG mkFiݳ{v=gݳ{v=gݳ{Hpython-debianbts-trusty/test/0000775000000000000000000000000012607726403013556 5ustar python-debianbts-trusty/test/test_debianbts.py0000664000000000000000000003533412607726344017136 0ustar #!/usr/bin/env python # -*- coding: utf-8 -*- # debianbts_test.py - Unittests for debianbts.py. # Copyright (C) 2009 Bastian Venthur # # 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; either version 2 of the License, or # (at your option) any later version. # # 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. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import division, unicode_literals, absolute_import, print_function import datetime import email.message import math import random import unittest import logging import threading try: import unittest.mock as mock except ImportError: import mock from pysimplesoap.simplexml import SimpleXMLElement import debianbts as bts logger = logging.getLogger(__name__) logging.basicConfig(level=logging.NOTSET) class DebianBtsTestCase(unittest.TestCase): def setUp(self): self.b1 = bts.Bugreport() self.b1.severity = 'normal' self.b2 = bts.Bugreport() self.b2.severity = 'normal' def test_get_usertag_empty(self): """get_usertag should return empty dict if no bugs are found.""" d = bts.get_usertag("thisisatest@debian.org") self.assertEqual(d, dict()) def test_get_usertag(self): """get_usertag should return dict with tag(s) and buglist(s).""" d = bts.get_usertag("debian-python@lists.debian.org") self.assertEqual(type(d), type(dict())) for k, v in d.items(): self.assertEqual(type(""), type(k)) self.assertEqual(type([]), type([])) for bug in v: self.assertEqual(type(bug), int) def test_get_usertag_filters(self): """get_usertag should return only requested tags""" tags = bts.get_usertag("debian-python@lists.debian.org") self.assertTrue(isinstance(tags, dict)) randomKey0 = random.choice(list(tags.keys())) randomKey1 = random.choice(list(tags.keys())) filtered_tags = bts.get_usertag( "debian-python@lists.debian.org", randomKey0, randomKey1) self.assertEqual(len(filtered_tags), 2) self.assertEqual(set(filtered_tags[randomKey0]), set(tags[randomKey0])) self.assertEqual(set(filtered_tags[randomKey1]), set(tags[randomKey1])) def test_get_bugs_empty(self): """get_bugs should return empty list if no matching bugs where found.""" l = bts.get_bugs("package", "thisisatest") self.assertEqual(l, []) def test_get_bugs(self): """get_bugs should return list of bugnumbers.""" l = bts.get_bugs("submitter", "venthur@debian.org") self.assertFalse(len(l) == 0) self.assertEqual(type(l), type([])) for i in l: self.assertEqual(type(i), type(int())) def test_get_bugs_list(self): """previous versions of python-debianbts accepted malformed key-value lists.""" l = bts.get_bugs('submitter', 'venthur@debian.org', 'severity', 'normal') l2 = bts.get_bugs(['submitter', 'venthur@debian.org', 'severity', 'normal']) self.assertFalse(len(l) == 0) l.sort() l2.sort() self.assertEqual(l, l2) def test_newest_bugs(self): """newest_bugs should return list of bugnumbers.""" l = bts.newest_bugs(10) self.assertEqual(type(l), type([])) for i in l: self.assertEqual(type(i), type(int())) def test_newest_bugs_amount(self): """newest_bugs(amount) should return a list of len 'amount'. """ for i in 0, 1, 10: l = bts.newest_bugs(i) self.assertEqual(len(l), i) def test_get_bug_log(self): """get_bug_log should return the correct data types.""" bl = bts.get_bug_log(223344) self.assertEqual(type(bl), type([])) for i in bl: self.assertEqual(type(i), type(dict())) self.assertTrue("attachments" in i) self.assertEqual(type(i["attachments"]), type(list())) self.assertTrue("body" in i) self.assertTrue(isinstance(i["body"], type(''))) self.assertTrue("header" in i) self.assertTrue(isinstance(i["header"], type(''))) self.assertTrue("msg_num" in i) self.assertEqual(type(i["msg_num"]), type(int())) def test_get_bug_log_with_attachments(self): """get_bug_log should include attachments""" buglogs = bts.get_bug_log(400000) for bl in buglogs: self.assertTrue("attachments" in bl) def test_bug_log_message(self): """dict returned by get_bug_log has a email.Message field""" buglogs = bts.get_bug_log(400012) for buglog in buglogs: self.assertTrue('message' in buglog) msg = buglog['message'] self.assertIsInstance(msg, email.message.Message) self.assertFalse(msg.is_multipart()) self.assertTrue('Subject' in msg) self._assert_unicode(msg.get_payload()) def test_bug_log_message_unicode(self): """test parsing of bug_log mail with non ascii characters""" buglogs = bts.get_bug_log(773321) buglog = buglogs[1] msg_payload = buglog['message'].get_payload() self._assert_unicode(msg_payload) self.assertTrue('é' in msg_payload) def test_empty_get_status(self): """get_status should return empty list if bug doesn't exits""" bugs = bts.get_status(0) self.assertEqual(type(bugs), list) self.assertEqual(len(bugs), 0) def test_sample_get_status(self): """test retrieving of a "known" bug status""" bugs = bts.get_status(486212) self.assertEqual(len(bugs), 1) bug = bugs[0] self.assertEqual(bug.bug_num, 486212) self.assertEqual(bug.date, datetime.datetime(2008, 6, 14, 10, 30, 2)) self.assertTrue(bug.subject.startswith('[reportbug-ng] segm')) self.assertEqual(bug.package, 'reportbug-ng') self.assertEqual(bug.severity, 'normal') self.assertEqual(bug.tags, ['help']) self.assertEqual(bug.blockedby, []) self.assertEqual(bug.blocks, []) self.assertEqual(bug.summary, '') self.assertEqual(bug.location, 'archive') self.assertEqual(bug.source, 'reportbug-ng') self.assertEqual(bug.log_modified, datetime.datetime(2008, 8, 17, 7, 26, 22)) self.assertEqual(bug.pending, 'done') self.assertEqual(bug.done, True) self.assertEqual(bug.archived, True) self.assertEqual(bug.found_versions, ['reportbug-ng/0.2008.06.04']) self.assertEqual(bug.fixed_versions, ['reportbug-ng/1.0']) self.assertEqual(bug.affects, []) def test_bug_str(self): """test string conversion of a Bugreport""" self.b2.package = 'foo-pkg' self.b2.bug_num = 12222 s = str(self.b2) self.assertTrue(isinstance(s, str)) # byte string in py2, unicode in py3 self.assertTrue('bug_num: 12222\n' in s) self.assertTrue('package: foo-pkg\n' in s) def test_get_status_affects(self): """test a bug with "affects" field""" bugs = bts.get_status(290501, 770490) self.assertEqual(len(bugs), 2) self.assertEqual(bugs[0].affects, []) self.assertEqual(bugs[1].affects, ['conkeror']) def test_status_batches_large_bug_counts(self): """get_status should perform requests in batches to reduce server load.""" with mock.patch.object(bts, '_build_soap_client') as mock_build_client: mock_build_client.return_value = mock_client = mock.Mock() mock_client.call.return_value = SimpleXMLElement( '') nr = bts.BATCH_SIZE + 10.0 calls = int(math.ceil(nr / bts.BATCH_SIZE)) bts.get_status([722226] * int(nr)) self.assertEqual(mock_client.call.call_count, calls) def test_status_batches_multiple_arguments(self): """get_status should batch multiple arguments into one request.""" with mock.patch.object(bts, '_build_soap_client') as mock_build_client: mock_build_client.return_value = mock_client = mock.Mock() mock_client.call.return_value = SimpleXMLElement( '') batch_size = bts.BATCH_SIZE calls = 1 bts.get_status(*list(range(batch_size))) self.assertEqual(mock_client.call.call_count, calls) calls += 2 bts.get_status(*list(range(batch_size + 1))) self.assertEqual(mock_client.call.call_count, calls) def test_comparison(self): """comparison of two bugs""" self.b1.archived = True self.b2.done = True self.assertTrue(self.b2 > self.b1) self.assertTrue(self.b2 >= self.b1) self.assertFalse(self.b2 == self.b1) self.assertFalse(self.b2 < self.b1) self.assertFalse(self.b2 <= self.b1) def test_comparison_equal(self): """comparison of two bug which are equal regarding their relative order""" self.b1.done = True self.b2.done = True self.assertFalse(self.b2 > self.b1) self.assertTrue(self.b2 >= self.b1) self.assertTrue(self.b2 == self.b1) self.assertFalse(self.b2 < self.b1) self.assertTrue(self.b2 <= self.b1) def test_get_bugs_int_bugs(self): """It is possible to pass a list of bug number to get_bugs""" bugs = bts.get_bugs('bugs', [400010, 400012], 'archive', True) self.assertEquals(set(bugs), set((400010, 400012))) def test_get_bugs_single_int_bug(self): """bugs parameter in get_bugs can be a list of int or a int""" bugs1 = bts.get_bugs('bugs', 400040, 'archive', True) bugs2 = bts.get_bugs('bugs', [400040], 'archive', True) self.assertEquals(bugs1, bugs2) def test_mergedwith(self): """Mergedwith is always a list of int.""" # this one is merged with two other bugs m = bts.get_status(486212)[0].mergedwith self.assertTrue(len(m) == 2) for i in m: self.assertEqual(type(i), type(int())) # this one was merged with one bug m = bts.get_status(433550)[0].mergedwith self.assertTrue(len(m) == 1) self.assertEqual(type(m[0]), type(int())) # this one was not merged m = bts.get_status(474955)[0].mergedwith self.assertEqual(m, list()) def test_base64_status_fields(self): """fields in bug status are sometimes base64-encoded""" bug = bts.get_status(711111)[0] self._assert_unicode(bug.originator) self.assertTrue(bug.originator.endswith('gmail.com>')) self.assertTrue('ł' in bug.originator) def test_base64_buglog_body(self): """buglog body is sometimes base64 encoded""" buglog = bts.get_bug_log(773321) body = buglog[1]['body'] self._assert_unicode(buglog[1]['body']) self.assertTrue('é' in body) def test_string_status_originator(self): """test reading of bug status originator that is not base64-encoded""" bug = bts.get_status(711112)[0] self._assert_unicode(bug.originator) self.assertTrue(bug.originator.endswith('debian.org>')) def test_unicode_convertion_in_str(self): """string representation must deal with unicode correctly.""" [bug] = bts.get_status(773321) try: bug.__str__() except UnicodeEncodeError: self.fail() def test_regression_588954(self): """Get_bug_log must convert the body correctly to unicode.""" try: bts.get_bug_log(582010) except UnicodeDecodeError: self.fail() def test_regression_590073(self): """bug.blocks is sometimes a str sometimes an int.""" try: # test the int case # TODO: test the string case bts.get_status(568657) except TypeError: self.fail() def test_regression_590725(self): """bug.body utf sometimes contains invalid continuation bytes.""" try: bts.get_bug_log(578363) bts.get_bug_log(570825) except UnicodeDecodeError: self.fail() def test_regression_670446(self): """affects should be split by ','""" bug = bts.get_status(657408)[0] self.assertEqual( bug.affects, ['epiphany-browser-dev', 'libwebkit-dev']) def test_regression_799528(self): """fields of buglog are sometimes base64 encoded.""" # bug with base64 encoding originator [bug] = bts.get_status(711111) self.assertTrue('ł' in bug.originator) # bug with base64 encoding subject [bug] = bts.get_status(779005) self.assertTrue('‘' in bug.subject) def _assert_unicode(self, string): """asserts for type of a unicode string, depending on python version""" if bts.PY2: self.assertIsInstance(string, unicode) else: self.assertIsInstance(string, str) class ThreadingTestCase(unittest.TestCase): """this class tests that the module is thread safe""" def setUp(self): self._thread_failed = False def test_multithreading(self): threads = [ threading.Thread(target=self._get_bugs_thread, args=(pkg,)) for pkg in ('python3-gst-1.0', 'libsoxr0') ] + [ threading.Thread(target=self._get_bug_log_thread, args=(bug_n,)) for bug_n in (300000, 300001) ] for thread in threads: thread.start() for thread in threads: thread.join() if self._thread_failed: self.fail('multithreaded calls failed') def _get_bugs_thread(self, pkg): try: bugs = bts.get_bugs('package', pkg) except Exception as exc: self._thread_failed = True print('threaded get_bugs() call failed ' 'with exception {} {}'.format(type(exc), exc)) def _get_bug_log_thread(self, bug_num): try: bug_logs = bts.get_bug_log(bug_num) except Exception as exc: self._thread_failed = True print('threaded get_bug_log() call failed ' 'with exception {} {}'.format(type(exc), exc)) if __name__ == "__main__": unittest.main() python-debianbts-trusty/MANIFEST0000664000000000000000000000013412607400035013714 0ustar # file GENERATED by distutils, do NOT edit setup.py src/debianbts.py test/test_debianbts.py python-debianbts-trusty/debian/0000775000000000000000000000000012670566344014027 5ustar python-debianbts-trusty/debian/compat0000664000000000000000000000000211742763773015231 0ustar 9 python-debianbts-trusty/debian/changelog0000664000000000000000000002472512670566344015713 0ustar python-debianbts (2.6.0~ubuntu14.04.1) trusty-backports; urgency=medium * No-change backport to trusty (LP: #1556172) -- Scott Kitterman Fri, 11 Mar 2016 11:05:56 -0500 python-debianbts (2.6.0) unstable; urgency=medium * Gaetano made python-debianbts thread safe by dynamically creating SoapClients instead of using a module-level one. (Closes: #801585) -- Bastian Venthur Thu, 15 Oct 2015 15:35:16 +0200 python-debianbts (2.5.2) unstable; urgency=medium * Applied patch by Gaetano, that fixes Bugreport.__str__ throwing unicode decode errors * Fix bug where buglog body is sometimes not base64 decoded by applying patch by Gaetano. (Closes: #801585, #801347) -- Bastian Venthur Tue, 13 Oct 2015 09:46:08 +0200 python-debianbts (2.5.1) unstable; urgency=medium * Applied patch by Gaetano fixing a bad import of the email module. Thanks Gaetano! * Use semver semantics for versioning from now on -- Bastian Venthur Mon, 12 Oct 2015 15:16:14 +0200 python-debianbts (2.5) unstable; urgency=medium * Fix "get_bugs(..., 'bugs', [123, 456], ...) triggers serverside exception: The 'bugs' parameter ("HASH(0x315ced8)") to Debbugs::Bugs::get_bugs was a 'hashref'" Merged modified version of patch by Gaetano Guerriero. Thanks! (Closes: #801360) * Changed docstrings to numpy standard -- Bastian Venthur Sun, 11 Oct 2015 15:48:42 +0200 python-debianbts (2.4) unstable; urgency=medium * Fix "base64-encoded strings" merged patch by Gaetano that parses all fields of Bugreport via _parse_string_el. (Closes: #799528) -- Bastian Venthur Sun, 27 Sep 2015 13:37:22 +0200 python-debianbts (2.3) unstable; urgency=medium * Added logging to unit tests * Fixed some unittests * Fix "base64-encoded strings" by merging pull request by Gaetano Guerriero. (Closes: #799528) Thank you Gaetano! -- Bastian Venthur Sun, 20 Sep 2015 15:29:19 +0200 python-debianbts (2.2) unstable; urgency=medium * Added patch by Gaetano to provide proper email messages in Buglog. * Upload to unstable -- Bastian Venthur Thu, 17 Sep 2015 12:41:37 +0200 python-debianbts (2.1) experimental; urgency=medium * Port to Python 3. Thanks again Gaetano Guerriero for providing the initial patch! The code works now under Python2/3 simultaneously (Closes: #732644) -- Bastian Venthur Fri, 28 Aug 2015 09:30:41 +0200 python-debianbts (2.0) experimental; urgency=medium * Merged patch that ports python-debianbts from soappy to simplesoap, by Gaetano Guerriero. Thank you very much! * allow get_bugs(a, b, c, d) and get_bugs([a, b, c, d]) -- Bastian Venthur Thu, 27 Aug 2015 14:01:33 +0200 python-debianbts (1.14) unstable; urgency=medium * Fix "performance regression in get_status()" by applying patch from James McCoy. Thank you very much James! (Closes: #795198) * Made code more PEP8 compliant * Made unit tests more discoverable for nosetests -- Bastian Venthur Wed, 12 Aug 2015 10:05:35 +0200 python-debianbts (1.13) unstable; urgency=medium * Fix "reportbug: HTTPError 500 Internal Server Error" by merging patch contributed by James McCoi, thanks James! (Closes: #722226) * Bugs are now downloaded in batches of 500 bugs * Bumped standards version * Transitioned the package from python-support to dh_python2 -- Bastian Venthur Thu, 23 Jul 2015 17:01:28 +0200 python-debianbts (1.12) unstable; urgency=medium * Added HTTP_PROXY support by applying the patch from Raphael Kubo da Costa, thank you very much! (Closes: #630496) * Fix "Bugreport.affects returns an array of chars, not strings" by splitting the input string (Closes: #670446) -- Bastian Venthur Mon, 07 Jul 2014 14:50:29 +0200 python-debianbts (1.11) unstable; urgency=low * Uploaded lots of changes contributed by Jari Aalto. Thank you very much man! * Remove unused dpatch and upgrade to packaging format "3.0 (native)". * Update to Standards-Version to 3.9.3.1 and debhelper to 9. * Add build-arch and build-indep targets to rules file. * Fix copyright-with-old-dh-make-debian-copyright (Lintian). * Fix spelling-error-in-description (Lintian). * Fix copyright-refers-to-symlink-license (Lintian). * Merged with patch from Michael, improving distutils setup. -- Bastian Venthur Mon, 16 Apr 2012 10:40:08 +0100 python-debianbts (1.10) unstable; urgency=low * Switched to distutils -- Bastian Venthur Thu, 24 Feb 2011 10:04:22 +0100 python-debianbts (1.9) unstable; urgency=low * Fixed issue in unicode handling in Bugreport.__str__ method * Added documentation for 'bugs' keyword in get_bugs -- Bastian Venthur Fri, 06 Aug 2010 21:38:29 -0400 python-debianbts (1.8) unstable; urgency=low * Fix "please add a Vcs-Browser field in source stanza" (Closes: #590929) * Fixed the types of the mergedwith-, blocks-, blockedby-, and affects-fields of the Bugreport class: the first three changed from "String" to "list of Strings" the last one from "String" to list of Strings" * Removed the keywords-, found_date-, fixed_date-, and id-attributes as they are either not fully implemented in the BTS or superfluous * Added Unittests to ensure the above works as expected * Further improved the documentation of the methods and the Bugreport class -- Bastian Venthur Sun, 01 Aug 2010 22:47:06 -0400 python-debianbts (1.7) unstable; urgency=low * Fix "remove *.pyc from source package" Added rm *.pyc rule in clean targets. (Closes: #590722) * Fix "UnicodeDecodeError: 'utf8' codec can't decode byte 0xe4 in position 44: invalid continuation byte" We replace invalid unicode characters now (Closes: #590725) -- Bastian Venthur Thu, 29 Jul 2010 22:21:01 +0200 python-debianbts (1.6) unstable; urgency=low * Fix "reportbug-ng: coercing to Unicode: need string or buffer, int found", apparently "blocks" is sometimes a string and sometimes an int. (Closes: #590073) -- Bastian Venthur Sat, 24 Jul 2010 15:01:00 +0200 python-debianbts (1.5) unstable; urgency=low * Fix "UnicodeDecodeError on get_bug_log() and other methods", added regression test for this bug. (Closes: #588954) -- Bastian Venthur Sat, 17 Jul 2010 14:59:17 +0200 python-debianbts (1.4~bpo50+1) lenny-backports; urgency=low * Rebuild for lenny-backports. -- Bastian Venthur Tue, 22 Jun 2010 23:00:05 +0200 python-debianbts (1.4) unstable; urgency=low * Fix "[python-debianbts] Typo that generates incorrect output" Fixed trivial typo (Closes: #566554) -- Bastian Venthur Sun, 24 Jan 2010 12:51:03 +0100 python-debianbts (1.3) unstable; urgency=low * Removed id, found and fixed (Closes: #550945) - According to Don id is superfluous and will vanish, use bug_num instead - found and fixed are only partly implemented in debbugs, found_versions and fixed_versions give the information you need. -- Bastian Venthur Wed, 14 Oct 2009 23:52:19 +0200 python-debianbts (1.2) unstable; urgency=low * Fixed typo in debian/pyversions -- Bastian Venthur Sun, 11 Oct 2009 13:14:57 +0200 python-debianbts (1.1) unstable; urgency=low * Make python-debianbts depend on python >= 2.5 - (Closes: #550569) python-debianbts: fails to compile with Python 2.4!! - (Closes: #550571) python-debianbts: Incompatible with python2.4!! -- Bastian Venthur Sun, 11 Oct 2009 12:34:03 +0200 python-debianbts (1.0) unstable; urgency=low * Major version number jump breaks backwards compatibility * Added support for usertags * Bugreport class provides exactly the members, provided by SOAP even if they don't make sense: - id and bug_nr seem to be identical all the time - found and found_versions as well - keywords and tags also - even fixed and fixed date - summary is always empty, but subject gives the summary - ... and probably some other quirks * get_bug_log now returns a dict instead of an object * removed HTMLStripper class -- we use SOAP for a while now * removed get_html_fulltext -- bugs.debian.org/bugnr does the trick too * Major refactings * Added unittests -- Bastian Venthur Sat, 10 Oct 2009 20:20:31 +0200 python-debianbts (0.6) unstable; urgency=low * Updated VCS-git field in debian/control, we moved to github * Bumped standards version (no changes) -- Bastian Venthur Sat, 19 Sep 2009 16:29:45 +0200 python-debianbts (0.5) unstable; urgency=low The "greetings from Graz" release * Fix "python-glpk and python-debianbts: error when trying to install together" Removed __init__.py (Closes: #546561) * Fix "submitter field is always None" (Closes: #542651) -- Bastian Venthur Mon, 14 Sep 2009 10:59:30 +0200 python-debianbts (0.4) unstable; urgency=low * Fix "incomplete documentation for debianbts.get_bugs()", added "correspondent" to docstring (Closes: #529588) * Fix "timestamps represented as strings", first- and lastaction are now datetimeobjects, thanks Jakub (Closes: #529488) * Added VCS-git field to debian/control * Bumped standards version * Replaced dh_clean -k with dh_prep in debian/rules * Replaced Author(s) with Author in debian/copyright -- Bastian Venthur Sun, 07 Jun 2009 15:03:52 +0200 python-debianbts (0.3) unstable; urgency=low * Added support for Tags -- Bastian Venthur Sat, 11 Oct 2008 17:16:38 +0200 python-debianbts (0.2.1) unstable; urgency=low * Corrected priority from extra to optional * Fixed short and long package description to make lintian happy -- Bastian Venthur Fri, 11 Jul 2008 14:38:17 +0200 python-debianbts (0.2) unstable; urgency=low * Changed SOAP namespace to Debbugs/SOAP/V1, thanks Don for the hint! * Added values for Bugreports to compare their severities * Added HTML Parser and get_html_fulltext(bugnr) -- Bastian Venthur Mon, 07 Jul 2008 23:26:58 +0200 python-debianbts (0.1) unstable; urgency=low * Initial Release. -- Bastian Venthur Sun, 06 Jul 2008 19:40:04 +0200 python-debianbts-trusty/debian/copyright0000664000000000000000000000236411742763773015773 0ustar This package was debianized by Bastian Venthur on Sun, 06 Jul 2008 19:40:04 +0200. It was downloaded from Upstream Author: Bastian Venthur Copyright: Copyright (C) 2007-2008 Bastian Venthur License: This package 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; either version 2 of the License, or (at your option) any later version. This package 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. You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-2'. The Debian packaging is Copyright (C) 2008, Bastian Venthur and is licensed under the GPL, see above. python-debianbts-trusty/debian/rules0000775000000000000000000000025212576513637015110 0ustar #!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 export PYBUILD_NAME=debianbts %: dh $@ --with python2,python3 --buildsystem=pybuild python-debianbts-trusty/debian/control0000664000000000000000000000226512576513637015441 0ustar Source: python-debianbts Section: python Priority: optional Maintainer: Bastian Venthur Build-Depends: debhelper (>= 9), dh-python, python (>= 2.6.6-3~), python3 (>= 3.4), python-mock, python-pysimplesoap, python3-pysimplesoap Standards-Version: 3.9.6 X-Python-Version: >= 2.7 X-Python3-Version: >= 3.4 Vcs-Git: git://github.com/venthur/python-debianbts.git Vcs-Browser: http://github.com/venthur/python-debianbts Package: python-debianbts Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python-pysimplesoap Provides: ${python:Provides} Description: Python interface to Debian's Bug Tracking System This package provides the debianbts module, which allows one to query Debian's BTS via it's SOAP-interface and returns the answer in Python's native data types. Package: python3-debianbts Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python3-pysimplesoap Provides: ${python:Provides} Description: Python interface to Debian's Bug Tracking System This package provides the debianbts module, which allows one to query Debian's BTS via it's SOAP-interface and returns the answer in Python's native data types. python-debianbts-trusty/debian/source/0000775000000000000000000000000011742763773015333 5ustar python-debianbts-trusty/debian/source/format0000664000000000000000000000001511742763773016542 0ustar 3.0 (native) python-debianbts-trusty/debian/docs0000664000000000000000000000002512607174155014672 0ustar README.md THANKS.txt python-debianbts-trusty/THANKS.txt0000664000000000000000000000022212607135672014326 0ustar * Gaetano Guerriero for helping to migrate from SOAPpy to PySimpleSOAP, for helping to migrate from Python2 to Python3, and countless bugfixes! python-debianbts-trusty/src/0000775000000000000000000000000012607726627013376 5ustar python-debianbts-trusty/src/__init__.py0000664000000000000000000000000011212725613015456 0ustar python-debianbts-trusty/src/__pycache__/0000775000000000000000000000000012607726630015600 5ustar python-debianbts-trusty/src/debianbts.py0000664000000000000000000003762512607726344015714 0ustar #!/usr/bin/env python # debianbts.py - Methods to query Debian's BTS. # Copyright (C) 2007-2015 Bastian Venthur # # 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; either version 2 of the License, or # (at your option) any later version. # # 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. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ Query Debian's Bug Tracking System (BTS). This module provides a layer between Python and Debian's BTS. It provides methods to query the BTS using the BTS' SOAP interface, and the Bugreport class which represents a bugreport from the BTS. """ from __future__ import division, unicode_literals, absolute_import, print_function import base64 import email.feedparser from datetime import datetime import os import sys from pysimplesoap.client import SoapClient from pysimplesoap.simplexml import SimpleXMLElement # Support running from Debian infrastructure ca_path = '/etc/ssl/ca-debian' if os.path.isdir(ca_path): os.environ['SSL_CERT_DIR'] = ca_path # please follow the semver semantics, i.e. MAJOR.MINOR.PATCH where # MAJOR: incompatible API changes # MINOR: add backwards-comptible functionality # PATCH: backwards-compatible bug fixes. __version__ = '2.6.0' PY2 = sys.version_info.major == 2 # Setup the soap server # Default values URL = 'https://bugs.debian.org/cgi-bin/soap.cgi' NS = 'Debbugs/SOAP/V1' BTS_URL = 'https://bugs.debian.org/' # Max number of bugs to send in a single get_status request BATCH_SIZE = 500 class Bugreport(object): """Represents a bugreport from Debian's Bug Tracking System. A bugreport object provides all attributes provided by the SOAP interface. Most of the attributes are strings, the others are marked. Attributes ---------- bug_num : int The bugnumber severity : str Severity of the bugreport tags : list of strings Tags of the bugreport subject : str The subject/title of the bugreport originator : str Submitter of the bugreport mergedwith : list of ints List of bugnumbers this bug was merged with package : str Package of the bugreport source : str Source package of the bugreport date : datetime Date of bug creation log_modified : datetime Date of update of the bugreport done : boolean Is the bug fixed or not archived : bool Is the bug archived or not unarchived : bool Was the bug unarchived or not fixed_versions : list of strings List of versions, can be empty even if bug is fixed found_versions : list of strings List of version numbers where bug was found forwarded : str A URL or email address blocks: list of ints List of bugnumbers this bug blocks blockedby : list of int List of bugnumbers which block this bug pending : str Either 'pending' or 'done' msgid : str Message ID of the bugreport owner : str Who took responsibility for fixing this bug location : str Either 'db-h' or 'archive' affects : list of str List of Packagenames summary : str Arbitrary text """ def __init__(self): self.originator = None self.date = None self.subject = None self.msgid = None self.package = None self.tags = None self.done = None self.forwarded = None self.mergedwith = None self.severity = None self.owner = None self.found_versions = None self.fixed_versions = None self.blocks = None self.blockedby = None self.unarchived = None self.summary = None self.affects = None self.log_modified = None self.location = None self.archived = None self.bug_num = None self.source = None self.pending = None # The ones below are also there but not used # self.fixed = None # self.found = None # self.fixed_date = None # self.found_date = None # self.keywords = None # self.id = None def __unicode__(self): s = '\n'.join('{}: {}'.format(key, value) for key, value in self.__dict__.items()) return s + '\n' if PY2: def __str__(self): return self.__unicode__().encode('utf-8') else: __str__ = __unicode__ def __lt__(self, other): """Compare a bugreport with another. The more open and urgent a bug is, the greater the bug is: outstanding > resolved > archived critical > grave > serious > important > normal > minor > wishlist. Openness always beats urgency, eg an archived bug is *always* smaller than an outstanding bug. This sorting is useful for displaying bugreports in a list and sorting them in a useful way. """ return self._get_value() < other._get_value() def __le__(self, other): return not self.__gt__(other) def __gt__(self, other): return self._get_value() > other._get_value() def __ge__(self, other): return not self.__lt__(other) def __eq__(self, other): return self._get_value() == other._get_value() def __ne__(self, other): return not self.__eq__(other) def _get_value(self): if self.archived: # archived and done val = 0 elif self.done: # not archived and done val = 10 else: # not done val = 20 val += {"critical": 7, "grave": 6, "serious": 5, "important": 4, "normal": 3, "minor": 2, "wishlist": 1}[self.severity] return val def get_status(*nrs): """Returns a list of Bugreport objects. Given a list of bugnumbers this method returns a list of Bugreport objects. Parameters ---------- nrs : int or list of ints the bugnumbers Returns ------- bugs : list of Bugreport objects """ # If we called get_status with one single bug, we get a single bug, # if we called it with a list of bugs, we get a list, # No available bugreports returns an empty list bugs = [] list_ = [] for nr in nrs: if isinstance(nr, list): list_.extend(nr) else: list_.append(nr) # Process the input in batches to avoid hitting resource limits on the BTS soap_client = _build_soap_client() for i in range(0, len(list_), BATCH_SIZE): slice_ = list_[i:i + BATCH_SIZE] # I build body by hand, pysimplesoap doesn't generate soap Arrays # without using wsdl method_el = SimpleXMLElement('') _build_int_array_el('arg0', method_el, slice_) reply = soap_client.call('get_status', method_el) for bug_item_el in reply('s-gensym3').children() or []: bug_el = bug_item_el.children()[1] bugs.append(_parse_status(bug_el)) return bugs def get_usertag(email, *tags): """Get buglists by usertags. Parameters ---------- email : str tags : tuple of strings If tags are given the dictionary is limited to the matching tags, if no tags are given all available tags are returned. Returns ------- mapping : dict a mapping of useertag -> buglist """ reply = _soap_client_call('get_usertag', email, *tags) map_el = reply('s-gensym3') mapping = {} # element in response can have standard type # xsi:type=apachens:Map (example, for email debian-python@lists.debian.org) # OR no type, in this case keys are the names of child elements and # the array is contained in the child elements type_attr = map_el.attributes().get('xsi:type') if type_attr and type_attr.value == 'apachens:Map': for usertag_el in map_el.children() or []: tag = _uc(str(usertag_el('key'))) buglist_el = usertag_el('value') mapping[tag] = [int(bug) for bug in buglist_el.children() or []] else: for usertag_el in map_el.children() or []: tag = _uc(usertag_el.get_name()) mapping[tag] = [int(bug) for bug in usertag_el.children() or []] return mapping def get_bug_log(nr): """Get Buglogs. A buglog is a dictionary with the following mappings: * "header" => string * "body" => string * "attachments" => list * "msg_num" => int * "message" => email.message.Message Parameters ---------- nr : int the bugnumber Returns ------- buglogs : list of dicts """ reply = _soap_client_call('get_bug_log', nr) items_el = reply('soapenc:Array') buglogs = [] for buglog_el in items_el.children(): buglog = {} buglog["header"] = _parse_string_el(buglog_el("header")) buglog["body"] = _parse_string_el(buglog_el("body")) buglog["msg_num"] = int(buglog_el("msg_num")) # server always returns an empty attachments array ? buglog["attachments"] = [] mail_parser = email.feedparser.FeedParser() mail_parser.feed(buglog["header"]) mail_parser.feed(buglog["body"]) buglog["message"] = mail_parser.close() buglogs.append(buglog) return buglogs def newest_bugs(amount): """Returns the newest bugs. This method can be used to query the BTS for the n newest bugs. Parameters ---------- amount : int the number of desired bugs. E.g. if `amount` is 10 the method will return the 10 latest bugs. Returns ------- bugs : list of int the bugnumbers """ reply = _soap_client_call('newest_bugs', amount) items_el = reply('soapenc:Array') return [int(item_el) for item_el in items_el.children() or []] def get_bugs(*key_value): """Get list of bugs matching certain criteria. The conditions are defined by key value pairs. Possible keys are: * "package": bugs for the given package * "submitter": bugs from the submitter * "maint": bugs belonging to a maintainer * "src": bugs belonging to a source package * "severity": bugs with a certain severity * "status": can be either "done", "forwarded", or "open" * "tag": see http://www.debian.org/Bugs/Developer#tags for available tags * "owner": bugs which are assigned to `owner` * "bugs": takes single int or list of bugnumbers, filters the list according to given criteria * "correspondent": bugs where `correspondent` has sent a mail to Arguments --------- key_value : str Returns ------- bugs : list of ints the bugnumbers Examples -------- >>> get_bugs('package', 'gtk-qt-engine', 'severity', 'normal') [12345, 23456] """ # previous versions also accepted get_bugs(['package', 'gtk-qt-engine', 'severity', 'normal']) # if key_value is a list in a one elemented tuple, remove the # wrapping list if len(key_value) == 1 and isinstance(key_value[0], list): key_value = tuple(key_value[0]) # pysimplesoap doesn't generate soap Arrays without using wsdl # I build body by hand, converting list to array and using standard # pysimplesoap marshalling for other types method_el = SimpleXMLElement('') for arg_n, kv in enumerate(key_value): arg_name = 'arg' + str(arg_n) if isinstance(kv, (list, tuple)): _build_int_array_el(arg_name, method_el, kv) else: method_el.marshall(arg_name, kv) soap_client = _build_soap_client() reply = soap_client.call('get_bugs', method_el) items_el = reply('soapenc:Array') return [int(item_el) for item_el in items_el.children() or []] def _parse_status(bug_el): """Return a bugreport object from a given status xml element""" bug = Bugreport() # plain fields for field in ('originator', 'subject', 'msgid', 'package', 'severity', 'owner', 'summary', 'location', 'source', 'pending', 'forwarded'): setattr(bug, field, _parse_string_el(bug_el(field))) bug.date = datetime.utcfromtimestamp(float(bug_el('date'))) bug.log_modified = datetime.utcfromtimestamp(float(bug_el('log_modified'))) bug.tags = [_uc(tag) for tag in str(bug_el('tags')).split()] bug.done = _parse_bool(bug_el('done')) bug.archived = _parse_bool(bug_el('archived')) bug.unarchived = _parse_bool(bug_el('unarchived')) bug.bug_num = int(bug_el('bug_num')) bug.mergedwith = [int(i) for i in str(bug_el('mergedwith')).split()] bug.blockedby = [int(i) for i in str(bug_el('blockedby')).split()] bug.blocks = [int(i) for i in str(bug_el('blocks')).split()] bug.found_versions = [_uc(str(el)) for el in bug_el('found_versions').children() or []] bug.fixed_versions = [_uc(str(el)) for el in bug_el('fixed_versions').children() or []] affects = [_f for _f in str(bug_el('affects')).split(',') if _f] bug.affects = [_uc(a).strip() for a in affects] # Also available, but unused or broken # bug.keywords = [_uc(keyword) for keyword in # str(bug_el('keywords')).split()] # bug.fixed = _parse_crappy_soap(tmp, "fixed") # bug.found = _parse_crappy_soap(tmp, "found") # bug.found_date = [datetime.utcfromtimestamp(i) for i in tmp["found_date"]] # bug.fixed_date = [datetime.utcfromtimestamp(i) for i in tmp["fixed_date"]] return bug def _build_soap_client(): """Factory method that creates a SoapClient. For thread-safety we create SoapClients on demand instead of using a module-level one. Returns ------- sc : SoapClient instance """ return SoapClient(location=URL, namespace=NS, soap_ns='soap') def _soap_client_call(method_name, *args): """wrapper to work around a pysimplesoap bug https://github.com/pysimplesoap/pysimplesoap/issues/31""" soap_client = _build_soap_client() soap_args = [] for arg_n, arg in enumerate(args): soap_args.append(('arg' + str(arg_n), arg)) return getattr(soap_client, method_name)(soap_client, *soap_args) def _build_int_array_el(el_name, parent, list_): """build a soapenc:Array made of ints called `el_name` as a child of `parent`""" el = parent.add_child(el_name) el.add_attribute('xmlns:soapenc', 'http://schemas.xmlsoap.org/soap/encoding/') el.add_attribute('xsi:type', 'soapenc:Array') el.add_attribute('soapenc:arrayType', 'xsd:int[{:d}]'.format(len(list_))) for item in list_: item_el = el.add_child('item', str(item)) item_el.add_attribute('xsi:type', 'xsd:int') return el def _parse_bool(el): """parse a boolean value from a xml element""" value = str(el) return not value.strip() in ('', '0') def _parse_string_el(el): """read a string element, maybe encoded in base64""" value = str(el) el_type = el.attributes().get('xsi:type') if el_type and el_type.value == 'xsd:base64Binary': value = base64.b64decode(value) if not PY2: value = value.decode('utf-8') value = _uc(value) return value """ Convert string to unicode. This method only exists to unify the unicode conversion in this module. """ if PY2: def _uc(string): return string.decode('utf-8', 'replace') else: def _uc(string): return string python-debianbts-trusty/README.md0000664000000000000000000000030012607136364014050 0ustar ## python-debianbts Python-debianbts is a Python library that allows for querying Debian's [Bug Tracking System](https://bugs.debian.org). Python-debianbts is Python2 and Python3 compatible.