Compare commits

..

72 Commits

Author SHA1 Message Date
Lance Stout
79f1aa0e1b Update version and README for 1.0 release. 2012-01-03 17:04:15 -05:00
Lance Stout
fb5a6a7d71 Merge pull request #132 from rhcarvalho/master
Fix a typo in several files.
2012-01-02 13:10:46 -08:00
Rodolfo Carvalho
7d1c5f4a2b Fix a typo in several files.
This fixes several instances of "intial" for "initial".
2012-01-02 18:59:39 -02:00
Lance Stout
1b0fd76b45 Merge pull request #131 from rhcarvalho/master
Small changes to the examples
2011-12-31 17:27:57 -08:00
Rodolfo Carvalho
46e93bea09 Remove unused import.
I forgot about this one before!
2011-12-31 20:14:24 -02:00
Rodolfo Carvalho
cbc6a0296b Ask interactively for missing command line arguments.
Instead of complaining that the arguments were not given, ask interactively for input.
This example was the only one to behave differently from the others.
2011-12-31 19:54:14 -02:00
Rodolfo Carvalho
cc63bef179 Remove unused imports in the examples. 2011-12-31 19:50:53 -02:00
Rodolfo Carvalho
cbcfa156c4 Add missing import. 2011-12-31 19:48:03 -02:00
Lance Stout
4a12e1059a Add proxy docs. 2011-12-31 12:33:32 -05:00
Lance Stout
f9cd051209 Add echo component briefing. 2011-12-31 02:00:54 -05:00
Lance Stout
03bc38f7e3 Add docs on using Iq stanzas. 2011-12-31 01:28:41 -05:00
Lance Stout
4e23a4e08e Merge pull request #130 from rhcarvalho/master
Some small fixes
2011-12-30 20:14:54 -08:00
Rodolfo Carvalho
8cafa8578f Update examples to use the block'' argument instead of the deprecated threaded''. 2011-12-30 17:25:03 -02:00
Rodolfo Carvalho
b74ea47650 Fix docstring of a method of Message stanzas. 2011-12-30 17:08:32 -02:00
Rodolfo Carvalho
2dc230a68b Replace pydns with dnspython in the comments of the examples. 2011-12-30 00:08:05 -02:00
Lance Stout
522f0dac16 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-12-27 18:03:08 -05:00
Lance Stout
cd5ae944ec Merge pull request #128 from correl/rpc_value_conversion_fix
XEP-0009 tests updated for Python3
2011-12-27 15:03:01 -08:00
Lance Stout
42a86fe0d4 Disconnect when a SyntaxError is found.
This should resolve issue #102
2011-12-27 18:01:26 -05:00
Correl Roush
e928b9c434 XEP-0009: Updated tests to work in python 3 2011-12-20 21:19:51 -05:00
Lance Stout
fb55d9e9d1 Add comma to fix pubsub error conditions.
Fixes issue #127
2011-12-20 12:30:35 -05:00
Lance Stout
74e7e5a291 Merge pull request #125 from correl/rpc_value_conversion_fix
XEP-0009 XML-RPC value conversion fixes and unit tests
2011-12-20 09:29:03 -08:00
Correl Roush
6c58b8cc4b XEP-0009: Updated RPC value conversion code
Updated the XML-RPC value conversion to correctly apply namespaces, and
fixed an error uncovered by the tests in the XML -> Python conversion of
dateTime values.
2011-12-20 02:03:06 -05:00
Correl Roush
2b3d11a7a5 XEP-0009: Added value conversion unit tests
Added tests for bidirectional conversion of all XML-RPC data types
2011-12-20 02:01:34 -05:00
Correl Roush
9950208d06 Fixes Issue #123: Corrected boolean xml to python conversion 2011-12-16 17:41:16 +00:00
Lance Stout
a67e16d1b7 Merge pull request #122 from correl/acl_check_fix
XEP-0009: ACL.check fix
2011-12-15 16:14:29 -08:00
Correl Roush
c98a22e065 Fixed Issue 93: ACL.check jid parameter should be a string value 2011-12-15 21:58:33 +00:00
Lance Stout
d496417deb Allow XEP-0082 to return datetime objects without having to format and reparse. 2011-12-15 12:02:08 -08:00
Lance Stout
116bb6e1b9 Use OrderedDicts instead of regular dictionaries when returning values from forms. 2011-12-13 09:00:45 -08:00
Lance Stout
9c6dde5d22 Ensure that item fields have the proper type.
The item fields were not setting their type based on the reported
field's type attribute, so values were not being encoded properly.

Fixes issue #121
2011-12-13 08:59:39 -08:00
Lance Stout
fc8a13df5a Allow disco info/items handlers to return full Iq stanzas.
Only allowing handlers to return a DiscoInfo/DiscoItem stanza works
for the majority of cases, but does not allow for the addition of
an RSM stanza, or other extensions.

An Iq stanza returned by a handler must already be configured as
a reply.
2011-12-12 19:38:32 -08:00
Lance Stout
85e9042db6 Pass the Iq stanza to disco item handlers. 2011-12-12 16:34:24 -08:00
Lance Stout
62e6d6fb4c Fix iterable substanzas when added as normal plugin.
If an iterable plugin was an enabled, it wasn't added to
the iterables list.
2011-12-11 17:04:58 -08:00
Lance Stout
16c72e8efd Use UTC for xep_0082.date. 2011-12-09 23:59:33 -08:00
Lance Stout
efe1b9f5a9 Allow sending stanzas on session_end.
May set self.disconnect_wait=True so that all disconnect
calls wait for the send queue to empty, unless explicitly
overridden with wait=False.

The session_end now fires before closing the socket so
that final stanzas may be sent, such as unavailable presences
for components.
2011-12-09 23:56:39 -08:00
Lance Stout
65dbddb6b6 Fix logging when loading plugins. 2011-12-09 20:57:08 -08:00
Lance Stout
2a67a31120 Prevent hang when terminating during delayed connection. 2011-12-07 22:16:58 -08:00
Lance Stout
a720c3348b Updated last bit of core files to use new API format. 2011-12-05 20:37:47 -08:00
Lance Stout
79ac60b6e8 Fix example boilerplate code syntax. 2011-12-05 08:57:57 -08:00
Lance Stout
e01c2d222a More doc updates 2011-12-05 08:55:05 -08:00
Lance Stout
8922e2050a Update the API docs for XMLStream 2011-12-04 20:35:17 -08:00
Lance Stout
a85891c611 Add API docs for the scheduler 2011-12-04 16:43:05 -08:00
Lance Stout
2586fdffda Update api docs for handlers and matchers 2011-12-04 16:26:14 -08:00
Lance Stout
c9dc9ec11e Update supported XEP list 2011-12-04 15:39:49 -08:00
Lance Stout
b9332142c9 Update api docs for JID 2011-12-04 13:42:46 -08:00
Lance Stout
b7b53362e1 Ensure that adhoc command clients have form plugin registered.
The form plugin was being registered on first use for providers,
but not for clients receiving the form.

NOTE: Use of non-form payloads will have this issue - adhoc command
      clients will need to have an expectation beforehand of what
      the command payload will be to properly load stanza plugins.
2011-12-04 01:24:35 -08:00
Lance Stout
68cf66a5fe Ensure that saving a roster item includes the correct subscription value.
Fixes issue #118
2011-11-28 15:00:35 -08:00
Florent Le Coz
4eb7eeb40f Send the encoded data (bytes) and not the str, on the socket. 2011-11-25 01:45:43 +08:00
Lance Stout
a1d64fa215 Experimental support for handling SSL write errors. 2011-11-23 23:59:05 -08:00
Lance Stout
5f44c0e678 Add docs for filesocket 2011-11-22 16:33:38 -08:00
Lance Stout
b87c4d786d Update tostring docs, plus more doc cleanup 2011-11-22 16:25:33 -08:00
Lance Stout
329b0df3f6 Some more docs house cleaning 2011-11-22 15:25:24 -08:00
Lance Stout
6906c15e8e Update docs for tostring 2011-11-22 15:25:02 -08:00
Lance Stout
ff5421cefc Moar docs! 2011-11-21 23:28:19 -08:00
Lance Stout
4498e992a2 Add more stanzabase docs 2011-11-21 23:17:39 -08:00
Lance Stout
2d610dfdc8 Fix stream handler test for multiple handlers to exist properly. 2011-11-21 22:03:43 -08:00
Lance Stout
2b0a05ee32 Update stanzabase docs 2011-11-21 21:51:19 -08:00
Lance Stout
bc2d0ee9a8 Update docs index 2011-11-20 17:29:54 -08:00
Lance Stout
862a2a1440 Ensure that reconnection happens properly after connection loss.
Calling reconnect() simultaneously from multiple threads (like when
using XEP-0199 keepalive) could break because the connection state
can transition and break the state expectations in one of the
reconnect() calls.
2011-11-20 12:18:37 -08:00
Lance Stout
fba60ffff1 Convert daemon threads back into normal threads.
This may need to be reverted if CTRL-C handling breaks, but everything
works fine so far in testing.

Resolves issue #95.
2011-11-20 12:17:35 -08:00
Lance Stout
d1a945a305 Tidy up logging some more 2011-11-19 19:18:43 -08:00
Lance Stout
685b9ab102 Fix logging exceptions from formatting issues. 2011-11-19 19:08:27 -08:00
Lance Stout
24f27c0fe3 Pass generic connection errors to XMLStream.exception() 2011-11-19 19:01:07 -08:00
Lance Stout
3019c82d8a Use a list comprehension instead of filter() to work with Python3. 2011-11-19 18:49:18 -08:00
Lance Stout
f9d0b55ca3 Add unit test for copying stanzas when passed to events. 2011-11-19 18:43:38 -08:00
Lance Stout
b54cc97e4c Merge remote-tracking branch 'vijayp/master' into HEAD
Conflicts:
	examples/ping.py
	sleekxmpp/basexmpp.py
	sleekxmpp/clientxmpp.py
	sleekxmpp/features/feature_bind/bind.py
	sleekxmpp/features/feature_mechanisms/mechanisms.py
	sleekxmpp/plugins/gmail_notify.py
	sleekxmpp/plugins/jobs.py
	sleekxmpp/plugins/xep_0009/remote.py
	sleekxmpp/plugins/xep_0009/rpc.py
	sleekxmpp/plugins/xep_0012.py
	sleekxmpp/plugins/xep_0045.py
	sleekxmpp/plugins/xep_0050/adhoc.py
	sleekxmpp/plugins/xep_0078/legacyauth.py
	sleekxmpp/plugins/xep_0085/chat_states.py
	sleekxmpp/plugins/xep_0199/ping.py
	sleekxmpp/plugins/xep_0224/attention.py
	sleekxmpp/xmlstream/handler/waiter.py
	sleekxmpp/xmlstream/matcher/xmlmask.py
	sleekxmpp/xmlstream/xmlstream.py

Conflicts:
	examples/ping.py
	sleekxmpp/basexmpp.py
	sleekxmpp/clientxmpp.py
	sleekxmpp/features/feature_bind/bind.py
	sleekxmpp/features/feature_mechanisms/mechanisms.py
	sleekxmpp/plugins/gmail_notify.py
	sleekxmpp/plugins/jobs.py
	sleekxmpp/plugins/xep_0009/remote.py
	sleekxmpp/plugins/xep_0009/rpc.py
	sleekxmpp/plugins/xep_0012.py
	sleekxmpp/plugins/xep_0045.py
	sleekxmpp/plugins/xep_0050/adhoc.py
	sleekxmpp/plugins/xep_0078/legacyauth.py
	sleekxmpp/plugins/xep_0085/chat_states.py
	sleekxmpp/plugins/xep_0199/ping.py
	sleekxmpp/plugins/xep_0224/attention.py
	sleekxmpp/xmlstream/handler/waiter.py
	sleekxmpp/xmlstream/matcher/xmlmask.py
	sleekxmpp/xmlstream/xmlstream.py
2011-11-19 18:23:26 -08:00
Vijay Pandurangan
e3b9d5abbf double copy 2011-11-19 16:03:17 -08:00
Vijay Pandurangan
2332970cf2 elide unnecessary copy 2011-11-19 16:02:41 -08:00
Vijay Pandurangan
48af3d3322 remove unnecessary copies when only one handler matches. This was taking up ~ 15% of CPU on moderate load. 2011-11-19 15:59:38 -08:00
Lance Stout
429c94d6a9 Tidy up logging calls. 2011-11-19 12:07:57 -08:00
Vijay Pandurangan
deb52ad350 This change stops sleekxmpp from spending huge amounts of time unnecessarily computing logging data that may never be used. This is a HUGE performance improvement; in some of my test runs, unnecessary string creation was accounting for > 60% of all CPU time.
Note that using % in a string will _always_ perform the sting substitutions, because the strings are constructed before the function is called. So log.debug('%s' % expensiveoperation()) will take about the same CPU time whether or not the logging level is DEBUG or INFO. if you use , no substitutions are performed unless the string is actually logged
2011-11-20 03:39:05 +08:00
Vijay Pandurangan
6f3cc77bb5 This change stops sleekxmpp from spending huge amounts of time unnecessarily computing logging data that may never be used. This is a HUGE performance improvement; in some of my test runs, unnecessary string creation was accounting for > 60% of all CPU time.
Note that using % in a string will _always_ perform the sting substitutions, because the strings are constructed before the function is called. So log.debug('%s' % expensiveoperation()) will take about the same CPU time whether or not the logging level is DEBUG or INFO. if you use , no substitutions are performed unless the string is actually logged
2011-11-19 11:30:44 -08:00
Lance Stout
1baf139ca4 Bump next release version to 1.0-RC4 2011-11-18 16:40:17 -08:00
86 changed files with 2895 additions and 2250 deletions

View File

@@ -44,7 +44,13 @@ The latest source code for SleekXMPP may be found on `Github
``master`` branch, while the latest development version is in the
``develop`` branch.
**Stable Releases**
**Latest Release**
- `1.0 <http://github.com/fritzy/SleekXMPP/zipball/1.0>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
**Older Stable Releases**
- `1.0 RC3 <http://github.com/fritzy/SleekXMPP/zipball/1.0-RC3>`_
- `1.0 RC2 <http://github.com/fritzy/SleekXMPP/zipball/1.0-RC2>`_
- `1.0 RC1 <http://github.com/fritzy/SleekXMPP/zipball/1.0-RC1>`_
@@ -55,9 +61,6 @@ The latest source code for SleekXMPP may be found on `Github
- `1.0 Beta2 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta2>`_
- `1.0 Beta1 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta1>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
Installing DNSPython
---------------------
If you are using Python3 and wish to use dnspython, you will have to checkout and
@@ -151,7 +154,7 @@ SleekXMPP projects::
format='%(levelname)-8s %(message)s')
xmpp = EchoBot('somejid@example.com', 'use_getpass')
xmpp.connect():
xmpp.connect()
xmpp.process(block=True)
@@ -162,8 +165,8 @@ Credits
`@fritzy <http://twitter.com/fritzy>`_
Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
<http://code.google.com/p/seesmic-as3-xmpp/>`_, and a member of the XMPP
Council.
<http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of
the XMPP Council.
**Co-Author:** Lance Stout
`lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,

View File

@@ -122,14 +122,14 @@ if __name__ == '__main__':
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
#load xml config
logging.info("Loading config file: %s" % opts.configfile)
logging.info("Loading config file: %s" , opts.configfile)
config = configparser.RawConfigParser()
config.read(opts.configfile)
#init
logging.info("Account 1 is %s" % config.get('account1', 'jid'))
logging.info("Account 1 is %s" , config.get('account1', 'jid'))
xmpp1 = TestClient(config.get('account1','jid'), config.get('account1','pass'))
logging.info("Account 2 is %s" % config.get('account2', 'jid'))
logging.info("Account 2 is %s" , config.get('account2', 'jid'))
xmpp2 = TestClient(config.get('account2','jid'), config.get('account2','pass'))
xmpp1.registerPlugin('xep_0004')

View File

@@ -186,14 +186,14 @@ if __name__ == '__main__':
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
#load xml config
logging.info("Loading config file: %s" % opts.configfile)
logging.info("Loading config file: %s" , opts.configfile)
config = configparser.RawConfigParser()
config.read(opts.configfile)
#init
logging.info("Account 1 is %s" % config.get('account1', 'jid'))
logging.info("Account 1 is %s" , config.get('account1', 'jid'))
xmpp1 = TestClient(config.get('account1','jid'), config.get('account1','pass'))
logging.info("Account 2 is %s" % config.get('account2', 'jid'))
logging.info("Account 2 is %s" , config.get('account2', 'jid'))
xmpp2 = TestClient(config.get('account2','jid'), config.get('account2','pass'))
xmpp1.registerPlugin('xep_0004')

View File

@@ -329,11 +329,11 @@ if __name__ == '__main__':
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
#load xml config
logging.info("Loading config file: %s" % opts.configfile)
logging.info("Loading config file: %s" , opts.configfile)
config = ET.parse(os.path.expanduser(opts.configfile)).find('auth')
#init
logging.info("Logging in as %s" % config.attrib['jid'])
logging.info("Logging in as %s" , config.attrib['jid'])
plugin_config = {}

View File

@@ -1,70 +0,0 @@
.highlight .hll { background-color: #ffffcc }
.highlight { background: #000000; color: #f6f3e8; }
.highlight .c { color: #7C7C7C; } /* Comment */
.highlight .err { color: #f6f3e8; } /* Error */
.highlight .g { color: #f6f3e8; } /* Generic */
.highlight .k { color: #00ADEE; } /* Keyword */
.highlight .l { color: #f6f3e8; } /* Literal */
.highlight .n { color: #f6f3e8; } /* Name */
.highlight .o { color: #f6f3e8; } /* Operator */
.highlight .x { color: #f6f3e8; } /* Other */
.highlight .p { color: #f6f3e8; } /* Punctuation */
.highlight .cm { color: #7C7C7C; } /* Comment.Multiline */
.highlight .cp { color: #96CBFE; } /* Comment.Preproc */
.highlight .c1 { color: #7C7C7C; } /* Comment.Single */
.highlight .cs { color: #7C7C7C; } /* Comment.Special */
.highlight .gd { color: #f6f3e8; } /* Generic.Deleted */
.highlight .ge { color: #f6f3e8; } /* Generic.Emph */
.highlight .gr { color: #ffffff; background-color: #ff0000 } /* Generic.Error */
.highlight .gh { color: #f6f3e8; font-weight: bold; } /* Generic.Heading */
.highlight .gi { color: #f6f3e8; } /* Generic.Inserted */
.highlight .go { color: #070707; } /* Generic.Output */
.highlight .gp { color: #f6f3e8; } /* Generic.Prompt */
.highlight .gs { color: #f6f3e8; } /* Generic.Strong */
.highlight .gu { color: #f6f3e8; font-weight: bold; } /* Generic.Subheading */
.highlight .gt { color: #ffffff; font-weight: bold; background-color: #FF6C60 } /* Generic.Traceback */
.highlight .kc { color: #6699CC; } /* Keyword.Constant */
.highlight .kd { color: #6699CC; } /* Keyword.Declaration */
.highlight .kn { color: #6699CC; } /* Keyword.Namespace */
.highlight .kp { color: #6699CC; } /* Keyword.Pseudo */
.highlight .kr { color: #6699CC; } /* Keyword.Reserved */
.highlight .kt { color: #FFFFB6; } /* Keyword.Type */
.highlight .ld { color: #f6f3e8; } /* Literal.Date */
.highlight .m { color: #FF73FD; } /* Literal.Number */
.highlight .s { color: #F46DBA;/*#A8FF60;*/ } /* Literal.String */
.highlight .na { color: #f6f3e8; } /* Name.Attribute */
.highlight .nb { color: #f6f3e8; } /* Name.Builtin */
.highlight .nc { color: #f6f3e8; } /* Name.Class */
.highlight .no { color: #99CC99; } /* Name.Constant */
.highlight .nd { color: #f6f3e8; } /* Name.Decorator */
.highlight .ni { color: #E18964; } /* Name.Entity */
.highlight .ne { color: #f6f3e8; } /* Name.Exception */
.highlight .nf { color: #F64DBA; } /* Name.Function */
.highlight .nl { color: #f6f3e8; } /* Name.Label */
.highlight .nn { color: #f6f3e8; } /* Name.Namespace */
.highlight .nx { color: #f6f3e8; } /* Name.Other */
.highlight .py { color: #f6f3e8; } /* Name.Property */
.highlight .nt { color: #00ADEE; } /* Name.Tag */
.highlight .nv { color: #C6C5FE; } /* Name.Variable */
.highlight .ow { color: #ffffff; } /* Operator.Word */
.highlight .w { color: #f6f3e8; } /* Text.Whitespace */
.highlight .mf { color: #FF73FD; } /* Literal.Number.Float */
.highlight .mh { color: #FF73FD; } /* Literal.Number.Hex */
.highlight .mi { color: #FF73FD; } /* Literal.Number.Integer */
.highlight .mo { color: #FF73FD; } /* Literal.Number.Oct */
.highlight .sb { color: #A8FF60; } /* Literal.String.Backtick */
.highlight .sc { color: #A8FF60; } /* Literal.String.Char */
.highlight .sd { color: #A8FF60; } /* Literal.String.Doc */
.highlight .s2 { color: #A8FF60; } /* Literal.String.Double */
.highlight .se { color: #A8FF60; } /* Literal.String.Escape */
.highlight .sh { color: #A8FF60; } /* Literal.String.Heredoc */
.highlight .si { color: #A8FF60; } /* Literal.String.Interpol */
.highlight .sx { color: #A8FF60; } /* Literal.String.Other */
.highlight .sr { color: #A8FF60; } /* Literal.String.Regex */
.highlight .s1 { color: #A8FF60; } /* Literal.String.Single */
.highlight .ss { color: #A8FF60; } /* Literal.String.Symbol */
.highlight .bp { color: #f6f3e8; } /* Name.Builtin.Pseudo */
.highlight .vc { color: #C6C5FE; } /* Name.Variable.Class */
.highlight .vg { color: #C6C5FE; } /* Name.Variable.Global */
.highlight .vi { color: #C6C5FE; } /* Name.Variable.Instance */
.highlight .il { color: #FF73FD; } /* Literal.Number.Integer.Long */

View File

@@ -1,35 +0,0 @@
{#
basic/defindex.html
~~~~~~~~~~~~~~~~~~~
Default template for the "index" page.
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
{% set title = _('Overview') %}
{% block body %}
<h1>{{ docstitle|e }}</h1>
<p>
Welcome! This is
{% block description %}the documentation for {{ project|e }}
{{ release|e }}{% if last_updated %}, last updated {{ last_updated|e }}{% endif %}{% endblock %}.
</p>
{% block tables %}
<p><strong>{{ _('Indices and tables:') }}</strong></p>
<table class="contentstable" align="center"><tr>
<td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">{{ _('Complete Table of Contents') }}</a><br>
<span class="linkdescr">{{ _('lists all sections and subsections') }}</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("search") }}">{{ _('Search Page') }}</a><br>
<span class="linkdescr">{{ _('search this documentation') }}</span></p>
</td><td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("modindex") }}">{{ _('Global Module Index') }}</a><br>
<span class="linkdescr">{{ _('quick access to all modules') }}</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">{{ _('General Index') }}</a><br>
<span class="linkdescr">{{ _('all functions, classes, terms') }}</span></p>
</td></tr>
</table>
{% endblock %}
{% endblock %}

View File

@@ -1,61 +0,0 @@
{% extends "defindex.html" %}
{% block tables %}
<p><strong>Parts of the documentation:</strong></p>
<table class="contentstable" align="center"><tr>
<td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("whatsnew/" + version) }}">What's new in Python {{ version }}?</a><br/>
<span class="linkdescr">or <a href="{{ pathto("whatsnew/index") }}">all "What's new" documents</a> since 2.0</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("tutorial/index") }}">Tutorial</a><br/>
<span class="linkdescr">start here</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("library/index") }}">Library Reference</a><br/>
<span class="linkdescr">keep this under your pillow</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("reference/index") }}">Language Reference</a><br/>
<span class="linkdescr">describes syntax and language elements</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("using/index") }}">Python Setup and Usage</a><br/>
<span class="linkdescr">how to use Python on different platforms</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("howto/index") }}">Python HOWTOs</a><br/>
<span class="linkdescr">in-depth documents on specific topics</span></p>
</td><td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("extending/index") }}">Extending and Embedding</a><br/>
<span class="linkdescr">tutorial for C/C++ programmers</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("c-api/index") }}">Python/C API</a><br/>
<span class="linkdescr">reference for C/C++ programmers</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("install/index") }}">Installing Python Modules</a><br/>
<span class="linkdescr">information for installers &amp; sys-admins</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("distutils/index") }}">Distributing Python Modules</a><br/>
<span class="linkdescr">sharing modules with others</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("documenting/index") }}">Documenting Python</a><br/>
<span class="linkdescr">guide for documentation authors</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("faq/index") }}">FAQs</a><br/>
<span class="linkdescr">frequently asked questions (with answers!)</span></p>
</td></tr>
</table>
<p><strong>Indices and tables:</strong></p>
<table class="contentstable" align="center"><tr>
<td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("py-modindex") }}">Global Module Index</a><br/>
<span class="linkdescr">quick access to all modules</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">General Index</a><br/>
<span class="linkdescr">all functions, classes, terms</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("glossary") }}">Glossary</a><br/>
<span class="linkdescr">the most important terms explained</span></p>
</td><td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("search") }}">Search page</a><br/>
<span class="linkdescr">search this documentation</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">Complete Table of Contents</a><br/>
<span class="linkdescr">lists all sections and subsections</span></p>
</td></tr>
</table>
<p><strong>Meta information:</strong></p>
<table class="contentstable" align="center"><tr>
<td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("bugs") }}">Reporting bugs</a></p>
<p class="biglink"><a class="biglink" href="{{ pathto("about") }}">About the documentation</a></p>
</td><td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("license") }}">History and License of Python</a></p>
<p class="biglink"><a class="biglink" href="{{ pathto("copyright") }}">Copyright</a></p>
</td></tr>
</table>
{% endblock %}

View File

@@ -1,69 +0,0 @@
{#
haiku/layout.html
~~~~~~~~~~~~~~~~~
Sphinx layout template for the haiku theme.
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
{% set script_files = script_files + ['_static/theme_extras.js'] %}
{% set css_files = css_files + ['_static/print.css'] %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}{% endblock %}
{% macro nav() %}
<p>
{%- block haikurel1 %}
{%- endblock %}
{%- if prev %}
«&#160;&#160;<a href="{{ prev.link|e }}">{{ prev.title }}</a>
&#160;&#160;::&#160;&#160;
{%- endif %}
<a class="uplink" href="{{ pathto(master_doc) }}">{{ _('Contents') }}</a>
{%- if next %}
&#160;&#160;::&#160;&#160;
<a href="{{ next.link|e }}">{{ next.title }}</a>&#160;&#160;»
{%- endif %}
{%- block haikurel2 %}
{%- endblock %}
</p>
{% endmacro %}
{% block content %}
<div class="header">
{%- block haikuheader %}
{%- if theme_full_logo != "false" %}
<a href="{{ pathto('index') }}">
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
</a>
{%- else %}
{%- if logo -%}
<img class="rightlogo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
{%- endif -%}
<h1 class="heading">
<a href="{{ pathto('index') }}"><span>{{ project|e }}</span></a>
</h1>
<h2 class="heading"><span>{{ shorttitle|e }}</span></h2>
{%- endif %}
{%- endblock %}
</div>
<div class="topnav">
{{ nav() }}
</div>
<div class="content">
{#{%- if display_toc %}
<div id="toc">
<h3>Table Of Contents</h3>
{{ toc }}
</div>
{%- endif %}#}
{% block body %}{% endblock %}
</div>
<div class="bottomnav">
{{ nav() }}
</div>
{% endblock %}

View File

@@ -1,5 +1,5 @@
========
basexmpp
BaseXMPP
========
.. module:: sleekxmpp.basexmpp

View File

@@ -1,17 +1,8 @@
==========
clientxmpp
ClientXMPP
==========
.. module:: sleekxmpp.clientxmpp
.. autoclass:: ClientXMPP
.. automethod:: connect
.. automethod:: register_feature
.. automethod:: get_roster
.. automethod:: update_roster
.. automethod:: del_roster_item
:members:

View File

@@ -0,0 +1,8 @@
=============
ComponentXMPP
=============
.. module:: sleekxmpp.componentxmpp
.. autoclass:: ComponentXMPP
:members:

14
docs/api/exceptions.rst Normal file
View File

@@ -0,0 +1,14 @@
Exceptions
==========
.. module:: sleekxmpp.exceptions
.. autoexception:: XMPPError
:members:
.. autoexception:: IqError
:members:
.. autoexception:: IqTimeout
:members:

View File

@@ -1,8 +0,0 @@
=========
xmlstream
=========
.. module:: sleekxmpp.xmlstream
.. autoclass:: XMLStream
:members:

View File

@@ -0,0 +1,12 @@
.. module:: sleekxmpp.xmlstream.filesocket
.. _filesocket:
Python 2.6 File Socket Shims
============================
.. autoclass:: FileSocket
:members:
.. autoclass:: Socket26
:members:

View File

@@ -0,0 +1,24 @@
Stanza Handlers
===============
The Basic Handler
-----------------
.. module:: sleekxmpp.xmlstream.handler.base
.. autoclass:: BaseHandler
:members:
Callback
--------
.. module:: sleekxmpp.xmlstream.handler.callback
.. autoclass:: Callback
:members:
Waiter
------
.. module:: sleekxmpp.xmlstream.handler.waiter
.. autoclass:: Waiter
:members:

View File

@@ -0,0 +1,7 @@
Jabber IDs (JID)
=================
.. module:: sleekxmpp.xmlstream.jid
.. autoclass:: JID
:members:

View File

@@ -0,0 +1,41 @@
Stanza Matchers
===============
The Basic Matcher
-----------------
.. module:: sleekxmpp.xmlstream.matcher.base
.. autoclass:: MatcherBase
:members:
ID Matching
-----------
.. module:: sleekxmpp.xmlstream.matcher.id
.. autoclass:: MatcherId
:members:
Stanza Path Matching
--------------------
.. module:: sleekxmpp.xmlstream.matcher.stanzapath
.. autoclass:: StanzaPath
:members:
XPath
-----
.. module:: sleekxmpp.xmlstream.matcher.xpath
.. autoclass:: MatchXPath
:members:
XMLMask
-------
.. module:: sleekxmpp.xmlstream.matcher.xmlmask
.. autoclass:: MatchXMLMask
:members:

View File

@@ -0,0 +1,11 @@
=========
Scheduler
=========
.. module:: sleekxmpp.xmlstream.scheduler
.. autoclass:: Task
:members:
.. autoclass:: Scheduler
:members:

View File

@@ -0,0 +1,123 @@
.. _stanzabase:
==============
Stanza Objects
==============
.. module:: sleekxmpp.xmlstream.stanzabase
The :mod:`~sleekmxpp.xmlstream.stanzabase` module provides a wrapper for the
standard :mod:`~xml.etree.ElementTree` module that makes working with XML
less painful. Instead of having to manually move up and down an element
tree and insert subelements and attributes, you can interact with an object
that behaves like a normal dictionary or JSON object, which silently maps
keys to XML attributes and elements behind the scenes.
Overview
--------
The usefulness of this layer grows as the XML you have to work with
becomes nested. The base unit here, :class:`ElementBase`, can map to a
single XML element, or several depending on how advanced of a mapping
is desired from interface keys to XML structures. For example, a single
:class:`ElementBase` derived class could easily describe:
.. code-block:: xml
<message to="user@example.com" from="friend@example.com">
<body>Hi!</body>
<x:extra>
<x:item>Custom item 1</x:item>
<x:item>Custom item 2</x:item>
<x:item>Custom item 3</x:item>
</x:extra>
</message>
If that chunk of XML were put in the :class:`ElementBase` instance
``msg``, we could extract the data from the XML using::
>>> msg['extra']
['Custom item 1', 'Custom item 2', 'Custom item 3']
Provided we set up the handler for the ``'extra'`` interface to load the
``<x:item>`` element content into a list.
The key concept is that given an XML structure that will be repeatedly
used, we can define a set of :term:`interfaces` which when we read from,
write to, or delete, will automatically manipulate the underlying XML
as needed. In addition, some of these interfaces may in turn reference
child objects which expose interfaces for particularly complex child
elements of the original XML chunk.
.. seealso::
:ref:`create-stanza-interfaces`.
Because the :mod:`~sleekxmpp.xmlstream.stanzabase` module was developed
as part of an `XMPP <http://xmpp.org>`_ library, these chunks of XML are
referred to as :term:`stanzas <stanza>`, and in SleekXMPP we refer to a
subclass of :class:`ElementBase` which defines the interfaces needed for
interacting with a given :term:`stanza` a :term:`stanza object`.
To make dealing with more complicated and nested :term:`stanzas <stanza>`
or XML chunks easier, :term:`stanza objects <stanza object>` can be
composed in two ways: as iterable child objects or as plugins. Iterable
child stanzas, or :term:`substanzas`, are accessible through a special
``'substanzas'`` interface. This option is useful for stanzas which
may contain more than one of the same kind of element. When there is
only one child element, the plugin method is more useful. For plugins,
a parent stanza object delegates one of its XML child elements to the
plugin stanza object. Here is an example:
.. code-block:: xml
<iq type="result">
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="client" type="bot" name="SleekXMPP Bot" />
</query>
</iq>
We can can arrange this stanza into two objects: an outer, wrapper object for
dealing with the ``<iq />`` element and its attributes, and a plugin object to
control the ``<query />`` payload element. If we give the plugin object the
name ``'disco_info'`` (using its :attr:`ElementBase.plugin_attrib` value), then
we can access the plugin as so::
>>> iq['disco_info']
'<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="client" type="bot" name="SleekXMPP Bot" />
</query>'
We can then drill down through the plugin object's interfaces as desired::
>>> iq['disco_info']['identities']
[('client', 'bot', 'SleekXMPP Bot')]
Plugins may also add new interfaces to the parent stanza object as if they
had been defined by the parent directly, and can also override the behaviour
of an interface defined by the parent.
.. seealso::
- :ref:`create-stanza-plugins`
- :ref:`create-extension-plugins`
- :ref:`override-parent-interfaces`
Registering Stanza Plugins
--------------------------
.. autofunction:: register_stanza_plugin
ElementBase
-----------
.. autoclass:: ElementBase
:members:
:private-members:
:special-members:
StanzaBase
----------
.. autoclass:: StanzaBase
:members:

View File

@@ -0,0 +1,46 @@
.. module:: sleekxmpp.xmlstream.tostring
.. _tostring:
XML Serialization
=================
Since the XML layer of SleekXMPP is based on :mod:`~xml.etree.ElementTree`,
why not just use the built-in :func:`~xml.etree.ElementTree.tostring`
method? The answer is that using that method produces ugly results when
using namespaces. The :func:`tostring()` method used here intelligently
hides namespaces when able and does not introduce excessive namespace
prefixes::
>>> from sleekxmpp.xmlstream.tostring import tostring
>>> from xml.etree import cElementTree as ET
>>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>')
>>> ET.tostring(xml)
'<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>'
>>> tostring(xml)
'<foo xmlns="bar"><baz /></foo>'
As a side effect of this namespace hiding, using :func:`tostring()` may
produce unexpected results depending on how the :func:`tostring()` method
is invoked. For example, when sending XML on the wire, the main XMPP
stanzas with their namespace of ``jabber:client`` will not include the
namespace because that is already declared by the stream header. But, if
you create a :class:`~sleekxmpp.stanza.message.Message` instance and dump
it to the terminal, the ``jabber:client`` namespace will appear.
.. autofunction:: tostring
Escaping Special Characters
---------------------------
In order to prevent errors when sending arbitrary text as the textual
content of an XML element, certain characters must be escaped. These
are: ``&``, ``<``, ``>``, ``"``, and ``'``. The default escaping
mechanism is to replace those characters with their equivalent escape
entities: ``&amp;``, ``&lt;``, ``&gt;``, ``&apos;``, and ``&quot;``.
In the future, the use of CDATA sections may be allowed to reduce the
size of escaped text or for when other XMPP processing agents do not
undertand these entities.
.. autofunction:: xml_escape

View File

@@ -0,0 +1,10 @@
==========
XML Stream
==========
.. module:: sleekxmpp.xmlstream.xmlstream
.. autoexception:: RestartStream
.. autoclass:: XMLStream
:members:

View File

@@ -17,21 +17,21 @@ of the tedium of creating/manipulating XML.
The Foundation: XMLStream
-------------------------
``XMLStream`` is a mostly XMPP-agnostic class whose purpose is to read
and write from a bi-directional XML stream. It also allows for callback
functions to execute when XML matching given patterns is received; these
callbacks are also referred to as :term:`stream handlers <stream handler>`.
The class also provides a basic eventing system which can be triggered
either manually or on a timed schedule.
:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` is a mostly XMPP-agnostic
class whose purpose is to read and write from a bi-directional XML stream.
It also allows for callback functions to execute when XML matching given
patterns is received; these callbacks are also referred to as :term:`stream
handlers <stream handler>`. The class also provides a basic eventing system
which can be triggered either manually or on a timed schedule.
The Main Threads
~~~~~~~~~~~~~~~~
``XMLStream`` instances run using at least three background threads: the
send thread, the read thread, and the scheduler thread. The send thread is
in charge of monitoring the send queue and writing text to the outgoing
XML stream. The read thread pulls text off of the incoming XML stream and
stores the results in an event queue. The scheduler thread is used to emit
events after a given period of time.
:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` instances run using at
least three background threads: the send thread, the read thread, and the
scheduler thread. The send thread is in charge of monitoring the send queue
and writing text to the outgoing XML stream. The read thread pulls text off
of the incoming XML stream and stores the results in an event queue. The
scheduler thread is used to emit events after a given period of time.
Additionally, the main event processing loop may be executed in its
own thread if SleekXMPP is being used in the background for another
@@ -61,9 +61,10 @@ when this bit of XML is received (with an assumed namespace of
new object is determined using a map of namespaced element names to
classes.
Our incoming XML is thus turned into a ``Message`` :term:`stanza object`
because the namespaced element name ``{jabber:client}message`` is
associated with the class ``sleekxmpp.stanza.Message``.
Our incoming XML is thus turned into a :class:`~sleekxmpp.stanza.Message`
:term:`stanza object` because the namespaced element name
``{jabber:client}message`` is associated with the class
:class:`~sleekxmpp.stanza.Message`.
2. **Match stanza objects to callbacks.**
@@ -72,8 +73,8 @@ when this bit of XML is received (with an assumed namespace of
:term:`stanza object` is paired with a reference to the handler and
placed into the event queue.
Our ``Message`` object is thus paired with the message stanza handler
``BaseXMPP._handle_message`` to create the tuple::
Our :class:`~sleekxmpp.stanza.Message` object is thus paired with the message stanza handler
:meth:`BaseXMPP._handle_message` to create the tuple::
('stanza', stanza_obj, handler)
@@ -88,7 +89,7 @@ when this bit of XML is received (with an assumed namespace of
parameter.
.. warning::
The callback, aka :term:`stream handler`, is executed in the main
The callback, aka :term:`stream handler`, is executed in the main event
processing thread. If the handler blocks, event processing will also
block.
@@ -96,20 +97,22 @@ when this bit of XML is received (with an assumed namespace of
Since a :term:`stream handler` shouldn't block, if extensive processing
for a stanza is required (such as needing to send and receive an
``Iq`` stanza), then custom events must be used. These events are not
explicitly tied to the incoming XML stream and may be raised at any
time. Importantly, these events may be handled in their own thread.
:class:`~sleekxmpp.stanza.Iq` stanza), then custom events must be used.
These events are not explicitly tied to the incoming XML stream and may
be raised at any time. Importantly, these events may be handled in their
own thread.
When the event is raised, a copy of the stanza is created for each
handler registered for the event. In contrast to :term:`stream handlers <stream handler>`,
these functions are referred to as :term:`event handlers <event handler>`.
Each stanza/handler pair is then put into the event queue.
handler registered for the event. In contrast to :term:`stream handlers
<stream handler>`, these functions are referred to as :term:`event
handlers <event handler>`. Each stanza/handler pair is then put into the
event queue.
.. note::
It is possible to skip the event queue and process an event immediately
by using ``direct=True`` when raising the event.
The code for ``BaseXMPP._handle_message`` follows this pattern, and
The code for :meth:`BaseXMPP._handle_message` follows this pattern, and
raises a ``'message'`` event::
self.event('message', msg)
@@ -145,125 +148,30 @@ when this bit of XML is received (with an assumed namespace of
Raising XMPP Awareness: BaseXMPP
--------------------------------
While ``XMLStream`` attempts to shy away from anything too XMPP specific,
``BaseXMPP``'s sole purpose is to provide foundational support for sending
and receiving XMPP stanzas. This support includes registering the basic
message, presence, and iq stanzas, methods for creating and sending
stanzas, and default handlers for incoming messages and keeping track of
presence notifications.
While :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` attempts to shy away
from anything too XMPP specific, :class:`~sleekxmpp.basexmpp.BaseXMPP`'s
sole purpose is to provide foundational support for sending and receiving
XMPP stanzas. This support includes registering the basic message,
presence, and iq stanzas, methods for creating and sending stanzas, and
default handlers for incoming messages and keeping track of presence
notifications.
The plugin system for adding new XEP support is also maintained by
``BaseXMPP``.
:class:`~sleekxmpp.basexmpp.BaseXMPP`.
.. index:: ClientXMPP, BaseXMPP
ClientXMPP
----------
``ClientXMPP`` extends ``BaseXMPP`` with additional logic for connecting to
an XMPP server by performing DNS lookups. It also adds support for stream
:class:`~sleekxmpp.clientxmpp.ClientXMPP` extends
:class:`~sleekxmpp.clientxmpp.BaseXMPP` with additional logic for connecting
to an XMPP server by performing DNS lookups. It also adds support for stream
features such as STARTTLS and SASL.
.. index:: ComponentXMPP, BaseXMPP
ComponentXMPP
-------------
``ComponentXMPP`` is only a thin layer on top of ``BaseXMPP`` that
implements the component handshake protocol.
.. index::
double: object; stanza
Stanza Objects: A Brief Look
----------------------------
.. seealso::
See :ref:`api-stanza-objects` for a more detailed overview.
Almost worthy of their own standalone library, :term:`stanza objects <stanza object>`
are wrappers for XML objects which expose dictionary like interfaces
for manipulating their XML content. For example, consider the XML:
.. code-block:: xml
<message />
A very plain element to start with, but we can create a :term:`stanza object`
using ``sleekxmpp.stanza.Message`` as so::
msg = Message(xml=ET.fromstring("<message />"))
The ``Message`` stanza class defines interfaces such as ``'body'`` and
``'to'``, so we can assign values to those interfaces to include new XML
content::
msg['body'] = "Following so far?"
msg['to'] = 'user@example.com'
Dumping the XML content of ``msg`` (using ``msg.xml``), we find:
.. code-block:: xml
<message to="user@example.com">
<body>Following so far?</body>
</message>
The process is similar for reading from interfaces and deleting interface
contents. A :term:`stanza object` behaves very similarly to a regular
``dict`` object: you may assign to keys, read from keys, and ``del`` keys.
Stanza interfaces come with built-in behaviours such as adding/removing
attribute and sub element values. However, a lot of the time more custom
logic is needed. This can be provided by defining methods of the form
``get_*``, ``set_*``, and ``del_*`` for any interface which requires custom
behaviour.
Stanza Plugins
~~~~~~~~~~~~~~
Since it is generally possible to embed one XML element inside another,
:term:`stanza objects <stanza object>` may be nested. Nested
:term:`stanza objects <stanza object>` are referred to as :term:`stanza plugins <stanza plugin>`
or :term:`substanzas <substanza>`.
A :term:`stanza plugin` exposes its own interfaces by adding a new
interface to its parent stanza. To demonstrate, consider these two stanza
class definitions using ``sleekxmpp.xmlstream.ElementBase``:
.. code-block:: python
class Parent(ElementBase):
name = "the-parent-xml-element-name"
namespace = "the-parent-namespace"
interfaces = set(('foo', 'bar'))
class Child(ElementBase):
name = "the-child-xml-element-name"
namespace = "the-child-namespace"
plugin_attrib = 'child'
interfaces = set(('baz',))
If we register the ``Child`` stanza as a plugin of the ``Parent`` stanza as
so, using ``sleekxmpp.xmlstream.register_stanza_plugin``::
register_stanza_plugin(Parent, Child)
Then we can access content in the child stanza through the parent.
Note that the interface used to access the child stanza is the same as
``Child.plugin_attrib``::
parent = Parent()
parent['foo'] = 'a'
parent['child']['baz'] = 'b'
The above code would produce:
.. code-block:: xml
<the-parent-xml-element xmlns="the-parent-namespace" foo="a">
<the-child-xml-element xmlsn="the-child-namespace" baz="b" />
</the-parent-xml-element>
It is also possible to allow a :term:`substanza` to appear multiple times
by using ``iterable=True`` in the ``register_stanza_plugin`` call. All
iterable :term:`substanzas <substanza>` can be accessed using a standard
``substanzas`` interface.
:class:`~sleekxmpp.componentxmpp.ComponentXMPP` is only a thin layer on top of
:class:`~sleekxmpp.basexmpp.BaseXMPP` that implements the component handshake
protocol.

View File

@@ -16,7 +16,7 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
@@ -25,7 +25,7 @@ import sys, os
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -50,7 +50,7 @@ copyright = u'2011, Nathan Fritz, Lance Stout'
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.0RC1'
release = '1.0RC3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -81,7 +81,7 @@ exclude_patterns = ['_build']
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'default'
pygments_style = 'tango'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
@@ -91,7 +91,7 @@ pygments_style = 'default'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'haiku'
html_theme = 'nature'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -218,3 +218,5 @@ man_pages = [
('index', 'sleekxmpp', u'SleekXMPP Documentation',
[u'Nathan Fritz, Lance Stout'], 1)
]
intersphinx_mapping = {'python': ('http://docs.python.org/3.2', 'python-objects.inv')}

View File

@@ -1,3 +1,5 @@
.. _create-plugin:
Creating a SleekXMPP Plugin
===========================

View File

@@ -1,2 +1,75 @@
.. _echocomponent:
=================================
Create and Run a Server Component
=================================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed SleekXMPP, do so now by either checking out a version
from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
.. code-block:: sh
pip install sleekxmpp # Or: easy_install sleekxmpp
Many XMPP applications eventually graduate to requiring to run as a server
component in order to meet scalability requirements. To demonstrate how to
turn an XMPP client bot into a component, we'll turn the echobot example
(:ref:`echobot`) into a component version.
The first difference is that we will add an additional import statement:
.. code-block:: python
from sleekxmpp.componentxmpp import ComponentXMPP
Likewise, we will change the bot's class definition to match:
.. code-block:: python
class EchoComponent(ComponentXMPP):
def __init__(self, jid, secret, server, port):
ComponentXMPP.__init__(self, jid, secret, server, port)
A component instance requires two extra parameters compared to a client
instance: ``server`` and ``port``. These specifiy the name and port of
the XMPP server that will be accepting the component. For example, for
a MUC component, the following could be used:
.. code-block:: python
muc = ComponentXMPP('muc.sleekxmpp.com', '******', 'sleekxmpp.com', 5555)
.. note::
The ``server`` value is **NOT** derived from the provided JID for the
component, unlike with client connections.
One difference with the component version is that we do not have
to handle the :term:`session_start` event if we don't wish to deal
with presence.
The other, main difference with components is that the
``'from'`` value for every stanza must be explicitly set, since
components may send stanzas from multiple JIDs. To do so,
the :meth:`~sleekxmpp.basexmpp.BaseXMPP.send_message()` and
:meth:`~sleekxmpp.basexmpp.BaseXMPP.send_presence()` accept the parameters
``mfrom`` and ``pfrom``, respectively. For any method that uses
:class:`~sleekxmpp.stanza.iq.Iq` stanzas, ``ifrom`` may be used.
Final Product
-------------
.. include:: ../../examples/echo_component.py
:literal:

View File

@@ -1,2 +1,182 @@
Send/Receive IQ Stanzas
=======================
Unlike :class:`~sleekxmpp.stanza.message.Message` and
:class:`~sleekxmpp.stanza.presence.Presence` stanzas which only use
text data for basic usage, :class:`~sleekxmpp.stanza.iq.Iq` stanzas
require using XML payloads, and generally entail creating a new
SleekXMPP plugin to provide the necessary convenience methods to
make working with them easier.
Basic Use
---------
XMPP's use of :class:`~sleekxmpp.stanza.iq.Iq` stanzas is built around
namespaced ``<query />`` elements. For clients, just sending the
empty ``<query />`` element will suffice for retrieving information. For
example, a very basic implementation of service discovery would just
need to be able to send:
.. code-block:: xml
<iq to="user@example.com" type="get" id="1">
<query xmlns="http://jabber.org/protocol/disco#info" />
</iq>
Creating Iq Stanzas
~~~~~~~~~~~~~~~~~~~
SleekXMPP provides built-in support for creating basic :class:`~sleekxmpp.stanza.iq.Iq`
stanzas this way. The relevant methods are:
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_get`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_set`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_result`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_error`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_query`
These methods all follow the same pattern: create or modify an existing
:class:`~sleekxmpp.stanza.iq.Iq` stanza, set the ``'type'`` value based
on the method name, and finally add a ``<query />`` element with the given
namespace. For example, to produce the query above, you would use:
.. code-block:: python
self.make_iq_get(queryxmlns='http://jabber.org/protocol/disco#info',
ito='user@example.com')
Sending Iq Stanzas
~~~~~~~~~~~~~~~~~~
Once an :class:`~sleekxmpp.stanza.iq.Iq` stanza is created, sending it
over the wire is done using its :meth:`~sleekxmpp.stanza.iq.Iq.send()`
method, like any other stanza object. However, there are a few extra
options to control how to wait for the query's response.
These options are:
* ``block``: The default behaviour is that :meth:`~sleekxmpp.stanza.iq.Iq.send()`
will block until a response is received and the response stanza will be the
return value. Setting ``block`` to ``False`` will cause the call to return
immediately. In which case, you will need to arrange some way to capture
the response stanza if you need it.
* ``timeout``: When using the blocking behaviour, the call will eventually
timeout with an error. The default timeout is 30 seconds, but this may
be overidden two ways. To change the timeout globally, set:
.. code-block:: python
self.response_timeout = 10
To change the timeout for a single call, the ``timeout`` parameter works:
.. code-block:: python
iq.send(timeout=60)
* ``callback``: When not using a blocking call, using the ``callback``
argument is a simple way to register a handler that will execute
whenever a response is finally received. Using this method, there
is no timeout limit. In case you need to remove the callback, the
name of the newly created callback is returned.
.. code-block:: python
cb_name = iq.send(callback=self.a_callback)
# ... later if we need to cancel
self.remove_handler(cb_name)
Properly working with :class:`~sleekxmpp.stanza.iq.Iq` stanzas requires
handling the intended, normal flow, error responses, and timed out
requests. To make this easier, two exceptions may be thrown by
:meth:`~sleekxmpp.stanza.iq.Iq.send()`: :exc:`~sleekxmpp.exceptions.IqError`
and :exc:`~sleekxmpp.exceptions.IqTimeout`. These exceptions only
apply to the default, blocking calls.
.. code-block:: python
try:
resp = iq.send()
# ... do stuff with expected Iq result
except IqError as e:
err_resp = e.iq
# ... handle error case
except IqTimeout:
# ... no response received in time
pass
If you do not care to distinguish between errors and timeouts, then you
can combine both cases with a generic :exc:`~sleekxmpp.exceptions.XMPPError`
exception:
.. code-block:: python
try:
resp = iq.send()
except XMPPError:
# ... Don't care about the response
pass
Advanced Use
------------
Going beyond the basics provided by SleekXMPP requires building at least a
rudimentary SleekXMPP plugin to create a :term:`stanza object` for
interfacting with the :class:`~sleekxmpp.stanza.iq.Iq` payload.
.. seealso::
* :ref:`create-plugin`
* :ref:`work-with-stanzas`
* :ref:`using-handlers-matchers`
The typical way to respond to :class:`~sleekxmpp.stanza.iq.Iq` requests is
to register stream handlers. As an example, suppose we create a stanza class
named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``,
and has a :attr:`~sleekxmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
of ``custom_xep``.
There are two types of incoming :class:`~sleekxmpp.stanza.iq.Iq` requests:
``get`` and ``set``. You can register a handler that will accept both and then
filter by type as needed, as so:
.. code-block:: python
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq/custom_xep'),
self._handle_custom_iq))
# ...
def _handle_custom_iq(self, iq):
if iq['type'] == 'get':
# ...
pass
elif iq['type'] == 'set':
# ...
pass
else:
# ... This will capture error responses too
pass
If you want to filter out query types beforehand, you can adjust the matching
filter by using ``@type=get`` or ``@type=set`` if you are using the recommended
:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
.. code-block:: python
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq@type=get/custom_xep'),
self._handle_custom_iq_get))
# ...
def _handle_custom_iq_get(self, iq):
assert(iq['type'] == 'get')

View File

@@ -1,2 +1,42 @@
.. _proxy:
=========================
Enable HTTP Proxy Support
=========================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
In some instances, you may wish to route XMPP traffic through
an HTTP proxy, probably to get around restrictive firewalls.
SleekXMPP provides support for basic HTTP proxying with DIGEST
authentication.
Enabling proxy support is done in two steps. The first is to instruct SleekXMPP
to use a proxy, and the second is to configure the proxy details:
.. code-block:: python
xmpp = ClientXMPP(...)
xmpp.use_proxy = True
xmpp.proxy_config = {
'host': 'proxy.example.com',
'port': 5555,
'username': 'example_user',
'password': '******'
}
The ``'username'`` and ``'password'`` fields are optional if the proxy does not
require authentication.
The Final Product
-----------------
.. include:: ../../examples/proxy_echo_client.py
:literal:

View File

@@ -1,2 +1,4 @@
.. _using-handlers-matchers:
Using Stream Handlers and Matchers
==================================

30
docs/howto/stanzas.rst Normal file
View File

@@ -0,0 +1,30 @@
.. _work-with-stanzas:
How to Work with Stanza Objects
===============================
.. _create-stanza-interfaces:
Defining Stanza Interfaces
--------------------------
.. _create-stanza-plugins:
Creating Stanza Plugins
-----------------------
.. _create-extension-plugins:
Creating a Stanza Extension
---------------------------
.. _override-parent-interfaces:
Overriding a Parent Stanza
--------------------------

View File

@@ -12,13 +12,8 @@ SleekXMPP
``master`` branch, while the latest development version is in the
``develop`` branch.
**Stable Releases**
- `1.0 Beta6.1 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta6.1>`_
- `1.0 Beta5 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta5>`_
- `1.0 Beta4 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta4>`_
- `1.0 Beta3 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta3>`_
- `1.0 Beta2 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta2>`_
- `1.0 Beta1 <http://github.com/fritzy/SleekXMPP/zipball/1.0-Beta1>`_
**Latest Stable Release**
- `1.0 RC3 <http://github.com/fritzy/SleekXMPP/zipball/1.0-RC3>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
@@ -84,8 +79,10 @@ Tutorials, FAQs, and How To Guides
.. toctree::
:maxdepth: 1
faq
xeps
xmpp_tdg
howto/stanzas
create_plugin
features
sasl
@@ -113,8 +110,35 @@ API Reference
event_index
api/clientxmpp
api/componentxmpp
api/basexmpp
api/xmlstream
api/exceptions
api/xmlstream/jid
api/xmlstream/stanzabase
api/xmlstream/handler
api/xmlstream/matcher
api/xmlstream/xmlstream
api/xmlstream/scheduler
api/xmlstream/tostring
api/xmlstream/filesocket
Core Stanzas
~~~~~~~~~~~~
.. toctree::
:maxdepth: 2
api/stanza/rootstanza
api/stanza/message
api/stanza/presence
api/stanza/iq
api/stanza/error
api/stanza/stream_error
Plugins
~~~~~~~
.. toctree::
:maxdepth: 2
Additional Info
---------------

BIN
docs/python-objects.inv Normal file

Binary file not shown.

View File

@@ -1,2 +1,50 @@
Supported XEPS
==============
======= ============================= ================
XEP Description Notes
======= ============================= ================
`0004`_ Data forms
`0009`_ Jabber RPC
`0012`_ Last Activity
`0030`_ Service Discovery
`0033`_ Extended Stanza Addressing
`0045`_ Multi-User Chat (MUC) Client-side only
`0050`_ Ad-hoc Commands
`0059`_ Result Set Management
`0060`_ Publish/Subscribe (PubSub) Client-side only
`0066`_ Out-of-band Data
`0078`_ Non-SASL Authentication
`0082`_ XMPP Date and Time Profiles
`0085`_ Chat-State Notifications
`0086`_ Error Condition Mappings
`0092`_ Software Version
`0128`_ Service Discovery Extensions
`0202`_ Entity Time
`0203`_ Delayed Delivery
`0224`_ Attention
`0249`_ Direct MUC Invitations
======= ============================= ================
.. _0004: http://xmpp.org/extensions/xep-0004.html
.. _0009: http://xmpp.org/extensions/xep-0009.html
.. _0012: http://xmpp.org/extensions/xep-0012.html
.. _0030: http://xmpp.org/extensions/xep-0030.html
.. _0033: http://xmpp.org/extensions/xep-0033.html
.. _0045: http://xmpp.org/extensions/xep-0045.html
.. _0050: http://xmpp.org/extensions/xep-0050.html
.. _0059: http://xmpp.org/extensions/xep-0059.html
.. _0060: http://xmpp.org/extensions/xep-0060.html
.. _0066: http://xmpp.org/extensions/xep-0066.html
.. _0078: http://xmpp.org/extensions/xep-0078.html
.. _0082: http://xmpp.org/extensions/xep-0082.html
.. _0085: http://xmpp.org/extensions/xep-0085.html
.. _0086: http://xmpp.org/extensions/xep-0086.html
.. _0092: http://xmpp.org/extensions/xep-0092.html
.. _0128: http://xmpp.org/extensions/xep-0128.html
.. _0199: http://xmpp.org/extensions/xep-0199.html
.. _0202: http://xmpp.org/extensions/xep-0202.html
.. _0203: http://xmpp.org/extensions/xep-0203.html
.. _0224: http://xmpp.org/extensions/xep-0224.html
.. _0249: http://xmpp.org/extensions/xep-0249.html

View File

@@ -11,7 +11,6 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
@@ -41,7 +40,7 @@ class CommandBot(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -50,7 +49,7 @@ class CommandBot(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -72,7 +71,7 @@ class CommandBot(sleekxmpp.ClientXMPP):
def _handle_command(self, iq, session):
"""
Respond to the intial request for a command.
Respond to the initial request for a command.
Arguments:
iq -- The iq stanza containing the command request.
@@ -192,14 +191,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -11,7 +11,6 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
@@ -44,7 +43,7 @@ class CommandUserBot(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message)
@@ -54,7 +53,7 @@ class CommandUserBot(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -198,14 +197,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -10,7 +10,6 @@
"""
import sys
import time
import logging
import getpass
from optparse import OptionParser
@@ -61,7 +60,7 @@ class Disco(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -70,7 +69,7 @@ class Disco(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
In this case, we send disco#info and disco#items
@@ -188,13 +187,13 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
else:
print("Unable to connect.")

View File

@@ -11,7 +11,6 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
@@ -41,7 +40,7 @@ class EchoBot(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -55,7 +54,7 @@ class EchoBot(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -132,14 +131,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -11,7 +11,7 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
import sleekxmpp
@@ -116,7 +116,7 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -11,7 +11,7 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
import sleekxmpp
@@ -44,7 +44,7 @@ class MUCBot(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -68,7 +68,7 @@ class MUCBot(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -161,9 +161,14 @@ if __name__ == '__main__':
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if None in [opts.jid, opts.password, opts.room, opts.nick]:
optp.print_help()
sys.exit(1)
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.room is None:
opts.room = raw_input("MUC room: ")
if opts.nick is None:
opts.nick = raw_input("MUC nickname: ")
# Setup the MUCBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
@@ -175,14 +180,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -11,7 +11,6 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
@@ -44,7 +43,7 @@ class PingTest(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -53,7 +52,7 @@ class PingTest(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -72,7 +71,7 @@ class PingTest(sleekxmpp.ClientXMPP):
self.disconnect()
sys.exit(1)
else:
logging.info("Success! RTT: %s" % str(result))
logging.info("Success! RTT: %s", str(result))
self.disconnect()
@@ -129,14 +128,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -11,7 +11,6 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
@@ -41,7 +40,7 @@ class EchoBot(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -55,7 +54,7 @@ class EchoBot(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -156,14 +155,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -10,7 +10,6 @@
"""
import sys
import time
import logging
import getpass
import threading
@@ -43,7 +42,7 @@ class RosterBrowser(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster. We need threaded=True so that the
# session_start handler doesn't block event processing
# while we wait for presence stanzas to arrive.
@@ -58,7 +57,7 @@ class RosterBrowser(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -160,14 +159,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
else:
print("Unable to connect.")

View File

@@ -11,7 +11,6 @@
import sys
import logging
import time
import getpass
from optparse import OptionParser
@@ -46,7 +45,7 @@ class SendMsgBot(sleekxmpp.ClientXMPP):
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can intialize
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
@@ -55,7 +54,7 @@ class SendMsgBot(sleekxmpp.ClientXMPP):
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an intial
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
@@ -131,14 +130,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the pydns library installed, you will need
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(threaded=False)
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.basexmpp
~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module provides the common XMPP functionality
for both clients and components.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from __future__ import with_statement, unicode_literals
@@ -43,71 +49,59 @@ class BaseXMPP(XMLStream):
with XMPP. It also provides a plugin mechanism to easily extend
and add support for new XMPP features.
Attributes:
auto_authorize -- Manage automatically accepting roster
subscriptions.
auto_subscribe -- Manage automatically requesting mutual
subscriptions.
is_component -- Indicates if this stream is for an XMPP component.
jid -- The XMPP JID for this stream.
plugin -- A dictionary of loaded plugins.
plugin_config -- A dictionary of plugin configurations.
plugin_whitelist -- A list of approved plugins.
sentpresence -- Indicates if an initial presence has been sent.
roster -- A dictionary containing subscribed JIDs and
their presence statuses.
Methods:
Iq -- Factory for creating an Iq stanzas.
Message -- Factory for creating Message stanzas.
Presence -- Factory for creating Presence stanzas.
get -- Return a plugin given its name.
make_iq -- Create and initialize an Iq stanza.
make_iq_error -- Create an Iq stanza of type 'error'.
make_iq_get -- Create an Iq stanza of type 'get'.
make_iq_query -- Create an Iq stanza with a given query.
make_iq_result -- Create an Iq stanza of type 'result'.
make_iq_set -- Create an Iq stanza of type 'set'.
make_message -- Create and initialize a Message stanza.
make_presence -- Create and initialize a Presence stanza.
make_query_roster -- Create a roster query.
process -- Overrides XMLStream.process.
register_plugin -- Load and configure a plugin.
register_plugins -- Load and configure multiple plugins.
send_message -- Create and send a Message stanza.
send_presence -- Create and send a Presence stanza.
send_presence_subscribe -- Send a subscription request.
:param default_ns: Ensure that the correct default XML namespace
is used during initialization.
"""
def __init__(self, jid='', default_ns='jabber:client'):
"""
Adapt an XML stream for use with XMPP.
Arguments:
default_ns -- Ensure that the correct default XML namespace
is used during initialization.
"""
XMLStream.__init__(self)
# To comply with PEP8, method names now use underscores.
# Deprecated method names are re-mapped for backwards compatibility.
self.default_ns = default_ns
self.stream_ns = 'http://etherx.jabber.org/streams'
self.namespace_map[self.stream_ns] = 'stream'
#: An identifier for the stream as given by the server.
self.stream_id = None
#: The JabberID (JID) used by this connection.
self.boundjid = JID(jid)
#: A dictionary mapping plugin names to plugins.
self.plugin = {}
#: Configuration options for whitelisted plugins.
#: If a plugin is registered without any configuration,
#: and there is an entry here, it will be used.
self.plugin_config = {}
#: A list of plugins that will be loaded if
#: :meth:`register_plugins` is called.
self.plugin_whitelist = []
#: The main roster object. This roster supports multiple
#: owner JIDs, as in the case for components. For clients
#: which only have a single JID, see :attr:`client_roster`.
self.roster = roster.Roster(self)
self.roster.add(self.boundjid.bare)
#: The single roster for the bound JID. This is the
#: equivalent of::
#:
#: self.roster[self.boundjid.bare]
self.client_roster = self.roster[self.boundjid.bare]
#: The distinction between clients and components can be
#: important, primarily for choosing how to handle the
#: ``'to'`` and ``'from'`` JIDs of stanzas.
self.is_component = False
#: Flag indicating that the initial presence broadcast has
#: been sent. Until this happens, some servers may not
#: behave as expected when sending stanzas.
self.sentpresence = False
#: A reference to :mod:`sleekxmpp.stanza` to make accessing
#: stanza classes easier.
self.stanza = sleekxmpp.stanza
self.register_handler(
@@ -161,40 +155,36 @@ class BaseXMPP(XMLStream):
register_stanza_plugin(Message, HTMLIM)
def start_stream_handler(self, xml):
"""
Save the stream ID once the streams have been established.
"""Save the stream ID once the streams have been established.
Overrides XMLStream.start_stream_handler.
Arguments:
xml -- The incoming stream's root element.
:param xml: The incoming stream's root element.
"""
self.stream_id = xml.get('id', '')
def process(self, *args, **kwargs):
"""
Overrides XMLStream.process.
Initialize the XML streams and begin processing events.
"""Initialize plugins and begin processing the XML stream.
The number of threads used for processing stream events is determined
by HANDLER_THREADS.
by :data:`HANDLER_THREADS`.
Arguments:
block -- If block=False then event dispatcher will run
in a separate thread, allowing for the stream to be
used in the background for another application.
Otherwise, process(block=True) blocks the current thread.
Defaults to False.
:param bool block: If ``False``, then event dispatcher will run
in a separate thread, allowing for the stream to be
used in the background for another application.
Otherwise, ``process(block=True)`` blocks the current
thread. Defaults to ``False``.
:param bool threaded: **DEPRECATED**
If ``True``, then event dispatcher will run
in a separate thread, allowing for the stream to be
used in the background for another application.
Defaults to ``True``. This does **not** mean that no
threads are used at all if ``threaded=False``.
**threaded is deprecated and included for API compatibility**
threaded -- If threaded=True then event dispatcher will run
in a separate thread, allowing for the stream to be
used in the background for another application.
Defaults to True.
Regardless of these threading options, these threads will
always exist:
Event handlers and the send queue will be threaded
regardless of these parameters.
- The event queue processor
- The send queue processor
- The scheduler
"""
for name in self.plugin:
if not self.plugin[name].post_inited:
@@ -202,15 +192,13 @@ class BaseXMPP(XMLStream):
return XMLStream.process(self, *args, **kwargs)
def register_plugin(self, plugin, pconfig={}, module=None):
"""
Register and configure a plugin for use in this stream.
"""Register and configure a plugin for use in this stream.
Arguments:
plugin -- The name of the plugin class. Plugin names must
:param plugin: The name of the plugin class. Plugin names must
be unique.
pconfig -- A dictionary of configuration data for the plugin.
Defaults to an empty dictionary.
module -- Optional refence to the module containing the plugin
:param pconfig: A dictionary of configuration data for the plugin.
Defaults to an empty dictionary.
:param module: Optional refence to the module containing the plugin
class if using custom plugins.
"""
try:
@@ -239,25 +227,24 @@ class BaseXMPP(XMLStream):
self.plugin[plugin] = getattr(module, plugin)(self, pconfig)
# Let XEP/RFC implementing plugins have some extra logging info.
spec = '(CUSTOM) '
spec = '(CUSTOM) %s'
if self.plugin[plugin].xep:
spec = "(XEP-%s) " % self.plugin[plugin].xep
elif self.plugin[plugin].rfc:
spec = "(RFC-%s) " % self.plugin[plugin].rfc
desc = (spec, self.plugin[plugin].description)
log.debug("Loaded Plugin %s%s" % desc)
log.debug("Loaded Plugin %s %s" % desc)
except:
log.exception("Unable to load plugin: %s", plugin)
def register_plugins(self):
"""
Register and initialize all built-in plugins.
"""Register and initialize all built-in plugins.
Optionally, the list of plugins loaded may be limited to those
contained in self.plugin_whitelist.
contained in :attr:`plugin_whitelist`.
Plugin configurations stored in self.plugin_config will be used.
Plugin configurations stored in :attr:`plugin_config` will be used.
"""
if self.plugin_whitelist:
plugin_list = self.plugin_whitelist
@@ -276,19 +263,15 @@ class BaseXMPP(XMLStream):
self.plugin[plugin].post_init()
def __getitem__(self, key):
"""
Return a plugin given its name, if it has been registered.
"""
"""Return a plugin given its name, if it has been registered."""
if key in self.plugin:
return self.plugin[key]
else:
log.warning("""Plugin "%s" is not loaded.""" % key)
log.warning("Plugin '%s' is not loaded.", key)
return False
def get(self, key, default):
"""
Return a plugin given its name, if it has been registered.
"""
"""Return a plugin given its name, if it has been registered."""
return self.plugin.get(key, default)
def Message(self, *args, **kwargs):
@@ -304,16 +287,18 @@ class BaseXMPP(XMLStream):
return Presence(self, *args, **kwargs)
def make_iq(self, id=0, ifrom=None, ito=None, itype=None, iquery=None):
"""
Create a new Iq stanza with a given Id and from JID.
"""Create a new Iq stanza with a given Id and from JID.
Arguments:
id -- An ideally unique ID value for this stanza thread.
Defaults to 0.
ifrom -- The from JID to use for this stanza.
ito -- The destination JID for this stanza.
itype -- The Iq's type, one of: get, set, result, or error.
iquery -- Optional namespace for adding a query element.
:param id: An ideally unique ID value for this stanza thread.
Defaults to 0.
:param ifrom: The from :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza.
:param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza.
:param itype: The :class:`~sleekxmpp.stanza.iq.Iq`'s type,
one of: ``'get'``, ``'set'``, ``'result'``,
or ``'error'``.
:param iquery: Optional namespace for adding a query element.
"""
iq = self.Iq()
iq['id'] = str(id)
@@ -324,17 +309,17 @@ class BaseXMPP(XMLStream):
return iq
def make_iq_get(self, queryxmlns=None, ito=None, ifrom=None, iq=None):
"""
Create an Iq stanza of type 'get'.
"""Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type ``'get'``.
Optionally, a query element may be added.
Arguments:
queryxmlns -- The namespace of the query to use.
ito -- The destination JID for this stanza.
ifrom -- The from JID to use for this stanza.
iq -- Optionally use an existing stanza instead
of generating a new one.
:param queryxmlns: The namespace of the query to use.
:param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza.
:param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza.
:param iq: Optionally use an existing stanza instead
of generating a new one.
"""
if not iq:
iq = self.Iq()
@@ -348,14 +333,16 @@ class BaseXMPP(XMLStream):
def make_iq_result(self, id=None, ito=None, ifrom=None, iq=None):
"""
Create an Iq stanza of type 'result' with the given ID value.
Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type
``'result'`` with the given ID value.
Arguments:
id -- An ideally unique ID value. May use self.new_id().
ito -- The destination JID for this stanza.
ifrom -- The from JID to use for this stanza.
iq -- Optionally use an existing stanza instead
of generating a new one.
:param id: An ideally unique ID value. May use :meth:`new_id()`.
:param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza.
:param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza.
:param iq: Optionally use an existing stanza instead
of generating a new one.
"""
if not iq:
iq = self.Iq()
@@ -371,17 +358,22 @@ class BaseXMPP(XMLStream):
def make_iq_set(self, sub=None, ito=None, ifrom=None, iq=None):
"""
Create an Iq stanza of type 'set'.
Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type ``'set'``.
Optionally, a substanza may be given to use as the
stanza's payload.
Arguments:
sub -- A stanza or XML object to use as the Iq's payload.
ito -- The destination JID for this stanza.
ifrom -- The from JID to use for this stanza.
iq -- Optionally use an existing stanza instead
of generating a new one.
:param sub: Either an
:class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
stanza object or an
:class:`~xml.etree.ElementTree.Element` XML object
to use as the :class:`~sleekxmpp.stanza.iq.Iq`'s payload.
:param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza.
:param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza.
:param iq: Optionally use an existing stanza instead
of generating a new one.
"""
if not iq:
iq = self.Iq()
@@ -398,19 +390,20 @@ class BaseXMPP(XMLStream):
condition='feature-not-implemented',
text=None, ito=None, ifrom=None, iq=None):
"""
Create an Iq stanza of type 'error'.
Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type ``'error'``.
Arguments:
id -- An ideally unique ID value. May use self.new_id().
type -- The type of the error, such as 'cancel' or 'modify'.
Defaults to 'cancel'.
condition -- The error condition.
Defaults to 'feature-not-implemented'.
text -- A message describing the cause of the error.
ito -- The destination JID for this stanza.
ifrom -- The from JID to use for this stanza.
iq -- Optionally use an existing stanza instead
of generating a new one.
:param id: An ideally unique ID value. May use :meth:`new_id()`.
:param type: The type of the error, such as ``'cancel'`` or
``'modify'``. Defaults to ``'cancel'``.
:param condition: The error condition. Defaults to
``'feature-not-implemented'``.
:param text: A message describing the cause of the error.
:param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza.
:param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza.
:param iq: Optionally use an existing stanza instead
of generating a new one.
"""
if not iq:
iq = self.Iq()
@@ -426,15 +419,16 @@ class BaseXMPP(XMLStream):
def make_iq_query(self, iq=None, xmlns='', ito=None, ifrom=None):
"""
Create or modify an Iq stanza to use the given
query namespace.
Create or modify an :class:`~sleekxmpp.stanza.iq.Iq` stanza
to use the given query namespace.
Arguments:
iq -- Optional Iq stanza to modify. A new
stanza is created otherwise.
xmlns -- The query's namespace.
ito -- The destination JID for this stanza.
ifrom -- The from JID to use for this stanza.
:param iq: Optionally use an existing stanza instead
of generating a new one.
:param xmlns: The query's namespace.
:param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza.
:param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza.
"""
if not iq:
iq = self.Iq()
@@ -446,12 +440,10 @@ class BaseXMPP(XMLStream):
return iq
def make_query_roster(self, iq=None):
"""
Create a roster query element.
"""Create a roster query element.
Arguments:
iq -- Optional Iq stanza to modify. A new stanza
is created otherwise.
:param iq: Optionally use an existing stanza instead
of generating a new one.
"""
if iq:
iq['query'] = 'jabber:iq:roster'
@@ -460,18 +452,19 @@ class BaseXMPP(XMLStream):
def make_message(self, mto, mbody=None, msubject=None, mtype=None,
mhtml=None, mfrom=None, mnick=None):
"""
Create and initialize a new Message stanza.
Create and initialize a new
:class:`~sleekxmpp.stanza.message.Message` stanza.
Arguments:
mto -- The recipient of the message.
mbody -- The main contents of the message.
msubject -- Optional subject for the message.
mtype -- The message's type, such as 'chat' or 'groupchat'.
mhtml -- Optional HTML body content.
mfrom -- The sender of the message. if sending from a client,
be aware that some servers require that the full JID
of the sender be used.
mnick -- Optional nickname of the sender.
:param mto: The recipient of the message.
:param mbody: The main contents of the message.
:param msubject: Optional subject for the message.
:param mtype: The message's type, such as ``'chat'`` or
``'groupchat'``.
:param mhtml: Optional HTML body content in the form of a string.
:param mfrom: The sender of the message. if sending from a client,
be aware that some servers require that the full JID
of the sender be used.
:param mnick: Optional nickname of the sender.
"""
message = self.Message(sto=mto, stype=mtype, sfrom=mfrom)
message['body'] = mbody
@@ -485,16 +478,16 @@ class BaseXMPP(XMLStream):
def make_presence(self, pshow=None, pstatus=None, ppriority=None,
pto=None, ptype=None, pfrom=None, pnick=None):
"""
Create and initialize a new Presence stanza.
Create and initialize a new
:class:`~sleekxmpp.stanza.presence.Presence` stanza.
Arguments:
pshow -- The presence's show value.
pstatus -- The presence's status message.
ppriority -- This connections' priority.
pto -- The recipient of a directed presence.
ptype -- The type of presence, such as 'subscribe'.
pfrom -- The sender of the presence.
pnick -- Optional nickname of the presence's sender.
:param pshow: The presence's show value.
:param pstatus: The presence's status message.
:param ppriority: This connection's priority.
:param pto: The recipient of a directed presence.
:param ptype: The type of presence, such as ``'subscribe'``.
:param pfrom: The sender of the presence.
:param pnick: Optional nickname of the presence's sender.
"""
presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto)
if pshow is not None:
@@ -509,18 +502,19 @@ class BaseXMPP(XMLStream):
def send_message(self, mto, mbody, msubject=None, mtype=None,
mhtml=None, mfrom=None, mnick=None):
"""
Create, initialize, and send a Message stanza.
Create, initialize, and send a new
:class:`~sleekxmpp.stanza.message.Message` stanza.
Arguments:
mto -- The recipient of the message.
mbody -- The main contents of the message.
msubject -- Optional subject for the message.
mtype -- The message's type, such as 'chat' or 'groupchat'.
mhtml -- Optional HTML body content.
mfrom -- The sender of the message. if sending from a client,
be aware that some servers require that the full JID
of the sender be used.
mnick -- Optional nickname of the sender.
:param mto: The recipient of the message.
:param mbody: The main contents of the message.
:param msubject: Optional subject for the message.
:param mtype: The message's type, such as ``'chat'`` or
``'groupchat'``.
:param mhtml: Optional HTML body content in the form of a string.
:param mfrom: The sender of the message. if sending from a client,
be aware that some servers require that the full JID
of the sender be used.
:param mnick: Optional nickname of the sender.
"""
self.make_message(mto, mbody, msubject, mtype,
mhtml, mfrom, mnick).send()
@@ -528,16 +522,16 @@ class BaseXMPP(XMLStream):
def send_presence(self, pshow=None, pstatus=None, ppriority=None,
pto=None, pfrom=None, ptype=None, pnick=None):
"""
Create, initialize, and send a Presence stanza.
Create, initialize, and send a new
:class:`~sleekxmpp.stanza.presence.Presence` stanza.
Arguments:
pshow -- The presence's show value.
pstatus -- The presence's status message.
ppriority -- This connections' priority.
pto -- The recipient of a directed presence.
ptype -- The type of presence, such as 'subscribe'.
pfrom -- The sender of the presence.
pnick -- Optional nickname of the presence's sender.
:param pshow: The presence's show value.
:param pstatus: The presence's status message.
:param ppriority: This connection's priority.
:param pto: The recipient of a directed presence.
:param ptype: The type of presence, such as ``'subscribe'``.
:param pfrom: The sender of the presence.
:param pnick: Optional nickname of the presence's sender.
"""
# Python2.6 chokes on Unicode strings for dict keys.
args = {str('pto'): pto,
@@ -555,13 +549,14 @@ class BaseXMPP(XMLStream):
def send_presence_subscription(self, pto, pfrom=None,
ptype='subscribe', pnick=None):
"""
Create, initialize, and send a Presence stanza of type 'subscribe'.
Create, initialize, and send a new
:class:`~sleekxmpp.stanza.presence.Presence` stanza of
type ``'subscribe'``.
Arguments:
pto -- The recipient of a directed presence.
pfrom -- The sender of the presence.
ptype -- The type of presence. Defaults to 'subscribe'.
pnick -- Nickname of the presence's sender.
:param pto: The recipient of a directed presence.
:param pfrom: The sender of the presence.
:param ptype: The type of presence, such as ``'subscribe'``.
:param pnick: Optional nickname of the presence's sender.
"""
presence = self.makePresence(ptype=ptype,
pfrom=pfrom,
@@ -574,9 +569,7 @@ class BaseXMPP(XMLStream):
@property
def jid(self):
"""
Attribute accessor for bare jid
"""
"""Attribute accessor for bare jid"""
log.warning("jid property deprecated. Use boundjid.bare")
return self.boundjid.bare
@@ -587,9 +580,7 @@ class BaseXMPP(XMLStream):
@property
def fulljid(self):
"""
Attribute accessor for full jid
"""
"""Attribute accessor for full jid"""
log.warning("fulljid property deprecated. Use boundjid.full")
return self.boundjid.full
@@ -600,9 +591,7 @@ class BaseXMPP(XMLStream):
@property
def resource(self):
"""
Attribute accessor for jid resource
"""
"""Attribute accessor for jid resource"""
log.warning("resource property deprecated. Use boundjid.resource")
return self.boundjid.resource
@@ -613,9 +602,7 @@ class BaseXMPP(XMLStream):
@property
def username(self):
"""
Attribute accessor for jid usernode
"""
"""Attribute accessor for jid usernode"""
log.warning("username property deprecated. Use boundjid.user")
return self.boundjid.user
@@ -626,9 +613,7 @@ class BaseXMPP(XMLStream):
@property
def server(self):
"""
Attribute accessor for jid host
"""
"""Attribute accessor for jid host"""
log.warning("server property deprecated. Use boundjid.host")
return self.boundjid.server
@@ -639,47 +624,33 @@ class BaseXMPP(XMLStream):
@property
def auto_authorize(self):
"""
Auto accept or deny subscription requests.
"""Auto accept or deny subscription requests.
If True, auto accept subscription requests.
If False, auto deny subscription requests.
If None, don't automatically respond.
If ``True``, auto accept subscription requests.
If ``False``, auto deny subscription requests.
If ``None``, don't automatically respond.
"""
return self.roster.auto_authorize
@auto_authorize.setter
def auto_authorize(self, value):
"""
Auto accept or deny subscription requests.
If True, auto accept subscription requests.
If False, auto deny subscription requests.
If None, don't automatically respond.
"""
self.roster.auto_authorize = value
@property
def auto_subscribe(self):
"""
Auto send requests for mutual subscriptions.
"""Auto send requests for mutual subscriptions.
If True, auto send mutual subscription requests.
If ``True``, auto send mutual subscription requests.
"""
return self.roster.auto_subscribe
@auto_subscribe.setter
def auto_subscribe(self, value):
"""
Auto send requests for mutual subscriptions.
If True, auto send mutual subscription requests.
"""
self.roster.auto_subscribe = value
def set_jid(self, jid):
"""Rip a JID apart and claim it as our own."""
log.debug("setting jid to %s" % jid)
log.debug("setting jid to %s", jid)
self.boundjid.full = jid
def getjidresource(self, fulljid):
@@ -713,17 +684,16 @@ class BaseXMPP(XMLStream):
self.roster[pto][pfrom].handle_unavailable(presence)
def _handle_new_subscription(self, stanza):
"""
Attempt to automatically handle subscription requests.
"""Attempt to automatically handle subscription requests.
Subscriptions will be approved if the request is from
a whitelisted JID, of self.auto_authorize is True. They
will be rejected if self.auto_authorize is False. Setting
self.auto_authorize to None will disable automatic
a whitelisted JID, of :attr:`auto_authorize` is True. They
will be rejected if :attr:`auto_authorize` is False. Setting
:attr:`auto_authorize` to ``None`` will disable automatic
subscription handling (except for whitelisted JIDs).
If a subscription is accepted, a request for a mutual
subscription will be sent if self.auto_subscribe is True.
subscription will be sent if :attr:`auto_subscribe` is ``True``.
"""
roster = self.roster[stanza['to'].bare]
item = self.roster[stanza['to'].bare][stanza['from'].bare]
@@ -762,8 +732,7 @@ class BaseXMPP(XMLStream):
self.roster[pto][pfrom].handle_unsubscribed(presence)
def _handle_presence(self, presence):
"""
Process incoming presence stanzas.
"""Process incoming presence stanzas.
Update the roster with presence information.
"""
@@ -779,23 +748,20 @@ class BaseXMPP(XMLStream):
return
def exception(self, exception):
"""
Process any uncaught exceptions, notably IqError and
IqTimeout exceptions.
"""Process any uncaught exceptions, notably
:class:`~sleekxmpp.exceptions.IqError` and
:class:`~sleekxmpp.exceptions.IqTimeout` exceptions.
Overrides XMLStream.exception.
Arguments:
exception -- An unhandled exception object.
:param exception: An unhandled :class:`Exception` object.
"""
if isinstance(exception, IqError):
iq = exception.iq
log.error('%s: %s' % (iq['error']['condition'],
iq['error']['text']))
log.error('%s: %s', iq['error']['condition'],
iq['error']['text'])
log.warning('You should catch IqError exceptions')
elif isinstance(exception, IqTimeout):
iq = exception.iq
log.error('Request timed out: %s' % iq)
log.error('Request timed out: %s', iq)
log.warning('You should catch IqTimeout exceptions')
else:
log.exception(exception)

View File

@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.clientxmpp
~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module provides XMPP functionality that
is specific to client connections.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from __future__ import absolute_import, unicode_literals
@@ -41,37 +47,30 @@ log = logging.getLogger(__name__)
class ClientXMPP(BaseXMPP):
"""
SleekXMPP's client class. ( Use only for good, not for evil.)
SleekXMPP's client class. (Use only for good, not for evil.)
Typical Use:
xmpp = ClientXMPP('user@server.tld/resource', 'password')
xmpp.process(block=False) // when block is True, it blocks the current
// thread. False by default.
Typical use pattern:
Attributes:
.. code-block:: python
Methods:
connect -- Overrides XMLStream.connect.
del_roster_item -- Delete a roster item.
get_roster -- Retrieve the roster from the server.
register_feature -- Register a stream feature.
update_roster -- Update a roster item.
xmpp = ClientXMPP('user@server.tld/resource', 'password')
# ... Register plugins and event handlers ...
xmpp.connect()
xmpp.process(block=False) # block=True will block the current
# thread. By default, block=False
:param jid: The JID of the XMPP user account.
:param password: The password for the XMPP user account.
:param ssl: **Deprecated.**
:param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that
will be loaded when calling
:meth:`~sleekxmpp.basexmpp.BaseXMPP.register_plugins()`.
:param escape_quotes: **Deprecated.**
"""
def __init__(self, jid, password, ssl=False, plugin_config={},
plugin_whitelist=[], escape_quotes=True, sasl_mech=None):
"""
Create a new SleekXMPP client.
Arguments:
jid -- The JID of the XMPP user account.
password -- The password for the XMPP user account.
ssl -- Deprecated.
plugin_config -- A dictionary of plugin configurations.
plugin_whitelist -- A list of approved plugins that will be loaded
when calling register_plugins.
escape_quotes -- Deprecated.
"""
BaseXMPP.__init__(self, jid, 'jabber:client')
self.set_jid(jid)
@@ -121,21 +120,19 @@ class ClientXMPP(BaseXMPP):
def connect(self, address=tuple(), reattempt=True,
use_tls=True, use_ssl=False):
"""
Connect to the XMPP server.
"""Connect to the XMPP server.
When no address is given, a SRV lookup for the server will
be attempted. If that fails, the server user in the JID
will be used.
Arguments:
address -- A tuple containing the server's host and port.
reattempt -- If True, reattempt the connection if an
error occurs. Defaults to True.
use_tls -- Indicates if TLS should be used for the
connection. Defaults to True.
use_ssl -- Indicates if the older SSL connection method
should be used. Defaults to False.
:param address -- A tuple containing the server's host and port.
:param reattempt: If ``True``, repeat attempting to connect if an
error occurs. Defaults to ``True``.
:param use_tls: Indicates if TLS should be used for the
connection. Defaults to ``True``.
:param use_ssl: Indicates if the older SSL connection method
should be used. Defaults to ``False``.
"""
self.session_started_event.clear()
if not address:
@@ -146,13 +143,10 @@ class ClientXMPP(BaseXMPP):
reattempt=reattempt)
def get_dns_records(self, domain, port=None):
"""
Get the DNS records for a domain.
Overriddes XMLStream.get_dns_records to use SRV.
"""Get the DNS records for a domain, including SRV records.
Arguments:
domain -- The domain in question.
port -- If the results don't include a port, use this one.
:param domain: The domain in question.
:param port: If the results don't include a port, use this one.
"""
if port is None:
port = self.default_port
@@ -164,11 +158,11 @@ class ClientXMPP(BaseXMPP):
address = (answer.target.to_text()[:-1], answer.port)
answers.append((address, answer.priority, answer.weight))
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
log.warning("No SRV records for %s" % domain)
log.warning("No SRV records for %s", domain)
answers = super(ClientXMPP, self).get_dns_records(domain, port)
except dns.exception.Timeout:
log.warning("DNS resolution timed out " + \
"for SRV record of %s" % domain)
"for SRV record of %s", domain)
answers = super(ClientXMPP, self).get_dns_records(domain, port)
return answers
else:
@@ -177,17 +171,15 @@ class ClientXMPP(BaseXMPP):
return [((domain, port), 0, 0)]
def register_feature(self, name, handler, restart=False, order=5000):
"""
Register a stream feature.
"""Register a stream feature handler.
Arguments:
name -- The name of the stream feature.
handler -- The function to execute if the feature is received.
restart -- Indicates if feature processing should halt with
this feature. Defaults to False.
order -- The relative ordering in which the feature should
be negotiated. Lower values will be attempted
earlier when available.
:param name: The name of the stream feature.
:param handler: The function to execute if the feature is received.
:param restart: Indicates if feature processing should halt with
this feature. Defaults to ``False``.
:param order: The relative ordering in which the feature should
be negotiated. Lower values will be attempted
earlier when available.
"""
self._stream_feature_handlers[name] = (handler, restart)
self._stream_feature_order.append((order, name))
@@ -195,53 +187,51 @@ class ClientXMPP(BaseXMPP):
def update_roster(self, jid, name=None, subscription=None, groups=[],
block=True, timeout=None, callback=None):
"""
Add or change a roster item.
"""Add or change a roster item.
Arguments:
jid -- The JID of the entry to modify.
name -- The user's nickname for this JID.
subscription -- The subscription status. May be one of
'to', 'from', 'both', or 'none'. If set
to 'remove', the entry will be deleted.
groups -- The roster groups that contain this item.
block -- Specify if the roster request will block
until a response is received, or a timeout
occurs. Defaults to True.
timeout -- The length of time (in seconds) to wait
for a response before continuing if blocking
is used. Defaults to self.response_timeout.
callback -- Optional reference to a stream handler function.
Will be executed when the roster is received.
Implies block=False.
:param jid: The JID of the entry to modify.
:param name: The user's nickname for this JID.
:param subscription: The subscription status. May be one of
``'to'``, ``'from'``, ``'both'``, or
``'none'``. If set to ``'remove'``,
the entry will be deleted.
:param groups: The roster groups that contain this item.
:param block: Specify if the roster request will block
until a response is received, or a timeout
occurs. Defaults to ``True``.
:param timeout: The length of time (in seconds) to wait
for a response before continuing if blocking
is used. Defaults to
:attr:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`.
:param callback: Optional reference to a stream handler function.
Will be executed when the roster is received.
Implies ``block=False``.
"""
return self.client_roster.update(jid, name, subscription, groups,
block, timeout, callback)
def del_roster_item(self, jid):
"""
Remove an item from the roster by setting its subscription
status to 'remove'.
"""Remove an item from the roster.
This is done by setting its subscription status to ``'remove'``.
Arguments:
jid -- The JID of the item to remove.
:param jid: The JID of the item to remove.
"""
return self.client_roster.remove(jid)
def get_roster(self, block=True, timeout=None, callback=None):
"""
Request the roster from the server.
"""Request the roster from the server.
Arguments:
block -- Specify if the roster request will block until a
response is received, or a timeout occurs.
Defaults to True.
timeout -- The length of time (in seconds) to wait for a response
:param block: Specify if the roster request will block until a
response is received, or a timeout occurs.
Defaults to ``True``.
:param timeout: The length of time (in seconds) to wait for a response
before continuing if blocking is used.
Defaults to self.response_timeout.
callback -- Optional reference to a stream handler function. Will
be executed when the roster is received.
Implies block=False.
Defaults to
:attr:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`.
:param callback: Optional reference to a stream handler function. Will
be executed when the roster is received.
Implies ``block=False``.
"""
iq = self.Iq()
iq['type'] = 'get'
@@ -260,11 +250,9 @@ class ClientXMPP(BaseXMPP):
self.features = set()
def _handle_stream_features(self, features):
"""
Process the received stream features.
"""Process the received stream features.
Arguments:
features -- The features stanza.
:param features: The features stanza.
"""
for order, name in self._stream_feature_order:
if name in features['features']:
@@ -275,13 +263,12 @@ class ClientXMPP(BaseXMPP):
return True
def _handle_roster(self, iq, request=False):
"""
Update the roster after receiving a roster stanza.
"""Update the roster after receiving a roster stanza.
Arguments:
iq -- The roster stanza.
request -- Indicates if this stanza is a response
to a request for the roster.
:param iq: The roster stanza.
:param request: Indicates if this stanza is a response
to a request for the roster, and not an
empty acknowledgement from the server.
"""
if iq['type'] == 'set' or (iq['type'] == 'result' and request):
for jid in iq['roster']['items']:

View File

@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.clientxmpp
~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module provides XMPP functionality that
is specific to external server component connections.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from __future__ import absolute_import
@@ -32,28 +38,22 @@ class ComponentXMPP(BaseXMPP):
Use only for good, not for evil.
Methods:
connect -- Overrides XMLStream.connect.
incoming_filter -- Overrides XMLStream.incoming_filter.
start_stream_handler -- Overrides XMLStream.start_stream_handler.
:param jid: The JID of the component.
:param secret: The secret or password for the component.
:param host: The server accepting the component.
:param port: The port used to connect to the server.
:param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that
will be loaded when calling
:meth:`~sleekxmpp.basexmpp.BaseXMPP.register_plugins()`.
:param use_jc_ns: Indicates if the ``'jabber:client'`` namespace
should be used instead of the standard
``'jabber:component:accept'`` namespace.
Defaults to ``False``.
"""
def __init__(self, jid, secret, host, port,
def __init__(self, jid, secret, host=None, port=None,
plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
"""
Arguments:
jid -- The JID of the component.
secret -- The secret or password for the component.
host -- The server accepting the component.
port -- The port used to connect to the server.
plugin_config -- A dictionary of plugin configurations.
plugin_whitelist -- A list of desired plugins to load
when using register_plugins.
use_js_ns -- Indicates if the 'jabber:client' namespace
should be used instead of the standard
'jabber:component:accept' namespace.
Defaults to False.
"""
if use_jc_ns:
default_ns = 'jabber:client'
else:
@@ -81,26 +81,42 @@ class ComponentXMPP(BaseXMPP):
self.add_event_handler('presence_probe',
self._handle_probe)
def connect(self):
"""
Connect to the server.
def connect(self, host=None, port=None, use_ssl=False,
use_tls=True, reattempt=True):
"""Connect to the server.
Overrides XMLStream.connect.
Setting ``reattempt`` to ``True`` will cause connection attempts to
be made every second until a successful connection is established.
:param host: The name of the desired server for the connection.
Defaults to :attr:`server_host`.
:param port: Port to connect to on the server.
Defauts to :attr:`server_port`.
:param use_ssl: Flag indicating if SSL should be used by connecting
directly to a port using SSL.
:param use_tls: Flag indicating if TLS should be used, allowing for
connecting to a port without using SSL immediately and
later upgrading the connection.
:param reattempt: Flag indicating if the socket should reconnect
after disconnections.
"""
log.debug("Connecting to %s:%s" % (self.server_host,
self.server_port))
return XMLStream.connect(self, self.server_host,
self.server_port)
if host is None:
host = self.server_host
if port is None:
port = self.server_port
log.debug("Connecting to %s:%s", host, port)
return XMLStream.connect(self, host=host, port=port,
use_ssl=use_ssl,
use_tls=use_tls,
reattempt=reattempt)
def incoming_filter(self, xml):
"""
Pre-process incoming XML stanzas by converting any 'jabber:client'
namespaced elements to the component's default namespace.
Pre-process incoming XML stanzas by converting any
``'jabber:client'`` namespaced elements to the component's
default namespace.
Overrides XMLStream.incoming_filter.
Arguments:
xml -- The XML stanza to pre-process.
:param xml: The XML stanza to pre-process.
"""
if xml.tag.startswith('{jabber:client}'):
xml.tag = xml.tag.replace('jabber:client', self.default_ns)
@@ -117,10 +133,7 @@ class ComponentXMPP(BaseXMPP):
Once the streams are established, attempt to handshake
with the server to be accepted as a component.
Overrides BaseXMPP.start_stream_handler.
Arguments:
xml -- The incoming stream's root element.
:param xml: The incoming stream's root element.
"""
BaseXMPP.start_stream_handler(self, xml)
@@ -136,11 +149,9 @@ class ComponentXMPP(BaseXMPP):
self.send_xml(handshake, now=True)
def _handle_handshake(self, xml):
"""
The handshake has been accepted.
"""The handshake has been accepted.
Arguments:
xml -- The reply handshake stanza.
:param xml: The reply handshake stanza.
"""
self.session_started_event.set()
self.event("session_start")

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.exceptions
~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
@@ -13,37 +16,35 @@ class XMPPError(Exception):
A generic exception that may be raised while processing an XMPP stanza
to indicate that an error response stanza should be sent.
The exception method for stanza objects extending RootStanza will create
an error stanza and initialize any additional substanzas using the
extension information included in the exception.
The exception method for stanza objects extending
:class:`~sleekxmpp.stanza.rootstanza.RootStanza` will create an error
stanza and initialize any additional substanzas using the extension
information included in the exception.
Meant for use in SleekXMPP plugins and applications using SleekXMPP.
Extension information can be included to add additional XML elements
to the generated error stanza.
:param condition: The XMPP defined error condition.
Defaults to ``'undefined-condition'``.
:param text: Human readable text describing the error.
:param etype: The XMPP error type, such as ``'cancel'`` or ``'modify'``.
Defaults to ``'cancel'``.
:param extension: Tag name of the extension's XML content.
:param extension_ns: XML namespace of the extensions' XML content.
:param extension_args: Content and attributes for the extension
element. Same as the additional arguments to
the :class:`~xml.etree.ElementTree.Element`
constructor.
:param clear: Indicates if the stanza's contents should be
removed before replying with an error.
Defaults to ``True``.
"""
def __init__(self, condition='undefined-condition', text=None,
etype='cancel', extension=None, extension_ns=None,
extension_args=None, clear=True):
"""
Create a new XMPPError exception.
Extension information can be included to add additional XML elements
to the generated error stanza.
Arguments:
condition -- The XMPP defined error condition.
Defaults to 'undefined-condition'.
text -- Human readable text describing the error.
etype -- The XMPP error type, such as cancel or modify.
Defaults to 'cancel'.
extension -- Tag name of the extension's XML content.
extension_ns -- XML namespace of the extensions' XML content.
extension_args -- Content and attributes for the extension
element. Same as the additional arguments to
the ET.Element constructor.
clear -- Indicates if the stanza's contents should be
removed before replying with an error.
Defaults to True.
"""
if extension_args is None:
extension_args = {}
@@ -68,6 +69,8 @@ class IqTimeout(XMPPError):
condition='remote-server-timeout',
etype='cancel')
#: The :class:`~sleekxmpp.stanza.iq.Iq` stanza whose response
#: did not arrive before the timeout expired.
self.iq = iq
class IqError(XMPPError):
@@ -83,4 +86,5 @@ class IqError(XMPPError):
text=iq['error']['text'],
etype=iq['error']['type'])
#: The :class:`~sleekxmpp.stanza.iq.Iq` error result stanza.
self.iq = iq

View File

@@ -42,7 +42,7 @@ class feature_bind(base_plugin):
Arguments:
features -- The stream features stanza.
"""
log.debug("Requesting resource: %s" % self.xmpp.boundjid.resource)
log.debug("Requesting resource: %s", self.xmpp.boundjid.resource)
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq.enable('bind')
@@ -55,7 +55,7 @@ class feature_bind(base_plugin):
self.xmpp.features.add('bind')
log.info("Node set to: %s" % self.xmpp.boundjid.full)
log.info("Node set to: %s", self.xmpp.boundjid.full)
if 'session' not in features['features']:
log.debug("Established Session")

View File

@@ -123,7 +123,7 @@ class feature_mechanisms(base_plugin):
def _handle_fail(self, stanza):
"""SASL authentication failed. Disconnect and shutdown."""
log.info("Authentication failed: %s" % stanza['condition'])
log.info("Authentication failed: %s", stanza['condition'])
self.xmpp.event("failed_auth", stanza, direct=True)
self.xmpp.disconnect()
return True

View File

@@ -58,8 +58,8 @@ class feature_starttls(base_plugin):
self.xmpp.send(features['starttls'], now=True)
return True
else:
log.warning("The module tlslite is required to log in" +\
" to some servers, and has not been found.")
log.warning("The module tlslite is required to log in" + \
" to some servers, and has not been found.")
return False
def _handle_starttls_proceed(self, proceed):

View File

@@ -121,7 +121,7 @@ class gmail_notify(base.base_plugin):
def handle_gmail(self, iq):
mailbox = iq['mailbox']
approx = ' approximately' if mailbox['estimated'] else ''
log.info('Gmail: Received%s %s emails' % (approx, mailbox['total-matched']))
log.info('Gmail: Received%s %s emails', approx, mailbox['total-matched'])
self.last_result_time = mailbox['result-time']
self.xmpp.event('gmail_messages', iq)
@@ -140,7 +140,7 @@ class gmail_notify(base.base_plugin):
if query is None:
log.info("Gmail: Checking for new emails")
else:
log.info('Gmail: Searching for emails matching: "%s"' % query)
log.info('Gmail: Searching for emails matching: "%s"', query)
iq = self.xmpp.Iq()
iq['type'] = 'get'
iq['to'] = self.xmpp.boundjid.bare

View File

@@ -43,7 +43,7 @@ class jobs(base.base_plugin):
iq['psstate']['payload'] = state
result = iq.send()
if result is None or type(result) == bool or result['type'] != 'result':
log.error("Unable to change %s:%s to %s" % (node, jobid, state))
log.error("Unable to change %s:%s to %s", node, jobid, state)
return False
return True

View File

@@ -96,11 +96,11 @@ class Form(ElementBase):
self.xml.append(itemXML)
reported_vars = self['reported'].keys()
for var in reported_vars:
fieldXML = ET.Element('{%s}field' % FormField.namespace)
itemXML.append(fieldXML)
field = FormField(xml=fieldXML)
field = FormField()
field._type = self['reported'][var]['type']
field['var'] = var
field['value'] = values.get(var, None)
itemXML.append(field.xml)
def add_reported(self, var, ftype=None, label='', desc='', **kwargs):
kwtype = kwargs.get('type', None)
@@ -159,7 +159,7 @@ class Form(ElementBase):
items = []
itemsXML = self.xml.findall('{%s}item' % self.namespace)
for itemXML in itemsXML:
item = {}
item = OrderedDict()
fieldsXML = itemXML.findall('{%s}field' % FormField.namespace)
for fieldXML in fieldsXML:
field = FormField(xml=fieldXML)
@@ -168,7 +168,7 @@ class Form(ElementBase):
return items
def get_reported(self):
fields = {}
fields = OrderedDict()
xml = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
FormField.namespace))
for field in xml:
@@ -177,7 +177,7 @@ class Form(ElementBase):
return fields
def get_values(self):
values = {}
values = OrderedDict()
fields = self['fields']
for var in fields:
values[var] = fields[var]['value']

View File

@@ -42,46 +42,46 @@ def py2xml(*args):
def _py2xml(*args):
for x in args:
val = ET.Element("value")
val = ET.Element("{%s}value" % _namespace)
if x is None:
nil = ET.Element("nil")
nil = ET.Element("{%s}nil" % _namespace)
val.append(nil)
elif type(x) is int:
i4 = ET.Element("i4")
i4 = ET.Element("{%s}i4" % _namespace)
i4.text = str(x)
val.append(i4)
elif type(x) is bool:
boolean = ET.Element("boolean")
boolean = ET.Element("{%s}boolean" % _namespace)
boolean.text = str(int(x))
val.append(boolean)
elif type(x) is str:
string = ET.Element("string")
string = ET.Element("{%s}string" % _namespace)
string.text = x
val.append(string)
elif type(x) is float:
double = ET.Element("double")
double = ET.Element("{%s}double" % _namespace)
double.text = str(x)
val.append(double)
elif type(x) is rpcbase64:
b64 = ET.Element("base64")
b64 = ET.Element("{%s}base64" % _namespace)
b64.text = x.encoded()
val.append(b64)
elif type(x) is rpctime:
iso = ET.Element("dateTime.iso8601")
iso = ET.Element("{%s}dateTime.iso8601" % _namespace)
iso.text = str(x)
val.append(iso)
elif type(x) in (list, tuple):
array = ET.Element("array")
data = ET.Element("data")
array = ET.Element("{%s}array" % _namespace)
data = ET.Element("{%s}data" % _namespace)
for y in x:
data.append(_py2xml(y))
array.append(data)
val.append(array)
elif type(x) is dict:
struct = ET.Element("struct")
struct = ET.Element("{%s}struct" % _namespace)
for y in x.keys():
member = ET.Element("member")
name = ET.Element("name")
member = ET.Element("{%s}member" % _namespace)
name = ET.Element("{%s}name" % _namespace)
name.text = y
member.append(name)
member.append(_py2xml(x[y]))
@@ -105,18 +105,18 @@ def _xml2py(value):
if value.find('{%s}int' % namespace) is not None:
return int(value.find('{%s}int' % namespace).text)
if value.find('{%s}boolean' % namespace) is not None:
return bool(value.find('{%s}boolean' % namespace).text)
return bool(int(value.find('{%s}boolean' % namespace).text))
if value.find('{%s}string' % namespace) is not None:
return value.find('{%s}string' % namespace).text
if value.find('{%s}double' % namespace) is not None:
return float(value.find('{%s}double' % namespace).text)
if value.find('{%s}base64') is not None:
return rpcbase64(value.find('base64' % namespace).text)
if value.find('{%s}Base64') is not None:
if value.find('{%s}base64' % namespace) is not None:
return rpcbase64(value.find('{%s}base64' % namespace).text.encode())
if value.find('{%s}Base64' % namespace) is not None:
# Older versions of XEP-0009 used Base64
return rpcbase64(value.find('Base64' % namespace).text)
if value.find('{%s}dateTime.iso8601') is not None:
return rpctime(value.find('{%s}dateTime.iso8601'))
return rpcbase64(value.find('{%s}Base64' % namespace).text.encode())
if value.find('{%s}dateTime.iso8601' % namespace) is not None:
return rpctime(value.find('{%s}dateTime.iso8601' % namespace).text)
if value.find('{%s}struct' % namespace) is not None:
struct = {}
for member in value.find('{%s}struct' % namespace).findall('{%s}member' % namespace):
@@ -138,13 +138,13 @@ class rpcbase64(object):
self.data = data
def decode(self):
return base64.decodestring(self.data)
return base64.b64decode(self.data)
def __str__(self):
return self.decode()
return self.decode().decode()
def encoded(self):
return self.data
return self.data.decode()

View File

@@ -20,7 +20,7 @@ log = logging.getLogger(__name__)
def _intercept(method, name, public):
def _resolver(instance, *args, **kwargs):
log.debug("Locally calling %s.%s with arguments %s." % (instance.FQN(), method.__name__, args))
log.debug("Locally calling %s.%s with arguments %s.", instance.FQN(), method.__name__, args)
try:
value = method(instance, *args, **kwargs)
if value == NotImplemented:
@@ -113,6 +113,9 @@ class ACL:
def check(cls, rules, jid, resource):
if rules is None:
return cls.DENY # No rules means no access!
jid = str(jid) # Check the string representation of the JID.
if not jid:
return cls.DENY # Can't check an empty JID.
for rule in rules:
policy = cls._check(rule, jid, resource)
if policy is not None:
@@ -381,7 +384,7 @@ class Proxy(Endpoint):
try:
if attribute._rpc:
def _remote_call(*args, **kwargs):
log.debug("Remotely calling '%s.%s' with arguments %s." % (self._endpoint.FQN(), attribute._rpc_name, args))
log.debug("Remotely calling '%s.%s' with arguments %s.", self._endpoint.FQN(), attribute._rpc_name, args)
return self._endpoint.session._call_remote(self._endpoint.target_jid, "%s.%s" % (self._endpoint.FQN(), attribute._rpc_name), self._callback, *args, **kwargs)
return _remote_call
except:
@@ -449,7 +452,7 @@ class RemoteSession(object):
self._event.wait()
def _notify(self, event):
log.debug("RPC Session as %s started." % self._client.boundjid.full)
log.debug("RPC Session as %s started.", self._client.boundjid.full)
self._client.sendPresence()
self._event.set()
pass
@@ -461,7 +464,7 @@ class RemoteSession(object):
if name is None:
name = method.__name__
key = "%s.%s" % (endpoint, name)
log.debug("Registering call handler for %s (%s)." % (key, method))
log.debug("Registering call handler for %s (%s).", key, method)
with self._lock:
if key in self._entries:
raise KeyError("A handler for %s has already been regisered!" % endpoint)
@@ -469,7 +472,7 @@ class RemoteSession(object):
return key
def _register_acl(self, endpoint, acl):
log.debug("Registering ACL %s for endpoint %s." % (repr(acl), endpoint))
log.debug("Registering ACL %s for endpoint %s.", repr(acl), endpoint)
with self._lock:
self._acls[endpoint] = acl
@@ -562,7 +565,7 @@ class RemoteSession(object):
iq.send()
return future.get_value(30)
else:
log.debug("[RemoteSession] _call_remote %s" % callback)
log.debug("[RemoteSession] _call_remote %s", callback)
self._register_callback(pid, callback)
iq.send()
@@ -601,11 +604,11 @@ class RemoteSession(object):
error.send()
except Exception as e:
if isinstance(e, KeyError):
log.error("No handler available for %s!" % pmethod)
log.error("No handler available for %s!", pmethod)
error = self._client.plugin['xep_0009']._item_not_found(iq)
else:
traceback.print_exc(file=sys.stderr)
log.error("An unexpected problem occurred invoking method %s!" % pmethod)
log.error("An unexpected problem occurred invoking method %s!", pmethod)
error = self._client.plugin['xep_0009']._undefined_condition(iq)
#! print "[REMOTE.PY] _handle_remote_procedure_call AN ERROR SHOULD BE SENT NOW %s " % e
error.send()

View File

@@ -128,22 +128,22 @@ class xep_0009(base.base_plugin):
def _handle_method_call(self, iq):
type = iq['type']
if type == 'set':
log.debug("Incoming Jabber-RPC call from %s" % iq['from'])
log.debug("Incoming Jabber-RPC call from %s", iq['from'])
self.xmpp.event('jabber_rpc_method_call', iq)
else:
if type == 'error' and ['rpc_query'] is None:
self.handle_error(iq)
else:
log.debug("Incoming Jabber-RPC error from %s" % iq['from'])
log.debug("Incoming Jabber-RPC error from %s", iq['from'])
self.xmpp.event('jabber_rpc_error', iq)
def _handle_method_response(self, iq):
if iq['rpc_query']['method_response']['fault'] is not None:
log.debug("Incoming Jabber-RPC fault from %s" % iq['from'])
log.debug("Incoming Jabber-RPC fault from %s", iq['from'])
#self._on_jabber_rpc_method_fault(iq)
self.xmpp.event('jabber_rpc_method_fault', iq)
else:
log.debug("Incoming Jabber-RPC response from %s" % iq['from'])
log.debug("Incoming Jabber-RPC response from %s", iq['from'])
self.xmpp.event('jabber_rpc_method_response', iq)
def _handle_error(self, iq):

View File

@@ -71,10 +71,10 @@ class xep_0012(base.base_plugin):
def handle_last_activity_query(self, iq):
if iq['type'] == 'get':
log.debug("Last activity requested by %s" % iq['from'])
log.debug("Last activity requested by %s", iq['from'])
self.xmpp.event('last_activity_request', iq)
elif iq['type'] == 'result':
log.debug("Last activity result from %s" % iq['from'])
log.debug("Last activity result from %s", iq['from'])
self.xmpp.event('last_activity', iq)
def handle_last_activity(self, iq):

View File

@@ -268,7 +268,7 @@ class xep_0030(base_plugin):
"""
if local or jid is None:
log.debug("Looking up local disco#info data " + \
"for %s, node %s." % (jid, node))
"for %s, node %s.", jid, node)
info = self._run_node_handler('get_info', jid, node, kwargs)
return self._fix_default_info(info)
@@ -542,7 +542,7 @@ class xep_0030(base_plugin):
"""
if iq['type'] == 'get':
log.debug("Received disco info query from " + \
"<%s> to <%s>." % (iq['from'], iq['to']))
"<%s> to <%s>.", iq['from'], iq['to'])
if self.xmpp.is_component:
jid = iq['to'].full
else:
@@ -551,14 +551,17 @@ class xep_0030(base_plugin):
jid,
iq['disco_info']['node'],
iq)
iq.reply()
if info:
info = self._fix_default_info(info)
iq.set_payload(info.xml)
iq.send()
if isinstance(info, Iq):
info.send()
else:
iq.reply()
if info:
info = self._fix_default_info(info)
iq.set_payload(info.xml)
iq.send()
elif iq['type'] == 'result':
log.debug("Received disco info result from" + \
"%s to %s." % (iq['from'], iq['to']))
"%s to %s.", iq['from'], iq['to'])
self.xmpp.event('disco_info', iq)
def _handle_disco_items(self, iq):
@@ -572,21 +575,25 @@ class xep_0030(base_plugin):
"""
if iq['type'] == 'get':
log.debug("Received disco items query from " + \
"<%s> to <%s>." % (iq['from'], iq['to']))
"<%s> to <%s>.", iq['from'], iq['to'])
if self.xmpp.is_component:
jid = iq['to'].full
else:
jid = iq['to'].bare
items = self._run_node_handler('get_items',
jid,
iq['disco_items']['node'])
iq.reply()
if items:
iq.set_payload(items.xml)
iq.send()
iq['disco_items']['node'],
iq)
if isinstance(items, Iq):
items.send()
else:
iq.reply()
if items:
iq.set_payload(items.xml)
iq.send()
elif iq['type'] == 'result':
log.debug("Received disco items result from" + \
"%s to %s." % (iq['from'], iq['to']))
"%s to %s.", iq['from'], iq['to'])
self.xmpp.event('disco_items', iq)
def _fix_default_info(self, info):

View File

@@ -127,7 +127,7 @@ class xep_0045(base.base_plugin):
def handle_groupchat_invite(self, inv):
""" Handle an invite into a muc.
"""
logging.debug("MUC invite to %s from %s: %s" % (inv['from'], inv["from"], inv))
logging.debug("MUC invite to %s from %s: %s", inv['from'], inv["from"], inv)
if inv['from'] not in self.rooms.keys():
self.xmpp.event("groupchat_invite", inv)
@@ -149,7 +149,7 @@ class xep_0045(base.base_plugin):
if entry['nick'] not in self.rooms[entry['room']]:
got_online = True
self.rooms[entry['room']][entry['nick']] = entry
log.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry))
log.debug("MUC presence from %s/%s : %s", entry['room'],entry['nick'], entry)
self.xmpp.event("groupchat_presence", pr)
self.xmpp.event("muc::%s::presence" % entry['room'], pr)
if got_offline:

View File

@@ -17,6 +17,7 @@ from sleekxmpp.xmlstream import register_stanza_plugin, JID
from sleekxmpp.plugins.base import base_plugin
from sleekxmpp.plugins.xep_0050 import stanza
from sleekxmpp.plugins.xep_0050 import Command
from sleekxmpp.plugins.xep_0004 import Form
log = logging.getLogger(__name__)
@@ -92,7 +93,8 @@ class xep_0050(base_plugin):
StanzaPath('iq@type=set/command'),
self._handle_command))
register_stanza_plugin(Iq, stanza.Command)
register_stanza_plugin(Iq, Command)
register_stanza_plugin(Command, Form)
self.xmpp.add_event_handler('command_execute',
self._handle_command_start,
@@ -147,7 +149,7 @@ class xep_0050(base_plugin):
Access control may be implemented in the provided handler.
Command workflow is done across a sequence of command handlers. The
first handler is given the intial Iq stanza of the request in order
first handler is given the initial Iq stanza of the request in order
to support access control. Subsequent handlers are given only the
payload items of the command. All handlers will receive the command's
session data.
@@ -211,8 +213,7 @@ class xep_0050(base_plugin):
key = (iq['to'].full, node)
name, handler = self.commands.get(key, ('Not found', None))
if not handler:
log.debug('Command not found: %s, %s' % (key, self.commands))
log.debug('Command not found: %s, %s', key, self.commands)
initial_session = {'id': sessionid,
'from': iq['from'],
'to': iq['to'],

View File

@@ -22,7 +22,7 @@ class PubsubErrorCondition(ElementBase):
'max-items-exceeded', 'max-nodes-exceeded',
'nodeid-required', 'not-in-roster-group',
'not-subscribed', 'payload-too-big',
'payload-required' 'pending-subscription',
'payload-required', 'pending-subscription',
'presence-subscription-required', 'subid-required',
'too-many-subscriptions', 'unsupported'))
condition_ns = 'http://jabber.org/protocol/pubsub#errors'

View File

@@ -60,12 +60,12 @@ class xep_0078(base_plugin):
try:
resp = iq.send(now=True)
except IqError:
log.info("Authentication failed: %s" % resp['error']['condition'])
log.info("Authentication failed: %s", resp['error']['condition'])
self.xmpp.event('failed_auth', direct=True)
self.xmpp.disconnect()
return True
except IqTimeout:
log.info("Authentication failed: %s" % 'timeout')
log.info("Authentication failed: %s", 'timeout')
self.xmpp.event('failed_auth', direct=True)
self.xmpp.disconnect()
return True

View File

@@ -76,7 +76,7 @@ def format_datetime(time_obj):
return '%sZ' % timestamp
return timestamp
def date(year=None, month=None, day=None):
def date(year=None, month=None, day=None, obj=False):
"""
Create a date only timestamp for the given instant.
@@ -86,17 +86,22 @@ def date(year=None, month=None, day=None):
year -- Integer value of the year (4 digits)
month -- Integer value of the month
day -- Integer value of the day of the month.
obj -- If True, return the date object instead
of a formatted string. Defaults to False.
"""
today = dt.datetime.today()
today = dt.datetime.utcnow()
if year is None:
year = today.year
if month is None:
month = today.month
if day is None:
day = today.day
return format_date(dt.date(year, month, day))
value = dt.date(year, month, day)
if obj:
return value
return format_date(value)
def time(hour=None, min=None, sec=None, micro=None, offset=None):
def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
"""
Create a time only timestamp for the given instant.
@@ -110,6 +115,8 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None):
offset -- Either a positive or negative number of seconds
to offset from UTC to match a desired timezone,
or a tzinfo object.
obj -- If True, return the time object instead
of a formatted string. Defaults to False.
"""
now = dt.datetime.utcnow()
if hour is None:
@@ -124,12 +131,14 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None):
offset = tzutc()
elif not isinstance(offset, dt.tzinfo):
offset = tzoffset(None, offset)
time = dt.time(hour, min, sec, micro, offset)
return format_time(time)
value = dt.time(hour, min, sec, micro, offset)
if obj:
return value
return format_time(value)
def datetime(year=None, month=None, day=None, hour=None,
min=None, sec=None, micro=None, offset=None,
separators=True):
separators=True, obj=False):
"""
Create a datetime timestamp for the given instant.
@@ -146,6 +155,8 @@ def datetime(year=None, month=None, day=None, hour=None,
offset -- Either a positive or negative number of seconds
to offset from UTC to match a desired timezone,
or a tzinfo object.
obj -- If True, return the datetime object instead
of a formatted string. Defaults to False.
"""
now = dt.datetime.utcnow()
if year is None:
@@ -167,9 +178,11 @@ def datetime(year=None, month=None, day=None, hour=None,
elif not isinstance(offset, dt.tzinfo):
offset = tzoffset(None, offset)
date = dt.datetime(year, month, day, hour,
value = dt.datetime(year, month, day, hour,
min, sec, micro, offset)
return format_datetime(date)
if obj:
return value
return format_datetime(value)
class xep_0082(base_plugin):

View File

@@ -45,5 +45,5 @@ class xep_0085(base_plugin):
def _handle_chat_state(self, msg):
state = msg['chat_state']
log.debug("Chat State: %s, %s" % (state, msg['from'].jid))
log.debug("Chat State: %s, %s", state, msg['from'].jid)
self.xmpp.event('chatstate_%s' % state, msg)

View File

@@ -118,7 +118,7 @@ class xep_0199(base_plugin):
Arguments:
iq -- The ping request.
"""
log.debug("Pinged by %s" % iq['from'])
log.debug("Pinged by %s", iq['from'])
iq.reply().send()
def send_ping(self, jid, timeout=None, errorfalse=False,
@@ -141,7 +141,7 @@ class xep_0199(base_plugin):
is received. Useful in conjunction with
the option block=False.
"""
log.debug("Pinging %s" % jid)
log.debug("Pinging %s", jid)
if timeout is None:
timeout = self.timeout
@@ -167,7 +167,7 @@ class xep_0199(base_plugin):
if not block:
return None
log.debug("Pong: %s %f" % (jid, delay))
log.debug("Pong: %s %f", jid, delay)
return delay

View File

@@ -68,5 +68,5 @@ class xep_0224(base_plugin):
Arguments:
msg -- A message stanza with an attention element.
"""
log.debug("Received attention request from: %s" % msg['from'])
log.debug("Received attention request from: %s", msg['from'])
self.xmpp.event('attention', msg)

View File

@@ -172,6 +172,7 @@ class RosterItem(object):
Save the item's state information to an external datastore,
if one has been provided.
"""
self['subscription'] = self._subscription()
if self.db:
self.db.save(self.owner, self.jid,
self._state, self._db_state)

View File

@@ -79,7 +79,7 @@ class Message(RootStanza):
return self
def normal(self):
"""Set the message type to 'chat'."""
"""Set the message type to 'normal'."""
self['type'] = 'normal'
return self

View File

@@ -80,8 +80,7 @@ class RootStanza(StanzaBase):
self['error']['type'] = 'cancel'
self.send()
# log the error
log.exception('Error handling {%s}%s stanza' %
(self.namespace, self.name))
log.exception('Error handling {%s}%s stanza' , self.namespace, self.name)
# Finally raise the exception to a global exception handler
self.stream.exception(e)

View File

@@ -9,5 +9,5 @@
# We don't want to have to import the entire library
# just to get the version info for setup.py
__version__ = '1.0rc3'
__version_info__ = (1, 0, 0, 'rc3', 0)
__version__ = '1.0'
__version_info__ = (1, 0, 0, '', 0)

View File

@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.filesocket
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module is a shim for correcting deficiencies in the file
socket implementation of Python2.6.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from socket import _fileobject
@@ -12,12 +18,11 @@ import socket
class FileSocket(_fileobject):
"""
Create a file object wrapper for a socket to work around
"""Create a file object wrapper for a socket to work around
issues present in Python 2.6 when using sockets as file objects.
The parser for xml.etree.cElementTree requires a file, but we will
be reading from the XMPP connection socket instead.
The parser for :class:`~xml.etree.cElementTree` requires a file, but
we will be reading from the XMPP connection socket instead.
"""
def read(self, size=4096):
@@ -31,8 +36,7 @@ class FileSocket(_fileobject):
class Socket26(socket._socketobject):
"""
A custom socket implementation that uses our own FileSocket class
"""A custom socket implementation that uses our own FileSocket class
to work around issues in Python 2.6 when using sockets as files.
"""

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.handler.base
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
import weakref
@@ -16,78 +19,62 @@ class BaseHandler(object):
incoming stanzas so that the stanza may be processed in some way.
Stanzas may be matched with multiple handlers.
Handler execution may take place in two phases. The first is during
the stream processing itself. The second is after stream processing
and during SleekXMPP's main event loop. The prerun method is used
for execution during stream processing, and the run method is used
during the main event loop.
Handler execution may take place in two phases: during the incoming
stream processing, and in the main event loop. The :meth:`prerun()`
method is executed in the first case, and :meth:`run()` is called
during the second.
Attributes:
name -- The name of the handler.
stream -- The stream this handler is assigned to.
Methods:
match -- Compare a stanza with the handler's matcher.
prerun -- Handler execution during stream processing.
run -- Handler execution during the main event loop.
check_delete -- Indicate if the handler may be removed from use.
:param string name: The name of the handler.
:param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
derived object that will be used to determine if a
stanza should be accepted by this handler.
:param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
instance that the handle will respond to.
"""
def __init__(self, name, matcher, stream=None):
"""
Create a new stream handler.
Arguments:
name -- The name of the handler.
matcher -- A matcher object from xmlstream.matcher that will be
used to determine if a stanza should be accepted by
this handler.
stream -- The XMLStream instance the handler should monitor.
"""
#: The name of the handler
self.name = name
#: The XML stream this handler is assigned to
self.stream = None
if stream is not None:
self.stream = weakref.ref(stream)
else:
self.stream = None
stream.register_handler(self)
self._destroy = False
self._payload = None
self._matcher = matcher
if stream is not None:
stream.registerHandler(self)
def match(self, xml):
"""
Compare a stanza or XML object with the handler's matcher.
"""Compare a stanza or XML object with the handler's matcher.
Arguments
xml -- An XML or stanza object.
:param xml: An XML or
:class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object
"""
return self._matcher.match(xml)
def prerun(self, payload):
"""
Prepare the handler for execution while the XML stream is being
processed.
"""Prepare the handler for execution while the XML
stream is being processed.
Arguments:
payload -- A stanza object.
:param payload: A :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
object.
"""
self._payload = payload
def run(self, payload):
"""
Execute the handler after XML stream processing and during the
"""Execute the handler after XML stream processing and during the
main event loop.
Arguments:
payload -- A stanza object.
:param payload: A :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
object.
"""
self._payload = payload
def check_delete(self):
"""
Check if the handler should be removed from the list of stream
handlers.
"""Check if the handler should be removed from the list
of stream handlers.
"""
return self._destroy

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.handler.callback
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from sleekxmpp.xmlstream.handler.base import BaseHandler
@@ -18,48 +21,42 @@ class Callback(BaseHandler):
The handler may execute the callback either during stream
processing or during the main event loop.
Callback functions are all executed in the same thread, so be
aware if you are executing functions that will block for extended
periods of time. Typically, you should signal your own events using the
SleekXMPP object's event() method to pass the stanza off to a threaded
event handler for further processing.
Callback functions are all executed in the same thread, so be aware if
you are executing functions that will block for extended periods of
time. Typically, you should signal your own events using the SleekXMPP
object's :meth:`~sleekxmpp.xmlstream.xmlstream.XMLStream.event()`
method to pass the stanza off to a threaded event handler for further
processing.
Methods:
prerun -- Overrides BaseHandler.prerun
run -- Overrides BaseHandler.run
:param string name: The name of the handler.
:param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
derived object for matching stanza objects.
:param pointer: The function to execute during callback.
:param bool thread: **DEPRECATED.** Remains only for
backwards compatibility.
:param bool once: Indicates if the handler should be used only
once. Defaults to False.
:param bool instream: Indicates if the callback should be executed
during stream processing instead of in the
main event loop.
:param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
instance this handler should monitor.
"""
def __init__(self, name, matcher, pointer, thread=False,
once=False, instream=False, stream=None):
"""
Create a new callback handler.
Arguments:
name -- The name of the handler.
matcher -- A matcher object for matching stanza objects.
pointer -- The function to execute during callback.
thread -- DEPRECATED. Remains only for backwards compatibility.
once -- Indicates if the handler should be used only
once. Defaults to False.
instream -- Indicates if the callback should be executed
during stream processing instead of in the
main event loop.
stream -- The XMLStream instance this handler should monitor.
"""
BaseHandler.__init__(self, name, matcher, stream)
self._pointer = pointer
self._once = once
self._instream = instream
def prerun(self, payload):
"""
Execute the callback during stream processing, if
the callback was created with instream=True.
"""Execute the callback during stream processing, if
the callback was created with ``instream=True``.
Overrides BaseHandler.prerun
Arguments:
payload -- The matched stanza object.
:param payload: The matched
:class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
"""
if self._once:
self._destroy = True
@@ -67,16 +64,13 @@ class Callback(BaseHandler):
self.run(payload, True)
def run(self, payload, instream=False):
"""
Execute the callback function with the matched stanza payload.
"""Execute the callback function with the matched stanza payload.
Overrides BaseHandler.run
Arguments:
payload -- The matched stanza object.
instream -- Force the handler to execute during
stream processing. Used only by prerun.
Defaults to False.
:param payload: The matched
:class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
:param bool instream: Force the handler to execute during stream
processing. This should only be used by
:meth:`prerun()`. Defaults to ``False``.
"""
if not self._instream or instream:
self._pointer(payload)

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.handler.waiter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
import logging
@@ -22,83 +25,63 @@ log = logging.getLogger(__name__)
class Waiter(BaseHandler):
"""
The Waiter handler allows an event handler to block
until a particular stanza has been received. The handler
will either be given the matched stanza, or False if the
waiter has timed out.
The Waiter handler allows an event handler to block until a
particular stanza has been received. The handler will either be
given the matched stanza, or ``False`` if the waiter has timed out.
Methods:
check_delete -- Overrides BaseHandler.check_delete
prerun -- Overrides BaseHandler.prerun
run -- Overrides BaseHandler.run
wait -- Wait for a stanza to arrive and return it to
an event handler.
:param string name: The name of the handler.
:param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
derived object for matching stanza objects.
:param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
instance this handler should monitor.
"""
def __init__(self, name, matcher, stream=None):
"""
Create a new Waiter.
Arguments:
name -- The name of the waiter.
matcher -- A matcher object to detect the desired stanza.
stream -- Optional XMLStream instance to monitor.
"""
BaseHandler.__init__(self, name, matcher, stream=stream)
self._payload = queue.Queue()
def prerun(self, payload):
"""
Store the matched stanza.
"""Store the matched stanza when received during processing.
Overrides BaseHandler.prerun
Arguments:
payload -- The matched stanza object.
:param payload: The matched
:class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
"""
self._payload.put(payload)
def run(self, payload):
"""
Do not process this handler during the main event loop.
Overrides BaseHandler.run
Arguments:
payload -- The matched stanza object.
"""
"""Do not process this handler during the main event loop."""
pass
def wait(self, timeout=None):
"""
Block an event handler while waiting for a stanza to arrive.
"""Block an event handler while waiting for a stanza to arrive.
Be aware that this will impact performance if called from a
non-threaded event handler.
Will return either the received stanza, or False if the waiter
timed out.
Will return either the received stanza, or ``False`` if the
waiter timed out.
Arguments:
timeout -- The number of seconds to wait for the stanza to
arrive. Defaults to the global default timeout
value sleekxmpp.xmlstream.RESPONSE_TIMEOUT.
:param int timeout: The number of seconds to wait for the stanza
to arrive. Defaults to the the stream's
:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`
value.
"""
if timeout is None:
timeout = self.stream().response_timeout
try:
stanza = self._payload.get(True, timeout)
except queue.Empty:
stanza = False
log.warning("Timed out waiting for %s" % self.name)
elapsed_time = 0
stanza = False
while elapsed_time < timeout and not self.stream().stop.is_set():
try:
stanza = self._payload.get(True, 1)
break
except queue.Empty:
elapsed_time += 1
if elapsed_time >= timeout:
log.warning("Timed out waiting for %s", self.name)
self.stream().remove_handler(self.name)
return stanza
def check_delete(self):
"""
Always remove waiters after use.
Overrides BaseHandler.check_delete
"""
"""Always remove waiters after use."""
return True

View File

@@ -1,15 +1,22 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.jid
~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module allows for working with Jabber IDs (JIDs) by
providing accessors for the various components of a JID.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from __future__ import unicode_literals
class JID(object):
"""
A representation of a Jabber ID, or JID.
@@ -19,18 +26,16 @@ class JID(object):
When a resource is not used, the JID is called a bare JID.
The JID is a full JID otherwise.
Attributes:
jid -- Alias for 'full'.
full -- The value of the full JID.
bare -- The value of the bare JID.
user -- The username portion of the JID.
domain -- The domain name portion of the JID.
server -- Alias for 'domain'.
resource -- The resource portion of the JID.
**JID Properties:**
:jid: Alias for ``full``.
:full: The value of the full JID.
:bare: The value of the bare JID.
:user: The username portion of the JID.
:domain: The domain name portion of the JID.
:server: Alias for ``domain``.
:resource: The resource portion of the JID.
Methods:
reset -- Use a new JID value.
regenerate -- Recreate the JID from its components.
:param string jid: A string of the form ``'[user@]domain[/resource]'``.
"""
def __init__(self, jid):
@@ -38,11 +43,9 @@ class JID(object):
self.reset(jid)
def reset(self, jid):
"""
Start fresh from a new JID string.
"""Start fresh from a new JID string.
Arguments:
jid - The new JID value.
:param string jid: A string of the form ``'[user@]domain[/resource]'``.
"""
if isinstance(jid, JID):
jid = jid.full
@@ -53,12 +56,10 @@ class JID(object):
self._bare = None
def __getattr__(self, name):
"""
Handle getting the JID values, using cache if available.
"""Handle getting the JID values, using cache if available.
Arguments:
name -- One of: user, server, domain, resource,
full, or bare.
:param name: One of: user, server, domain, resource,
full, or bare.
"""
if name == 'resource':
if self._resource is None and '/' in self._jid:
@@ -83,8 +84,7 @@ class JID(object):
return self._bare or ""
def __setattr__(self, name, value):
"""
Edit a JID by updating it's individual values, resetting the
"""Edit a JID by updating it's individual values, resetting the
generated JID in the end.
Arguments:
@@ -137,7 +137,5 @@ class JID(object):
return self.full == other.full
def __ne__(self, other):
"""
Two JIDs are considered unequal if they are not equal.
"""
"""Two JIDs are considered unequal if they are not equal."""
return not self == other

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.matcher.base
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
@@ -13,21 +16,15 @@ class MatcherBase(object):
Base class for stanza matchers. Stanza matchers are used to pick
stanzas out of the XML stream and pass them to the appropriate
stream handlers.
:param criteria: Object to compare some aspect of a stanza against.
"""
def __init__(self, criteria):
"""
Create a new stanza matcher.
Arguments:
criteria -- Object to compare some aspect of a stanza
against.
"""
self._criteria = criteria
def match(self, xml):
"""
Check if a stanza matches the stored criteria.
"""Check if a stanza matches the stored criteria.
Meant to be overridden.
"""

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.matcher.id
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from sleekxmpp.xmlstream.matcher.base import MatcherBase
@@ -14,19 +17,13 @@ class MatcherId(MatcherBase):
"""
The ID matcher selects stanzas that have the same stanza 'id'
interface value as the desired ID.
Methods:
match -- Overrides MatcherBase.match.
"""
def match(self, xml):
"""
Compare the given stanza's 'id' attribute to the stored
id value.
"""Compare the given stanza's ``'id'`` attribute to the stored
``id`` value.
Overrides MatcherBase.match.
Arguments:
xml -- The stanza to compare against.
:param xml: The :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
stanza to compare against.
"""
return xml['id'] == self._criteria

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.matcher.stanzapath
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from sleekxmpp.xmlstream.matcher.base import MatcherBase
@@ -15,24 +18,17 @@ class StanzaPath(MatcherBase):
The StanzaPath matcher selects stanzas that match a given "stanza path",
which is similar to a normal XPath except that it uses the interfaces and
plugins of the stanza instead of the actual, underlying XML.
In most cases, the stanza path and XPath should be identical, but be
aware that differences may occur.
Methods:
match -- Overrides MatcherBase.match.
"""
def match(self, stanza):
"""
Compare a stanza against a "stanza path". A stanza path is similar to
an XPath expression, but uses the stanza's interfaces and plugins
instead of the underlying XML. For most cases, the stanza path and
XPath should be identical, but be aware that differences may occur.
instead of the underlying XML. See the documentation for the stanza
:meth:`~sleekxmpp.xmlstream.stanzabase.ElementBase.match()` method
for more information.
Overrides MatcherBase.match.
Arguments:
stanza -- The stanza object to compare against.
:param stanza: The :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
stanza to compare against.
"""
return stanza.match(self._criteria)

View File

@@ -30,66 +30,59 @@ class MatchXMLMask(MatcherBase):
XML pattern, or mask. For example, message stanzas with body elements
could be matched using the mask:
.. code-block:: xml
<message xmlns="jabber:client"><body /></message>
Use of XMLMask is discouraged, and XPath or StanzaPath should be used
instead.
Use of XMLMask is discouraged, and
:class:`~sleekxmpp.xmlstream.matcher.xpath.MatchXPath` or
:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath`
should be used instead.
The use of namespaces in the mask comparison is controlled by
IGNORE_NS. Setting IGNORE_NS to True will disable namespace based matching
for ALL XMLMask matchers.
``IGNORE_NS``. Setting ``IGNORE_NS`` to ``True`` will disable namespace
based matching for ALL XMLMask matchers.
Methods:
match -- Overrides MatcherBase.match.
setDefaultNS -- Set the default namespace for the mask.
:param criteria: Either an :class:`~xml.etree.ElementTree.Element` XML
object or XML string to use as a mask.
"""
def __init__(self, criteria):
"""
Create a new XMLMask matcher.
Arguments:
criteria -- Either an XML object or XML string to use as a mask.
"""
MatcherBase.__init__(self, criteria)
if isinstance(criteria, str):
self._criteria = ET.fromstring(self._criteria)
self.default_ns = 'jabber:client'
def setDefaultNS(self, ns):
"""
Set the default namespace to use during comparisons.
"""Set the default namespace to use during comparisons.
Arguments:
ns -- The new namespace to use as the default.
:param ns: The new namespace to use as the default.
"""
self.default_ns = ns
def match(self, xml):
"""
Compare a stanza object or XML object against the stored XML mask.
"""Compare a stanza object or XML object against the stored XML mask.
Overrides MatcherBase.match.
Arguments:
xml -- The stanza object or XML object to compare against.
:param xml: The stanza object or XML object to compare against.
"""
if hasattr(xml, 'xml'):
xml = xml.xml
return self._mask_cmp(xml, self._criteria, True)
def _mask_cmp(self, source, mask, use_ns=False, default_ns='__no_ns__'):
"""
Compare an XML object against an XML mask.
"""Compare an XML object against an XML mask.
Arguments:
source -- The XML object to compare against the mask.
mask -- The XML object serving as the mask.
use_ns -- Indicates if namespaces should be respected during
the comparison.
default_ns -- The default namespace to apply to elements that
do not have a specified namespace.
Defaults to "__no_ns__".
:param source: The :class:`~xml.etree.ElementTree.Element` XML object
to compare against the mask.
:param mask: The :class:`~xml.etree.ElementTree.Element` XML object
serving as the mask.
:param use_ns: Indicates if namespaces should be respected during
the comparison.
:default_ns: The default namespace to apply to elements that
do not have a specified namespace.
Defaults to ``"__no_ns__"``.
"""
use_ns = not IGNORE_NS
@@ -102,8 +95,7 @@ class MatchXMLMask(MatcherBase):
try:
mask = ET.fromstring(mask)
except ExpatError:
log.warning("Expat error: %s\nIn parsing: %s" % ('', mask))
log.warning("Expat error: %s\nIn parsing: %s", '', mask)
if not use_ns:
# Compare the element without using namespaces.
source_tag = source.tag.split('}', 1)[-1]
@@ -149,14 +141,13 @@ class MatchXMLMask(MatcherBase):
return True
def _get_child(self, xml, tag):
"""
Return a child element given its tag, ignoring namespace values.
"""Return a child element given its tag, ignoring namespace values.
Returns None if the child was not found.
Returns ``None`` if the child was not found.
Arguments:
xml -- The XML object to search for the given child tag.
tag -- The name of the subelement to find.
:param xml: The :class:`~xml.etree.ElementTree.Element` XML object
to search for the given child tag.
:param tag: The name of the subelement to find.
"""
tag = tag.split('}')[-1]
try:

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.matcher.xpath
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from sleekxmpp.xmlstream.stanzabase import ET
@@ -22,30 +25,34 @@ class MatchXPath(MatcherBase):
The XPath matcher selects stanzas whose XML contents matches a given
XPath expression.
Note that using this matcher may not produce expected behavior when using
attribute selectors. For Python 2.6 and 3.1, the ElementTree find method
does not support the use of attribute selectors. If you need to support
Python 2.6 or 3.1, it might be more useful to use a StanzaPath matcher.
.. warning::
If the value of IGNORE_NS is set to true, then XPath expressions will
be matched without using namespaces.
Using this matcher may not produce expected behavior when using
attribute selectors. For Python 2.6 and 3.1, the ElementTree
:meth:`~xml.etree.ElementTree.Element.find()` method does
not support the use of attribute selectors. If you need to
support Python 2.6 or 3.1, it might be more useful to use a
:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
Methods:
match -- Overrides MatcherBase.match.
If the value of :data:`IGNORE_NS` is set to ``True``, then XPath
expressions will be matched without using namespaces.
"""
def match(self, xml):
"""
Compare a stanza's XML contents to an XPath expression.
If the value of IGNORE_NS is set to true, then XPath expressions
will be matched without using namespaces.
If the value of :data:`IGNORE_NS` is set to ``True``, then XPath
expressions will be matched without using namespaces.
Note that in Python 2.6 and 3.1 the ElementTree find method does
not support attribute selectors in the XPath expression.
.. warning::
Arguments:
xml -- The stanza object to compare against.
In Python 2.6 and 3.1 the ElementTree
:meth:`~xml.etree.ElementTree.Element.find()` method does not
support attribute selectors in the XPath expression.
:param xml: The :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
stanza to compare against.
"""
if hasattr(xml, 'xml'):
xml = xml.xml

View File

@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.scheduler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module provides a task scheduler that works better
with SleekXMPP's threading usage than the stock version.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
import time
@@ -24,50 +30,47 @@ class Task(object):
A scheduled task that will be executed by the scheduler
after a given time interval has passed.
Attributes:
name -- The name of the task.
seconds -- The number of seconds to wait before executing.
callback -- The function to execute.
args -- The arguments to pass to the callback.
kwargs -- The keyword arguments to pass to the callback.
repeat -- Indicates if the task should repeat.
Defaults to False.
qpointer -- A pointer to an event queue for queuing callback
:param string name: The name of the task.
:param int seconds: The number of seconds to wait before executing.
:param callback: The function to execute.
:param tuple args: The arguments to pass to the callback.
:param dict kwargs: The keyword arguments to pass to the callback.
:param bool repeat: Indicates if the task should repeat.
Defaults to ``False``.
:param pointer: A pointer to an event queue for queuing callback
execution instead of executing immediately.
Methods:
run -- Either queue or execute the callback.
reset -- Reset the task's timer.
"""
def __init__(self, name, seconds, callback, args=None,
kwargs=None, repeat=False, qpointer=None):
"""
Create a new task.
Arguments:
name -- The name of the task.
seconds -- The number of seconds to wait before executing.
callback -- The function to execute.
args -- The arguments to pass to the callback.
kwargs -- The keyword arguments to pass to the callback.
repeat -- Indicates if the task should repeat.
Defaults to False.
qpointer -- A pointer to an event queue for queuing callback
execution instead of executing immediately.
"""
#: The name of the task.
self.name = name
#: The number of seconds to wait before executing.
self.seconds = seconds
#: The function to execute once enough time has passed.
self.callback = callback
#: The arguments to pass to :attr:`callback`.
self.args = args or tuple()
#: The keyword arguments to pass to :attr:`callback`.
self.kwargs = kwargs or {}
#: Indicates if the task should repeat after executing,
#: using the same :attr:`seconds` delay.
self.repeat = repeat
#: The time when the task should execute next.
self.next = time.time() + self.seconds
#: The main event queue, which allows for callbacks to
#: be queued for execution instead of executing immediately.
self.qpointer = qpointer
def run(self):
"""
Execute the task's callback.
"""Execute the task's callback.
If an event queue was supplied, place the callback in the queue;
otherwise, execute the callback immediately.
@@ -81,9 +84,7 @@ class Task(object):
return self.repeat
def reset(self):
"""
Reset the task's timer so that it will repeat.
"""
"""Reset the task's timer so that it will repeat."""
self.next = time.time() + self.seconds
@@ -93,48 +94,42 @@ class Scheduler(object):
A threaded scheduler that allows for updates mid-execution unlike the
scheduler in the standard library.
http://docs.python.org/library/sched.html#module-sched
Based on: http://docs.python.org/library/sched.html#module-sched
Attributes:
addq -- A queue storing added tasks.
schedule -- A list of tasks in order of execution times.
thread -- If threaded, the thread processing the schedule.
run -- Indicates if the scheduler is running.
stop -- Threading event indicating if the main process
has been stopped.
Methods:
add -- Add a new task to the schedule.
process -- Process and schedule tasks.
quit -- Stop the scheduler.
:param parentstop: An :class:`~threading.Event` to signal stopping
the scheduler.
"""
def __init__(self, parentstop=None):
"""
Create a new scheduler.
Arguments:
parentstop -- A threading event indicating if the main process has
been stopped.
"""
#: A queue for storing tasks
self.addq = queue.Queue()
#: A list of tasks in order of execution time.
self.schedule = []
#: If running in threaded mode, this will be the thread processing
#: the schedule.
self.thread = None
#: A flag indicating that the scheduler is running.
self.run = False
#: An :class:`~threading.Event` instance for signalling to stop
#: the scheduler.
self.stop = parentstop
#: Lock for accessing the task queue.
self.schedule_lock = threading.RLock()
def process(self, threaded=True):
"""
Begin accepting and processing scheduled tasks.
"""Begin accepting and processing scheduled tasks.
Arguments:
threaded -- Indicates if the scheduler should execute in its own
thread. Defaults to True.
:param bool threaded: Indicates if the scheduler should execute
in its own thread. Defaults to ``True``.
"""
if threaded:
self.thread = threading.Thread(name='sheduler_process',
self.thread = threading.Thread(name='scheduler_process',
target=self._process)
self.thread.daemon = True
self.thread.start()
else:
self._process()
@@ -184,18 +179,16 @@ class Scheduler(object):
def add(self, name, seconds, callback, args=None,
kwargs=None, repeat=False, qpointer=None):
"""
Schedule a new task.
"""Schedule a new task.
Arguments:
name -- The name of the task.
seconds -- The number of seconds to wait before executing.
callback -- The function to execute.
args -- The arguments to pass to the callback.
kwargs -- The keyword arguments to pass to the callback.
repeat -- Indicates if the task should repeat.
Defaults to False.
qpointer -- A pointer to an event queue for queuing callback
:param string name: The name of the task.
:param int seconds: The number of seconds to wait before executing.
:param callback: The function to execute.
:param tuple args: The arguments to pass to the callback.
:param dict kwargs: The keyword arguments to pass to the callback.
:param bool repeat: Indicates if the task should repeat.
Defaults to ``False``.
:param pointer: A pointer to an event queue for queuing callback
execution instead of executing immediately.
"""
try:
@@ -212,12 +205,10 @@ class Scheduler(object):
self.schedule_lock.release()
def remove(self, name):
"""
Remove a scheduled task ahead of schedule, and without
"""Remove a scheduled task ahead of schedule, and without
executing it.
Arguments:
name -- The name of the task to remove.
:param string name: The name of the task to remove.
"""
try:
self.schedule_lock.acquire()

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,16 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.xmlstream.tostring
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the file LICENSE for copying permission.
This module converts XML objects into Unicode strings and
intelligently includes namespaces only when necessary to
keep the output readable.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
import sys
@@ -14,26 +21,28 @@ if sys.version_info < (3, 0):
def tostring(xml=None, xmlns='', stanza_ns='', stream=None,
outbuffer='', top_level=False):
"""
Serialize an XML object to a Unicode string.
"""Serialize an XML object to a Unicode string.
If namespaces are provided using xmlns or stanza_ns, then elements
that use those namespaces will not include the xmlns attribute in
the output.
If namespaces are provided using ``xmlns`` or ``stanza_ns``, then
elements that use those namespaces will not include the xmlns attribute
in the output.
Arguments:
xml -- The XML object to serialize. If the value is None,
then the XML object contained in this stanza
object will be used.
xmlns -- Optional namespace of an element wrapping the XML
object.
stanza_ns -- The namespace of the stanza object that contains
the XML object.
stream -- The XML stream that generated the XML object.
outbuffer -- Optional buffer for storing serializations during
recursive calls.
top_level -- Indicates that the element is the outermost
element.
:param XML xml: The XML object to serialize.
:param string xmlns: Optional namespace of an element wrapping the XML
object.
:param string stanza_ns: The namespace of the stanza object that contains
the XML object.
:param stream: The XML stream that generated the XML object.
:param string outbuffer: Optional buffer for storing serializations
during recursive calls.
:param bool top_level: Indicates that the element is the outermost
element.
:type xml: :py:class:`~xml.etree.ElementTree.Element`
:type stream: :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
:rtype: Unicode string
"""
# Add previous results to the start of the output.
output = [outbuffer]
@@ -102,11 +111,10 @@ def tostring(xml=None, xmlns='', stanza_ns='', stream=None,
def xml_escape(text):
"""
Convert special characters in XML to escape sequences.
"""Convert special characters in XML to escape sequences.
Arguments:
text -- The XML text to convert.
:param string text: The XML text to convert.
:rtype: Unicode string
"""
if sys.version_info < (3, 0):
if type(text) != types.UnicodeType:

File diff suppressed because it is too large Load Diff

View File

@@ -68,7 +68,10 @@ class TestElementBase(SleekTest):
'baz': '',
'foo2': {'bar': '',
'baz': 'b'},
'substanzas': [{'__childtag__': '{foo}subfoo',
'substanzas': [{'__childtag__': '{foo}foo2',
'bar': '',
'baz': 'b'},
{'__childtag__': '{foo}subfoo',
'bar': 'c',
'baz': ''}]}
self.failUnless(values == expected,

View File

@@ -6,23 +6,27 @@
See the file LICENSE for copying permission.
"""
import base64
from sleekxmpp.plugins.xep_0009.stanza.RPC import RPCQuery, MethodCall, \
MethodResponse
from sleekxmpp.plugins.xep_0009.binding import py2xml
from sleekxmpp.plugins.xep_0009.binding import py2xml, xml2py, rpcbase64, \
rpctime
from sleekxmpp.stanza.iq import Iq
from sleekxmpp.test.sleektest import SleekTest
from sleekxmpp.xmlstream.stanzabase import register_stanza_plugin
from sleekxmpp.xmlstream.tostring import tostring
import unittest
class TestJabberRPC(SleekTest):
def setUp(self):
register_stanza_plugin(Iq, RPCQuery)
register_stanza_plugin(RPCQuery, MethodCall)
register_stanza_plugin(RPCQuery, MethodCall)
register_stanza_plugin(RPCQuery, MethodResponse)
def testMethodCall(self):
iq = self.Iq()
iq['rpc_query']['method_call']['method_name'] = 'system.exit'
@@ -50,6 +54,235 @@ class TestJabberRPC(SleekTest):
</query>
</iq>
""", use_values=False)
suite = unittest.TestLoader().loadTestsFromTestCase(TestJabberRPC)
def testConvertNil(self):
params = [None]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<nil />
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Nil to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(params, xml2py(expected_xml),
"XML to nil conversion")
def testConvertBoolean(self):
params = [True, False]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<boolean>1</boolean>
</value>
</param>
<param>
<value>
<boolean>0</boolean>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Boolean to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(params, xml2py(expected_xml),
"XML to boolean conversion")
def testConvertString(self):
params = ["'This' & \"That\""]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<string>&apos;This&apos; &amp; &quot;That&quot;</string>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"String to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(params, xml2py(expected_xml),
"XML to string conversion")
def testConvertInteger(self):
params = [32767, -32768]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<i4>32767</i4>
</value>
</param>
<param>
<value>
<i4>-32768</i4>
</value>
</param>
</params>
""")
alternate_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<int>32767</int>
</value>
</param>
<param>
<value>
<int>-32768</int>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Integer to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(params, xml2py(expected_xml),
"XML to boolean conversion")
self.assertEqual(params, xml2py(alternate_xml),
"Alternate XML to boolean conversion")
def testConvertDouble(self):
params = [3.14159265]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<double>3.14159265</double>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Double to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(params, xml2py(expected_xml),
"XML to double conversion")
def testConvertBase64(self):
params = [rpcbase64(base64.b64encode(b"Hello, world!"))]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<base64>SGVsbG8sIHdvcmxkIQ==</base64>
</value>
</param>
</params>
""")
alternate_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<Base64>SGVsbG8sIHdvcmxkIQ==</Base64>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Base64 to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(list(map(lambda x: x.decode(), params)),
list(map(lambda x: x.decode(), xml2py(expected_xml))),
"XML to base64 conversion")
self.assertEqual(list(map(lambda x: x.decode(), params)),
list(map(lambda x: x.decode(), xml2py(alternate_xml))),
"Alternate XML to base64 conversion")
def testConvertDateTime(self):
params = [rpctime("20111220T01:50:00")]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<dateTime.iso8601>20111220T01:50:00</dateTime.iso8601>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"DateTime to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(list(map(lambda x: x.iso8601(), params)),
list(map(lambda x: x.iso8601(), xml2py(expected_xml))),
None)
def testConvertArray(self):
params = [[1,2,3], ('a', 'b', 'c')]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<array>
<data>
<value><i4>1</i4></value>
<value><i4>2</i4></value>
<value><i4>3</i4></value>
</data>
</array>
</value>
</param>
<param>
<value>
<array>
<data>
<value><string>a</string></value>
<value><string>b</string></value>
<value><string>c</string></value>
</data>
</array>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Array to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(list(map(list, params)), xml2py(expected_xml),
"XML to array conversion")
def testConvertStruct(self):
params = [{"foo": "bar", "baz": False}]
params_xml = py2xml(*params)
expected_xml = self.parse_xml("""
<params xmlns="jabber:iq:rpc">
<param>
<value>
<struct>
<member>
<name>foo</name>
<value><string>bar</string></value>
</member>
<member>
<name>baz</name>
<value><boolean>0</boolean></value>
</member>
</struct>
</value>
</param>
</params>
""")
self.assertTrue(self.compare(expected_xml, params_xml),
"Struct to XML conversion\nExpected: %s\nGot: %s" % (
tostring(expected_xml), tostring(params_xml)))
self.assertEqual(params, xml2py(expected_xml),
"XML to struct conversion")
suite = unittest.TestLoader().loadTestsFromTestCase(TestJabberRPC)

View File

@@ -1,5 +1,6 @@
import time
from sleekxmpp import Message
from sleekxmpp.test import *
from sleekxmpp.xmlstream.handler import *
from sleekxmpp.xmlstream.matcher import *
@@ -152,5 +153,49 @@ class TestHandlers(SleekTest):
self.failUnless(events == ['foo'],
"Iq callback was not executed: %s" % events)
def testMultipleHandlersForStanza(self):
"""
Test that multiple handlers for a single stanza work
without clobbering each other.
"""
def handler_1(msg):
msg.reply("Handler 1: %s" % msg['body']).send()
def handler_2(msg):
msg.reply("Handler 2: %s" % msg['body']).send()
def handler_3(msg):
msg.reply("Handler 3: %s" % msg['body']).send()
self.xmpp.add_event_handler('message', handler_1)
self.xmpp.add_event_handler('message', handler_2)
self.xmpp.add_event_handler('message', handler_3)
self.recv("""
<message to="tester@localhost" from="user@example.com">
<body>Testing</body>
</message>
""")
# This test is brittle, depending on the fact that handlers
# will be checked in the order they are registered.
self.send("""
<message to="user@example.com">
<body>Handler 1: Testing</body>
</message>
""")
self.send("""
<message to="user@example.com">
<body>Handler 2: Testing</body>
</message>
""")
self.send("""
<message to="user@example.com">
<body>Handler 3: Testing</body>
</message>
""")
suite = unittest.TestLoader().loadTestsFromTestCase(TestHandlers)