pybot-commits Mailing List for pybot (Page 4)
Brought to you by:
niemeyer
You can subscribe to this list here.
2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(6) |
Dec
(7) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2002 |
Jan
|
Feb
|
Mar
(1) |
Apr
(7) |
May
(1) |
Jun
(14) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2003 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(56) |
Jun
(4) |
Jul
|
Aug
(85) |
Sep
(2) |
Oct
|
Nov
|
Dec
|
From: Gustavo N. <nie...@us...> - 2003-08-22 05:52:46
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv28249 Modified Files: README Log Message: Updated. Index: README =================================================================== RCS file: /cvsroot/pybot/pybot/README,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** README 16 May 2003 17:28:03 -0000 1.3 --- README 21 Aug 2003 18:46:27 -0000 1.4 *************** *** 13,16 **** --- 13,24 ---- and its python module (http://pysqlite.sf.net). + Download + -------- + + Current access to its source is available trough CVS only, in the + sourceforge pybot project: + + http://sourceforge.net/projects/pybot + End user features ----------------- *************** *** 26,30 **** - full online help; - auto recover from network errors; - - flood protection; - auto timming of messages to avoid being kicked by the server. - lots of additional functionalities through available modules; --- 34,37 ---- *************** *** 138,141 **** --- 145,152 ---- services to send messages and notices using pybot (demo client included); + + - remoteinfo + Allows pybot to acquire knowledge from remote URLs in a very + flexible way. vim:st=4:sw=4:et |
From: Gustavo N. <nie...@us...> - 2003-08-22 05:48:42
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv13519 Modified Files: ChangeLog Log Message: * modules/remoteinfo.py: Introduced "allow" concept, which defines which channels/servers are allowed to "see" some remoteinfo. Index: ChangeLog =================================================================== RCS file: /cvsroot/pybot/pybot/ChangeLog,v retrieving revision 1.19 retrieving revision 1.20 diff -C2 -d -r1.19 -r1.20 *** ChangeLog 21 Aug 2003 18:43:08 -0000 1.19 --- ChangeLog 21 Aug 2003 20:32:50 -0000 1.20 *************** *** 3,6 **** --- 3,7 ---- defines which channels/servers are allowed to "see" some remoteinfo. + * modules/messages.py: Prevent messages to the bot itself. 2003-08-19 Gustavo Niemeyer <nie...@co...> |
From: Gustavo N. <nie...@us...> - 2003-08-22 05:37:56
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv29789 Modified Files: ChangeLog Log Message: * modules/servercontrol.py: Minor typos fixed. * modules/log.py: Only allow log searching inside the channel that the message has been typed. Index: ChangeLog =================================================================== RCS file: /cvsroot/pybot/pybot/ChangeLog,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** ChangeLog 2 Jun 2003 13:36:09 -0000 1.17 --- ChangeLog 19 Aug 2003 21:59:10 -0000 1.18 *************** *** 1,2 **** --- 1,7 ---- + 2003-08-19 Gustavo Niemeyer <nie...@co...> + * modules/servercontrol.py: Minor typos fixed. + * modules/log.py: Only allow log searching inside + the channel that the message has been typed. + 2003-06-02 Gustavo Niemeyer <nie...@co...> * modules/remoteinfo.py: New module! |
From: Gustavo N. <nie...@us...> - 2003-08-22 05:37:56
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv29789/pybot/modules Modified Files: log.py servercontrol.py Log Message: * modules/servercontrol.py: Minor typos fixed. * modules/log.py: Only allow log searching inside the channel that the message has been typed. Index: log.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/log.py,v retrieving revision 1.13 retrieving revision 1.14 diff -C2 -d -r1.13 -r1.14 *** log.py 20 May 2003 17:06:53 -0000 1.13 --- log.py 19 Aug 2003 21:59:10 -0000 1.14 *************** *** 103,112 **** return None ! def search(self, regexp, max, searchline): p = re.compile(regexp, re.I) l = [] cursor = db.cursor() ! cursor.execute("select * from log where src != '' and dest != '' " ! "order by timestamp") row = cursor.fetchone() while row: --- 103,112 ---- return None ! def search(self, regexp, max, target, searchline): p = re.compile(regexp, re.I) l = [] cursor = db.cursor() ! cursor.execute("select * from log where src != '' and dest == %s " ! "order by timestamp", (target,)) row = cursor.fetchone() while row: *************** *** 169,173 **** if not logmsg: msg.answer("%:", "Sorry, I haven't seen %s for a while..." % nick) ! elif mm.hasperm(msg, "log"): msg.answer("%:", "I have seen %s %s, with the " "following message:" % --- 169,173 ---- if not logmsg: msg.answer("%:", "Sorry, I haven't seen %s for a while..." % nick) ! elif mm.hasperm(msg, "log") and msg.target == logmsg.dest: msg.answer("%:", "I have seen %s %s, with the " "following message:" % *************** *** 191,195 **** max = 5 logmsgs = self.log.search(m.group("regexp"), max, ! msg.rawline) if logmsgs: llen = len(logmsgs) --- 191,195 ---- max = 5 logmsgs = self.log.search(m.group("regexp"), max, ! msg.target, msg.rawline) if logmsgs: llen = len(logmsgs) Index: servercontrol.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/servercontrol.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** servercontrol.py 12 May 2003 20:42:21 -0000 1.3 --- servercontrol.py 19 Aug 2003 21:59:10 -0000 1.4 *************** *** 391,398 **** l.append("%s (hosts: %s)" % (servername, ", ".join(hosts))) ! msg.answer("%:", "You're connected to the following " "servers:", ", ".join(l)) else: ! msg.answer("%:", "You're not connected to any servers", [".", "!"]) else: --- 391,398 ---- l.append("%s (hosts: %s)" % (servername, ", ".join(hosts))) ! msg.answer("%:", "I'm connected to the following " "servers:", ", ".join(l)) else: ! msg.answer("%:", "I'm not connected to any servers", [".", "!"]) else: |
From: Gustavo N. <nie...@us...> - 2003-08-22 05:32:33
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv3802 Modified Files: log.py Log Message: Also consider servername when checking for log search context. Index: log.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/log.py,v retrieving revision 1.14 retrieving revision 1.15 diff -C2 -d -r1.14 -r1.15 *** log.py 19 Aug 2003 21:59:10 -0000 1.14 --- log.py 19 Aug 2003 22:42:37 -0000 1.15 *************** *** 103,112 **** return None ! def search(self, regexp, max, target, searchline): p = re.compile(regexp, re.I) l = [] cursor = db.cursor() ! cursor.execute("select * from log where src != '' and dest == %s " ! "order by timestamp", (target,)) row = cursor.fetchone() while row: --- 103,113 ---- return None ! def search(self, servername, target, regexp, max, searchline): p = re.compile(regexp, re.I) l = [] cursor = db.cursor() ! cursor.execute("select * from log where servername == %s and " ! "dest == %s and src != '' order by timestamp", ! (servername, target)) row = cursor.fetchone() while row: *************** *** 190,195 **** if mm.hasperm(msg, "log"): max = 5 ! logmsgs = self.log.search(m.group("regexp"), max, ! msg.target, msg.rawline) if logmsgs: llen = len(logmsgs) --- 191,198 ---- if mm.hasperm(msg, "log"): max = 5 ! logmsgs = self.log.search(msg.server.servername, ! msg.target, ! m.group("regexp"), max, ! msg.rawline) if logmsgs: llen = len(logmsgs) |
From: Gustavo N. <nie...@us...> - 2003-08-22 03:00:00
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv28383 Modified Files: TODO Log Message: Updated. Index: TODO =================================================================== RCS file: /cvsroot/pybot/pybot/TODO,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** TODO 2 Jun 2003 13:36:09 -0000 1.10 --- TODO 21 Aug 2003 18:47:19 -0000 1.11 *************** *** 13,18 **** - Setup pybot nick when connecting. - - Limit log search to the same server. - - Develop admin module for direct sql access. --- 13,16 ---- |
From: Gustavo N. <nie...@us...> - 2003-08-22 02:08:33
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv13519/pybot/modules Modified Files: messages.py Log Message: * modules/remoteinfo.py: Introduced "allow" concept, which defines which channels/servers are allowed to "see" some remoteinfo. Index: messages.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/messages.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** messages.py 12 May 2003 20:42:21 -0000 1.7 --- messages.py 21 Aug 2003 20:32:50 -0000 1.8 *************** *** 90,93 **** --- 90,98 ---- message = m.group("message") snick = STRIPNICK.sub(r"\1", nick.lower()) + if snick == STRIPNICK.sub(r"\1", msg.server.user.nick): + msg.answer("%:", ["I'm not used to talk to myself.", + "I don't talk to myself.", + "Are you nuts?"]) + return 0 flags = "" if priv: |
From: Gustavo N. <nie...@us...> - 2003-08-22 00:50:56
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv19245/pybot/modules Modified Files: messages.py Log Message: Fixed unloading of permission hook. Index: messages.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/messages.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** messages.py 21 Aug 2003 20:58:04 -0000 1.9 --- messages.py 21 Aug 2003 21:01:41 -0000 1.10 *************** *** 59,63 **** hooks.unregister("UserJoined", self.checkmsgs) mm.unregister_help(HELP) ! mm.register_perm("messages") def checkmsgs(self, server, target, user, asked): --- 59,63 ---- hooks.unregister("UserJoined", self.checkmsgs) mm.unregister_help(HELP) ! mm.unregister_perm("messages") def checkmsgs(self, server, target, user, asked): |
From: Gustavo N. <nie...@us...> - 2003-08-22 00:24:49
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv26043 Modified Files: pybot.conf Log Message: Commented out permission:admins setting. Index: pybot.conf =================================================================== RCS file: /cvsroot/pybot/pybot/pybot.conf,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** pybot.conf 29 May 2003 05:11:29 -0000 1.8 --- pybot.conf 21 Aug 2003 21:39:39 -0000 1.9 *************** *** 21,25 **** ; system (send "help permission" to pybot for more information), or just ; append an entry here and restart pybot. ! admins = niemeyer@conectiva [userdata] --- 21,25 ---- ; system (send "help permission" to pybot for more information), or just ; append an entry here and restart pybot. ! ;admins = niemeyer@conectiva [userdata] |
From: Gustavo N. <nie...@us...> - 2003-08-21 23:32:50
|
Update of /cvsroot/pybot/pybot/contrib In directory sc8-pr-cvs1:/tmp/cvs-serv12435/contrib Modified Files: PybotRemoteInfo Log Message: * modules/servercontrol.py: Allow setting the bot nick when connecting. * modules/remoteinfo.py: Added 'c' flag to handle CTCP messages. Index: PybotRemoteInfo =================================================================== RCS file: /cvsroot/pybot/pybot/contrib/PybotRemoteInfo,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** PybotRemoteInfo 2 Jun 2003 13:36:09 -0000 1.1 --- PybotRemoteInfo 21 Aug 2003 23:24:15 -0000 1.2 *************** *** 7,10 **** --- 7,12 ---- ## http://example.com/moin.cgi/PybotRemoteInfo?action=raw ## with regex \s(?:\[(?P<flags>\w*)\])?\s*(?P<trigger>.*?)\s*::(?P<msg>.*) + ## + ## Don't forget to use "allow remoteinfo". = Pybot Remote Information System = *************** *** 21,24 **** --- 23,27 ---- * '''a''' -- disable the inclusion of the user nick at the start of the line on channel messages; * '''g''' -- check even channel messages not targeted to pybot; + * '''c''' -- check also CTCP messages with ACTION type (/me) * '''s''' -- makes the regex case sensitive. |
From: Gustavo N. <nie...@us...> - 2003-08-21 23:32:47
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv12435/pybot/modules Modified Files: remoteinfo.py servercontrol.py Log Message: * modules/servercontrol.py: Allow setting the bot nick when connecting. * modules/remoteinfo.py: Added 'c' flag to handle CTCP messages. Index: remoteinfo.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/remoteinfo.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** remoteinfo.py 21 Aug 2003 18:43:08 -0000 1.2 --- remoteinfo.py 21 Aug 2003 23:24:15 -0000 1.3 *************** *** 101,104 **** --- 101,105 ---- hooks.register("Message", self.message) hooks.register("Message", self.message_remoteinfo, priority=1000) + hooks.register("CTCP", self.message_remoteinfo, priority=1000) # load remote[ ]info [from] <url> [each <n>[ ](s[econds]|m[inutes]|h[ours])] [[with|using] regex <regex>] *************** *** 131,134 **** --- 132,136 ---- hooks.unregister("Message", self.message) hooks.unregister("Message", self.message_remoteinfo, priority=1000) + hooks.unregister("CTCP", self.message_remoteinfo, priority=1000) mm.unregister_help(HELP) mm.unregister_help(HELP_SYNTAX) *************** *** 237,240 **** --- 239,244 ---- if not ('g' in flags or msg.forme): continue + if msg.ctcp and (msg.ctcp != "ACTION" or 'c' not in flags): + continue m = p.match(msg.line) if m: *************** *** 260,264 **** ret = 0 return ret ! def message(self, msg): if not msg.forme: --- 264,268 ---- ret = 0 return ret ! def message(self, msg): if not msg.forme: Index: servercontrol.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/servercontrol.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** servercontrol.py 19 Aug 2003 21:59:10 -0000 1.4 --- servercontrol.py 21 Aug 2003 23:24:15 -0000 1.5 *************** *** 17,21 **** # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! from pybot import mm, hooks, servers, main, db from pybot.user import User from string import join --- 17,21 ---- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! from pybot import mm, hooks, servers, main, db, config from pybot.user import User from string import join *************** *** 24,30 **** HELP_CONNECT = """ If you want me to connect to another server, use "connect [to] [server] ! <server> [[with|using] servername <servername>]". If you provide a ! servername, all commands and configurations will use that servername. This ! is useful since IRC servers can change their domains. """,""" To make me to disconnect or reconnect to a given server, you can use --- 24,31 ---- HELP_CONNECT = """ If you want me to connect to another server, use "connect [to] [server] ! <server> [[with|using] servername <servername>] [and] ! [[with|using] nick <nick>]". If you provide a servername, all commands and ! configurations will use that servername. This is useful since IRC servers ! can change their domains. """,""" To make me to disconnect or reconnect to a given server, you can use *************** *** 77,82 **** db.table("channel", "servername,channel,keyword") ! # connect [to] [server] <server> [[with|using] servername <servername>] ! self.re1 = re.compile(r"connect\s+(?:to\s+)?(?:server\s+)?(?P<server>\S+)(?:(?:\s+with|\s+using)?\s+servername\s+(?P<servername>\S+))?\s*$", re.I) # (re|dis)connect [to|from] <server> [with <reason>] --- 78,88 ---- db.table("channel", "servername,channel,keyword") ! if config.has_option("global", "default_nick"): ! self.default_nick = config.get("global", "default_nick") ! else: ! self.default_nick = "_pybot_" ! ! # connect [to] [server] <server> [[with|using] servername <servername>] [and] [[with|using] nick <nick>] ! self.re1 = re.compile(r"connect\s+(?:to\s+)?(?:server\s+)?(?P<server>\S+)(?:(?:\s+with|\s+using)?\s+servername\s+(?P<servername>\S+))?(?:\s+and)?(?:(?:\s+with|\s+using)?\s+nick\s+(?P<nick>\S+))?\s*$", re.I) # (re|dis)connect [to|from] <server> [with <reason>] *************** *** 223,226 **** --- 229,233 ---- host = m.group("server") servername = m.group("servername") or host + nick = m.group("nick") or self.default_nick cursor = db.cursor() cursor.execute("select * from server where servername=%s", *************** *** 233,237 **** msg.answer("%:", ["Connecting", "I'm going there", "At your order", "No problems", "Right now", "Ok"], [".", "!"]) cursor.execute("insert into server values (%s,%s,%s,%s,%s)", ! servername, "pybot", "pybot", "0", "PyBot") cursor.execute("insert into host values (%s,%s)", servername, host) --- 240,244 ---- msg.answer("%:", ["Connecting", "I'm going there", "At your order", "No problems", "Right now", "Ok"], [".", "!"]) cursor.execute("insert into server values (%s,%s,%s,%s,%s)", ! servername, nick, "pybot", "0", "PyBot") cursor.execute("insert into host values (%s,%s)", servername, host) |
From: Gustavo N. <nie...@us...> - 2003-08-21 23:32:47
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv12435 Modified Files: ChangeLog TODO pybot.conf Log Message: * modules/servercontrol.py: Allow setting the bot nick when connecting. * modules/remoteinfo.py: Added 'c' flag to handle CTCP messages. Index: ChangeLog =================================================================== RCS file: /cvsroot/pybot/pybot/ChangeLog,v retrieving revision 1.21 retrieving revision 1.22 diff -C2 -d -r1.21 -r1.22 *** ChangeLog 21 Aug 2003 20:58:04 -0000 1.21 --- ChangeLog 21 Aug 2003 23:24:15 -0000 1.22 *************** *** 1,6 **** 2003-08-21 Gustavo Niemeyer <nie...@co...> * modules/remoteinfo.py: Introduced "allow" concept, which defines which channels/servers are allowed to "see" some ! remoteinfo. * modules/messages.py: Prevent messages to the bot itself. Fixed sending of messages when user talks directly to the --- 1,8 ---- 2003-08-21 Gustavo Niemeyer <nie...@co...> + * modules/servercontrol.py: Allow setting the bot nick when + connecting. * modules/remoteinfo.py: Introduced "allow" concept, which defines which channels/servers are allowed to "see" some ! remoteinfo. Added 'c' flag to handle CTCP messages. * modules/messages.py: Prevent messages to the bot itself. Fixed sending of messages when user talks directly to the Index: TODO =================================================================== RCS file: /cvsroot/pybot/pybot/TODO,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** TODO 21 Aug 2003 18:47:19 -0000 1.11 --- TODO 21 Aug 2003 23:24:15 -0000 1.12 *************** *** 2,7 **** This are some of the features to be implemented in the future: - - A better website for pybot. - - Adapt distutils classes to install every necessary file in their respective directories. --- 2,5 ---- *************** *** 11,15 **** - Cross identification between servers (is this useful?). ! - Setup pybot nick when connecting. - Develop admin module for direct sql access. --- 9,13 ---- - Cross identification between servers (is this useful?). ! - Implement nickserv identification (with generic messages on connection). - Develop admin module for direct sql access. Index: pybot.conf =================================================================== RCS file: /cvsroot/pybot/pybot/pybot.conf,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** pybot.conf 21 Aug 2003 21:39:39 -0000 1.9 --- pybot.conf 21 Aug 2003 23:24:15 -0000 1.10 *************** *** 11,14 **** --- 11,15 ---- [global] ;http_proxy = http://127.0.0.1:8080 + ;default_nick = _pybot_ [permission] |
From: Gustavo N. <nie...@us...> - 2003-06-02 13:36:16
|
Update of /cvsroot/pybot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv22381/pybot Modified Files: server.py Log Message: * modules/remoteinfo.py: New module! * server.py: If unable to transform string to ISO-8859-1, send it in UTF8. Index: server.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/server.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** server.py 12 May 2003 23:02:58 -0000 1.5 --- server.py 2 Jun 2003 13:36:09 -0000 1.6 *************** *** 169,172 **** --- 169,177 ---- line = self._outlines[0][0]+"\r\n" del self._outlines[0] + if isinstance(line, unicode): + try: + line = line.encode('iso-8859-1') + except UnicodeError: + line = line.encode('utf8') self._socket.send(line) lines_sent = lines_sent + 1 *************** *** 296,299 **** --- 301,309 ---- self._outlines_lock.acquire() for line in self._outlines: + if isinstance(line, unicode): + try: + line = line.encode('iso-8859-1') + except UnicodeError: + line = line.encode('utf8') print line self._outlines = [] |
From: Gustavo N. <nie...@us...> - 2003-06-02 13:36:16
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv22381 Modified Files: ChangeLog TODO Log Message: * modules/remoteinfo.py: New module! * server.py: If unable to transform string to ISO-8859-1, send it in UTF8. Index: ChangeLog =================================================================== RCS file: /cvsroot/pybot/pybot/ChangeLog,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** ChangeLog 29 May 2003 05:11:29 -0000 1.16 --- ChangeLog 2 Jun 2003 13:36:09 -0000 1.17 *************** *** 1,2 **** --- 1,9 ---- + 2003-06-02 Gustavo Niemeyer <nie...@co...> + * modules/remoteinfo.py: New module! + + 2003-05-30 Gustavo Niemeyer <nie...@co...> + * server.py: If unable to transform string to ISO-8859-1, send + it in UTF8. + 2003-05-29 Gustavo Niemeyer <nie...@co...> * modules/google.py: Introduced basic google module using the Index: TODO =================================================================== RCS file: /cvsroot/pybot/pybot/TODO,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** TODO 29 May 2003 05:11:29 -0000 1.9 --- TODO 2 Jun 2003 13:36:09 -0000 1.10 *************** *** 22,24 **** - Check ideas from http://olut.ton.tut.fi/~karhu/ and http://supybot.sf.net. - --- 22,23 ---- |
From: Gustavo N. <nie...@us...> - 2003-06-02 13:36:16
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv22381/pybot/modules Modified Files: infopack.py social.py Added Files: remoteinfo.py Log Message: * modules/remoteinfo.py: New module! * server.py: If unable to transform string to ISO-8859-1, send it in UTF8. --- NEW FILE: remoteinfo.py --- # Copyright (c) 2000-2001 Gustavo Niemeyer <nie...@co...> # # This file is part of pybot. # # pybot 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. # # pybot 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 pybot; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from pybot import config, options, hooks, mm, db import thread, time import urllib import re import os HELP = """ You can ask me to load information from remote URLs using "load remote info [from] <url> [each <n>(s|m|h)] [[with] regex <regex>]" and to unload or reload using "(re|un)load remote info [from] <url>". To check what is being remotely loaded, use "show remote[ ]info[s]". To load and unload remote infos you'll need the "remoteinfo" permission, and to show and reload you need either the "remoteinfo" or the "remoteinforeload" permission. """,""" The default reload interval is 10 minutes. To understand how to build the regex for the remote info command or how to build the remote information check "help remote info syntax". """ HELP_SYNTAX = """ Loading remote information consists in reading a remote URL, splitting that information in lines, checking each line against a regular expression, and including the matching lines as items in a database for further processing. Each of these items will have a "trigger", which is also a regular expression, a corresponding "msg" which will be sent when the trigger is activated, and some "flags" which configure the item. """,""" Even though you may provide your own regular expression that is checked against each item line, the default one is similar to the following: "(<(?P<flags>.*?)>)? (?P<trigger>.*?) => (?P<msg>.*)". It means that you could use in the given URL lines like "Does it work\? => Yes! it does!", or with flags and groups like "<g> Hi (?P<someone>.*) => /me says hi to %(someone)s as well.". """,""" Messages may start with /me or /notice to have the expected meanings. The current supported flags are: 'a' to disable the inclusion of the user nick at the start of the line on channel messages; 'g' to check even channel messages not targeted to myself; and 's' which makes the trigger case sensitive. """ PERM_REMOTEINFO = """ Users with the "remoteinfo" permission can work with remote information settings. Check "help remoteinfo" for more information. """ PERM_RELOADREMOTEINFO = """ Users with the "reloadremoteinfo" permission can ask me to reload information from remote URLs. Notice that the "remoteinfo" permission allows that as well. Check "help remoteinfo" for more information. """ DEFAULTREGEX = "\s*(?:<(?P<flags>[^>]*)>\s*)?(?P<trigger>.*?)\s*=>\s*(?P<msg>.*)" DEFAULTINTERVAL = "10m" class Info: def __init__(self): self.lasttime = 0 self.patterns = {} def __repr__(self): return "<Info: %s>" % `self.patterns` class RemoteInfo: def __init__(self): db.table("remoteinfo", "url,regex,interval") self.info = options.get("RemoteInfo.info", {}) self.info_lock = options.get("RemoteInfo.info_lock", {}) self.lock = thread.allocate_lock() if config.has_option("global", "http_proxy"): self.proxy = config.get("global", "http_proxy") else: self.proxy = None hooks.register("Message", self.message) hooks.register("Message", self.message_remoteinfo, priority=1000) # load remote[ ]info [from] <url> [each <n>[ ](s[econds]|m[inutes]|h[ours])] [[with|using] regex <regex>] self.re1 = re.compile(r"load\s+remote\s*info\s+(?:from\s+)?(?P<url>\S+)(?:\s+each\s+(?P<interval>[0-9]+)\s*(?P<intervalunit>se?c?o?n?d?s?|mi?n?u?t?e?s?|ho?u?r?s?))?(?:\s+(?:with\s+|using\s+)?regex\s+(?P<regex>.*))?\s*$", re.I) # (un|re)load remote[ ]info [from] <url> self.re2 = re.compile(r"(?P<cmd>un|re)load\s+remote\s*info\s+(?:from\s+)?(?P<url>\S+)\s*$", re.I) # show remote[ ]info[s] self.re3 = re.compile(r"show\s+remote\s+infos?$", re.I) # remote[ ]info mm.register_help("remote\s*info?$", HELP, "remoteinfo") mm.register_help("remote\s*infos?\s*syntax$", HELP_SYNTAX, "remoteinfo syntax") mm.register_perm("remoteinfo", PERM_REMOTEINFO) mm.register_perm("reloadremoteinfo", PERM_RELOADREMOTEINFO) mm.hooktimer(30, self.reload_all, ()) if not self.info: self.reload_all() def unload(self): mm.unhooktimer(30, self.reload_all, ()) hooks.unregister("Message", self.message) hooks.unregister("Message", self.message_remoteinfo, priority=1000) mm.unregister_help(HELP) mm.unregister_help(HELP_SYNTAX) mm.unregister_perm("remoteinfo") mm.unregister_perm("reloadremoteinfo") def lock_url(self, url): self.lock.acquire() try: lock = self.info_lock[url] except KeyError: lock = thread.allocate_lock() self.info_lock[url] = lock self.lock.release() return lock.acquire(0) def unlock_url(self, url): try: lock = self.info_lock[url].release() except KeyError: pass def reload_all(self): cursor = db.cursor() cursor.execute("select * from remoteinfo") now = time.time() for row in cursor.fetchall(): info = self.info.get(row.url) if not info or now-info.lasttime > int(row.interval): self.reload(row.url, row.regex) def reload(self, url, regex=None): if not regex: cursor = db.cursor() cursor.execute("select regex from remoteinfo where url=%s", (url,)) row = cursor.fetchone() if not row: return regex = row.regex if self.lock_url(url): thread.start_new_thread(self._reload, (url, regex)) def _reload(self, url, regex): try: urlopener = urllib.URLopener() if self.proxy: proxy = {"http": self.proxy} urlopener.proxies.update(proxy) try: infourl = urlopener.open(url) except: import traceback traceback.print_exc() else: info = Info() p = re.compile(regex) for line in infourl.read().splitlines(): m = p.match(line) if m: try: trigger = m.group("trigger") msg = m.group("msg") flags = m.group("flags") or "" except IndexError: continue if not trigger or not msg: continue trigger = trigger.strip() if trigger[-1] != "$": trigger += "$" msg = msg.strip() flags = flags.strip() try: if 's' in flags: trigger_re = re.compile(trigger) else: trigger_re = re.compile(trigger, re.I) except re.error: continue info.patterns[trigger_re] = (flags, msg) infourl.close() info.lasttime = time.time() self.info[url] = info finally: self.unlock_url(url) def message_remoteinfo(self, msg): ret = None for info in self.info.values(): for p in info.patterns: flags, infomsg = info.patterns[p] if not ('g' in flags or msg.forme): continue m = p.match(msg.line) if m: try: infomsg %= m.groupdict() except KeyError: continue ctcp = None notice = 0 withnick = ("a" not in flags) if infomsg.startswith("/me "): withnick = 0 infomsg = infomsg[4:] ctcp = "ACTION" elif infomsg.startswith("/notice "): withnick = 0 infomsg = infomsg[8:] notice = 1 if withnick: msg.answer("%:", infomsg, ctcp=ctcp, notice=notice) else: msg.answer(infomsg, ctcp=ctcp, notice=notice) ret = 0 return ret def message(self, msg): if not msg.forme: return None m = self.re1.match(msg.line) if m: if mm.hasperm(msg, "remoteinfo"): url = m.group("url") regex = (m.group("regex") or DEFAULTREGEX).strip() interval = m.group("interval") if not interval: interval = DEFAULTINTERVAL[:-1] unit = DEFAULTINTERVAL[-1] else: unit = m.group("intervalunit")[0] unitindex = ["s", "m", "h"].index(unit) unitfactor = [1, 60, 3600][unitindex] try: interval = int(interval)*unitfactor if interval == 0: raise ValueError except ValueError: msg.answer("%:", ["Hummm...", "Oops!", "Heh..."], ["This interval is not valid", "There's something wrong with the " "interval you provided"], ["!", "."]) return 0 try: m = re.compile(regex) except re.error: msg.answer("%:", ["Hummm...", "Oops!", "Heh..."], ["This regex is not valid", "There's something wrong with the " "regex you provided"], ["!", "."]) return 0 cursor = db.cursor() cursor.execute("select * from remoteinfo where url=%s", (url,)) if cursor.rowcount: msg.answer("%:", ["I can't do that.", "Nope.", None], ["I'm already loading that url", "Can't insert repeated urls", "This url is already in my database"], [".", "!"]) else: cursor.execute("insert into remoteinfo values " "(%s,%s,%s)", (url, regex, interval)) msg.answer("%:", ["Loading", "No problems", "Starting right now", "Sure"], [".", "!"]) self.reload(url, regex) else: msg.answer("%:", [("You're not", ["allowed to change remote info options", "that good", "allowed to do this"]), "Nope"], [".", "!"]) return 0 m = self.re2.match(msg.line) if m: cmd = m.group("cmd") url = m.group("url") cursor = db.cursor() cursor.execute("select * from remoteinfo where url=%s", (url,)) if not cursor.rowcount: msg.answer("%:", ["I can't do that.", "Nope.", None], ["I'm not loading that url", "This url is not in my database"], [".", "!"]) elif cmd == "un": if mm.hasperm(msg, "remoteinfo"): if not self.lock_url(url): msg.answer("%:", "Can't do that now. URL is " "being loaded in this exact " "moment. Try again in a few " "seconds.") else: if url in self.info: del self.info[url] if url in self.info_lock: del self.info_lock[url] # Unlocking is not really necessary, but # politically right. ;-) self.unlock_url(url) cursor.execute("delete from remoteinfo where url=%s", (url,)) msg.answer("%:", ["Done", "Of course", "Ready"], [".", "!"]) else: msg.answer("%:", [("You're not", ["allowed to change " "remote info options", "that good", "allowed to do this"]), "Nope"], [".", "!"]) else: if mm.hasperm(msg, "remoteinfo") or \ mm.hasperm(msg, "reloadremoteinfo"): msg.answer("%:", ["Will do that", "In a moment", "Will be ready in a moment", "Starting right now"], [".", "!"]) self.reload(url) else: msg.answer("%:", [("You're not", ["allowed to reload remote infos", "that good", "allowed to do this"]), "Nope"], [".", "!"]) return 0 m = self.re3.match(msg.line) if m: if mm.hasperm(msg, "remoteinfo") or \ mm.hasperm(msg, "reloadremoteinfo"): cursor = db.cursor() cursor.execute("select * from remoteinfo") if cursor.rowcount: msg.answer("%:", "The following remote info urls are " "being loaded:") for row in cursor.fetchall(): interval = int(row.interval) if interval % 3600 == 0: interval /= 3600 unit = "hour" elif interval % 60 == 0: interval /= 60 unit = "minute" else: unit = "second" if interval > 1: unit += "s" msg.answer("-", row.url, "each", str(interval), unit, "with regex", row.regex) else: msg.answer("%:", "No remote info urls are currently " "being loaded", [".", "!"]) else: msg.answer("%:", "You're not", ["allowed to show remote infos", "that good", "allowed to do this"], [".", "!"]) return 0 def __loadmodule__(): global mod mod = RemoteInfo() def __unloadmodule__(): global mod mod.unload() del mod # vim:ts=4:sw=4:et Index: infopack.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/infopack.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** infopack.py 16 May 2003 17:28:03 -0000 1.6 --- infopack.py 2 Jun 2003 13:36:09 -0000 1.7 *************** *** 36,40 **** PERM_INFOPACKADMIN = """ Users with the "infopackadmin" permission can change infopack ! settings. Check "help infopack" for more informatino. """ --- 36,40 ---- PERM_INFOPACKADMIN = """ Users with the "infopackadmin" permission can change infopack ! settings. Check "help infopack" for more information. """ Index: social.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/social.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** social.py 29 May 2003 16:31:13 -0000 1.8 --- social.py 2 Jun 2003 13:36:10 -0000 1.9 *************** *** 27,32 **** hooks.register("UnhandledMessage", self.unhandled_message) ! # (hi|hello|olá|ola|hola) [there|everybody|all|guys|folks|people|pessoal|pybot] [!|.] ! self.re1 = re.compile(r'(?:hi|hello|olá|ola|hola)(?:\s+(?:there|everybody|all|guys|folks|people|pessoal|(?P<nick>\w+)))?\s*[!.]*$', re.I) # pybot! --- 27,32 ---- hooks.register("UnhandledMessage", self.unhandled_message) ! # (re|hi|hello|hallo|olá|ola|hola|wb|welcome|good (morning|afternoon|evening)|bom dia|boa tarde|gutten tag) [there|back|everybody|all|guys|folks|people|pessoal|(a|para) todos|pybot] [!|.] ! self.re1 = re.compile(r'(?:re|hi|hello|hallo|olá|ola|hola|wb|welcome\s+back|good\s+(?:morning|afternoon|evening)|bom\s+dia|boa\s+tarde|gutten\s+tag)(?:\s+(?:there|everybody|all|guys|folks|people|pessoal|(à|a|para)\s+todos|(?P<nick>\w+)))?\s*[!.]*$', re.I) # pybot! |
From: Gustavo N. <nie...@us...> - 2003-06-02 13:36:16
|
Update of /cvsroot/pybot/pybot/contrib In directory sc8-pr-cvs1:/tmp/cvs-serv22381/contrib Added Files: PybotRemoteInfo Log Message: * modules/remoteinfo.py: New module! * server.py: If unable to transform string to ISO-8859-1, send it in UTF8. --- NEW FILE: PybotRemoteInfo --- ## This is a sample file for MoinMoin (http://moin.sourceforge.net) to ## use with the remoteinfo module. ## ## Add in pybot with the following message: ## ## pybot: load remote info from ## http://example.com/moin.cgi/PybotRemoteInfo?action=raw ## with regex \s(?:\[(?P<flags>\w*)\])?\s*(?P<trigger>.*?)\s*::(?P<msg>.*) = Pybot Remote Information System = == Documentation == This file will be loaded into pybot each 10 minutes to include additional intelligence in the bot. Each line in that file has the following syntax: {{{ [flags] regex:: message}}} Notice that the line must start with a space, and the flags part is optional. The following flags are understood: * '''a''' -- disable the inclusion of the user nick at the start of the line on channel messages; * '''g''' -- check even channel messages not targeted to pybot; * '''s''' -- makes the regex case sensitive. The message may also be prefixed by '''/me''' or '''/notice''' to have the expected meanings. Group replacement may happen on the message. Here is a nice example: {{{ [g] Hi (?P<someone>.*?)[.!]*:: /me says hi to %(someone)s as well!}}} If you don't want to wait 10 minutes to see your changes in pybot, you can ask him to load them immediately, with the following message: {{{pybot: reload remote info from https://moin.conectiva.com.br/PybotRemoteInfo?action=raw}}} == Information == Is remote\s*info working\?:: Sure! |
From: Gustavo N. <nie...@us...> - 2003-05-29 17:44:46
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv6390 Modified Files: google.py Log Message: Changed encoding to latin1. Index: google.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/google.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** google.py 29 May 2003 14:39:52 -0000 1.3 --- google.py 29 May 2003 17:44:43 -0000 1.4 *************** *** 66,70 **** SOAPpy.booleanType(1), "", SOAPpy.booleanType(0), "", ! "", "") if not len(result.resultElements): msg.answer("%:", ["Nothing found", --- 66,70 ---- SOAPpy.booleanType(1), "", SOAPpy.booleanType(0), "", ! "latin1", "latin1") if not len(result.resultElements): msg.answer("%:", ["Nothing found", |
From: Gustavo N. <nie...@us...> - 2003-05-29 16:31:16
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv5979 Modified Files: social.py Log Message: Added some spanish and portuguese greeting messages. Index: social.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/social.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** social.py 12 May 2003 20:42:21 -0000 1.7 --- social.py 29 May 2003 16:31:13 -0000 1.8 *************** *** 27,32 **** hooks.register("UnhandledMessage", self.unhandled_message) ! # (hi|hello) [there|everybody|all|guys|folks|pybot] [!|.] ! self.re1 = re.compile(r'(?:hi|hello)(?:\s+(?:there|everybody|all|guys|folks|(?P<nick>\w+)))?\s*[!.]*$', re.I) # pybot! --- 27,32 ---- hooks.register("UnhandledMessage", self.unhandled_message) ! # (hi|hello|olá|ola|hola) [there|everybody|all|guys|folks|people|pessoal|pybot] [!|.] ! self.re1 = re.compile(r'(?:hi|hello|olá|ola|hola)(?:\s+(?:there|everybody|all|guys|folks|people|pessoal|(?P<nick>\w+)))?\s*[!.]*$', re.I) # pybot! |
From: Gustavo N. <nie...@us...> - 2003-05-29 14:39:56
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv26715 Modified Files: google.py Log Message: Just inform the user about the google permission in the help text. Index: google.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/google.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** google.py 29 May 2003 14:33:02 -0000 1.2 --- google.py 29 May 2003 14:39:52 -0000 1.3 *************** *** 25,29 **** You may ask for google search results using "[search] google [<n>]: <search>". Unless the parameter n is used, the default is to show the ! first result found (with n=0). """ --- 25,30 ---- You may ask for google search results using "[search] google [<n>]: <search>". Unless the parameter n is used, the default is to show the ! first result found (with n=0). You'll need the "google" permission to ! use that feature. """ |
From: Gustavo N. <nie...@us...> - 2003-05-29 14:33:05
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv23087 Modified Files: google.py Log Message: If there's no snippet, don't even show '-'. Index: google.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/google.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** google.py 29 May 2003 05:11:29 -0000 1.1 --- google.py 29 May 2003 14:33:02 -0000 1.2 *************** *** 77,81 **** title = re.sub("<.*?>", "", e.title) snippet = re.sub("<.*?>", "", e.snippet) ! msg.answer("%:", "%s <%s> - %s" % (title, url, snippet)) except SOAPpy.Errors.Error: import traceback --- 77,84 ---- title = re.sub("<.*?>", "", e.title) snippet = re.sub("<.*?>", "", e.snippet) ! if snippet: ! msg.answer("%:", "%s <%s> - %s" % (title, url, snippet)) ! else: ! msg.answer("%:", "%s <%s>" % (title, url)) except SOAPpy.Errors.Error: import traceback |
From: Gustavo N. <nie...@us...> - 2003-05-29 05:11:33
|
Update of /cvsroot/pybot/pybot/pybot/util/SOAPpy In directory sc8-pr-cvs1:/tmp/cvs-serv3743/pybot/util/SOAPpy Added Files: Client.py Config.py Errors.py NS.py Parser.py SOAP.py SOAPBuilder.py Server.py Types.py Utilities.py WSDL.py __init__.py version.py Log Message: * modules/google.py: Introduced basic google module using the google SOAP api. * pybot/util/SOAPpy: Added SOAPpy 0.10.1 to the source tree. --- NEW FILE: Client.py --- """ ################################################################################ # # SOAPpy - Cayce Ullman (ca...@ac...) # Brian Matthews (bl...@ac...) # Gregory Warnes (gre...@gr...) # Christopher Blunck (bl...@gs...) # ################################################################################ # Copyright (c) 2003, Pfizer # Copyright (c) 2001, Cayce Ullman. # Copyright (c) 2001, Brian Matthews. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of actzero, inc. nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ################################################################################ """ from __future__ import nested_scopes #import xml.sax import urllib from types import * import re # SOAPpy modules from Errors import * from Config import Config from Parser import parseSOAPRPC from SOAPBuilder import buildSOAP from Utilities import * ident = '$Id: Client.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' from version import __version__ ################################################################################ # Client ################################################################################ def SOAPUserAgent(): return "SOAPpy " + __version__ + " (pywebsvcs.sf.net)" class SOAPAddress: def __init__(self, url, config = Config): proto, uri = urllib.splittype(url) # apply some defaults if uri[0:2] != '//': if proto != None: uri = proto + ':' + uri uri = '//' + uri proto = 'http' host, path = urllib.splithost(uri) try: int(host) host = 'localhost:' + host except: pass if not path: path = '/' if proto not in ('http', 'https'): raise IOError, "unsupported SOAP protocol" if proto == 'https' and not config.SSLclient: raise AttributeError, \ "SSL client not supported by this Python installation" self.user,host = urllib.splituser(host) self.proto = proto self.host = host self.path = path def __str__(self): return "%(proto)s://%(host)s%(path)s" % self.__dict__ __repr__ = __str__ class HTTPTransport: def getNS(self, original_namespace, data): """Extract the (possibly extended) namespace from the returned SOAP message.""" if type(original_namespace) == StringType: pattern="xmlns:\w+=['\"](" + original_namespace + ")['\"]" match = re.search(pattern, data) if match: return match.group(1) else: return original_namespace else: return original_namespace # Need a Timeout someday? def call(self, addr, data, namespace, soapaction = '', encoding = None, http_proxy = None, config = Config): import httplib if not isinstance(addr, SOAPAddress): addr = SOAPAddress(addr, config) # Build a request if http_proxy: real_addr = http_proxy real_path = addr.proto + "://" + addr.host + addr.path else: real_addr = addr.host real_path = addr.path if addr.proto == 'https': r = httplib.HTTPS(real_addr) else: r = httplib.HTTP(real_addr) r.putrequest("POST", real_path) r.putheader("Host", addr.host) r.putheader("User-agent", SOAPUserAgent()) t = 'text/xml'; if encoding != None: t += '; charset="%s"' % encoding r.putheader("Content-type", t) r.putheader("Content-length", str(len(data))) # if user is not a user:passwd format # we'll receive a failure from the server. . .I guess (??) if addr.user != None: val = base64.encodestring(addr.user) r.putheader('Authorization','Basic ' + val.replace('\012','')) r.putheader("SOAPAction", '"%s"' % soapaction) if config.dumpHeadersOut: s = 'Outgoing HTTP headers' debugHeader(s) print "POST %s %s" % (real_path, r._http_vsn_str) print "Host:", addr.host print "User-agent: SOAPpy " + __version__ + " (http://pywebsvcs.sf.net)" print "Content-type:", t print "Content-length:", len(data) print 'SOAPAction: "%s"' % soapaction debugFooter(s) r.endheaders() if config.dumpSOAPOut: s = 'Outgoing SOAP' debugHeader(s) print data, if data[-1] != '\n': print debugFooter(s) # send the payload r.send(data) # read response line code, msg, headers = r.getreply() if config.dumpHeadersIn: s = 'Incoming HTTP headers' debugHeader(s) if headers.headers: print "HTTP/1.? %d %s" % (code, msg) print "\n".join(map (lambda x: x.strip(), headers.headers)) else: print "HTTP/0.9 %d %s" % (code, msg) debugFooter(s) data = r.getfile().read() if config.dumpSOAPIn: s = 'Incoming SOAP' debugHeader(s) print data, if (len(data)>0) and (data[-1] != '\n'): print debugFooter(s) if code not in (200, 500): raise HTTPError(code, msg) def startswith(string, val): return string[0:len(val)] == val content_type = headers.get("content-type","text/xml") if code == 500 and not startswith(content_type, "text/xml"): raise HTTPError(code, msg) # get the new namespace if namespace is None: new_ns = None else: new_ns = self.getNS(namespace, data) # return response payload return data, new_ns ################################################################################ # SOAP Proxy ################################################################################ class SOAPProxy: def __init__(self, proxy, namespace = None, soapaction = '', header = None, methodattrs = None, transport = HTTPTransport, encoding = 'UTF-8', throw_faults = 1, unwrap_results = 1, http_proxy=None, config = Config,noroot = 0): # Test the encoding, raising an exception if it's not known if encoding != None: ''.encode(encoding) self.proxy = SOAPAddress(proxy, config) self.namespace = namespace self.soapaction = soapaction self.header = header self.methodattrs = methodattrs self.transport = transport() self.encoding = encoding self.throw_faults = throw_faults self.unwrap_results = unwrap_results self.http_proxy = http_proxy self.config = config self.noroot = noroot def invoke(self, method, args): return self.__call(method, args, {}) def __call(self, name, args, kw, ns = None, sa = None, hd = None, ma = None): ns = ns or self.namespace ma = ma or self.methodattrs if sa: # Get soapaction if type(sa) == TupleType: sa = sa[0] else: sa = self.soapaction if hd: # Get header if type(hd) == TupleType: hd = hd[0] else: hd = self.header hd = hd or self.header if ma: # Get methodattrs if type(ma) == TupleType: ma = ma[0] else: ma = self.methodattrs ma = ma or self.methodattrs m = buildSOAP(args = args, kw = kw, method = name, namespace = ns, header = hd, methodattrs = ma, encoding = self.encoding, config = self.config,noroot = self.noroot) r, self.namespace = self.transport.call(self.proxy, m, ns, sa, encoding = self.encoding, http_proxy = self.http_proxy, config = self.config) p, attrs = parseSOAPRPC(r, attrs = 1) try: throw_struct = self.throw_faults and \ isinstance (p, faultType) except: throw_struct = 0 if throw_struct: print p raise p # Bubble a regular result up, if there is only element in the # struct, assume that is the result and return it. # Otherwise it will return the struct with all the elements # as attributes. if self.unwrap_results: try: count = 0 for i in p.__dict__.keys(): if i[0] != "_": # don't move the private stuff count += 1 t = getattr(p, i) if count == 1: p = t # Only one piece of data, bubble it up except: pass if self.config.returnAllAttrs: return p, attrs return p def _callWithBody(self, body): return self.__call(None, body, {}) def __getattr__(self, name): # hook to catch method calls if name == '__del__': raise AttributeError, name return self.__Method(self.__call, name, config = self.config) # To handle attribute wierdness class __Method: # Some magic to bind a SOAP method to an RPC server. # Supports "nested" methods (e.g. examples.getStateName) -- concept # borrowed from xmlrpc/soaplib -- www.pythonware.com # Altered (improved?) to let you inline namespaces on a per call # basis ala SOAP::LITE -- www.soaplite.com def __init__(self, call, name, ns = None, sa = None, hd = None, ma = None, config = Config): self.__call = call self.__name = name self.__ns = ns self.__sa = sa self.__hd = hd self.__ma = ma self.__config = config return def __call__(self, *args, **kw): if self.__name[0] == "_": if self.__name in ["__repr__","__str__"]: return self.__repr__() else: return self.__f_call(*args, **kw) else: return self.__r_call(*args, **kw) def __getattr__(self, name): if name == '__del__': raise AttributeError, name if self.__name[0] == "_": # Don't nest method if it is a directive return self.__class__(self.__call, name, self.__ns, self.__sa, self.__hd, self.__ma) return self.__class__(self.__call, "%s.%s" % (self.__name, name), self.__ns, self.__sa, self.__hd, self.__ma) def __f_call(self, *args, **kw): if self.__name == "_ns": self.__ns = args elif self.__name == "_sa": self.__sa = args elif self.__name == "_hd": self.__hd = args elif self.__name == "_ma": self.__ma = args return self def __r_call(self, *args, **kw): return self.__call(self.__name, args, kw, self.__ns, self.__sa, self.__hd, self.__ma) def __repr__(self): return "<%s at %d>" % (self.__class__, id(self)) --- NEW FILE: Config.py --- """ ################################################################################ # Copyright (c) 2003, Pfizer # Copyright (c) 2001, Cayce Ullman. # Copyright (c) 2001, Brian Matthews. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of actzero, inc. nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ################################################################################ """ ident = '$Id: Config.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' import copy import socket try: from M2Crypto import SSL except: pass from NS import NS ################################################################################ # Configuration class ################################################################################ class SOAPConfig: __readonly = ('SSLserver', 'SSLclient') def __init__(self, config = None, **kw): d = self.__dict__ if config: if not isinstance(config, SOAPConfig): raise AttributeError, \ "initializer must be SOAPConfig instance" s = config.__dict__ for k, v in s.items(): if k[0] != '_': d[k] = v else: # Setting debug also sets returnFaultInfo, dumpFaultInfo, # dumpHeadersIn, dumpHeadersOut, dumpSOAPIn, and dumpSOAPOut self.debug = 0 # Setting namespaceStyle sets typesNamespace, typesNamespaceURI, # schemaNamespace, and schemaNamespaceURI self.namespaceStyle = '1999' self.strictNamespaces = 0 self.typed = 1 self.buildWithNamespacePrefix = 1 self.returnAllAttrs = 0 # New argument name handling mechanism. See # README.MethodParameterNaming for details self.specialArgs = 1 try: SSL; d['SSLserver'] = 1 except: d['SSLserver'] = 0 try: socket.ssl; d['SSLclient'] = 1 except: d['SSLclient'] = 0 for k, v in kw.items(): if k[0] != '_': setattr(self, k, v) def __setattr__(self, name, value): if name in self.__readonly: raise AttributeError, "readonly configuration setting" d = self.__dict__ if name in ('typesNamespace', 'typesNamespaceURI', 'schemaNamespace', 'schemaNamespaceURI'): if name[-3:] == 'URI': base, uri = name[:-3], 1 else: base, uri = name, 0 if type(value) == StringType: if NS.NSMAP.has_key(value): n = (value, NS.NSMAP[value]) elif NS.NSMAP_R.has_key(value): n = (NS.NSMAP_R[value], value) else: raise AttributeError, "unknown namespace" elif type(value) in (ListType, TupleType): if uri: n = (value[1], value[0]) else: n = (value[0], value[1]) else: raise AttributeError, "unknown namespace type" d[base], d[base + 'URI'] = n try: d['namespaceStyle'] = \ NS.STMAP_R[(d['typesNamespace'], d['schemaNamespace'])] except: d['namespaceStyle'] = '' elif name == 'namespaceStyle': value = str(value) if not NS.STMAP.has_key(value): raise AttributeError, "unknown namespace style" d[name] = value n = d['typesNamespace'] = NS.STMAP[value][0] d['typesNamespaceURI'] = NS.NSMAP[n] n = d['schemaNamespace'] = NS.STMAP[value][1] d['schemaNamespaceURI'] = NS.NSMAP[n] elif name == 'debug': d[name] = \ d['returnFaultInfo'] = \ d['dumpFaultInfo'] = \ d['dumpHeadersIn'] = \ d['dumpHeadersOut'] = \ d['dumpSOAPIn'] = \ d['dumpSOAPOut'] = value else: d[name] = value Config = SOAPConfig() --- NEW FILE: Errors.py --- """ ################################################################################ # # SOAPpy - Cayce Ullman (ca...@ac...) # Brian Matthews (bl...@ac...) # Gregory Warnes (gre...@gr...) # Christopher Blunck (bl...@gs...) # ################################################################################ # Copyright (c) 2003, Pfizer # Copyright (c) 2001, Cayce Ullman. # Copyright (c) 2001, Brian Matthews. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of actzero, inc. nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ################################################################################ """ ident = '$Id: Errors.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' import exceptions ################################################################################ # Exceptions ################################################################################ class Error(exceptions.Exception): def __init__(self, msg): self.msg = msg def __str__(self): return "<Error : %s>" % self.msg __repr__ = __str__ def __call__(self): return (msg,) class RecursionError(Error): pass class UnknownTypeError(Error): pass class HTTPError(Error): # indicates an HTTP protocol error def __init__(self, code, msg): self.code = code self.msg = msg def __str__(self): return "<HTTPError %s %s>" % (self.code, self.msg) __repr__ = __str__ def __call___(self): return (self.code, self.msg, ) class UnderflowError(exceptions.ArithmeticError): pass --- NEW FILE: NS.py --- """ ################################################################################ # # SOAPpy - Cayce Ullman (ca...@ac...) # Brian Matthews (bl...@ac...) # Gregory Warnes (gre...@gr...) # Christopher Blunck (bl...@gs...) # ################################################################################ # Copyright (c) 2003, Pfizer # Copyright (c) 2001, Cayce Ullman. # Copyright (c) 2001, Brian Matthews. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of actzero, inc. nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ################################################################################ """ from __future__ import nested_scopes ident = '$Id: NS.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' __version__ = "0.9.9-pre6-CVS" ############################################################################## # Namespace Class ################################################################################ def invertDict(dict): d = {} for k, v in dict.items(): d[v] = k return d class NS: XML = "http://www.w3.org/XML/1998/namespace" ENV = "http://schemas.xmlsoap.org/soap/envelope/" ENC = "http://schemas.xmlsoap.org/soap/encoding/" XSD = "http://www.w3.org/1999/XMLSchema" XSD2 = "http://www.w3.org/2000/10/XMLSchema" XSD3 = "http://www.w3.org/2001/XMLSchema" XSD_L = [XSD, XSD2, XSD3] EXSD_L= [ENC, XSD, XSD2, XSD3] XSI = "http://www.w3.org/1999/XMLSchema-instance" XSI2 = "http://www.w3.org/2000/10/XMLSchema-instance" XSI3 = "http://www.w3.org/2001/XMLSchema-instance" XSI_L = [XSI, XSI2, XSI3] URN = "http://soapinterop.org/xsd" # For generated messages XML_T = "xml" ENV_T = "SOAP-ENV" ENC_T = "SOAP-ENC" XSD_T = "xsd" XSD2_T= "xsd2" XSD3_T= "xsd3" XSI_T = "xsi" XSI2_T= "xsi2" XSI3_T= "xsi3" URN_T = "urn" NSMAP = {ENV_T: ENV, ENC_T: ENC, XSD_T: XSD, XSD2_T: XSD2, XSD3_T: XSD3, XSI_T: XSI, XSI2_T: XSI2, XSI3_T: XSI3, URN_T: URN} NSMAP_R = invertDict(NSMAP) STMAP = {'1999': (XSD_T, XSI_T), '2000': (XSD2_T, XSI2_T), '2001': (XSD3_T, XSI3_T)} STMAP_R = invertDict(STMAP) def __init__(self): raise Error, "Don't instantiate this" --- NEW FILE: Parser.py --- # SOAPpy modules from Config import Config from Types import * from NS import NS from Utilities import * import xml.sax from wstools.XMLname import fromXMLname try: from M2Crypto import SSL except: pass ident = '$Id: Parser.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' ################################################################################ # SOAP Parser ################################################################################ class RefHolder: def __init__(self, name, frame): self.name = name self.parent = frame self.pos = len(frame) self.subpos = frame.namecounts.get(name, 0) def __repr__(self): return "<%s %s at %d>" % (self.__class__, self.name, id(self)) class SOAPParser(xml.sax.handler.ContentHandler): class Frame: def __init__(self, name, kind = None, attrs = {}, rules = {}): self.name = name self.kind = kind self.attrs = attrs self.rules = rules self.contents = [] self.names = [] self.namecounts = {} self.subattrs = [] def append(self, name, data, attrs): self.names.append(name) self.contents.append(data) self.subattrs.append(attrs) if self.namecounts.has_key(name): self.namecounts[name] += 1 else: self.namecounts[name] = 1 def _placeItem(self, name, value, pos, subpos = 0, attrs = None): self.contents[pos] = value if attrs: self.attrs.update(attrs) def __len__(self): return len(self.contents) def __repr__(self): return "<%s %s at %d>" % (self.__class__, self.name, id(self)) def __init__(self, rules = None): xml.sax.handler.ContentHandler.__init__(self) self.body = None self.header = None self.attrs = {} self._data = None self._next = "E" # Keeping state for message validity self._stack = [self.Frame('SOAP')] # Make two dictionaries to store the prefix <-> URI mappings, and # initialize them with the default self._prem = {NS.XML_T: NS.XML} self._prem_r = {NS.XML: NS.XML_T} self._ids = {} self._refs = {} self._rules = rules def startElementNS(self, name, qname, attrs): # Workaround two sax bugs if name[0] == None and name[1][0] == ' ': name = (None, name[1][1:]) else: name = tuple(name) # First some checking of the layout of the message if self._next == "E": if name[1] != 'Envelope': raise Error, "expected `SOAP-ENV:Envelope', gto `%s:%s'" % \ (self._prem_r[name[0]], name[1]) if name[0] != NS.ENV: raise faultType, ("%s:VersionMismatch" % NS.ENV_T, "Don't understand version `%s' Envelope" % name[0]) else: self._next = "HorB" elif self._next == "HorB": if name[0] == NS.ENV and name[1] in ("Header", "Body"): self._next = None else: raise Error, \ "expected `SOAP-ENV:Header' or `SOAP-ENV:Body', " \ "got `%s'" % self._prem_r[name[0]] + ':' + name[1] elif self._next == "B": if name == (NS.ENV, "Body"): self._next = None else: raise Error, "expected `SOAP-ENV:Body', got `%s'" % \ self._prem_r[name[0]] + ':' + name[1] elif self._next == "": raise Error, "expected nothing, got `%s'" % \ self._prem_r[name[0]] + ':' + name[1] if len(self._stack) == 2: rules = self._rules else: try: rules = self._stack[-1].rules[name[1]] except: rules = None if type(rules) not in (NoneType, DictType): kind = rules else: kind = attrs.get((NS.ENC, 'arrayType')) if kind != None: del attrs._attrs[(NS.ENC, 'arrayType')] i = kind.find(':') if i >= 0: kind = (self._prem[kind[:i]], kind[i + 1:]) else: kind = None self.pushFrame(self.Frame(name[1], kind, attrs._attrs, rules)) self._data = '' # Start accumulating def pushFrame(self, frame): self._stack.append(frame) def popFrame(self): return self._stack.pop() def endElementNS(self, name, qname): # Workaround two sax bugs if name[0] == None and name[1][0] == ' ': ns, name = None, name[1][1:] else: ns, name = tuple(name) name = fromXMLname(name) # convert to SOAP 1.2 XML name encoding if self._next == "E": raise Error, "didn't get SOAP-ENV:Envelope" if self._next in ("HorB", "B"): raise Error, "didn't get SOAP-ENV:Body" cur = self.popFrame() attrs = cur.attrs idval = None if attrs.has_key((None, 'id')): idval = attrs[(None, 'id')] if self._ids.has_key(idval): raise Error, "duplicate id `%s'" % idval del attrs[(None, 'id')] root = 1 if len(self._stack) == 3: if attrs.has_key((NS.ENC, 'root')): root = int(attrs[(NS.ENC, 'root')]) # Do some preliminary checks. First, if root="0" is present, # the element must have an id. Next, if root="n" is present, # n something other than 0 or 1, raise an exception. if root == 0: if idval == None: raise Error, "non-root element must have an id" elif root != 1: raise Error, "SOAP-ENC:root must be `0' or `1'" del attrs[(NS.ENC, 'root')] while 1: href = attrs.get((None, 'href')) if href: if href[0] != '#': raise Error, "only do local hrefs right now" if self._data != None and self._data.strip() != '': raise Error, "hrefs can't have data" href = href[1:] if self._ids.has_key(href): data = self._ids[href] else: data = RefHolder(name, self._stack[-1]) if self._refs.has_key(href): self._refs[href].append(data) else: self._refs[href] = [data] del attrs[(None, 'href')] break kind = None if attrs: for i in NS.XSI_L: if attrs.has_key((i, 'type')): kind = attrs[(i, 'type')] del attrs[(i, 'type')] if kind != None: i = kind.find(':') if i >= 0: kind = (self._prem[kind[:i]], kind[i + 1:]) else: # XXX What to do here? (None, kind) is just going to fail in convertType kind = (None, kind) null = 0 if attrs: for i in (NS.XSI, NS.XSI2): if attrs.has_key((i, 'null')): null = attrs[(i, 'null')] del attrs[(i, 'null')] if attrs.has_key((NS.XSI3, 'nil')): null = attrs[(NS.XSI3, 'nil')] del attrs[(NS.XSI3, 'nil')] null = int(null) if null: if len(cur) or \ (self._data != None and self._data.strip() != ''): raise Error, "nils can't have data" data = None break if len(self._stack) == 2: if (ns, name) == (NS.ENV, "Header"): self.header = data = headerType(attrs = attrs) self._next = "B" break elif (ns, name) == (NS.ENV, "Body"): self.body = data = bodyType(attrs = attrs) self._next = "" break elif len(self._stack) == 3 and self._next == None: if (ns, name) == (NS.ENV, "Fault"): data = faultType() self._next = "" break if cur.rules != None: rule = cur.rules if type(rule) in (StringType, UnicodeType): # XXX Need a namespace here rule = (None, rule) elif type(rule) == ListType: rule = tuple(rule) # XXX What if rule != kind? if callable(rule): data = rule(self._data) elif type(rule) == DictType: data = structType(name = (ns, name), attrs = attrs) else: data = self.convertType(self._data, rule, attrs) break if (kind == None and cur.kind != None) or \ (kind == (NS.ENC, 'Array')): kind = cur.kind if kind == None: kind = 'ur-type[%d]' % len(cur) else: kind = kind[1] if len(cur.namecounts) == 1: elemsname = cur.names[0] else: elemsname = None data = self.startArray((ns, name), kind, attrs, elemsname) break if len(self._stack) == 3 and kind == None and \ len(cur) == 0 and \ (self._data == None or self._data.strip() == ''): data = structType(name = (ns, name), attrs = attrs) break if len(cur) == 0 and ns != NS.URN: # Nothing's been added to the current frame so it must be a # simple type. if kind == None: # If the current item's container is an array, it will # have a kind. If so, get the bit before the first [, # which is the type of the array, therefore the type of # the current item. kind = self._stack[-1].kind if kind != None: i = kind[1].find('[') if i >= 0: kind = (kind[0], kind[1][:i]) elif ns != None: kind = (ns, name) if kind != None: try: data = self.convertType(self._data, kind, attrs) except UnknownTypeError: data = None else: data = None if data == None: data = self._data or '' if len(attrs) == 0: try: data = str(data) except: pass break data = structType(name = (ns, name), attrs = attrs) break if isinstance(data, compoundType): for i in range(len(cur)): v = cur.contents[i] data._addItem(cur.names[i], v, cur.subattrs[i]) if isinstance(v, RefHolder): v.parent = data if root: self._stack[-1].append(name, data, attrs) if idval != None: self._ids[idval] = data if self._refs.has_key(idval): for i in self._refs[idval]: i.parent._placeItem(i.name, data, i.pos, i.subpos, attrs) del self._refs[idval] self.attrs[id(data)] = attrs if isinstance(data, anyType): data._setAttrs(attrs) self._data = None # Stop accumulating def endDocument(self): if len(self._refs) == 1: raise Error, \ "unresolved reference " + self._refs.keys()[0] elif len(self._refs) > 1: raise Error, \ "unresolved references " + ', '.join(self._refs.keys()) def startPrefixMapping(self, prefix, uri): self._prem[prefix] = uri self._prem_r[uri] = prefix def endPrefixMapping(self, prefix): try: del self._prem_r[self._prem[prefix]] del self._prem[prefix] except: pass def characters(self, c): if self._data != None: self._data += c arrayre = '^(?:(?P<ns>[^:]*):)?' \ '(?P<type>[^[]+)' \ '(?:\[(?P<rank>,*)\])?' \ '(?:\[(?P<asize>\d+(?:,\d+)*)?\])$' def startArray(self, name, kind, attrs, elemsname): if type(self.arrayre) == StringType: self.arrayre = re.compile (self.arrayre) offset = attrs.get((NS.ENC, "offset")) if offset != None: del attrs[(NS.ENC, "offset")] try: if offset[0] == '[' and offset[-1] == ']': offset = int(offset[1:-1]) if offset < 0: raise Exception else: raise Exception except: raise AttributeError, "invalid Array offset" else: offset = 0 try: m = self.arrayre.search(kind) if m == None: raise Exception t = m.group('type') if t == 'ur-type': return arrayType(None, name, attrs, offset, m.group('rank'), m.group('asize'), elemsname) elif m.group('ns') != None: return typedArrayType(None, name, (self._prem[m.group('ns')], t), attrs, offset, m.group('rank'), m.group('asize'), elemsname) else: return typedArrayType(None, name, (None, t), attrs, offset, m.group('rank'), m.group('asize'), elemsname) except: raise AttributeError, "invalid Array type `%s'" % kind # Conversion class DATETIMECONSTS: SIGNre = '(?P<sign>-?)' CENTURYre = '(?P<century>\d{2,})' YEARre = '(?P<year>\d{2})' MONTHre = '(?P<month>\d{2})' DAYre = '(?P<day>\d{2})' HOURre = '(?P<hour>\d{2})' MINUTEre = '(?P<minute>\d{2})' SECONDre = '(?P<second>\d{2}(?:\.\d*)?)' TIMEZONEre = '(?P<zulu>Z)|(?P<tzsign>[-+])(?P<tzhour>\d{2}):' \ '(?P<tzminute>\d{2})' BOSre = '^\s*' EOSre = '\s*$' __allres = {'sign': SIGNre, 'century': CENTURYre, 'year': YEARre, 'month': MONTHre, 'day': DAYre, 'hour': HOURre, 'minute': MINUTEre, 'second': SECONDre, 'timezone': TIMEZONEre, 'b': BOSre, 'e': EOSre} dateTime = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)sT' \ '%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % __allres timeInstant = dateTime timePeriod = dateTime time = '%(b)s%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % \ __allres date = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)s' \ '(%(timezone)s)?%(e)s' % __allres century = '%(b)s%(sign)s%(century)s(%(timezone)s)?%(e)s' % __allres gYearMonth = '%(b)s%(sign)s%(century)s%(year)s-%(month)s' \ '(%(timezone)s)?%(e)s' % __allres gYear = '%(b)s%(sign)s%(century)s%(year)s(%(timezone)s)?%(e)s' % \ __allres year = gYear gMonthDay = '%(b)s--%(month)s-%(day)s(%(timezone)s)?%(e)s' % __allres recurringDate = gMonthDay gDay = '%(b)s---%(day)s(%(timezone)s)?%(e)s' % __allres recurringDay = gDay gMonth = '%(b)s--%(month)s--(%(timezone)s)?%(e)s' % __allres month = gMonth recurringInstant = '%(b)s%(sign)s(%(century)s|-)(%(year)s|-)-' \ '(%(month)s|-)-(%(day)s|-)T' \ '(%(hour)s|-):(%(minute)s|-):(%(second)s|-)' \ '(%(timezone)s)?%(e)s' % __allres duration = '%(b)s%(sign)sP' \ '((?P<year>\d+)Y)?' \ '((?P<month>\d+)M)?' \ '((?P<day>\d+)D)?' \ '((?P<sep>T)' \ '((?P<hour>\d+)H)?' \ '((?P<minute>\d+)M)?' \ '((?P<second>\d*(?:\.\d*)?)S)?)?%(e)s' % \ __allres timeDuration = duration # The extra 31 on the front is: # - so the tuple is 1-based # - so months[month-1] is December's days if month is 1 months = (31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) def convertDateTime(self, value, kind): def getZoneOffset(d): zoffs = 0 try: if d['zulu'] == None: zoffs = 60 * int(d['tzhour']) + int(d['tzminute']) if d['tzsign'] != '-': zoffs = -zoffs except TypeError: pass return zoffs def applyZoneOffset(months, zoffs, date, minfield, posday = 1): if zoffs == 0 and (minfield > 4 or 0 <= date[5] < 60): return date if minfield > 5: date[5] = 0 if minfield > 4: date[4] = 0 if date[5] < 0: date[4] += int(date[5]) / 60 date[5] %= 60 date[4] += zoffs if minfield > 3 or 0 <= date[4] < 60: return date date[3] += date[4] / 60 date[4] %= 60 if minfield > 2 or 0 <= date[3] < 24: return date date[2] += date[3] / 24 date[3] %= 24 if minfield > 1: if posday and date[2] <= 0: date[2] += 31 # zoffs is at most 99:59, so the # day will never be less than -3 return date while 1: # The date[1] == 3 (instead of == 2) is because we're # going back a month, so we need to know if the previous # month is February, so we test if this month is March. leap = minfield == 0 and date[1] == 3 and \ date[0] % 4 == 0 and \ (date[0] % 100 != 0 or date[0] % 400 == 0) if 0 < date[2] <= months[date[1]] + leap: break date[2] += months[date[1] - 1] + leap date[1] -= 1 if date[1] > 0: break date[1] = 12 if minfield > 0: break date[0] -= 1 return date try: exp = getattr(self.DATETIMECONSTS, kind) except AttributeError: return None if type(exp) == StringType: exp = re.compile(exp) setattr (self.DATETIMECONSTS, kind, exp) m = exp.search(value) try: if m == None: raise Exception d = m.groupdict() f = ('century', 'year', 'month', 'day', 'hour', 'minute', 'second') fn = len(f) # Index of first non-None value r = [] if kind in ('duration', 'timeDuration'): if d['sep'] != None and d['hour'] == None and \ d['minute'] == None and d['second'] == None: raise Exception f = f[1:] for i in range(len(f)): s = d[f[i]] if s != None: if f[i] == 'second': s = float(s) else: try: s = int(s) except ValueError: s = long(s) if i < fn: fn = i r.append(s) if fn > len(r): # Any non-Nones? raise Exception if d['sign'] == '-': r[fn] = -r[fn] return tuple(r) if kind == 'recurringInstant': for i in range(len(f)): s = d[f[i]] if s == None or s == '-': if i > fn: raise Exception s = None else: if i < fn: fn = i if f[i] == 'second': s = float(s) else: try: s = int(s) except ValueError: s = long(s) r.append(s) s = r.pop(0) if fn == 0: r[0] += s * 100 else: fn -= 1 if fn < len(r) and d['sign'] == '-': r[fn] = -r[fn] cleanDate(r, fn) return tuple(applyZoneOffset(self.DATETIMECONSTS.months, getZoneOffset(d), r, fn, 0)) r = [0, 0, 1, 1, 0, 0, 0] for i in range(len(f)): field = f[i] s = d.get(field) if s != None: if field == 'second': s = float(s) else: try: s = int(s) except ValueError: s = long(s) if i < fn: fn = i r[i] = s if fn > len(r): # Any non-Nones? raise Exception s = r.pop(0) if fn == 0: r[0] += s * 100 else: fn -= 1 if d.get('sign') == '-': r[fn] = -r[fn] cleanDate(r, fn) zoffs = getZoneOffset(d) if zoffs: r = applyZoneOffset(self.DATETIMECONSTS.months, zoffs, r, fn) if kind == 'century': return r[0] / 100 s = [] for i in range(1, len(f)): if d.has_key(f[i]): s.append(r[i - 1]) if len(s) == 1: return s[0] return tuple(s) except Exception, e: raise Error, "invalid %s value `%s' - %s" % (kind, value, e) intlimits = \ { 'nonPositiveInteger': (0, None, 0), 'non-positive-integer': (0, None, 0), 'negativeInteger': (0, None, -1), 'negative-integer': (0, None, -1), 'long': (1, -9223372036854775808L, 9223372036854775807L), 'int': (0, -2147483648L, 2147483647), 'short': (0, -32768, 32767), 'byte': (0, -128, 127), 'nonNegativeInteger': (0, 0, None), 'non-negative-integer': (0, 0, None), 'positiveInteger': (0, 1, None), 'positive-integer': (0, 1, None), 'unsignedLong': (1, 0, 18446744073709551615L), 'unsignedInt': (0, 0, 4294967295L), 'unsignedShort': (0, 0, 65535), 'unsignedByte': (0, 0, 255), } floatlimits = \ { 'float': (7.0064923216240861E-46, -3.4028234663852886E+38, 3.4028234663852886E+38), 'double': (2.4703282292062327E-324, -1.7976931348623158E+308, 1.7976931348623157E+308), } zerofloatre = '[1-9]' def convertType(self, d, t, attrs): dnn = d or '' if t[0] in NS.EXSD_L: if t[1] == "integer": try: d = int(d) if len(attrs): d = long(d) except: d = long(d) return d if self.intlimits.has_key (t[1]): l = self.intlimits[t[1]] try: d = int(d) except: d = long(d) if l[1] != None and d < l[1]: raise UnderflowError, "%s too small" % d if l[2] != None and d > l[2]: raise OverflowError, "%s too large" % d if l[0] or len(attrs): return long(d) return d if t[1] == "string": if len(attrs): return unicode(dnn) try: return str(dnn) except: return dnn if t[1] == "boolean": d = d.strip().lower() if d in ('0', 'false'): return 0 if d in ('1', 'true'): return 1 raise AttributeError, "invalid boolean value" if self.floatlimits.has_key (t[1]): l = self.floatlimits[t[1]] s = d.strip().lower() if s == "nan": return ieee754.NaN elif s == "inf": return ieee754.PosInf elif s == "-inf": return ieee754.NegInf d = float(s) if str(d).lower() == 'nan': if s != 'nan': raise ValueError, "invalid %s" % t[1] elif str(d).lower() == '-inf': if s != '-inf': raise UnderflowError, "%s too small" % t[1] elif str(d).lower() == 'inf': if s != 'inf': raise OverflowError, "%s too large" % t[1] elif d < 0: if d < l[1]: raise UnderflowError, "%s too small" % t[1] elif d > 0: if d < l[0] or d > l[2]: raise OverflowError, "%s too large" % t[1] elif d == 0: if type(self.zerofloatre) == StringType: self.zerofloatre = re.compile(self.zerofloatre) if self.zerofloatre.search(s): raise UnderflowError, "invalid %s" % t[1] return d if t[1] in ("dateTime", "date", "timeInstant", "time"): return self.convertDateTime(d, t[1]) if t[1] == "decimal": return float(d) if t[1] in ("language", "QName", "NOTATION", "NMTOKEN", "Name", "NCName", "ID", "IDREF", "ENTITY"): return collapseWhiteSpace(d) if t[1] in ("IDREFS", "ENTITIES", "NMTOKENS"): d = collapseWhiteSpace(d) return d.split() if t[0] in NS.XSD_L: if t[1] in ("base64", "base64Binary"): return base64.decodestring(d) if t[1] == "hexBinary": return decodeHexString(d) if t[1] == "anyURI": return urllib.unquote(collapseWhiteSpace(d)) if t[1] in ("normalizedString", "token"): return collapseWhiteSpace(d) if t[0] == NS.ENC: if t[1] == "base64": return base64.decodestring(d) if t[0] == NS.XSD: if t[1] == "binary": try: e = attrs[(None, 'encoding')] if e == 'hex': return decodeHexString(d) elif e == 'base64': return base64.decodestring(d) except: pass raise Error, "unknown or missing binary encoding" if t[1] == "uri": return urllib.unquote(collapseWhiteSpace(d)) if t[1] == "recurringInstant": return self.convertDateTime(d, t[1]) if t[0] in (NS.XSD2, NS.ENC): if t[1] == "uriReference": return urllib.unquote(collapseWhiteSpace(d)) if t[1] == "timePeriod": return self.convertDateTime(d, t[1]) if t[1] in ("century", "year"): return self.convertDateTime(d, t[1]) if t[0] in (NS.XSD, NS.XSD2, NS.ENC): if t[1] == "timeDuration": return self.convertDateTime(d, t[1]) if t[0] == NS.XSD3: if t[1] == "anyURI": return urllib.unquote(collapseWhiteSpace(d)) if t[1] in ("gYearMonth", "gMonthDay"): return self.convertDateTime(d, t[1]) if t[1] == "gYear": return self.convertDateTime(d, t[1]) if t[1] == "gMonth": return self.convertDateTime(d, t[1]) if t[1] == "gDay": return self.convertDateTime(d, t[1]) if t[1] == "duration": return self.convertDateTime(d, t[1]) if t[0] in (NS.XSD2, NS.XSD3): if t[1] == "token": return collapseWhiteSpace(d) if t[1] == "recurringDate": return self.convertDateTime(d, t[1]) if t[1] == "month": return self.convertDateTime(d, t[1]) if t[1] == "recurringDay": return self.convertDateTime(d, t[1]) if t[0] == NS.XSD2: if t[1] == "CDATA": return collapseWhiteSpace(d) raise UnknownTypeError, "unknown type `%s'" % (t[0] + ':' + t[1]) ################################################################################ # call to SOAPParser that keeps all of the info ################################################################################ def _parseSOAP(xml_str, rules = None): try: from cStringIO import StringIO except ImportError: from StringIO import StringIO parser = xml.sax.make_parser() t = SOAPParser(rules = rules) parser.setContentHandler(t) e = xml.sax.handler.ErrorHandler() parser.setErrorHandler(e) inpsrc = xml.sax.xmlreader.InputSource() inpsrc.setByteStream(StringIO(xml_str)) # turn on namespace mangeling parser.setFeature(xml.sax.handler.feature_namespaces,1) try: parser.parse(inpsrc) except xml.sax.SAXParseException, e: parser._parser = None raise e return t ################################################################################ # SOAPParser's more public interface ################################################################################ def parseSOAP(xml_str, attrs = 0): t = _parseSOAP(xml_str) if attrs: return t.body, t.attrs return t.body def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None): t = _parseSOAP(xml_str, rules = rules) p = t.body._aslist[0] # Empty string, for RPC this translates into a void if type(p) in (type(''), type(u'')) and p in ('', u''): name = "Response" for k in t.body.__dict__.keys(): if k[0] != "_": name = k p = structType(name) if header or body or attrs: ret = (p,) if header : ret += (t.header,) if body: ret += (t.body,) if attrs: ret += (t.attrs,) return ret else: return p --- NEW FILE: SOAP.py --- """This file is here for backward compatibility with versions <= 0.9.9 Delete when 1.0.0 is released! """ ident = '$Id: SOAP.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' from Client import * from Config import * from Errors import * from NS import * from Parser import * from SOAPBuilder import * from Server import * from Types import * from Utilities import * import wstools import WSDL from warnings import warn warn(""" The sub-module SOAPpy.SOAP is deprecated and is only provided for short-term backward compatibility. Objects are now available directly within the SOAPpy module. Thus, instead of from SOAPpy import SOAP ... SOAP.SOAPProxy(...) use from SOAPpy import SOAPProxy ... SOAPProxy(...) instead. """, DeprecationWarning) --- NEW FILE: SOAPBuilder.py --- """ ################################################################################ # Copyright (c) 2003, Pfizer # Copyright (c) 2001, Cayce Ullman. # Copyright (c) 2001, Brian Matthews. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of actzero, inc. nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ################################################################################ """ ident = '$Id: SOAPBuilder.py,v 1.1 2003/05/29 05:11:29 niemeyer Exp $' import cgi import copy from wstools.XMLname import toXMLname, fromXMLname from wstools import ieee754 # SOAPpy modules from Config import Config from NS import NS from Types import * ################################################################################ # SOAP Builder ################################################################################ class SOAPBuilder: _xml_to... [truncated message content] |
From: Gustavo N. <nie...@us...> - 2003-05-29 05:11:33
|
Update of /cvsroot/pybot/pybot/pybot/util/SOAPpy/wstools In directory sc8-pr-cvs1:/tmp/cvs-serv3743/pybot/util/SOAPpy/wstools Added Files: TimeoutSocket.py Utility.py WSDLTools.py XMLSchema.py XMLname.py __init__.py ieee754.py Log Message: * modules/google.py: Introduced basic google module using the google SOAP api. * pybot/util/SOAPpy: Added SOAPpy 0.10.1 to the source tree. --- NEW FILE: TimeoutSocket.py --- """Based on code from timeout_socket.py, with some tweaks for compatibility. These tweaks should really be rolled back into timeout_socket, but it's not totally clear who is maintaining it at this point. In the meantime, we'll use a different module name for our tweaked version to avoid any confusion. The original timeout_socket is by: Scott Cotton <sc...@ch...> Lloyd Zusman <lj...@as...> Phil Mayes <pm...@ol...> Piers Lauder <pi...@cs...> Radovan Garabik <ga...@me...> """ ident = "$Id: TimeoutSocket.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" import string, socket, select, errno WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022) class TimeoutSocket: """A socket imposter that supports timeout limits.""" def __init__(self, timeout=20, sock=None): self.timeout = float(timeout) self.inbuf = '' if sock is None: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = sock self.sock.setblocking(0) self._rbuf = '' self._wbuf = '' def __getattr__(self, name): # Delegate to real socket attributes. return getattr(self.sock, name) def connect(self, *addr): timeout = self.timeout sock = self.sock try: # Non-blocking mode sock.setblocking(0) apply(sock.connect, addr) sock.setblocking(timeout != 0) return 1 except socket.error,why: if not timeout: raise sock.setblocking(1) if len(why.args) == 1: code = 0 else: code, why = why if code not in ( errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK ): raise r,w,e = select.select([],[sock],[],timeout) if w: try: apply(sock.connect, addr) return 1 except socket.error,why: if len(why.args) == 1: code = 0 else: code, why = why if code in (errno.EISCONN, WSAEINVAL): return 1 raise raise TimeoutError('socket connect() timeout.') def send(self, data, flags=0): total = len(data) next = 0 while 1: r, w, e = select.select([],[self.sock], [], self.timeout) if w: buff = data[next:next + 8192] sent = self.sock.send(buff, flags) next = next + sent if next == total: return total continue raise TimeoutError('socket send() timeout.') def recv(self, amt, flags=0): if select.select([self.sock], [], [], self.timeout)[0]: return self.sock.recv(amt, flags) raise TimeoutError('socket recv() timeout.') buffsize = 4096 handles = 1 def makefile(self, mode="r", buffsize=-1): self.handles = self.handles + 1 self.mode = mode return self def close(self): self.handles = self.handles - 1 if self.handles == 0 and self.sock.fileno() >= 0: self.sock.close() def read(self, n=-1): if not isinstance(n, type(1)): n = -1 if n >= 0: k = len(self._rbuf) if n <= k: data = self._rbuf[:n] self._rbuf = self._rbuf[n:] return data n = n - k L = [self._rbuf] self._rbuf = "" while n > 0: new = self.recv(max(n, self.buffsize)) if not new: break k = len(new) if k > n: L.append(new[:n]) self._rbuf = new[n:] break L.append(new) n = n - k return "".join(L) k = max(4096, self.buffsize) L = [self._rbuf] self._rbuf = "" while 1: new = self.recv(k) if not new: break L.append(new) k = min(k*2, 1024**2) return "".join(L) def readline(self, limit=-1): data = "" i = self._rbuf.find('\n') while i < 0 and not (0 < limit <= len(self._rbuf)): new = self.recv(self.buffsize) if not new: break i = new.find('\n') if i >= 0: i = i + len(self._rbuf) self._rbuf = self._rbuf + new if i < 0: i = len(self._rbuf) else: i = i+1 if 0 <= limit < len(self._rbuf): i = limit data, self._rbuf = self._rbuf[:i], self._rbuf[i:] return data def readlines(self, sizehint = 0): total = 0 list = [] while 1: line = self.readline() if not line: break list.append(line) total += len(line) if sizehint and total >= sizehint: break return list def writelines(self, list): self.send(''.join(list)) def write(self, data): self.send(data) def flush(self): pass class TimeoutError(Exception): pass --- NEW FILE: Utility.py --- # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. ident = "$Id: Utility.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" from string import join, strip, split from UserDict import UserDict from StringIO import StringIO import xml.dom.minidom, weakref import string, httplib, smtplib, urllib, socket from TimeoutSocket import TimeoutSocket, TimeoutError from StringIO import StringIO from urlparse import urlparse from httplib import HTTPConnection, HTTPSConnection class HTTPResponse: """Captures the information in an HTTP response message.""" def __init__(self, response): self.status = response.status self.reason = response.reason self.headers = response.msg self.body = response.read() or None response.close() class TimeoutHTTP(HTTPConnection): """A custom http connection object that supports socket timeout.""" def __init__(self, host, port=None, timeout=20): HTTPConnection.__init__(self, host, port) self.timeout = timeout def connect(self): self.sock = TimeoutSocket(self.timeout) self.sock.connect((self.host, self.port)) class TimeoutHTTPS(HTTPSConnection): """A custom https object that supports socket timeout. Note that this is not really complete. The builtin SSL support in the Python socket module requires a real socket (type) to be passed in to be hooked to SSL. That means our fake socket won't work and our timeout hacks are bypassed for send and recv calls. Since our hack _is_ in place at connect() time, it should at least provide some timeout protection.""" def __init__(self, host, port=None, timeout=20, **kwargs): if not hasattr(socket, 'ssl'): raise ValueError( 'This Python installation does not have SSL support.' ) HTTPSConnection.__init__(self, str(host), port, **kwargs) self.timeout = timeout def connect(self): sock = TimeoutSocket(self.timeout) sock.connect((self.host, self.port)) realsock = getattr(sock.sock, '_sock', sock.sock) ssl = socket.ssl(realsock, self.key_file, self.cert_file) self.sock = httplib.FakeSocket(sock, ssl) def urlopen(url, timeout=20, redirects=None): """A minimal urlopen replacement hack that supports timeouts for http. Note that this supports GET only.""" scheme, host, path, params, query, frag = urlparse(url) if not scheme in ('http', 'https'): return urllib.urlopen(url) if params: path = '%s;%s' % (path, params) if query: path = '%s?%s' % (path, query) if frag: path = '%s#%s' % (path, frag) if scheme == 'https': if not hasattr(socket, 'ssl'): raise ValueError( 'This Python installation does not have SSL support.' ) conn = TimeoutHTTPS(host, None, timeout) else: conn = TimeoutHTTP(host, None, timeout) conn.putrequest('GET', path) conn.putheader('Connection', 'close') conn.endheaders() response = None while 1: response = conn.getresponse() if response.status != 100: break conn._HTTPConnection__state = httplib._CS_REQ_SENT conn._HTTPConnection__response = None status = response.status # If we get an HTTP redirect, we will follow it automatically. if status >= 300 and status < 400: location = response.msg.getheader('location') if location is not None: response.close() if redirects is not None and redirects.has_key(location): raise RecursionError( 'Circular HTTP redirection detected.' ) if redirects is None: redirects = {} redirects[location] = 1 return urlopen(location, timeout, redirects) raise HTTPResponse(response) if not (status >= 200 and status < 300): raise HTTPResponse(response) body = StringIO(response.read()) response.close() return body class DOM: """The DOM singleton defines a number of XML related constants and provides a number of utility methods for DOM related tasks. It also provides some basic abstractions so that the rest of the package need not care about actual DOM implementation in use.""" # Namespace stuff related to the SOAP specification. NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/' NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/' NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope' NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding' NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2) NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2) NS_SOAP_ENV = NS_SOAP_ENV_1_1 NS_SOAP_ENC = NS_SOAP_ENC_1_1 _soap_uri_mapping = { NS_SOAP_ENV_1_1 : '1.1', NS_SOAP_ENV_1_2 : '1.2', } SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next' SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next' SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2) def SOAPUriToVersion(self, uri): """Return the SOAP version related to an envelope uri.""" value = self._soap_uri_mapping.get(uri) if value is not None: return value raise ValueError( 'Unsupported SOAP envelope uri: %s' % uri ) def GetSOAPEnvUri(self, version): """Return the appropriate SOAP envelope uri for a given human-friendly SOAP version string (e.g. '1.1').""" attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_') value = getattr(self, attrname, None) if value is not None: return value raise ValueError( 'Unsupported SOAP version: %s' % version ) def GetSOAPEncUri(self, version): """Return the appropriate SOAP encoding uri for a given human-friendly SOAP version string (e.g. '1.1').""" attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_') value = getattr(self, attrname, None) if value is not None: return value raise ValueError( 'Unsupported SOAP version: %s' % version ) def GetSOAPActorNextUri(self, version): """Return the right special next-actor uri for a given human-friendly SOAP version string (e.g. '1.1').""" attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_') value = getattr(self, attrname, None) if value is not None: return value raise ValueError( 'Unsupported SOAP version: %s' % version ) # Namespace stuff related to XML Schema. NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema' NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance' NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema' NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance' NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema' NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance' NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01) NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01) NS_XSD = NS_XSD_01 NS_XSI = NS_XSI_01 _xsd_uri_mapping = { NS_XSD_99 : NS_XSI_99, NS_XSD_00 : NS_XSI_00, NS_XSD_01 : NS_XSI_01, } for key, value in _xsd_uri_mapping.items(): _xsd_uri_mapping[value] = key def InstanceUriForSchemaUri(self, uri): """Return the appropriate matching XML Schema instance uri for the given XML Schema namespace uri.""" return self._xsd_uri_mapping.get(uri) def SchemaUriForInstanceUri(self, uri): """Return the appropriate matching XML Schema namespace uri for the given XML Schema instance namespace uri.""" return self._xsd_uri_mapping.get(uri) # Namespace stuff related to WSDL. NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/' NS_WSDL_ALL = (NS_WSDL_1_1,) NS_WSDL = NS_WSDL_1_1 NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/' NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/' NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/' NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,) NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,) NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,) NS_SOAP_BINDING = NS_SOAP_BINDING_1_1 NS_HTTP_BINDING = NS_HTTP_BINDING_1_1 NS_MIME_BINDING = NS_MIME_BINDING_1_1 NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http' NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,) NS_SOAP_HTTP = NS_SOAP_HTTP_1_1 _wsdl_uri_mapping = { NS_WSDL_1_1 : '1.1', } def WSDLUriToVersion(self, uri): """Return the WSDL version related to a WSDL namespace uri.""" value = self._wsdl_uri_mapping.get(uri) if value is not None: return value raise ValueError( 'Unsupported SOAP envelope uri: %s' % uri ) def GetWSDLUri(self, version): attr = 'NS_WSDL_%s' % join(split(version, '.'), '_') value = getattr(self, attr, None) if value is not None: return value raise ValueError( 'Unsupported WSDL version: %s' % version ) def GetWSDLSoapBindingUri(self, version): attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_') value = getattr(self, attr, None) if value is not None: return value raise ValueError( 'Unsupported WSDL version: %s' % version ) def GetWSDLHttpBindingUri(self, version): attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_') value = getattr(self, attr, None) if value is not None: return value raise ValueError( 'Unsupported WSDL version: %s' % version ) def GetWSDLMimeBindingUri(self, version): attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_') value = getattr(self, attr, None) if value is not None: return value raise ValueError( 'Unsupported WSDL version: %s' % version ) def GetWSDLHttpTransportUri(self, version): attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_') value = getattr(self, attr, None) if value is not None: return value raise ValueError( 'Unsupported WSDL version: %s' % version ) # Other xml namespace constants. NS_XMLNS = 'http://www.w3.org/2000/xmlns/' def isElement(self, node, name, nsuri=None): """Return true if the given node is an element with the given name and optional namespace uri.""" if node.nodeType == node.ELEMENT_NODE: return 0 return node.localName == name and \ (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri)) def getElement(self, node, name, nsuri=None, default=join): """Return the first child of node with a matching name and namespace uri, or the default if one is provided.""" nsmatch = self.nsUriMatch ELEMENT_NODE = node.ELEMENT_NODE for child in node.childNodes: if child.nodeType == ELEMENT_NODE: if ((child.localName == name or name is None) and (nsuri is None or nsmatch(child.namespaceURI, nsuri)) ): return child if default is not join: return default raise KeyError, name def getElementById(self, node, id, default=join): """Return the first child of node matching an id reference.""" attrget = self.getAttr ELEMENT_NODE = node.ELEMENT_NODE for child in node.childNodes: if child.nodeType == ELEMENT_NODE: if attrget(child, 'id') == id: return child if default is not join: return default raise KeyError, name def getMappingById(self, document, depth=None, element=None, mapping=None, level=1): """Create an id -> element mapping of those elements within a document that define an id attribute. The depth of the search may be controlled by using the (1-based) depth argument.""" if document is not None: element = document.documentElement mapping = {} attr = element._attrs.get('id', None) if attr is not None: mapping[attr.value] = element if depth is None or depth > level: level = level + 1 ELEMENT_NODE = element.ELEMENT_NODE for child in element.childNodes: if child.nodeType == ELEMENT_NODE: self.getMappingById(None, depth, child, mapping, level) return mapping def getElements(self, node, name, nsuri=None): """Return a sequence of the child elements of the given node that match the given name and optional namespace uri.""" nsmatch = self.nsUriMatch result = [] ELEMENT_NODE = node.ELEMENT_NODE for child in node.childNodes: if child.nodeType == ELEMENT_NODE: if ((child.localName == name or name is None) and ( (nsuri is None) or nsmatch(child.namespaceURI, nsuri))): result.append(child) return result def hasAttr(self, node, name, nsuri=None): """Return true if element has attribute with the given name and optional nsuri. If nsuri is not specified, returns true if an attribute exists with the given name with any namespace.""" if nsuri is None: if node._attrs.has_key(name): return 1 for item in node._attrsNS.keys(): if item[1] == name: return 1 return 0 return node.attrsNS.has_key((nsuri, name)) def getAttr(self, node, name, nsuri=None, default=join): """Return the value of the attribute named 'name' with the optional nsuri, or the default if one is specified. If nsuri is not specified, an attribute that matches the given name will be returned regardless of namespace.""" if nsuri is None: result = node._attrs.get(name, None) if result is None: for item in node._attrsNS.keys(): if item[1] == name: result = node._attrsNS[item] break else: result = node._attrsNS.get((nsuri, name), None) if result is not None: return result.value if default is not join: return default return '' def getElementText(self, node, preserve_ws=None): """Return the text value of an xml element node. Leading and trailing whitespace is stripped from the value unless the preserve_ws flag is passed with a true value.""" result = [] for child in node.childNodes: nodetype = child.nodeType if nodetype == child.TEXT_NODE or \ nodetype == child.CDATA_SECTION_NODE: result.append(child.nodeValue) value = join(result, '') if preserve_ws is None: value = strip(value) return value def findNamespaceURI(self, prefix, node): """Find a namespace uri given a prefix and a context node.""" attrkey = (self.NS_XMLNS, prefix) DOCUMENT_NODE = node.DOCUMENT_NODE ELEMENT_NODE = node.ELEMENT_NODE while 1: if node.nodeType != ELEMENT_NODE: node = node.parentNode continue result = node._attrsNS.get(attrkey, None) if result is not None: return result.value if hasattr(node, '__imported__'): raise DOMException('Value for prefix %s not found.' % prefix) node = node.parentNode if node.nodeType == DOCUMENT_NODE: raise DOMException('Value for prefix %s not found.' % prefix) def findDefaultNS(self, node): """Return the current default namespace uri for the given node.""" attrkey = (self.NS_XMLNS, 'xmlns') DOCUMENT_NODE = node.DOCUMENT_NODE ELEMENT_NODE = node.ELEMENT_NODE while 1: if node.nodeType != ELEMENT_NODE: node = node.parentNode continue result = node._attrsNS.get(attrkey, None) if result is not None: return result.value if hasattr(node, '__imported__'): raise DOMException('Cannot determine default namespace.') node = node.parentNode if node.nodeType == DOCUMENT_NODE: raise DOMException('Cannot determine default namespace.') def findTargetNS(self, node): """Return the defined target namespace uri for the given node.""" attrget = self.getAttr attrkey = (self.NS_XMLNS, 'xmlns') DOCUMENT_NODE = node.DOCUMENT_NODE ELEMENT_NODE = node.ELEMENT_NODE while 1: if node.nodeType != ELEMENT_NODE: node = node.parentNode continue result = attrget(node, 'targetNamespace', default=None) if result is not None: return result node = node.parentNode if node.nodeType == DOCUMENT_NODE: raise DOMException('Cannot determine target namespace.') def getTypeRef(self, element): """Return (namespaceURI, name) for a type attribue of the given element, or None if the element does not have a type attribute.""" typeattr = self.getAttr(element, 'type', default=None) if typeattr is None: return None parts = typeattr.split(':', 1) if len(parts) == 2: nsuri = self.findNamespaceURI(parts[0], element) else: nsuri = self.findDefaultNS(element) return (nsuri, parts[1]) def importNode(self, document, node, deep=0): """Implements (well enough for our purposes) DOM node import.""" nodetype = node.nodeType if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE): raise DOMException('Illegal node type for importNode') if nodetype == node.ENTITY_REFERENCE_NODE: deep = 0 clone = node.cloneNode(deep) self._setOwnerDoc(document, clone) clone.__imported__ = 1 return clone def _setOwnerDoc(self, document, node): node.ownerDocument = document for child in node.childNodes: self._setOwnerDoc(document, child) def nsUriMatch(self, value, wanted, strict=0, tt=type(())): """Return a true value if two namespace uri values match.""" if value == wanted or (type(wanted) is tt) and value in wanted: return 1 if not strict: wanted = type(wanted) is tt and wanted or (wanted,) value = value[-1:] != '/' and value or value[:-1] for item in wanted: if item == value or item[:-1] == value: return 1 return 0 def createDocument(self, nsuri, qname, doctype=None): """Create a new writable DOM document object.""" impl = xml.dom.minidom.getDOMImplementation() return impl.createDocument(nsuri, qname, doctype) def loadDocument(self, data): """Load an xml file from a file-like object and return a DOM document instance.""" return xml.dom.minidom.parse(data) def loadFromURL(self, url): """Load an xml file from a URL and return a DOM document.""" file = urlopen(url) try: result = self.loadDocument(file) finally: file.close() return result class DOMException(Exception): pass DOM = DOM() class Collection(UserDict): """Helper class for maintaining ordered named collections.""" def __init__(self, parent): UserDict.__init__(self) self.parent = weakref.ref(parent) self.list = [] def __getitem__(self, key): if type(key) is type(1): return self.list[key] return self.data[key] def __setitem__(self, key, item): item.parent = weakref.ref(self) self.list.append(item) self.data[key] = item def keys(self): return map(lambda i: i.name, self.list) def items(self): return map(lambda i: (i.name, i), self.list) def values(self): return self.list # This is a runtime guerilla patch for pulldom (used by minidom) so # that xml namespace declaration attributes are not lost in parsing. # We need them to do correct QName linking for XML Schema and WSDL. # The patch has been submitted to SF for the next Python version. from xml.dom.pulldom import PullDOM, START_ELEMENT if 1: def startPrefixMapping(self, prefix, uri): if not hasattr(self, '_xmlns_attrs'): self._xmlns_attrs = [] self._xmlns_attrs.append((prefix or 'xmlns', uri)) self._ns_contexts.append(self._current_context.copy()) self._current_context[uri] = prefix or '' PullDOM.startPrefixMapping = startPrefixMapping def startElementNS(self, name, tagName , attrs): # Retrieve xml namespace declaration attributes. xmlns_uri = 'http://www.w3.org/2000/xmlns/' xmlns_attrs = getattr(self, '_xmlns_attrs', None) if xmlns_attrs is not None: for aname, value in xmlns_attrs: attrs._attrs[(xmlns_uri, aname)] = value self._xmlns_attrs = [] uri, localname = name if uri: # When using namespaces, the reader may or may not # provide us with the original name. If not, create # *a* valid tagName from the current context. if tagName is None: prefix = self._current_context[uri] if prefix: tagName = prefix + ":" + localname else: tagName = localname if self.document: node = self.document.createElementNS(uri, tagName) else: node = self.buildDocument(uri, tagName) else: # When the tagname is not prefixed, it just appears as # localname if self.document: node = self.document.createElement(localname) else: node = self.buildDocument(None, localname) for aname,value in attrs.items(): a_uri, a_localname = aname if a_uri == xmlns_uri: if a_localname == 'xmlns': qname = a_localname else: qname = 'xmlns:' + a_localname attr = self.document.createAttributeNS(a_uri, qname) node.setAttributeNodeNS(attr) elif a_uri: prefix = self._current_context[a_uri] if prefix: qname = prefix + ":" + a_localname else: qname = a_localname attr = self.document.createAttributeNS(a_uri, qname) node.setAttributeNodeNS(attr) else: attr = self.document.createAttribute(a_localname) node.setAttributeNode(attr) attr.value = value self.lastEvent[1] = [(START_ELEMENT, node), None] self.lastEvent = self.lastEvent[1] self.push(node) PullDOM.startElementNS = startElementNS # # This is a runtime guerilla patch for minidom so # that xmlns prefixed attributes dont raise AttributeErrors # during cloning. # # Namespace declarations can appear in any start-tag, must look for xmlns # prefixed attribute names during cloning. # # key (attr.namespaceURI, tag) # ('http://www.w3.org/2000/xmlns/', u'xsd') <xml.dom.minidom.Attr instance at 0x82227c4> # ('http://www.w3.org/2000/xmlns/', 'xmlns') <xml.dom.minidom.Attr instance at 0x8414b3c> # # xml.dom.minidom.Attr.nodeName = xmlns:xsd # xml.dom.minidom.Attr.value = = http://www.w3.org/2001/XMLSchema if 1: def _clone_node(node, deep, newOwnerDocument): """ Clone a node and give it the new owner document. Called by Node.cloneNode and Document.importNode """ if node.ownerDocument.isSameNode(newOwnerDocument): operation = xml.dom.UserDataHandler.NODE_CLONED else: operation = xml.dom.UserDataHandler.NODE_IMPORTED if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: clone = newOwnerDocument.createElementNS(node.namespaceURI, node.nodeName) for attr in node.attributes.values(): clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value) prefix, tag = xml.dom.minidom._nssplit(attr.nodeName) if prefix == 'xmlns': a = clone.getAttributeNodeNS(attr.namespaceURI, tag) else: a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName) a.specified = attr.specified if deep: for child in node.childNodes: c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument) clone.appendChild(c) elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE: clone = newOwnerDocument.createDocumentFragment() if deep: for child in node.childNodes: c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument) clone.appendChild(c) elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE: clone = newOwnerDocument.createTextNode(node.data) elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE: clone = newOwnerDocument.createCDATASection(node.data) elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE: clone = newOwnerDocument.createProcessingInstruction(node.target, node.data) elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE: clone = newOwnerDocument.createComment(node.data) elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE: clone = newOwnerDocument.createAttributeNS(node.namespaceURI, node.nodeName) clone.specified = True clone.value = node.value elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE: assert node.ownerDocument is not newOwnerDocument operation = xml.dom.UserDataHandler.NODE_IMPORTED clone = newOwnerDocument.implementation.createDocumentType( node.name, node.publicId, node.systemId) clone.ownerDocument = newOwnerDocument if deep: clone.entities._seq = [] clone.notations._seq = [] for n in node.notations._seq: notation = Notation(n.nodeName, n.publicId, n.systemId) notation.ownerDocument = newOwnerDocument clone.notations._seq.append(notation) if hasattr(n, '_call_user_data_handler'): n._call_user_data_handler(operation, n, notation) for e in node.entities._seq: entity = Entity(e.nodeName, e.publicId, e.systemId, e.notationName) entity.actualEncoding = e.actualEncoding entity.encoding = e.encoding entity.version = e.version entity.ownerDocument = newOwnerDocument clone.entities._seq.append(entity) if hasattr(e, '_call_user_data_handler'): e._call_user_data_handler(operation, n, entity) else: # Note the cloning of Document and DocumentType nodes is # implemenetation specific. minidom handles those cases # directly in the cloneNode() methods. raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node)) # Check for _call_user_data_handler() since this could conceivably # used with other DOM implementations (one of the FourThought # DOMs, perhaps?). if hasattr(node, '_call_user_data_handler'): node._call_user_data_handler(operation, node, clone) return clone xml.dom.minidom._clone_node = _clone_node --- NEW FILE: WSDLTools.py --- # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. ident = "$Id: WSDLTools.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" from Utility import DOM, Collection from XMLSchema import XMLSchema from StringIO import StringIO import urllib class WSDLReader: """A WSDLReader creates WSDL instances from urls and xml data.""" [...1131 lines suppressed...] parts = [] for name in body.parts: parts.append(message.parts[name]) else: parts = message.parts.values() if parts: callinfo.setReturnParameter( parts[0].name, parts[0].element or parts[0].type, element_type = parts[0].element and 1 or 0 ) for part in parts[1:]: callinfo.addOutParameter( part.name, part.element or part.type, element_type = part.element and 1 or 0 ) return callinfo --- NEW FILE: XMLSchema.py --- # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. ident = "$Id: XMLSchema.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" import string, types, base64, re from Utility import DOM, Collection from StringIO import StringIO class SchemaReader: """A SchemaReader creates XMLSchema objects from urls and xml data.""" def loadFromStream(self, file): """Return an XMLSchema instance loaded from a file object.""" document = DOM.loadDocument(file) schema = XMLSchema() schema.load(document) return schema def loadFromString(self, data): """Return an XMLSchema instance loaded from an xml string.""" return self.loadFromStream(StringIO(data)) def loadFromURL(self, url): """Return an XMLSchema instance loaded from the given url.""" document = DOM.loadFromURL(url) schema = XMLSchema() schema.location = url schema.load(document) return schema def loadFromFile(self, filename): """Return an XMLSchema instance loaded from the given file.""" file = open(filename, 'rb') try: schema = self.loadFromStream(file) finally: file.close() return schema class SchemaError(Exception): pass class XMLSchema: # This is temporary, for the benefit of WSDL until the real thing works. def __init__(self, element): self.targetNamespace = DOM.getAttr(element, 'targetNamespace') self.element = element class realXMLSchema: """A schema is a collection of schema components derived from one or more schema documents, that is, one or more <schema> element information items. It represents the abstract notion of a schema rather than a single schema document (or other representation).""" def __init__(self): self.simpleTypes = Collection(self) self.complexTypes = Collection(self) self.attributes = Collection(self) self.elements = Collection(self) self.attrGroups = Collection(self) self.idConstraints=None self.modelGroups = None self.notations = None self.extensions = [] targetNamespace = None attributeFormDefault = 'unqualified' elementFormDefault = 'unqualified' blockDefault = None finalDefault = None location = None version = None id = None def load(self, document): if document.nodeType == document.DOCUMENT_NODE: schema = DOM.getElement(document, 'schema', None, None) else: schema = document if schema is None: raise SchemaError('Missing <schema> element.') self.namespace = namespace = schema.namespaceURI if not namespace in DOM.NS_XSD_ALL: raise SchemaError( 'Unknown XML schema namespace: %s.' % self.namespace ) for attrname in ( 'targetNamespace', 'attributeFormDefault', 'elementFormDefault', 'blockDefault', 'finalDefault', 'version', 'id' ): value = DOM.getAttr(schema, attrname, None, None) if value is not None: setattr(self, attrname, value) # Resolve imports and includes here? ## imported = {} ## while 1: ## imports = [] ## for element in DOM.getElements(definitions, 'import', NS_WSDL): ## location = DOM.getAttr(element, 'location') ## if not imported.has_key(location): ## imports.append(element) ## if not imports: ## break ## for element in imports: ## self._import(document, element) ## imported[location] = 1 for element in DOM.getElements(schema, None, None): localName = element.localName if not DOM.nsUriMatch(element.namespaceURI, namespace): self.extensions.append(element) continue elif localName == 'message': name = DOM.getAttr(element, 'name') docs = GetDocumentation(element) message = self.addMessage(name, docs) parts = DOM.getElements(element, 'part', NS_WSDL) message.load(parts) continue def _import(self, document, element): namespace = DOM.getAttr(element, 'namespace', default=None) location = DOM.getAttr(element, 'location', default=None) if namespace is None or location is None: raise WSDLError( 'Invalid import element (missing namespace or location).' ) # Sort-of support relative locations to simplify unit testing. The # WSDL specification actually doesn't allow relative URLs, so its # ok that this only works with urls relative to the initial document. location = urllib.basejoin(self.location, location) obimport = self.addImport(namespace, location) obimport._loaded = 1 importdoc = DOM.loadFromURL(location) try: if location.find('#') > -1: idref = location.split('#')[-1] imported = DOM.getElementById(importdoc, idref) else: imported = importdoc.documentElement if imported is None: raise WSDLError( 'Import target element not found for: %s' % location ) imported_tns = DOM.getAttr(imported, 'targetNamespace') importer_tns = namespace if imported_tns != importer_tns: return if imported.localName == 'definitions': imported_nodes = imported.childNodes else: imported_nodes = [imported] parent = element.parentNode for node in imported_nodes: if node.nodeType != node.ELEMENT_NODE: continue child = DOM.importNode(document, node, 1) parent.appendChild(child) child.setAttribute('targetNamespace', importer_tns) attrsNS = imported._attrsNS for attrkey in attrsNS.keys(): if attrkey[0] == DOM.NS_XMLNS: attr = attrsNS[attrkey].cloneNode(1) child.setAttributeNode(attr) finally: importdoc.unlink() class Element: """Common base class for element representation classes.""" def __init__(self, name=None, documentation=''): self.name = name self.documentation = documentation self.extensions = [] def addExtension(self, item): self.extensions.append(item) class SimpleTypeDefinition: """Represents an xml schema simple type definition.""" class ComplexTypeDefinition: """Represents an xml schema complex type definition.""" class AttributeDeclaration: """Represents an xml schema attribute declaration.""" class ElementDeclaration: """Represents an xml schema element declaration.""" def __init__(self, name, type=None, targetNamespace=None): self.name = name targetNamespace = None annotation = None nillable = 0 abstract = 0 default = None fixed = None scope = 'global' type = None form = 0 # Things we will not worry about for now. id_constraint_defs = None sub_group_exclude = None sub_group_affils = None disallowed_subs = None class AttributeGroupDefinition: """Represents an xml schema attribute group definition.""" class IdentityConstraintDefinition: """Represents an xml schema identity constraint definition.""" class ModelGroupDefinition: """Represents an xml schema model group definition.""" class NotationDeclaration: """Represents an xml schema notation declaration.""" class Annotation: """Represents an xml schema annotation.""" class ModelGroup: """Represents an xml schema model group.""" class Particle: """Represents an xml schema particle.""" class WildCard: """Represents an xml schema wildcard.""" class AttributeUse: """Represents an xml schema attribute use.""" class ElementComponent: namespace = '' name = '' type = None form = 'qualified | unqualified' scope = 'global or complex def' constraint = ('value', 'default | fixed') nillable = 0 id_constraint_defs = None sub_group_affil = None sub_group_exclusions = None disallowed_subs = 'substitution, extension, restriction' abstract = 0 minOccurs = 1 maxOccurs = 1 ref = '' class AttributeThing: name = '' namespace = '' typeName = '' typeUri = '' scope = 'global | local to complex def' constraint = ('value:default', 'value:fixed') use = 'optional | prohibited | required' class ElementDataType: namespace = '' name = '' element_form = 'qualified | unqualified' attr_form = None type_name = '' type_uri = '' def __init__(self, name, namespace, type_name, type_uri): self.namespace = namespace self.name = name # type may be anonymous... self.type_name = type_name self.type_uri = type_uri def checkValue(self, value, context): # Delegate value checking to the type of the element. typeref = (self.type_uri, self.type_name) handler = context.serializer.getType(typeref) return handler.checkValue(value, context) def serialize(self, name, namespace, value, context, **kwargs): if context.check_values: self.checkValue(value, context) # Delegate serialization to the type of the element. typeref = (self.type_uri, self.type_name) handler = context.serializer.getType(typeref) return handler.serialize(self.name, self.namespace, value, context) def deserialize(self, element, context): if element_is_null(element, context): return None # Delegate deserialization to the type of the element. typeref = (self.type_uri, self.type_name) handler = context.serializer.getType(typeref) return handler.deserialize(element, context) def parse_schema(data): targetNS = '' attributeFormDefault = 0 elementFormDefault = 0 blockDefault = '' finalDefault = '' language = None version = None id = '' --- NEW FILE: XMLname.py --- """Translate strings to and from SOAP 1.2 XML name encoding Implements rules for mapping application defined name to XML names specified by the w3 SOAP working group for SOAP version 1.2 in Appendix A of "SOAP Version 1.2 Part 2: Adjuncts", W3C Working Draft 17, December 2001, <http://www.w3.org/TR/soap12-part2/#namemap> Also see <http://www.w3.org/2000/xp/Group/xmlp-issues>. Author: Gregory R. Warnes <gre...@gr...> Date:: 2002-04-25 Version 0.9.0 """ ident = "$Id: XMLname.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" from re import * def _NCNameChar(x): return x.isalpha() or x.isdigit() or x=="." or x=='-' or x=="_" def _NCNameStartChar(x): return x.isalpha() or x=="_" def _toUnicodeHex(x): hexval = hex(ord(x[0]))[2:] hexlen = len(hexval) # Make hexval have either 4 or 8 digits by prepending 0's if (hexlen==1): hexval = "000" + hexval elif (hexlen==2): hexval = "00" + hexval elif (hexlen==3): hexval = "0" + hexval elif (hexlen==4): hexval = "" + hexval elif (hexlen==5): hexval = "000" + hexval elif (hexlen==6): hexval = "00" + hexval elif (hexlen==7): hexval = "0" + hexval elif (hexlen==8): hexval = "" + hexval else: raise Exception, "Illegal Value returned from hex(ord(x))" return "_x"+ hexval + "_" def _fromUnicodeHex(x): return eval( r'u"\u'+x[2:-1]+'"' ) def toXMLname(string): """Convert string to a XML name.""" if string.find(':') != -1 : (prefix, localname) = string.split(':',1) else: prefix = None localname = string T = unicode(localname) N = len(localname) X = []; for i in range(N) : if i< N-1 and T[i]==u'_' and T[i+1]==u'x': X.append(u'_x005F_') elif i==0 and N >= 3 and \ ( T[0]==u'x' or T[0]==u'X' ) and \ ( T[1]==u'm' or T[1]==u'M' ) and \ ( T[2]==u'l' or T[2]==u'L' ): X.append(u'_xFFFF_' + T[0]) elif (not _NCNameChar(T[i])) or (i==0 and not _NCNameStartChar(T[i])): X.append(_toUnicodeHex(T[i])) else: X.append(T[i]) return u''.join(X) def fromXMLname(string): """Convert XML name to unicode string.""" retval = sub(r'_xFFFF_','', string ) def fun( matchobj ): return _fromUnicodeHex( matchobj.group(0) ) retval = sub(r'_x[0-9A-Za-z]+_', fun, retval ) return retval --- NEW FILE: __init__.py --- #! /usr/bin/env python """WSDL parsing services package for Web Services for Python.""" ident = "$Id: __init__.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" import WSDLTools import ieee754 import XMLname --- NEW FILE: ieee754.py --- """Utilities for handling IEEE 754 floating point special values This class implements constants and functions for working with IEEE754 double-precision special values. It provides constants for NaN (Not a Number), NegInf (Negative Infinity), PosInf (Positive Infinity), and Inf (also Positive Infinity), as well as functions to test for these values. The code is implemented in pure python by taking advantage of the 'struct' standard module. Some efficiency could be gained by translating the core routines into C. See <http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html> for a description of the IEEE 754 floating point standard. Author: Gregory R. Warnes <gre...@gr...> Date:: 2003-03-25 Version 0.5.0 """ ident = "$Id: ieee754.py,v 1.1 2003/05/29 05:11:30 niemeyer Exp $" __version__ = "0.5.0" from struct import pack, unpack # check endianess _big_endian = pack('i',1)[0] != '\x01' # and define appropriate constants if(_big_endian): HW=0 LW=1 NaN = unpack('d', '\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF')[0] Inf = unpack('d', '\x7F\xF0\x00\x00\x00\x00\x00\x00')[0] PosInf = Inf NegInf = -Inf else: HW=1 LW=0 NaN = unpack('d', '\x00\x00\x00\x00\x00\x00\xf8\xff')[0] Inf = unpack('d', '\x00\x00\x00\x00\x00\x00\xf0\x7f')[0] PosInf = Inf NegInf = -Inf def _double_as_longs(dval): "Use unpack to decode a double precision float into two long integers" tmp = unpack('ll',pack('d', dval)) return (tmp[HW], tmp[LW] ) ## ## Functions to extract components of the IEEE 754 floating point format ## def sign(dval): "Extract the sign bit from a double-precision floating point value" ll = _double_as_longs(dval) return ll[0] >> 31 & 0x01 def exponent(dval): """Extract the exponentent bits from a double-precision floating point value. Note that for normalized values, the exponentdent bits have an offset of 1023. As a consequence, the actual exponentent is obtained by subtracting 1023 for the value returned by this function """ ll = _double_as_longs(dval) return ( ll[0] >> 20 ) & 0x7ff def mantissa(dval): ll = _double_as_longs(dval) mantissa1 = ( ll[0] & 0xfffffL ) << 32 mantissa2 = ll[1] return mantissa1 + mantissa2 ## ## Functions to test for IEEE 754 special values ## def is_NaN(value): "Determine if the argument is a IEEE 754 NaN (Not a Number) value." return ( exponent(value)==0x7ff and mantissa(value)!=0 ) def is_Infinite(value): """Determine if the argument is an infinite IEEE 754 value (positive or negative inifinity)""" return ( exponent(value)==0x7ff and mantissa(value)== 0 ) def is_Finite(value): """Determine if the argument is an finite IEEE 754 value (i.e., is not NaN, positive or negative inifinity)""" return ( exponent(value)!=0x7ff ) def is_Inf(value): "Determine if the argument is a IEEE 754 positive infinity value" return ( sign(value)==0 and exponent(value)==0x7ff \ and mantissa(value)== 0 ) is_PosInf = is_Inf def is_NegInf(value): "Determine if the argument is a IEEE 754 negative infinity value" return ( sign(value)==1 and exponent(value)==0x7ff and \ mantissa(value)== 0 ) |
From: Gustavo N. <nie...@us...> - 2003-05-29 05:11:32
|
Update of /cvsroot/pybot/pybot/pybot/modules In directory sc8-pr-cvs1:/tmp/cvs-serv3743/pybot/modules Modified Files: freshmeat.py Added Files: google.py Log Message: * modules/google.py: Introduced basic google module using the google SOAP api. * pybot/util/SOAPpy: Added SOAPpy 0.10.1 to the source tree. --- NEW FILE: google.py --- # Copyright (c) 2000-2003 Gustavo Niemeyer <nie...@co...> # # This file is part of pybot. # # pybot 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. # # pybot 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 pybot; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from pybot import mm, hooks, config from pybot.util import SOAPpy import thread import re HELP = """ You may ask for google search results using "[search] google [<n>]: <search>". Unless the parameter n is used, the default is to show the first result found (with n=0). """ PERM_GOOGLE = """ The "google" permission allows users to ask for google search results. Check "help google" for more information. """ class Google: def __init__(self): if config.has_option("global", "http_proxy"): self.proxy = config.get("global", "http_proxy") self.proxy = re.sub(".*://", "", self.proxy) else: self.proxy = None self.key = config.get("google", "license_key") hooks.register("Message", self.message) # [search] google [<n>]: <search> self.re1 = re.compile(r"(?:search\s+)?google(?:\s+(?P<n>\d+))?:\s*(?P<search>.+)$", re.I) mm.register_help("(?:search\s+)?google(?:\s+search)?", HELP, "google") mm.register_perm("google", PERM_GOOGLE) def unload(self): hooks.unregister("Message", self.message) mm.unregister_help(HELP) mm.unregister_perm("google") def search(self, msg, search, n): try: proxy = SOAPpy.SOAPProxy("http://api.google.com/search/beta2", namespace="urn:GoogleSearch", http_proxy=self.proxy) result = proxy.doGoogleSearch(self.key, search, n, 1, SOAPpy.booleanType(1), "", SOAPpy.booleanType(0), "", "", "") if not len(result.resultElements): msg.answer("%:", ["Nothing found", "Google couldn't find anything", "Google has its mind empty right now", "Google has never seen such thing"], [".", "!"]) else: e = result.resultElements[0] url = e.URL title = re.sub("<.*?>", "", e.title) snippet = re.sub("<.*?>", "", e.snippet) msg.answer("%:", "%s <%s> - %s" % (title, url, snippet)) except SOAPpy.Errors.Error: import traceback traceback.print_exc() msg.answer("%:", ["There was an error trying to ask google", "Something wrong happened while trying to" "ask google", "I got some problem while trying to do that"], [".", "!"]) def message(self, msg): if not msg.forme: return None m = self.re1.match(msg.line) if m: if mm.hasperm(msg, "google"): n = int(m.group("n") or 0) search = m.group("search") thread.start_new_thread(self.search, (msg, search, n)) else: msg.answer("%:", ["You can't", "You're not allowed to", "You're not good enough to"], ["do google actions", "use google"], ["!", "."]) return 0 def __loadmodule__(): global mod mod = Google() def __unloadmodule__(): global mod mod.unload() del mod # vim:ts=4:sw=4:et Index: freshmeat.py =================================================================== RCS file: /cvsroot/pybot/pybot/pybot/modules/freshmeat.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** freshmeat.py 12 May 2003 20:42:21 -0000 1.8 --- freshmeat.py 29 May 2003 05:11:29 -0000 1.9 *************** *** 38,46 **** class Freshmeat: def __init__(self): ! self.url = config.get("freshmeat", "url") ! if config.has_option("freshmeat", "proxy"): ! self.proxy = config.get("freshmeat", "proxy") else: self.proxy = None self.interval = config.getint("freshmeat", "interval") --- 38,46 ---- class Freshmeat: def __init__(self): ! if config.has_option("global", "http_proxy"): ! self.proxy = config.get("global", "http_proxy") else: self.proxy = None + self.url = config.get("freshmeat", "url") self.interval = config.getint("freshmeat", "interval") |
From: Gustavo N. <nie...@us...> - 2003-05-29 05:11:32
|
Update of /cvsroot/pybot/pybot In directory sc8-pr-cvs1:/tmp/cvs-serv3743 Modified Files: ChangeLog TODO pybot.conf Log Message: * modules/google.py: Introduced basic google module using the google SOAP api. * pybot/util/SOAPpy: Added SOAPpy 0.10.1 to the source tree. Index: ChangeLog =================================================================== RCS file: /cvsroot/pybot/pybot/ChangeLog,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** ChangeLog 16 May 2003 17:28:03 -0000 1.15 --- ChangeLog 29 May 2003 05:11:29 -0000 1.16 *************** *** 1,2 **** --- 1,7 ---- + 2003-05-29 Gustavo Niemeyer <nie...@co...> + * modules/google.py: Introduced basic google module using the + google SOAP api. + * pybot/util/SOAPpy: Added SOAPpy 0.10.1 to the source tree. + 2003-05-16 Gustavo Niemeyer <nie...@co...> * contrib/vera2info.py: Updated to add multiple values in the Index: TODO =================================================================== RCS file: /cvsroot/pybot/pybot/TODO,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** TODO 23 May 2003 21:29:29 -0000 1.8 --- TODO 29 May 2003 05:11:29 -0000 1.9 *************** *** 19,24 **** - Add timed messages in the messages.py module. - - Perhaps a google module (including a random link fetcher)? - - Perhaps a "karma" module, implementing word++ and word--? --- 19,22 ---- Index: pybot.conf =================================================================== RCS file: /cvsroot/pybot/pybot/pybot.conf,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** pybot.conf 12 May 2003 21:56:33 -0000 1.7 --- pybot.conf 29 May 2003 05:11:29 -0000 1.8 *************** *** 9,12 **** --- 9,15 ---- ; ~/.pybot/config -> ~/.pybot/data/ + [global] + ;http_proxy = http://127.0.0.1:8080 + [permission] ; This will tell pybot who should have unrestricted access to all *************** *** 38,46 **** [freshmeat] url = http://freshmeat.net/backend/recentnews.txt - proxy = http://proxy.conectiva:3128 interval = 10 [plock] dirpath = /cnc/distro/plocks/ ; vim:ft=dosini --- 41,51 ---- [freshmeat] url = http://freshmeat.net/backend/recentnews.txt interval = 10 [plock] dirpath = /cnc/distro/plocks/ + + [google] + ;license_key = YOUR-LICENSE-KEY ; vim:ft=dosini |
From: Gustavo N. <nie...@us...> - 2003-05-29 05:07:12
|
Update of /cvsroot/pybot/pybot/pybot/util/SOAPpy/wstools In directory sc8-pr-cvs1:/tmp/cvs-serv3020/wstools Log Message: Directory /cvsroot/pybot/pybot/pybot/util/SOAPpy/wstools added to the repository |