How to handle Python format errors in log message template? - python

I sometimes mess up the format in template strings for Python's logging module, for example:
import logging
logging.warning('%d', 1, 2) # BROKEN
In the console I can see a warning message (stack trace omitted):
--- Logging error ---
Traceback (most recent call last):
File ".../python3.7/logging/__init__.py", line 1034, in emit
msg = self.format(record)
File ".../python3.7/logging/__init__.py", line 880, in format
return fmt.format(record)
File ".../python3.7/logging/__init__.py", line 619, in format
record.message = record.getMessage()
File ".../python3.7/logging/__init__.py", line 380, in getMessage
msg = msg % self.args
TypeError: not all arguments converted during string formatting
Call stack:
...
Message: '%d'
Arguments: (1, 2)
However, neither my message nor this warning is sent to the log handler that would actually write it to a log file, send it to logstash etc.
I know that I can find these bugs with pylint using logging-too-few-args (E1206) and logging-too-many-args (E1205) however I still would prefer some kind of runtime fallback in case one of them slips through.
So unless one monitors stderr from outside of Python these kind of bugs are easy to overlook.
Is there a way to still log a message with a marker and the essentials parts, for example:
[LogBug] message='%d', arguments=(1, 2)
That way, the original information would still be preserved and one could periodically scan log files for the [LogBug] marker.

Could you use try/except, maybe? Possibly using re to extract the message and arguments from the exception message
import re
try:
logging.warning('%d', 1, 2)
except TypeError as e:
message = re.search(r'Message: ([^\n]*)[\n$]', e.message).group(1)
arguments = re.search(r'Arguments: ([^\n]*)[\n$]', e.message).group(1)
logging.error("[LogBug] message=%s, arguments=%s",message, arguments)
# if you want, you could re-raise this exception
If you do go with this solution you might also have to accommodate for error handling in case the TypeError isn't quite in the format you expect and thus the regex won't work and will raise an AttributeError when you try to call .group() on it.
There's no limit to how deep you could go with this, potentially, but if you make really really sure that this check works properly then it might be able to catch the others.
Alternatively, instead of trusting the logging package to do argument insertion, if you're using a recent version of python you could use format strings instead, which leaves no room for that kind of ambiguity:
i, j = 1, 2
logging.warning(f"{i}")

Related

In Python, how can the traceback module be used to find where an exception is generated?

I'm working on a convoluted FOSS project that utilizes GTK+3. When a flow graph is generated and attempted to run it, it generates the following error:
'Page' object has no attribute 'get_flow_graph'
There are 30 different files that have the generic "...object has no attribute..." exception listed in the code, and there are 4 files that call the function get_flow_graph().
So what I want to figure out is which of the 30 files that generate that particular error message is being executed, and preferably which of the 4 files with the function are causing the error in the first place.
I'm trying to use Python's traceback module to figure out where, specifically, the exception is being generated. I think I figured out the file that is calling the function that ultimately errors out, but I can't seem to get the traceback module to provide much more.
For example, if I wrap the function like this:
try:
fg = self.page.get_flow_graph()
except Exception:
traceback.print_exc()
then I just get
File "<redacted>", line 66, in _popen
fg = self.page.get_flow_graph()
AttributeError: 'Page' object has no attribute 'get_flow_graph'
'Page' object has no attribute 'get_proc'
as the output. So I get the original exception but a new get_proc error that doesn't help me but is obviously associated with trying to use traceback.
Maybe I'm not putting the trace in the correct file/location, or maybe I'm asking too much, but how should I write it to figure out the actual stack trace for the original AttributeError?
Does using
except AttributeError as e:
print(e.__traceback__.tb_lineno)
print(e.__traceback__.tb_frame)
instead, helps you further? (really asking, not being ironic)

Error for word2vec with GoogleNews-vectors-negative300.bin

the version of python is 3.6
I tried to execute my code but, there are still some errors as below:
Traceback (most recent call last):
File
"C:\Users\tmdgu\Desktop\NLP-master1\NLP-master\Ontology_Construction.py",
line 55, in
, binary=True)
File "E:\Program
Files\Python\Python35-32\lib\site-packages\gensim\models\word2vec.py",
line 1282, in load_word2vec_format
raise DeprecationWarning("Deprecated. Use gensim.models.KeyedVectors.load_word2vec_format instead.")
DeprecationWarning: Deprecated. Use
gensim.models.KeyedVectors.load_word2vec_format instead.
how to fix the code? or is the path to data wrong?
This is just a warning, not a fatal error. Your code likely still works.
"Deprecation" means a function's use has been marked by the authors as no longer encouraged.
The function typically still works, but may not for much longer – becoming unreliable or unavailable in some future library release. Often, there's a newer, more-preferred way to do the same thing, so you don't trigger the warning message.
Your warning message points you at the now-preferred way to load word-vectors of that format: use KeyedVectors.load_word2vec_format() instead.
Did you try using that, instead of whatever line of code (not shown in your question) that you were trying before seeing the warning?

Create SAFEARRAY of Strings in Python

I'm trying to call a COM method that requires a SafeArray of Strings to be passed as reference, which is then filled up with the method results. This is the code in VBA, which works flawlessly:
dimr RC as New RAS41.HECRASController
RC.Project_Open "c:\myProj.prj"
dim numMessages as Long
dim messages() as String
RC.Compute_CurrentPlan( numMessages, messages())
Now, I'm trying to do the same from with Python 3.4, using the win32com module. However, I'm stuck at trying to create the second parameter with the correct type, which according to combrowse.py should be "Pointer SafeArray String".
This was my first attempt:
import win32com
RC = win32com.client.Dispatch("RAS41.HECRASController")
RC.Project_Open("c:\\myProj.prj")
numMessages = 0
messages = []
RC.Compute_CurrentPlan(numMessages, messages)
I also tried constructing that variable as
messages = win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_BSTR, [])
but it didn't work either. Error messages look like this:
Traceback (most recent call last):
File "<pyshell#101>", line 1, in <module>
print(o.Compute_CurrentPlan(1,b))
File "<COMObject RAS41.HECRASController>", line 3, in Compute_CurrentPlan
File "C:\Python34\lib\site-packages\win32com\client\dynamic.py", line 282, in _ApplyTypes_
result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
TypeError: Objects for SAFEARRAYS must be sequences (of sequences), or a buffer object.
Make sure that you python variables are in the right format (Long and String). Try to use something like the following to get the variable types in shape:
messages = ['']
RC.Compute_CurrentPlan(long(numMessages), messages)
To be more flexible with your program you should check the variable types prior to the win32 call.
I realize this is an old question, but I ran into this issue and wanted to share the resolution. I was having issues defining the type of data for the first two arguments, but simply setting them to None works and your number of messages and compute messages are reported (I checked by assigning text = hec.Compute_CurrentPlan(None, None, True) and then print(test)). The third argument is Blocking Mode, set to True, meaning that the RAS computation will complete before moving to the next line of code. I am using Python 3.10.4 and HEC-RAS version 6.3.
import win32com.client
hec = win32com.client.Dispatch('RAS630.HECRASController')
hec.Project_Open(r"C:\myproj.prj")
hec.ShowRAS()
hec.Compute_CurrentPlan(None, None, True)
hec.QuitRAS()

python kivy async storage

I use Synchronous JsonStorage for my app and want to switch to Asynchronous.
My sync call was:
store.exists(store_index)
My not working async call is:
def callback_function(store,key,result):
print "exists:",result
store.exists(store_index, callback=callback_function)
this async call raises the following Exception:
store.exists(key=store_index,callback=callback_function)
TypeError: exists() got an unexpected keyword argument 'callback'
I've also tried this:
store.async_exists(store_index, callback=callback_function)
But this raised:
File "main.py", line 199, in __init__ store.async_exists(key=store_index,callback=colorButtonCallback)
File "/home/mike/Dokumente/py-spielwiese/venv/local/lib/python2.7/sitepackages/kivy/storage/__init__.py", line 152, in async_existskey=key, callback=callback)
TypeError: _schedule() got multiple values for keyword argument 'callback'
what am I doing wrong?
This is a bug in Kivy. Your last attempt was pretty much correct (equivalent to the code in #Anzel's answer, though #Anzel's code is a better way to write the same thing). But in the end you will still get the error thrown from _schedule. I've just submitted a PR to fix this in kivy-dev.
async_exists takes callback as arguments, then the key so try changing to:
store.async_exists(callback_function, store_index)
You can read async_exists to see the details.
Hope this helps.

Raise error with only last line of stack trace

It seems to me that commonly you may want a Python program to print (usually to standard error) only the last line of the stack trace, e.g.:
IOError: Error reading file 'b'plunk'': b'failed to load external entity "plunk"'
I've got this solution:
def print_error(ex:Exception, file) -> None:
print('{0}: {1}'.format(ex.__class__.__name__, ex), file=file)
Example usage:
try:
crash in some manner
except Exception as ex:
print_error(ex, sys.stderr)
There is nothing particularly wrong with this, but this feature seems so basic that I can't help but wonder if there isn't a simpler way to do it. Am I missing something? Or is this a good solution?
I don't know if there's a better way of doing it, but considering that it's all of 6 lines of code and I can't imagine it takes long to carry out at all, I don't think that you need a better one.

Categories