How do I expose a signal to DBUS in Python? - python-3.x

I know quiet well how to create methods – however, I wish to expose some signals using Python so that severall programms can be called at once.
class Bildschirm(dbus.service.Object):
DBUS_NAME = 'hm.retro.Retro'
DBUS_OBJECT_PATH = '/hm/retro/Retro'
DBUS_INTERFACE = 'hm.retro.Retro'
def __init__(self, _Visio):
self.Visio = _Visio
self.bus = dbus.SessionBus()
bus_name = dbus.service.BusName(self.DBUS_NAME, bus=self.bus)
super().__init__(bus_name, self.DBUS_OBJECT_PATH)
#dbus.service.method(DBUS_INTERFACE, in_signature='i')
def delta(self, nr):
weiter = self.Visio.akt_sender_nr
weiter = (weiter+nr) % SENDER_AM
self.Visio.akt_sender_nr = weiter
self.Visio.update_sender_labels()
self.Visio.screen.force_update()
self.Visio.screen.draw_next_frame()
#dbus.service.signal(DBUS_INTERFACE)
def config_change_visio(self):
self.Visio.load_config()
self.Visio.update_sender_labels()
self.Visio.update_preset_labels()
self.Visio.screen.force_update()
self.Visio.screen.draw_next_frame()
However, if I try to call the signal with dbus-send:
dbus-send --session --print-reply --dest=hm.retro.Retro /hm/retro/Retro hm.retro.Retro.config_change_visio
I get an error message:
Error org.freedesktop.DBus.Error.UnknownMethod: Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/dbus/service.py", line 654, in _message_cb
(candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
File "/usr/lib/python3/dist-packages/dbus/service.py", line 246, in _method_lookup
raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
dbus.exceptions.UnknownMethodException: org.freedesktop.DBus.Error.UnknownMethod: Unknown method: config_change_visio is not a valid method of interface hm.retro.Retro
What am I doing wrong?

If you pass --print-reply to dbus-send, you are implying that the message type you’re sending is a method call, as only method calls can have a reply. The dbus-send man page says:
--print-reply
Block for a reply to the message sent, and print any reply received in
a human-readable form. It also means the message type (--type=) is
method_call.
Remove the --print-reply argument from your dbus-send invocation, and add --type=signal and it should work.
Note that while it shouldn’t affect this bug, it’s more conventional for D-Bus signal names to be in CamelCase, not underscore_case. The D-Bus specification says:
It is conventional for member names on D-Bus to consist of capitalized
words with no punctuation ("camel-case"). Method names should usually
be verbs, such as GetItems, and signal names should usually be a
description of an event, such as ItemsChanged.
So perhaps your signal should be called VisioConfigChanged instead. Similarly, perhaps your method should be called something like AddDelta.

Related

How to zmq.poll() some socket and some sort of variable?

I'm attempting to poll a few sockets and a multiprocessing.Event
The documentation states:
A zmq.Socket or any Python object having a fileno() method that
returns a valid file descriptor.
which means that I can't use my Event but I should be able to use a file(as returned from open(...) or an io object (anything from the io library), but I'm having no success:
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3\helpers\pydev\pydevd.py", line 1683, in <module>
main()
File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3\helpers\pydev\pydevd.py", line 1677, in main
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3\helpers\pydev\pydevd.py", line 1087, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:\work\polldamnyou.py", line 122, in <module>
p = poller.poll(1000)
File "C:\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\Lib\site-packages\zmq\sugar\poll.py", line 99, in poll
return zmq_poll(self.sockets, timeout=timeout)
File "zmq\backend\cython\_poll.pyx", line 143, in zmq.backend.cython._poll.zmq_poll
File "zmq\backend\cython\_poll.pyx", line 123, in zmq.backend.cython._poll.zmq_poll
File "zmq\backend\cython\checkrc.pxd", line 25, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Unknown error
I have found the same question asked before but the solution was to use another socket which is sidestepping the problem. I am curious and want to see this working. Does any one have any clues what sort of object can be used in zmq.Poller other than a socket?
Edit: a few things I've tried
import traceback, os, zmq
def poll(poller):
try:
print('polled: ', poller.poll(3))
except zmq.error.ZMQError as e:
traceback.print_exc()
class Pollable:
def __init__(self):
self.fd = os.open('dump', os.O_RDWR | os.O_BINARY)
self.FD = self.fd
self.events = 0
self.EVENTS = 0
self.revents = 0
self.REVENTS = 0
def fileno(self):
return self.fd
def __getattribute__(self, item):
if item != '__class__':
print("requested: ", item)
return super().__getattribute__(item)
ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)
poller = zmq.Poller()
poller.register(sock, zmq.POLLIN)
poll(poller) # works
file = open('dump', 'w+b')
print("fileno: ", file.fileno())
poller.register(file, zmq.POLLIN)
poll(poller) # fails
file.events = 0
file.revents = 0
file.EVENTS = 0
file.REVENTS = 0
file.fd = file.fileno()
file.FD = file.fileno()
poll(poller) # still fails
poller.unregister(file)
file.close()
poll(poller) # works
fd = os.open('dump', os.O_RDWR|os.O_BINARY)
print("fd: ", fd)
dummy = Pollable()
poller.register(dummy, zmq.POLLIN)
poll(poller) # fails
__getattribute__ shows that that fd and fileno are being accessed, but nothing else, so what is still wrong?!
In case one has never worked with ZeroMQ,one may here enjoy to first look at "ZeroMQ Principles in less than Five Seconds"before diving into further details
Q : "what sort of object can be used in zmq.Poller other than a socket?"
Welcome to the lovely lands of the Zen-of-Zero. Both the published API and the pyzmq ReadTheDocs are clear and sound on this :
The zmq_poll() function provides a mechanism for applications to multiplex input/output events in a level-triggered fashion over a set of sockets. Each member of the array pointed to by the items argument is a zmq_pollitem_t structure. The nitems argument specifies the number of items in the items array. The zmq_pollitem_t structure is defined as follows :
typedef struct
{
void //*socket//;
int //fd//;
short //events//;
short //revents//;
} zmq_pollitem_t;
For each zmq_pollitem_t item, zmq_poll() shall examine either the ØMQ socket referenced by socket or the standard socket specified by the file descriptor fd, for the event(s) specified in events. If both socket and fd are set in a single zmq_pollitem_t, the ØMQ socket referenced by socket shall take precedence and the value of fd shall be ignored.
For each zmq_pollitem_t item, zmq_poll() shall first clear the revents member, and then indicate any requested events that have occurred by setting the bit corresponding to the event condition in the revents member.
If none of the requested events have occurred on any zmq_pollitem_t item, zmq_poll() shall wait timeout milliseconds for an event to occur on any of the requested items. If the value of timeout is 0, zmq_poll() shall return immediately. If the value of timeout is -1, zmq_poll() shall block indefinitely until a requested event has occurred on at least one zmq_pollitem_t.
Last, but not least,the API is explicit in warning about implementation :
The zmq_poll() function may be implemented or emulated using operating system interfaces other than poll(), and as such may be subject to the limits of those interfaces in ways not defined in this documentation.
Solution :
For any wished-to-have-poll()-ed object, that does not conform to the given mandatory property of having an ordinary fd-filedescriptor, implement a such mandatory property mediating-proxy, meeting the published API-specifications or do not use it with zmq_poll() for successful API calls.
There is no third option.

my first custom logging handler not working

I am trying to write custom handler for logging that would send logs to netcat
I can see on the receiving end that the connection is established and then closes, however, no messages are received and i can see no errors.
here is the code i am running
import socket
import time
import logging
hostname = '127.0.0.1'
port = '1234'
message = 'hello world!\n'
class Nc_handler(logging.Handler):
def __init__(self, hostname, port):
logging.Handler.__init__(self)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((hostname, int(port)))
def emit(self, content):
log_entry = self.format(content)
print("Checking if emit is run")
self.socket.send(log_entry.encode())
logger = logging.getLogger(__name__)
# set format
nc_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
# create nc handler
nc_handler = Nc_handler(hostname, port)
# set handler's level
nc_handler.setLevel(logging.INFO)
# set handler's format
nc_handler.setFormatter(nc_format)
logger.addHandler(nc_handler)
logger.info(message)
If i use nc_handler.emit('Hello') it throws an error:
File "handler.py", line 35, in <module>
nc_handler.emit(message)
File "handler.py", line 17, in emit
log_entry = self.format(content)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 869, in format
return fmt.format(record)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 608, in format
record.message = record.getMessage()
AttributeError: 'str' object has no attribute 'getMessage'
So i suspect i don't use Formatter correctly but I can't figure out what exactly I am doing wrong.
I would really appreciate any help or advice.
Method emit has a wrong signature: its argument must be a LogRecord object. Not a string.
That is because format method needs a LogRecord object.
That is the cause of the AttributeError exception.
I see also a confusion I don't understand: why using message as argument for logging.info?
message is an attribute of the LogRecord object too and is taken from what you logged explicitly.
Use a hard-coded string:
logging.info("Hello")
Or use a different variable:
myvar = "Hello"
logging.info(myvar)
Does it help?

What is the PySpin equivalent of the PyCapture2 camera Power ON function/method?

I am searching for a method to test if the camera is on for PTG camera.
In PyCapture2 the below code works but the presumed PySpin cam.DeviceConnectionStatus() will not work because the function seems not to be present.
PySpin Camera library version: 1.23.0.27
The Error:
Error: Spinnaker: GenICam::AccessException= Feature not present (reference not valid) : AccessException thrown (file 'IEnumerationT.h', line 341) [-2006]
(False, SpinnakerException("Spinnaker: GenICam::AccessException= Feature not present (reference not valid) : AccessException thrown (file 'IEnumerationT.h', line 341) [-2006]"))
I've tried also PySpin.Camera.DeviceConnectionStatus() but it gives the following error whether prior or after cam.Init():
Traceback (most recent call last):
File "X.py", line 82, in YZ
print (PySpin.Camera.DeviceConnectionStatus())
TypeError: 'property' object is not callable
Working PyCapture2 code:
def cameraOn(self, cam):
# Power on the Camera
cameraPower = 0x610
powerVal = 0x80000000
cam.writeRegister(cameraPower, powerVal)
# Waiting for camera to power up
retries = 10
timeToSleep = 0.1 #seconds
for i in range(retries):
sleep(timeToSleep)
try:
regVal = cam.readRegister(cameraPower)
except PyCapture2.Fc2error: # Camera might not respond to register reads during powerup.
pass
awake = True
if regVal == powerVal:
break
awake = False
if not awake:
print ("Could not wake Camera. Exiting...")
exit()
As it seems there is an IsValid() function available from the CameraBase() class in the PySpin/Spinnaker library. This function returns either the bool True once a connection could be made, communication was successful and the camera is still valid for use or a "False" respectively. However, this function does not turn the camera ON or OFF. Nor does it power from a sleep/wake state.
For unknown reasings the IsValid() function does not report back tracebacks for logging or debug purpose. So keep in mind to implement try/except for certain methods.
try:
... your code ...
except PySpin.SpinnakerException as error:
print('Error: %s' % error)
return False, error

Python - Pass a function (callback) variable between functions running in separate threads

I am trying to develop a Python 3.6 script which uses pika and threading modules.
I have a problem which I think is caused by my A) being very new to Python and coding in general, and B) my not understanding how to pass variables between functions when they are run in separate threads and already being passed a parameter in parentheses at the end of the receiving function name.
The reason I think this, is because when I do not use threading, I can pass a variable between functions simply by calling the receiving function name, and supplying the variable to be passed, in parentheses, a basic example is shown below:
def send_variable():
body = "this is a text string"
receive_variable(body)
def receive_variable(body):
print(body)
This when run, prints:
this is a text string
A working version of the code I need to to get working with threading is shown below - this uses straight functions (no threading) and I am using pika to receive messages from a (RabbitMQ) queue via the pika callback function, I then pass the body of the message received in the 'callback' function to the 'processing function' :
import pika
...mq connection variables set here...
# defines username and password credentials as variables set at the top of this script
credentials = pika.PlainCredentials(mq_user_name, mq_pass_word)
# defines mq server host, port and user credentials and creates a connection
connection = pika.BlockingConnection(pika.ConnectionParameters(host=mq_host, port=mq_port, credentials=credentials))
# creates a channel connection instance using the above settings
channel = connection.channel()
# defines the queue name to be used with the above channel connection instance
channel.queue_declare(queue=mq_queue)
def callback(ch, method, properties, body):
# passes (body) to processing function
body_processing(body)
# sets channel consume type, also sets queue name/message acknowledge settings based on variables set at top of script
channel.basic_consume(callback, queue=mq_queue, no_ack=mq_no_ack)
# tells the callback function to start consuming
channel.start_consuming()
# calls the callback function to start receiving messages from mq server
callback()
# above deals with pika connection and the main callback function
def body_processing(body):
...code to send a pika message every time a 'body' message is received...
This works fine however I want to translate this to run within a script that uses threading. When I do this I have to supply the parameter 'channel' to the function name that runs in its own thread - when I then try to include the 'body' parameter so that the 'processing_function' looks as per the below:
def processing_function(channel, body):
I get an error saying:
[function_name] is missing 1 positional argument: 'body'
I know that when using threading there is more code needed and I have included the actual code that I use for threading below so that you can see what I am doing:
...imports and mq variables and pika connection details are set here...
def get_heartbeats(channel):
channel.queue_declare(queue=queue1)
#print (' [*] Waiting for messages. To exit press CTRL+C')
def callback(ch, method, properties, body):
process_body(body)
#print (" Received %s" % (body))
channel.basic_consume(callback, queue=queue1, no_ack=no_ack)
channel.start_consuming()
def process_body(channel, body):
channel.queue_declare(queue=queue2)
#print (' [*] Waiting for Tick messages. To exit press CTRL+C')
# sets the mq host which pika client will use to send a message to
connection = pika.BlockingConnection(pika.ConnectionParameters(host=mq_host))
# create a channel connection instance
channel = connection.channel()
# declare a queue to be used by the channel connection instance
channel.queue_declare(queue=order_send_queue)
# send a message via the above channel connection settings
channel.basic_publish(exchange='', routing_key=send_queue, body='Test Message')
# send a message via the above channel settings
# close the channel connection instance
connection.close()
def manager():
# Channel 1 Connection Details - =======================================================================================
credentials = pika.PlainCredentials(mq_user_name, mq_password)
connection1 = pika.BlockingConnection(pika.ConnectionParameters(host=mq_host, credentials=credentials))
channel1 = connection1.channel()
# Channel 1 thread =====================================================================================================
t1 = threading.Thread(target=get_heartbeats, args=(channel1,))
t1.daemon = True
threads.append(t1)
# as this is thread 1 call to start threading is made at start threading section
# Channel 2 Connection Details - =======================================================================================
credentials = pika.PlainCredentials(mq_user_name, mq_password)
connection2 = pika.BlockingConnection(pika.ConnectionParameters(host=mq_host, credentials=credentials))
channel2 = connection2.channel()
# Channel 2 thread ====================================================================================================
t2 = threading.Thread(target=process_body, args=(channel2, body))
t2.daemon = True
threads.append(t2)
t2.start() # as this is thread 2 - we need to start the thread here
# Start threading
t1.start() # start the first thread - other threads will self start as they call t1.start() in their code block
for t in threads: # for all the threads defined
t.join() # join defined threads
manager() # run the manager module which starts threads that call each module
This when run produces the error
process_body() missing 1 required positional argument: (body)
and I do not understand why this is or how to fix it.
Thank you for taking the time to read this question and any help or advice you can supply is much appreciated.
Please keep in mind that I am new to python and coding so may need things spelled out rather than being able to understand more cryptic replies.
Thanks!
On further looking in to this and playing with the code it seems that if I edit the lines:
def process_body(channel, body):
to read
def process_body(body):
and
t2 = threading.Thread(target=process_body, args=(channel2, body))
so that it reads:
t2 = threading.Thread(target=process_body)
then the code seems to work as needed - I also see multiple script processes in htop so it appears that threading is working - I have left the script processing for 24 hours + and did not receive any errors...

Python 3 script using libnotify fails as cron job

I've got a Python 3 script that gets some JSON from a URL, processes it, and notifies me if there's any significant changes to the data I get. I've tried using notify2 and PyGObject's libnotify bindings (gi.repository.Notify) and get similar results with either method. This script works a-ok when I run it from a terminal, but chokes when cron tries to run it.
import notify2
from gi.repository import Notify
def notify_pygobject(new_stuff):
Notify.init('My App')
notify_str = '\n'.join(new_stuff)
print(notify_str)
popup = Notify.Notification.new('Hey! Listen!', notify_str,
'dialog-information')
popup.show()
def notify_notify2(new_stuff):
notify2.init('My App')
notify_str = '\n'.join(new_stuff)
print(notify_str)
popup = notify2.Notification('Hey! Listen!', notify_str,
'dialog-information')
popup.show()
Now, if I create a script that calls notify_pygobject with a list of strings, cron throws this error back at me via the mail spool:
Traceback (most recent call last):
File "/home/p0lar_bear/Documents/devel/notify-test/test1.py", line 3, in <module>
main()
File "/home/p0lar_bear/Documents/devel/notify-test/test1.py", line 4, in main
testlib.notify(notify_projects)
File "/home/p0lar_bear/Documents/devel/notify-test/testlib.py", line 8, in notify
popup.show()
File "/usr/lib/python3/dist-packages/gi/types.py", line 113, in function
return info.invoke(*args, **kwargs)
gi._glib.GError: Error spawning command line `dbus-launch --autolaunch=776643a88e264621544719c3519b8310 --binary-syntax --close-stderr': Child process exited with code 1
...and if I change it to call notify_notify2() instead:
Traceback (most recent call last):
File "/home/p0lar_bear/Documents/devel/notify-test/test2.py", line 3, in <module>
main()
File "/home/p0lar_bear/Documents/devel/notify-test/test2.py", line 4, in main
testlib.notify(notify_projects)
File "/home/p0lar_bear/Documents/devel/notify-test/testlib.py", line 13, in notify
notify2.init('My App')
File "/usr/lib/python3/dist-packages/notify2.py", line 93, in init
bus = dbus.SessionBus(mainloop=mainloop)
File "/usr/lib/python3/dist-packages/dbus/_dbus.py", line 211, in __new__
mainloop=mainloop)
File "/usr/lib/python3/dist-packages/dbus/_dbus.py", line 100, in __new__
bus = BusConnection.__new__(subclass, bus_type, mainloop=mainloop)
File "/usr/lib/python3/dist-packages/dbus/bus.py", line 122, in __new__
bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NotSupported: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11
I did some research and saw suggestions to put a PATH= into my crontab, or to export $DISPLAY (I did this within the script by calling os.system('export DISPLAY=:0')) but neither resulted in any change...
You are in the right track. This behavior is because cron is run in a multiuser headless environment (think of it as running as root in a terminal without GUI, kinda), so he doesn't know to what display (X Window Server session) and user target to. If your application open, for example, windows or notification to some user desktop, then this problems is raised.
I suppose you edit your cron with crontab -e and the entry looks like this:
m h dom mon dow command
Something like:
0 5 * * 1 /usr/bin/python /home/foo/myscript.py
Note that I use full path to Python, is better if this kind of situation where PATH environment variable could be different.
Then just change to:
0 5 * * 1 export DISPLAY=:0 && /usr/bin/python /home/foo/myscript.py
If this still doesn't work you need to allow your user to control the X Windows server:
Add to your .bash_rc:
xhost +si:localuser:$(whoami)
If you want to set the DISPLAY from within python like you attempted with os.system('export DISPLAY=:0'), you can do something like this
import os
if not 'DISPLAY' in os.environ:
os.environ['DISPLAY'] = ':0'
This will respect any DISPLAY that users may have on a multi-seat box, and fall back to the main head :0.
If ur notify function, regardless of Python version or notify library, does not track the notify id [in a Python list] and deleting the oldest before the queue is completely full or on error, then depending on the dbus settings (in Ubuntu it's 21 notification max) dbus will throw an error, maximum notifications reached!
from gi.repository import Notify
from gi.repository.GLib import GError
# Normally implemented as class variables.
DBUS_NOTIFICATION_MAX = 21
lstNotify = []
def notify_show(strSummary, strBody, strIcon="dialog-information"):
try:
# full queue, delete oldest
if len(lstNotify)==DBUS_NOTIFICATION_MAX:
#Get oldest id
lngOldID = lstNotify.pop(0)
Notify.Notification.clear(lngOldID)
del lngOldID
if len(lstNotify)==0:
lngLastID = 0
else:
lngLastID = lstNotify[len(lstNotify) -1] + 1
lstNotify.append(lngLastID)
notify = Notify.Notification.new(strSummary, strBody, strIcon)
notify.set_property('id', lngLastID)
print("notify_show id %(id)d " % {'id': notify.props.id} )
#notify.set_urgency(Notify.URGENCY_LOW)
notify.show()
except GError as e:
# Most likely exceeded max notifications
print("notify_show error ", e )
finally:
if notify is not None:
del notify
Although it may somehow be possible to ask dbus what the notification queue max limit is. Maybe someone can help ... Improve this until perfection.
Plz
Cuz gi.repository complete answers are spare to come by.

Resources