I actually joined StackOverflow to ask this question because I wasn't sure it was RPi specific, but as I was thinking about how to form the question, I realized it might be GPIO specific (for some reason that I'm not even aware of), so hopefully this is the right place to ask.
I'm running Raspbian Jessie 4.9.35-v7+ on a Pi 3 Model B. Over time, I've cobbled together a fairly basic python script to monitor a GPIO pin and send a network signal to a remote application. I run two slightly different copies of the script to monitor two different GPIO pins for two different network signals, and the scripts are launched from rc.local. A previous version of these scripts ran for months on end, but I just had the current version fail after maybe two weeks due to "RuntimeError: maximum recursion depth exceeded." The error output logged includes bouncing back and forth between two functions many times, so I ran this against the log:
cat script2.log | grep -c File
The log for script1 is 0 bytes, so I believe that command gives me a number that shows roughly how many levels of recursion the script encountered. The output was 984, and paired with the two functions I wrote repeating back and fourth all but 7 of those times, the 977 iterations seem high enough to indicate the problem lies in how my script is written as opposed to within an imported package. Here is an example of the script I am running:
#!/usr/bin/env python
import RPi.GPIO as GPIO
import signal
import socket
import time
import requests
#Define GPIO pin here:
thiscontact = 2
GPIO.setmode(GPIO.BCM)
GPIO.setup(thiscontact, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def watchopen():
thisposition = GPIO.input(thiscontact)
if thisposition == True:
while thisposition == True:
time.sleep(0.1)
thisposition = GPIO.input(thiscontact)
watchclose()
watchclose()
def watchclose():
thisposition = GPIO.input(thiscontact)
if thisposition == False:
while thisposition == False:
#Send Trigger
requests.get('https://192.0.2.248:55756/Srv/Api/TriggerFacade/ActivateDeactivateTrigger?{"triggerName":"SignalOne","deactivateAfterSeconds":"3"}', auth=('Username', 'Password'), verify=False, timeout=1)
time.sleep(0.9)
thisposition = GPIO.input(thiscontact)
watchopen()
watchopen()
def exit_handler(signal, frame):
GPIO.cleanup()
raise SystemExit
signal.signal(signal.SIGTERM, exit_handler)
signal.signal(signal.SIGINT, exit_handler)
watchopen()
This is what the end of the log file would look like from that example:
File "/usr/local/bin/Door6B.py", line 34, in watchclose
watchopen()
File "/usr/local/bin/Door6B.py", line 20, in watchopen
watchclose()
File "/usr/local/bin/Door6B.py", line 28, in watchclose
requests.get('https://192.0.2.248:55756/Srv/Api/TriggerFacade/ActivateDeactivateTrigger?{"triggerName":"SignalOne","deactivateAfterSeconds":"3"}', auth=('Username', 'Password'), verify=False, timeout=1)
File "/usr/lib/python2.7/dist-packages/requests/api.py", line 60, in get
return request('get', url, **kwargs)
File "/usr/lib/python2.7/dist-packages/requests/api.py", line 48, in request
session = sessions.Session()
File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 327, in __init__
self.mount('https://', HTTPAdapter())
File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 90, in __init__
self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 126, in init_poolmanager
block=block, **pool_kwargs)
File "/usr/lib/python2.7/dist-packages/urllib3/poolmanager.py", line 65, in __init__
dispose_func=lambda p: p.close())
File "/usr/lib/python2.7/dist-packages/urllib3/_collections.py", line 46, in __init__
self._container = self.ContainerCls()
RuntimeError: maximum recursion depth exceeded
Above that in the log file, the first two sections repeat 974 times following the first bit which is roughly the same:
Traceback (most recent call last):
File "/usr/local/bin/Door6B.py", line 40, in <module>
watchopen()
With a bit of searching, I've found advice such as "Rewriting the algorithm iteratively, if possible, is generally a better idea" and "For instance, it's quite possible that your outermost loop will hinge on a simple while." However, I believe I previously found that I had to loop in order for the script to continue running indefinitely when I tested last year, so I am guessing that those answers don't apply to me and the assumption with those answers is that the script needs to complete a task and end.
I'm not sure how to proceed for a number of reasons such as these:
1) The original version of this script that ran for months on end used socket instead of requests (and it looks like I never stopped importing that, so some script cleanup may be in order). Given this, while it seems unlikely to me, it may be possible that I'm not understanding recursion right and there is an issue in requests that caused this error vs the bouncing back and forth between my functions.
2) I have no idea how to make a script perform an action only when a switch is closed each time a switch is closed without this recursion, and I have no idea what to search for in order to find out how if that can even be done.
3) Even assuming the second piece of advice I found applies, if I remember correctly, I added the if loops around the while loops in order to prevent the script from terminating. However, logically speaking, even if the if loops are redundant and it would work with only while loops, I'm not sure that such a change would remove the recursion since each function still calls the other.
4) I suspect that I could have a bash script call the python script in a loop by only having the contents of watchclose() at the end of the python script followed by time.sleep(0.1) (or some sleep function in the bash loop), but I doubt that is the "right" way to deal with this from a programming standpoint or an efficiency standpoint.