Question about how to handle disconnections using xmpppy module

I am using the following Python3 code to receive a text message from a xmpp server and then send that message to an Apple Messages user. The code runs on a machine running MacOS Monterey (12.6.7):

#! /usr/bin/python3

# xmpppy library must be installed using   pip install xmpppy
# or import xmpp will not work

import xmpp
import subprocess
import sys

# Create a new XMPP client
client = xmpp.Client('xmpp.servername', debug=[])

# Connect to the XMPP server
client.connect()

# Authenticate the client
client.auth('xmppusername', 'xmpppassword')

# Send presence to indicate availability
client.sendInitPresence()

# Receive XMPP messages
def receive_message(conn, msg):
    # Place the received message in a variable
    received_message = msg.getBody()

    cmdTemplate = '''
    tell application "Messages"
        send "{0}" to buddy "applemessagesuser@icloud.com" of service 1
    end tell
    '''
    cmd = cmdTemplate.format(received_message)
    subprocess.run(['osascript', '-e', cmd], capture_output=False)

# Register the message event handler
client.RegisterHandler('message', receive_message)

# Keep the connection alive to receive messages
while True:
    client.Process(1)

# Disconnect from the XMPP server
client.disconnect()

The code runs great as long as the XMPP server is up and can be connected to, and in theory it should never exit, it should just keep listening for new messages from the XMPP server. The problem is that if the XMPP server has to be rebooted, or the internet connection goes down or something else interrupts the connection, the Python script quits with error messages similar to these:

  File "./xmppreceive.py", line 40, in <module>    client.Process(1)  File "/Users/user/Library/Python/3.8/lib/python/site-packages/xmpp/dispatcher.py", line 129, in Process
    raise ex
  File "/Users/user/Library/Python/3.8/lib/python/site-packages/xmpp/dispatcher.py", line 307, in dispatch
    handler['func'](session,stanza)
  File "/Users/user/Library/Python/3.8/lib/python/site-packages/xmpp/dispatcher.py", line 219, in streamErrorHandler
    raise exc((name,text))
xmpp.protocol.SystemShutdown: ('system-shutdown', 'Received SIGTERM')

Rather than fail completely, what I would like to see happen if the connection is lost is for the script to wait for it to come back up and then reconnect automatically. For example, wait ten seconds and retry the connection, and keep doing that until there server once again becomes available. But I can’t seem to figure out how to do that. I tried a couple of approaches I found online but they either errored out immediately (the program would not run at all) or the problem remained - if the server connection is lost the program just quits.

An inelegant solution is to run the Python script from a bash shell script and if the Python script exits, the bash script executes a “sleep 10” statement and then loops back and tries to launch the Python script again. But it seems kind of silly to have to do that - isn’t there some way to tell that the script has encountered an error condition and handle that within the Python script itself? Back in the days of BASIC (long, long ago) we could surround the potentially offending statement(s) with “ON ERROR GOTO …” statements; does Python have any similar error trapping capabilities, or is there a better way to handle this? It seems that the statement “client.Process(1)” is the point of failure here.

I’d suggest that you could maybe use a try/except branch. Have you looked into that option?

Yes I did. I don’t have the exact code I tried but from memory it was something like this:

# Keep the connection alive to receive messages

while True:
    try:
        client.Process(1)
    except:
        print("An error occurred, sleeping for 10 seconds...")
        time.sleep(10)

And I had to add an “import time” line up near the top. The problem was that when the connection was lost, it would print the error message ONCE, and then just go off into la-la land. Even when the server came back up (which generally takes about 30 to 45 seconds, so the error message should have been printed at least three or four times), the script remained running but totally unresponsive. Only killing the script completely and restarting it would get it to work again.

I see from this source that xmpppy is a Beta release and looks to be unsupported, having not been updated in almost five years — I wounder if there could be a better tool set that you could use. In any case, that’s not a good way to use a try/except branch.

Used correctly, a try/except branch will catch a particular error, such as, for example ‘service-unavailable’ (Note: I don’t know that said is in fact one of the error returns, it’s just an example of what could be). You could even have a counter so that after say, 10 attempts, you call sys.exit() and pass to that some appropriate message so that you get a graceful script exit.

As you’ve posted that code “from memory” rather than an actual code snippet, I’ve had to generalizing here, because of that lack of information that you’ve provided, but if you can post something that in fact will run, together with the error return from xmpppy, then we could possibly guide you better.

To add: I’ve just noticed that you’re using a sub process, so catching errors may be way more involved that I’d assumed as it’s not something that I’ve done.

I see from this source that xmpppy is a Beta release and looks to be unsupported, having not been updated in almost five years — I wounder if there could be a better tool set that you could use. In any case, that’s not a good way to use a try/except branch.

Used correctly, a try/except branch will catch a particular error, such as, for example ‘service-unavailable’ (Note: I don’t know that said is in fact one of the error returns, it’s just an example of what could be). You could even have a counter so that after say, 10 attempts, you call sys.exit() and pass to that some appropriate message so that you get a graceful script exit.

I don’t know of any other tools besides xmpppy, I was just copying something I found on the Internet somewhere and it appeared to work. As for try/except, a page I found at the time I tried that said that would work, in other words you don’t need to test for a particular error, although you can if you want to. And really I would not want to, because how can I possibly anticipate all the errors that might appear? It’s not like there is some way to find out all the possible errors you might see, but what I wanted was to handle them all in exactly the same way, wait ten seconds and retry. Now I realize that if there’s a syntax error it would loop forever (IF IT WORKED) but with the Print statement I would see that and might be motivated to find out exactly what the error is. But for anything other than a syntax error I would want it to just keep retrying every ten seconds.

As you’ve posted that code “from memory” rather than an actual code snippet, I’ve had to generalizing here, because of that lack of information that you’ve provided, but if you can post something that in fact will run, together with the error return from xmpppy, then we could possibly guide you better.

Um, I’ve already posted that. The code I posted “from memory” is exactly what I tried, other than that the Print string may have been slightly different. And when I did that, there was no error output at all - it printed the text ONCE, then silently hung. When I did NOT include the Try/Except statements, I got the errors shown in my first post.

To add: I’ve just noticed that you’re using a sub process, so catching errors may be way more involved that I’d assumed as it’s not something that I’ve done.

The only thing the sub process does is run a system command, in this case Apple’s “osascript” which actually sends the message. And that part works perfectly, or at least it has so far.

What I am hearing is that xmpppy is old and from the lack of other responses I assume not many people use it. I do recall when I was first trying to make this work that I found a page suggesting the use of some other module (can’t recall the name of it) and I actually tried that first but could not get it to work, then I found xmpppy and that in fact has been working quite well except for this one issue. Kind of a shame that it is apparently no longer maintained because it really does what it is supposed to do, and that’s despite the fact that it was almost certainly written for Python2, but still works (up to a point, at least) in Python3. Anyway, thank you for your suggestions, but I fear I am not experienced enough in Python to pursue this much further.

As an aside, the real problem here is that I need to pass text status messages from a computer running Linux to a computer on a completely different local network that runs MacOS, without opening the firewall on either system. So, using an already-existing XMPP server at a third location seemed like a good idea at the time, it’s just that receiving the XMPP messages proved to be a bit more tricky than I expected.