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 |