How to subscribe string data on roslibpy? - python

SO i want to make a separate print in line 7 but it throws an error:
NameError: name 'message' is not defined
This is a working code without line 7,
i am not familiar with lambda, i cant make any return of "message['data']" so that i can print it separately
from __future__ import print_function
import roslibpy
client = roslibpy.Ros(host='localhost', port=9090)
client.run()
listener = roslibpy.Topic(client, '/chatter', 'std_msgs/String')
listener.subscribe(lambda message: print('Heard talking: ' + message['data']))
print("ANOTHER PRINT",message['data']) # ERROR
try:
while True:
pass
except KeyboardInterrupt:
client.terminate()

The subscribe method of a Topic (your listener) expects a function as argument (that is executed every time a message is published for that topic). The function used here is an "on-the-fly" lambda-function. An equivalent would be:
def print_heard_talkling(message):
print('Heard talking: ' + message['data'])
and then
listener.subscribe(print_heard_talking)
Now you see that message is just a placeholder for an argument in a function definition, not an actually accessible variable in your code. And that's exactly what the error message is telling you.

Related

How to fix "TypeError: StreamingClient.add_rules() missing 1 required positional argument: 'add'"

I'm trying to use tweepy to stream tweets and print the tweet body. When I run my code I get the following error:
TypeError: StreamingClient.add_rules() missing 1 required positional argument: 'add'
I've been reading the documentation and as far as I can tell I'm using the right syntax. The tweepy documentation uses this as an example of adding rules:
streaming_client.add_rules(tweepy.StreamRule("Tweepy"))
streaming_client.filter()
My code is as follows:
from multiprocessing.connection import Listener
import tweepy
from dotenv import load_dotenv
import os
load_dotenv()
# twitter api consumer key, consumer secret, access token, access secret.
btoken=os.getenv('btoken')
streamingClient = tweepy.StreamingClient(f"{btoken}")
class client(tweepy.StreamingClient):
def on_data(self, data):
print(data.text)
return(True)
def on_error(self, status):
print(status)
# Stops stream if rate limit is reached (status code 420)
if status == 420:
return False
if status == 401:
print("Authentication Error")
return False
# Placeholder tracking keyword
trackKey = "walmart"
rule = tweepy.StreamRule((f"{trackKey} lang:en -is:retweet place_country:US"))
client.add_rules(rule)
client.filter
I'm using tweepy v4.10.1
Any help would be greatly appreciated and thanks in advance.
The error is because, after declaring the class client, you hadn't created an object of this class, but attempted to call the method (inherited by this class from the class tweepy.StreamingClient) directly.
Normally class methods receive at least one argument: the object of the class, which is usually called self. This argument is usually passed automatically if the method is called like this objectName.method(). In this example, objectName is passed to the method method() as an argument self. But since you call this method not from an object of the class, but from the class itself, no first argument self is passed automatically. So the method believes that rule is that object of the class tweepy.StreamingClient (but hadn't had enough time yet to verify that and trigger a separate error) and complains about not receiving the second argument add.

python more generic solution for using getattr

say I have a test file with the following content:
def a():
print('this is a')
def b(x):
print(x)
and also a main file:
import test
def try_cmd(cmd, params):
try:
getattr(functions, cmd)(params)
except Exception as error:
print(error)
while True:
cmd = input('Enter cmd')
params = input('Enter params')
do_command(cmd, params)
The purpose of the code should be to try to call a function from a different file, with the user giving the function name and if needed params for it to take.
What happens is if the value of cmd is 'a' and parmas is a random string do_command will not work because function a doesn't take params. However if cmd will be 'b' and params will be say '5' it will work. How do I get around that without forcing a to take params and not actually using it.
As in my comment on your question, you should write your functions to accept *args and **kwargs, but if you insist on not using this convention, try this:
def try_cmd(cmd, params):
func = getattr(functions, cmd)
try:
func(params)
except TypeError:
func()
except Exception as error:
print(error)
In my opinion, accepting *args and **kwargs is the better practice compared to using exception handling to manage branching.
If you're already importing your "test" file, you can look at locals() and globals(), for example:
getattr(locals()['test'],'a')()

How to use an error handling function on another function?

I have a script, main.py, that runs a few functions from other scripts in the directory. I had the very unoriginal idea to send myself Slack notifications if specific functions ran correctly or failed, i.e. error handling. How would I use an error handling function located in a separate script - call the file slack_notifications.py - in my main.py script? This linked question starts to get at the answer, but it doesn't get to the calling the error function from somewhere else.
main.py script:
import scraper_file
import slack_notifications
# picture scrape variable
scrape_object = 'Barack Obama'
# scraper function called
scraped_image = scraper_file.pic_scrape(scrape_object)
# error handling?
slack_notifications.scrape_status(scrape_object)
I have tried embedding the called scraper function into the scrape_status() function, like this: scraped_image = (slack_notifications.scrape_status(scraper_file.pic_scrape(scrape_object))) and a few other ways, but that doesn't really tell you if it ran successfully, right?
slack_notifications.py script:
testing = "dunderhead"
def scrape_status(x):
# if this function gets a positive return from the scrape function
# if this function gets any error from the scrape function
try:
x
print(x + ' worked!')
except:
print(x + ' failed!')
if __name__ == '__main__':
scrape_status(testing)
Is there a way to do this? I have been going off these instructions, too.
Code in your first link works because function input() was moved from outside error_handler() into inside error_handler() and it is executed inside `try/except.
Your code always runs function outside error handler scrape_status() and it sends only result from executed function - so it never runs it inside try/except.
You would have to send separatelly function's name (without () and arguments) and separatelly arguments - and then it can run it inside try/except
def scrape_status(function, *args):
try:
function(*args)
print(function.__name__ + ' worked!')
except:
print(function.__name__ + ' failed!')
But it means you have to send function as
scrape_status(scraper_file.pic_scrape, scrape_object)
instead of
scrape_status( scraper_file.pic_scrape(scrape_object) )
and this is not readable - so I don't know if it is good idea to create universal error handler.
Minimal working example
def scrape_status(function, *args, **kwargs):
try:
result = function(*args, **kwargs) # some functions may return result
print(function.__name__ + ' worked!')
return result
except:
print(function.__name__ + ' failed!')
# ----
# this function needs `arg1, arg2` to work correctly
def testing(arg1, arg2):
print(arg1, arg2)
# --- main ---
print('--- test 1 ---')
# send funciton's name `testing` and argument(s) `'hello', 'world'`
scrape_status(testing, 'hello', 'world') # OK
print('--- test 2 ---')
# send funciton's name `testing` without argument(s)
scrape_status(testing) # ERROR
Result:
--- test 1 ---
hello world
testing worked!
--- test 2 ---
testing failed!
EDIT:
I created new example using decorator based on answers in question General decorator to wrap try except in python?. Now code use readableand it needs to use decorator #scrape_status('') only when function is defined
#scrape_status('')
def testing(arg1, arg2):
print(arg1, arg2)
Minimal working code:
def scrape_status():
def decorate(function):
def applicator(*args, **kwargs):
try:
result = function(*args, **kwargs)
print(function.__name__ + ' worked!')
return result
except:
print(function.__name__ + ' failed!')
return applicator
return decorate
# ----
# this function needs `arg1, arg2` to work correctly
#scrape_status()
def testing(arg1, arg2):
print(arg1, arg2)
# --- main ---
print('--- test 1 ---')
# send function's name `testing` and argument(s) `'hello', 'world'`
testing('hello', 'world') # OK
print('--- test 2 ---')
# send function's name `testing` without argument(s)
testing() # ERROR
As I remeber there could be some module(s) with more complex decorators - ie. one decorator could repeate function 3 times if it was raising error. It could be uses to repeate question in input() if answer was wrong, or repeate downloading file if there was problem to download it.

Python TypeError to give more information about arguments

I'm trying to implement a system to help the user when calling functions/methods.
I know the user can just help(function) to get some kind of a documentation provided by me but I wanted to reimplement the TypeError do it would also print that documentation if available.
For example:
Suppose I have:
def foo(bar):
''' Adds 1 to 'bar' and prints output '''
print 1+bar
And the user decide to call foo() (with no arguments)
It will raise a TypeError like this:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-624891b0d01a> in <module>()
----> 1 foo()
TypeError: foo() takes exactly 1 argument (0 given)
I would like it to also print the information from the help(foo) as well. i.e.:
foo(bar)
Adds 1 to 'bar' and prints output
Any ideas on how to do that? I realise I need
to detect the function that raised the TypeError
get the help text for that function
add it to the raised TypeError.
for 1) this seems to work:
import sys, traceback
# Get latest traceback information
tb = sys.exc_info()[-1]
stk = traceback.extract_tb(tb, 1)
# Extract called function and remove '()' - This actually limits functionality as the user might had inputed some extra arguments for example
fname = stk[0][-1]
fname = str(fname).split('()')[0]
for 2) and 3) and have no ideas on how to proceed... =/
Very much appreciated!
Edit for 3) I'm trying to override the default behaviour of TypeError, so far with no success.
I created a new MyError class just to test it and made:
import exceptions
exception.TypeError = MyError
but whenever the TypeError is raised, the original version comes up and not MyError
Edit 2 Ok, found out that I actually need to override the sys.excepthook method.
As a test, I created:
import sys
def handler(exc, value, tb):
print 'Oops'
sys.excepthook = handler
However, whenever a error occurs it still brings the original error and not the 'Oops' message. Also, sys.excepthook still returns the original message:
<bound method TerminalInteractiveShell.excepthook of <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x10f4320d0>>
I also tried overriding the IPython.terminal.interactiveshell.TerminalInteractiveShell.excepthook with no success.
Any ideas on how to keep going?
For number two:
>>> def foo(bar):
... '''Adds "1" to bar and prints output'''
... return 1 + bar
...
>>> print foo.__doc__
Adds "1" to bar and prints output
For number three, you may want to use the raise keyword to raise an error. You could probably use this with your first solution, but I've never used the traceback module so I can't help you there, sorry.
Ok, I finally got it!
This answer is valid for both python and ipython!
(I only tested with python 2.7, minor changes might be needed for python 3)
import sys
import traceback
import inspect
def get_args(func):
"""Get function arguments, varargs, kwargs and defaults.
This function will be called whenever a exception is raised."""
args, varargs, kwargs, defs = inspect.getargspec(func)
if defs is not None:
# If there are defaults, include them in the main argument list
for i in range(-len(defs), 0):
args[i] += ' = {}'.format(defs[i])
if varargs is not None:
# If the function accept extra arguments, include them at the end of the list.
args.append('*' + varargs)
if kwargs is not None:
# If the function accept extra keyworded arguments, include them at the end of the list.
args.append('**' + kwargs)
return args # args contain information about all the function arguments.
def value_handler(exc, value, tb):
"""Changes the default message to include additional information.
This handler will be called whenever an exception happens."""
args = list(value) # Value typically contains the error message.
if exc == TypeError and '()' in str(value):
# I want to change only TypeError from function calls.
func_name = str(value).split('()')[0] # Obtain the function name from the error message.
func = globals()[func_name] # Get function object from globals
func_doc = func.__doc__ # Function docstring
func_args = get_args(func) # Get function arguments
if func_doc is not None: # Add docstring to the message
args[0] += '\nDoc: \t{}'.format(func_doc)
args[0] += '\nArgs: \t' + '\n\t'.join(func_args) # Finally, add all the arguments to the message.
# Apply changes to the original error
value.args = args
return value
def custom_exc(shell, exc, value, tb, tb_offset=None):
"""Add aditional information and call original excepthook
This version works with iPython"""
value = value_handler(exc, value, tb) # Changes the error message
shell.showtraceback((exc, value, tb), tb_offset=tb_offset) # Raise the error with the new message (keeping traceback and other information).
def custom_python_exc(exc, value, tb):
"""Add aditional information and call original excepthook
This version works with regular python"""
value = value_handler(exc, value, tb) # Changes the error message
sys.__excepthook__(exc, value, tb) # Raise the error with the new message (keeping traceback and other information).
try:
__IPYTHON__ # Check if we're running iPython
except NameError:
# Not iPython enviroment, override excepthook
sys.excepthook = custom_python_exc
else:
# iPython enviroment, need to set custom excepthook
get_ipython().set_custom_exc((Exception,), custom_exc)
With this, one should be able to add more information to any Error being raised.

Callbacks and events in python

I'm making a bot to link IRC and DC (direct connect) together. There is an existing implementation in C++ I've been following, but it doesn't have all the feature's we're after.
I'm using an IRC library for python which is really well coded. I can register some callback handlers for various IRC events (specifically receiving a public message). This callback function is able to reference objects created in the main python execution from the thread within the IRC library.
Here are my callbacks:
def on_connect(connection, event):
connection.join(ircSettings['channel'])
def on_disconnect(connection, event):
sys.exit()
def on_pubmsg(connection, event):
hubClient.sendMessage(event.source.split('!')[0] + ': ' + event.arguments[0])
And here's how I set them up:
# Create the IRC client
ircClient = irc.client.IRC()
try:
ircConnection = ircClient.server().connect(ircSettings['server'], ircSettin$
except irc.client.ServerConnectionError, x:
print x
sys.exit()
# Set the IRC event handlers
ircConnection.add_global_handler("welcome", on_connect)
ircConnection.add_global_handler("pubmsg", on_pubmsg)
ircConnection.add_global_handler("disconnect", on_disconnect)
I really like this solution, as it makes for very tidy code (particularly in this example). However, I have no idea how to modify my DC library to generate these events.
The main point of interest is the callback's ability to reference the hubClient, which is created in the main python program like so:
# Create the DC client
hubClient = DC.DirectConnect(dcSettings)
hubClient.connect(dcSettings['hub'])
Initially, I passed a function pointer to my DC library to run whenever a message is received:
def messageHandler(nick, msg):
if nick is not ircSettings['nick']:
ircConnection.privmsg(ircSettings['channel'], nick + ': ' + msg)
dcSettings = {
'nick': 'dans_bot',
'sharesize': 10*1024**3, # 10GB
'ip': '0.0.0.0', # XXX: This might not matter, but needed for library
'hub': ('192.168.1.129', 411),
'handler': messageHandler
}
But I get the error:
NameError: global name 'ircConnection' is not defined
How can I set up my DC client to create a callback in a way that I can still reference these local (to the main execution) objects?
Edit: I added a declaration for 'ircConnection'.
I suppose ircConnection is a third party module. And a simple import of that module may solve this error of global nameircConnectionis not defined. Try import ircConnection in your main module
The only problem in your code is that the reference to ircConnection is first seen inside the try-except block and if it fails then the var will be None. Just write ircConnection = None before try.
# Create the IRC client
ircClient = irc.client.IRC()
ircConnection = None
try:
ircConnection = ircClient.server().connect(ircSettings['server'], ircSettin$
except irc.client.ServerConnectionError, x:
print x
sys.exit()
# Set the IRC event handlers
ircConnection.add_global_handler("welcome", on_connect)
ircConnection.add_global_handler("pubmsg", on_pubmsg)
ircConnection.add_global_handler("disconnect", on_disconnect)

Categories