printing to stdout
I can run a shell script from the commandline as root in which I start a python script as user "ha". The output to stdout and stderr generated by the python script is visible in an xterm: #!/bin/dash exec 2>&1 chpst -u ha:ha:i2c -U ha /usr/local/ha/init.sh exec chpst -u ha:ha:i2c:gpio /usr/local/ha/wait4int.py So far so good. But when I run the script supervised by runit, I can see the output generated by the shell script "init.sh", but the output of the python script is not transferred to the supervised logging. The python script itself works, it reads out some I/O expanders on a Raspberry Pi. But the output of the "print" commands seems to disappear: [..] while True: if GPIO.input(23) == 1: # if still 0, another event has occurred GPIO.wait_for_edge(23, GPIO.FALLING) print ('---') while GPIO.input(23) == 0: for pcf in pcf_present: output = bus.read_byte(pcf) print ("%x: %x" % (pcf, output)) if GPIO.input(23) == 1: loopcntr = 0 break else: loopcntr += 1 if loopcntr >=20: print ('[ALERT] possible INT loop, disable 10 seconds') sleep (10) GPIO.cleanup() Anyone a hint? Note: I'm a newbie to python. -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Fri, 17 Aug 2018 08:31:22 +1000 Cameron Simpson wrote: > This isn't specific to Python, you'll find it with most programmes. > (The shell's builtin "echo" command is an exception.) [buffer explanation] I already suspectec a buffered output and to check if it was the buffer, I created a lot of output by pressing the button that generates an INT quite a lot of times. But apparently this wasn't enough. After all, the output is small: --- 38: ff 39: ff 3a: ff 3b: fb I fear I generated 4095 bytes instead of 4096. Some wise human being once said "Murphy was an optimist" :) > So let's look at your script: > > > print ("%x: %x" % (pcf, output)) > [...] > > print ('[ALERT] possible INT loop, disable 10 seconds') > > Your programme will be writing into a buffer. Your messages only go > out when enough have accrued to fill the buffer. > > To force te messages to go out in a timely manner you need to flush > the buffer. You have two choices here: call sys.stdout.flush() or > pass "flush=True" with the print call, eg: > > print(, flush=True) > > Just looking at your loop I would be inclined to just call flush once > at the bottom, _before_ the sleep() call: > > sys.stdout.flush() > > Your call; the performance difference will be small, so it tends to > come down to keeping your code readable and maintainable. Yep, the "sys.stdout.flush()" did the job :) I had already been mucking about with file=sys.stderr, but without result, just because of the fact that stderr is never buffered AFAIK (the supervised script "run" has an "exec 2>&1") Anyroad, it works :) Thnx! -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Fri, 17 Aug 2018 08:31:22 +1000 Cameron Simpson wrote: > Just looking at your loop I would be inclined to just call flush once > at the bottom, _before_ the sleep() call: > > sys.stdout.flush() > > Your call; the performance difference will be small, so it tends to > come down to keeping your code readable and maintainable. This is a working script I made. It initializes the I/O expanders, then it waits for an INT from these I/O expanders on GPIO23, reads the contents and sends which bit on which chip went up or down to a fifo (and stdout for logging) As I'm new to Python, just this question: are there any unPythony things in this code? ## #!/usr/bin/env python3 list_pcf = [0x38, 0x39, 0x3a, 0x3b] list_pcf_value = [] import sys from smbus import SMBus from time import sleep import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) bus = SMBus(1) # initialisation of the input devices: print ("[INFO] initialisation input devices") for i in range(len(list_pcf)): try: bus.write_byte(list_pcf[i], 0xff) # set device to 0xff output = bus.read_byte(list_pcf[i]) list_pcf_value.append(output) # append value to list print ("found: %d, input value: 0x%x" % (list_pcf[i], output)) except IOError: print ("[ALERT] I/O problem device 0x%x (init)" % pcf) sys.stdout.flush() # GPIO 23 set up as input. It is pulled up to stop false signals GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) loopcntr = 0 # detects if INT is kept low while True: if GPIO.input(23) == 1: # if still 0, another event has occurred GPIO.wait_for_edge(23, GPIO.FALLING) print ('---') while GPIO.input(23) == 0: for i in range(len(list_pcf)): try: output = bus.read_byte(list_pcf[i]) if output != list_pcf_value[i]: xor = list_pcf_value[i] ^ output for l in range(8): if xor & 0x1: updown = (output >> l) & 0x1 print ("%d bit %d: to %d" % (list_pcf[i],l,updown)) print("%d %d %d" % (list_pcf[i],l,updown), file=open('/mnt/ramdisk/var/lib/ha/events.fifo', 'w')) xor = xor >> 1 list_pcf_value[i] = output except IOError: print ("[ALERT] I/O problem device 0x%x" % list_pcf[i]) if GPIO.input(23) == 1: loopcntr = 0 break else: loopcntr += 1 if loopcntr >=20: print ('[ALERT] possible INT loop, disable 10 seconds') sleep (10) sys.stdout.flush() GPIO.cleanup() -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Sun, 19 Aug 2018 19:53:04 +1000 Cameron Simpson wrote: [Oops, apparently you set the Reply-To to python-list@python.org, normally that's no problem, but I did something wrong somewhere] > There are always unPythonic bits. Even after you've cleaned them all > up, since people will disagree about the finer points of Pythonicism > there will be bits both over and under cleaned. Although I do not understand exactly what zip is doing here (I presume I switch to use pointers instead of the values), I like the "enumerate" function, very handy :-) The code is now as follows: #!/usr/bin/env python3 import sys from smbus import SMBus from time import sleep import RPi.GPIO as GPIO list_pcf = [0x38, 0x39, 0x3a, 0x3b] list_pcf_value = [] GPIO.setmode(GPIO.BCM) bus = SMBus(1) # initialisation of the input devices: print ("[INFO] initialisation input devices") for pcf in list_pcf: try: bus.write_byte(pcf, 0xff) # set device to 0xff except IOError as e: print ("[ALERT] I/O problem device 0x%x (init): %s" % (pcf, e)) output = bus.read_byte(pcf) list_pcf_value.append(output) # append value to list print ("found pcf8574 at 0x%x, input value: 0x%x" % (pcf, output)) sys.stdout.flush() # GPIO 23 set up as input. It is pulled up to stop false signals GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) loopcntr = 0 # detects if INT is kept low while True: if GPIO.input(23) == 1: # if still 0, another event has occurred GPIO.wait_for_edge(23, GPIO.FALLING) while GPIO.input(23) == 0: for device_nr, (pcf, pcf_value) in enumerate(zip(list_pcf, list_pcf_value)): try: output = bus.read_byte(pcf) except IOError as e: print ("[ALERT] I/O problem device 0x%x: %s" % (pcf, e)) if output != pcf_value: xor = pcf_value ^ output for bit_pos in range(8): if xor & 0x1: up_down = (output >> bit_pos) & 0x1 print ("pcf8574 0x%x bit %d: to %d" % (pcf,bit_pos,up_down)) # send decimal values to event script: with open('/mnt/ramdisk/var/lib/ha/events.fifo', 'w') as fifo: print("%d %d %d" % (pcf,bit_pos,up_down), file=fifo) xor >>= 1 list_pcf_value[device_nr] = output if GPIO.input(23) == 1: loopcntr = 0 break loopcntr += 1 if loopcntr >=20: print ('[ALERT] possible INT loop, disabling 10 seconds') sys.stdout.flush() sleep (10) sys.stdout.flush() GPIO.cleanup() And it still works :-) Thnx! R. -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Sun, 19 Aug 2018 12:02:51 +0300 Marko Rauhamaa wrote: > richard lucassen : > > As I'm new to Python, just this question: are there any unPythony > > things in this code? > > Your code looks neat. Well, apparently there were quite a lot of things that makes the code more readable I'd say. And even better. But it was indeed not very unPythony. OTOH, I'm not a programmer, otherwise I would have written this in C ;-) R. -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Sun, 19 Aug 2018 19:53:04 +1000 Cameron Simpson wrote: > There are always unPythonic bits. Even after you've cleaned them all > up, since people will disagree about the finer points of Pythonicism > there will be bits both over and under cleaned. Although I do not understand what zip is doing exactly here (I presume I switch to use pointers instead of the values), I like the "enumerate" function, very handy :-) The code is now as follows: #!/usr/bin/env python3 import sys from smbus import SMBus from time import sleep import RPi.GPIO as GPIO list_pcf = [0x38, 0x39, 0x3a, 0x3b] list_pcf_value = [] GPIO.setmode(GPIO.BCM) bus = SMBus(1) # initialisation of the input devices: print ("[INFO] initialisation input devices") for pcf in list_pcf: try: bus.write_byte(pcf, 0xff) # set device to 0xff except IOError as e: print ("[ALERT] I/O problem device 0x%x (init): %s" % (pcf, e)) output = bus.read_byte(pcf) list_pcf_value.append(output) # append value to list print ("found pcf8574 at 0x%x, input value: 0x%x" % (pcf, output)) sys.stdout.flush() # GPIO 23 set up as input. It is pulled up to stop false signals GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) loopcntr = 0 # detects if INT is kept low while True: if GPIO.input(23) == 1: # if still 0, another event has occurred GPIO.wait_for_edge(23, GPIO.FALLING) while GPIO.input(23) == 0: for device_nr, (pcf, pcf_value) in enumerate(zip(list_pcf, list_pcf_value)): try: output = bus.read_byte(pcf) except IOError as e: print ("[ALERT] I/O problem device 0x%x: %s" % (pcf, e)) if output != pcf_value: xor = pcf_value ^ output for bit_pos in range(8): if xor & 0x1: up_down = (output >> bit_pos) & 0x1 print ("pcf8574 0x%x bit %d: to %d" % (pcf,bit_pos,up_down)) # send decimal values to event script: with open('/mnt/ramdisk/var/lib/ha/events.fifo', 'w') as fifo: print("%d %d %d" % (pcf,bit_pos,up_down), file=fifo) xor >>= 1 list_pcf_value[device_nr] = output if GPIO.input(23) == 1: loopcntr = 0 break loopcntr += 1 if loopcntr >=20: print ('[ALERT] possible INT loop, disabling 10 seconds') sys.stdout.flush() sleep (10) sys.stdout.flush() GPIO.cleanup() And it still works :-) Thnx! R. -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Sun, 19 Aug 2018 10:11:08 -0400 Joel Goldstick wrote: > > Well, apparently there were quite a lot of things that makes the > > code more readable I'd say. And even better. But it was indeed not > > very unPythony. OTOH, I'm not a programmer, otherwise I would have > > written this in C ;-) > > This strikes me as an odd conclusion. Raspberry Pi places a strong > emphasis on python. It certainly doesn't execute as fast as C can, > but it provides a conceptually higher level programming model. There > is extremely good community support for python with Pi (a huge plus), > and the code is much more understandable. It is faster to write code > with python, you can come back to it and understand it more readily at > some later time, as can others. And it runs 'fast enough' . So, no, > I don't think if you were a 'programmer' you would have used C to do > this project. But others may be of a different persuation. You've got absolutely a point that Python seems to be largely supported for Rpi. But I'll tell you something else: I just started to use a Rpi ;-) I agree that python code is much more understandable than C. > You seemed to have snipped your question about zip function. It takes > iterables (things like lists, tuples, dictionaries) as arguments and > pairs them together to form tuples. Look it up. Very useful. As and > example, if you have list1 = (1,2,3), and list2 = (4,5,6,7) and zip > them you will get ((1,4), (2,5),(3,6)). (It stops when the shortest > iterable is exhausted) > > Your allusion to pointers is misguided. Python is not like C or > assembler. You don't, and don't need to know where objects are > stored. Names are assigned to reference data objects I'll have another look at it, I was just searching for a clear explanation, but the page I found was not clear enough for me. I'll have to take some time for it... -- Richard Lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Sun, 19 Aug 2018 12:37:18 -0400 Joel Goldstick wrote: > > I'll have another look at it, I was just searching for a clear > > explanation, but the page I found was not clear enough for me. I'll > > have to take some time for it... > > try python.org tutorial, and search for terms like names, or name > binding. And also 'namespace' I'll have to get used to the language and I think it's a nice language. I just mucked about with the sqlite modules BTW. And it works :) -- Richard Lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list
Re: printing to stdout
On Mon, 20 Aug 2018 08:19:12 +1000 Cameron Simpson wrote: [sorry for late reply] > Someone else has descibed zip tersely: it pairs it the elements of 2 > lists. In fact it joins up matching elements of an arbitrary number > of iterables. Here is a 3 iterable example: > > >>> zip( (1,2,3), (4,5,6), (7,8,9) ) > > >>> list( zip( (1,2,3), (4,5,6), (7,8,9) ) ) > [(1, 4, 7), (2, 5, 8), (3, 6, 9)] > > See that is has collected the first element of each tuple, then the > second and so forth? Yep, an image or an example says more than 1000 pages of explanation > In my sentence above, "iterable" means any object you can iterate > over - any object you could use in a for-loop. So, obviously, a list > as in your programme. And also a tuple like (1,2,3) as in the example > above (a tuple is an unmodifiable list). ANd any number of other > things that will yield a sequence of values. > > In Python 3 (which you're using) zip returns a lazy object, which > does the zipping as you request the values. That is why the example > shows a base "zip()" returning a "zip object" - we hand it to list() > to get an actual list of all the values. This is fast (instant return > of the zip object without doing much work) and cheap in memory (no > need to make a huge result if you're zipping big lists) and > cheap in time (no need to make the whole result if you're only using > the first part of it). > > So, to the code using the zip: > > for device_nr, (pcf, pcf_value) in enumerate(zip(list_pcf, > list_pcf_value)): > > The inner zip yields (pcf, pcf_value) pairs from zipping list_pcf and > list_pcf_value, an iterable of: > > (pcf[0], pcf-value[0]), (pcf[1], pcf_value[1]), ... > > and you can write a for loop like this: > > for pcf, pcf_value in zip(list_pcf, list_pcf_value): > > to deal with each of those pairs in turn. > > However, since you also need the index (device_nr) in order to update > list_pcf_value: > > list_pcf_value[device_nr] = output > > we then hand the whole thing to enumerate: > > for device_nr, (pcf, pcf_value) in enumerate(zip(list_pcf, > list_pcf_value)): > > The enumerate does just what the previous one did, yields pairs of > (index, value). Each value is a pair from the zip, so it yields: > > (0, (pcf[0], pcf-value[0])), > (1, (pcf[1], pcf_value[1])), > ... > > and Python's tuple unpacking syntax is fairly generous, so you want > write: > > device_nr, (pcf, pcf_value) = (0, (pcf[0], pcf-value[0])) > > and you can stick that in the for loop, getting the syntax you're > using in the programme: > > for device_nr, (pcf, pcf_value) in enumerate(zip(list_pcf, > list_pcf_value)): Well, that's a nice and clear explanation, at the prompt I played a bit with the enumerate and zip functions and after all it's quite simple :) > >The code is now as follows: > > Much nicer. A few tiny nit picks: [..] changes applied > >while True: > > if GPIO.input(23) == 1: # if still 0, another event has occurred > >GPIO.wait_for_edge(23, GPIO.FALLING) > > Can the GPIO ops also raise IOErrors? Just wondering. Probably, sounds logigal, the GPIO pin is read and if something is wrong it will generate an I/O error. R. -- richard lucassen http://contact.xaq.nl/ -- https://mail.python.org/mailman/listinfo/python-list