Is there a way I can access the attribute in an Attribute Error without parsing the string? - error-handling

My python version is 3.6
I am trying to give a more helpful message on attribute errors in a CLI framework that I am building. I have the following code
print(cli_config.test_exension_config.input_menu)
Which produces the error AttributeError: 'CLIConfig' object has no attribute 'test_exension_config'
Perfect, however now I want to give a recommendation on the closest attribute match as the attributes are dynamically created from a yaml file.
test_extension:
input_menu: # "InputMenuConfig_instantiation_test"
var:
So the closest attribute match would be test_extension_config.
Below is me catching the error and about to give a recommendation.
def __getattribute__(self, name) -> Any:
try:
return super().__getattribute__(name)
except AttributeError as ae:
# chance to handle the attribute differently
attr = get_erroring_attr(ae)
closest_match = next(get_close_matches(attr, list(vars(self).keys())))
if closest_match: # probably will have some threshold based on 'edit distance'
return closest_match
# if not, re-raise the exception
raise ae
I am wanting to just receive the attribute
I can parse the args of AttributeError but I wanted to know if there was another way to access the actual attribute name that is erroring without parsing the message.
In other words, in the last code block I have a method get_erroring_attr(ae) that takes in the AttributeError.
What would be the cleanest definition of def get_erroring_attr(ae) that will return the erroring attribute?
UPDATE:
So I did this and it works. I would just like to remove parsing as much as possible.
def __getattribute__(self, name) -> Any:
try:
return super().__getattribute__(name)
except AttributeError as ae:
# chance to handle the attribute differently
attr = self.get_erroring_attr(ae)
closest_match = next(match for match in get_close_matches(attr, list(vars(self).keys())))
if closest_match: # probably will have some threshold based on 'edit distance'
traceback.print_exc()
print(CLIColors.build_error_string(f"ERROR: Did you mean {CLIColors.build_value_string(closest_match)}?"))
sys.exit()
# if not, re-raise the exception
raise ae
def get_erroring_attr(self, attr_error: AttributeError):
message = attr_error.args[0]
_, error_attr_name = self.parse_attr_error_message(message)
return error_attr_name
def parse_attr_error_message(self, attr_err_msg: str):
parsed_msg = re.findall("'([^']*)'", attr_err_msg)
return parsed_msg
Which produces

Related

Lego-EV3: How to fix EOFError when catching user-input via multiprocessing?

Currently, I am working with a EV3 lego robot that is controlled by several neurons. Now I want to modify the code (running on
python3) in such a way that one can change certain parameter values on the run via the shell (Ubuntu) in order to manipulate the robot's dynamics at any time (and for multiple times). Here is a schema of what I have achieved so far based on a short example code:
from multiprocessing import Process
from multiprocessing import SimpleQueue
import ev3dev.ev3 as ev3
class Neuron:
(definitions of class variables and update functions)
def check_input(queue):
while (True):
try:
new_para = str(input("Type 'parameter=value': "))
float(new_para[2:0]) # checking for float in input
var = new_para[0:2]
if (var == "k="): # change parameter k
queue.put(new_para)
elif (var == "g="): # change parameter g
queue.put(new_para)
else:
print("Error". Type 'k=...' or 'g=...')
queue.put(0) # put anything in queue
except (ValueError, EOFError):
print("New value is not a number. Try again!")
(some neuron-specific initializations)
queue = SimpleQueue()
check = Process(target=check_input, args=(queue,))
check.start()
while (True):
if (not queue.empty()):
cmd = queue.get()
var = cmd[0]
val = float(cmd[2:])
if (var == "k"):
Neuron.K = val
elif (var == "g"):
Neuron.g = val
(updating procedure for neurons, writing data to file)
Since I am new to multiprocessing there are certainly some mistakes concerning taking care of locking, efficiency and so on but the robot moves and input fields occur in the shell. However, the current problem is that it's actually impossible to make an input:
> python3 controller_multiprocess.py
> Type 'parameter=value': New value is not a number. Try again!
> Type 'parameter=value': New value is not a number. Try again!
> Type 'parameter=value': New value is not a number. Try again!
> ... (and so on)
I know that this behaviour is caused by putting the exception of EOFError due to the fact that this error occurs when the exception is removed (and the process crashes). Hence, the program just rushes through the try-loop here and assumes that no input (-> empty string) was made over and over again. Why does this happen? - when not called as a threaded procedure the program patiently waits for an input as expected. And how can one fix or bypass this issue so that changing parameters gets possible as wanted?
Thanks in advance!

Using try except block in Odoo server action

I'm defining a server action in Odoo 10. Within this action I am trying to use the following code:
for data in datas:
try:
inventory_level = int(data[context['inventory_level_column']].strip())
except TypeError:
continue
However, I receive an error:
ValueError: <type 'exceptions.NameError'>: "name 'TypeError' is not defined"
Is it not possible to catch errors within the context of an Odoo server action? Why is TypeError not defined?
The code written on a server action is passed through to safe_eval method. There the __builtins__ are stripped and replaced (thus the exceptions.NameError class is removed.
You can check this behaviour on odoo/tools/safe_eval.py on the definition of safe_eval method. See globals_dict['__builtins__'] = _BUILTINS where _BUILTINS does not contain this exception.
Exception is the parent class of all exception classes, so if you want to catch exception then just specify top most parent class in except and in e you will get error message.
for data in datas:
try:
inventory_level = int(data[context['inventory_level_column']].strip())
except Exception ,e:
print e
pass

What's the equivalent of moment-yielding (from Tornado) in Twisted?

Part of the implementation of inlineCallbacks is this:
if isinstance(result, Deferred):
# a deferred was yielded, get the result.
def gotResult(r):
if waiting[0]:
waiting[0] = False
waiting[1] = r
else:
_inlineCallbacks(r, g, deferred)
result.addBoth(gotResult)
if waiting[0]:
# Haven't called back yet, set flag so that we get reinvoked
# and return from the loop
waiting[0] = False
return deferred
result = waiting[1]
# Reset waiting to initial values for next loop. gotResult uses
# waiting, but this isn't a problem because gotResult is only
# executed once, and if it hasn't been executed yet, the return
# branch above would have been taken.
waiting[0] = True
waiting[1] = None
As it is shown, if in am inlineCallbacks-decorated function I make a call like this:
#inlineCallbacks
def myfunction(a, b):
c = callsomething(a)
yield twisted.internet.defer.succeed(None)
print callsomething2(b, c)
This yield will get back to the function immediately (this means: it won't be re-scheduled but immediately continue from the yield). This contrasts with Tornado's tornado.gen.moment (which isn't more than an already-resolved Future with a result of None), which makes the yielder re-schedule itself, regardless the future being already resolved or not.
How can I run a behavior like the one Tornado does when yielding a dummy future like moment?
The equivalent might be something like a yielding a Deferred that doesn't fire until "soon". reactor.callLater(0, ...) is generally accepted to create a timed event that doesn't run now but will run pretty soon. You can easily get a Deferred that fires based on this using twisted.internet.task.deferLater(reactor, 0, lambda: None).
You may want to look at alternate scheduling tools instead, though (in both Twisted and Tornado). This kind of re-scheduling trick generally only works in small, simple applications. Its effectiveness diminishes the more tasks concurrently employ it.
Consider whether something like twisted.internet.task.cooperate might provide a better solution instead.

Django traceback on queries

I want a traceback from every query executed during a request, so I can find where they're coming from and reduce the count/complexity.
I'm using this excellent snippet of middleware to list and time queries, but I don't know where in the they're coming from.
I've poked around in django/db/models/sql/compiler.py but apparent form getting a local version of django and editing that code I can't see how to latch on to queries. Is there a signal I can use? it seems like there isn't a signal on every query.
Is it possible to specify the default Manager?
(I know about django-toolbar, I'm hoping there's a solution without using it.)
An ugly but effective solution (eg. it prints the trace on all queries and only requires one edit) is to add the following to the bottom of settings.py:
import django.db.backends.utils as bakutils
import traceback
bakutils.CursorDebugWrapper_orig = bakutils.CursorWrapper
def print_stack_in_project():
stack = traceback.extract_stack()
for path, lineno, func, line in stack:
if 'lib/python' in path or 'settings.py' in path:
continue
print 'File "%s", line %d, in %s' % (path, lineno, func)
print ' %s' % line
class CursorDebugWrapperLoud(bakutils.CursorDebugWrapper_orig):
def execute(self, sql, params=None):
try:
return super(CursorDebugWrapperLoud, self).execute(sql, params)
finally:
print_stack_in_project()
print sql
print '\n\n\n'
def executemany(self, sql, param_list):
try:
return super(CursorDebugWrapperLoud, self).executemany(sql, param_list)
finally:
print_stack_in_project()
print sql
print '\n\n\n'
bakutils.CursorDebugWrapper = CursorDebugWrapperLoud
Still not sure if there is a more elegant way of doing this?
Django debug toolbar will tell you what you want with spectacular awesomeness.

What is the standard code to check for valid/existing id in database-backed view in web2py?

In web2py, suppose I have the following url:
www.example.com/customer/view/1
Which is backed by a view() function in the customer controller and displays the customer with id of 1 from a database.
In the view function I want to handle the following error cases:
No argument is passed (/customer/view/) -> raise 404
A non-integer argument is passed (/customer/view/apple) -> raise 404
An integer argument is passed but does not exist in database (/customer/view/999999) -> raise 404
An application error occurs such as cannot connect to database -> raise 500 exception
What is the standard/canonical/correct way to handle these cases in the controller function in way that returns the proper HTTP error? It's such a common scenario that I'd like to do it in a correct and succinct way every time.
This almost works except it doesn't distinguish between id not valid/existing errors and any other errors:
def view():
try:
customer = db(db.customer.id==request.args(0, cast=int)).select()[0]
except:
raise HTTP(404, 'Cannot find that customer')
return dict(customer=customer)
def view():
id = request.args(0, cast=int, otherwise=lambda: None)
customer = db(db.customer.id == id).select().first()
if not customer:
raise HTTP(404, 'Cannot find that customer' if id
else 'Missing/invalid customer ID')
return dict(customer=customer)
If the cast fails in request.args(), by default it will raise its own HTTP(404), but you won't have control over the message. So, you can instead use the otherwise argument to return None in that case. The database query will then return None if the arg is missing or a non-integer, or if the customer is not found in the database.