Is there a way to "capture" the error message printed out by sys.exit() during testing and compare it to another string?
Some background: in the Python script I'm working on, I've been using sys.exit() to print out a more specific error message (and avoid the traceback which usually arises).
try:
do_something()
except IOError:
sys.exit('my error message')
Other times, I just use the regular message (esp. with ConfigParser):
except ConfigParser.NoSectionError as err:
sys.exit(err)
I would like to capture the error message there and perhaps use an assertIs(err, 'my intended error message') to compare.
The script I'm working on has both Python 2 & 3 versions, so I'd appreciate some examples if there are differences between them for doing this.
sys.exit dosn't do anythin else then raising SystemExit, which you can catch like any other exception.
The example about the context manager just shows you how you can use it to get the exception which was thrown in the with block if you need to perform checks on it.
In the case of SystemExit this would look like this:
with self.assertRaises(SystemExit) as cm:
sys.exit('some message')
self.assertEqual(cm.exception.args[0], 'some message')
...
assertEqual is part of unittest's TestCase, so won't help you if you're not using it. You would have to shell off the process to see what happens. Why not write some unit tests instead?
Check out the examples in traceback and the provided exit block.
https://docs.python.org/3/library/traceback.html
Python 3.6.6 |Anaconda custom (64-bit)| (default, Jun 28 2018, 11:27:44) [MSC v.1900 64 bit (AMD64)]
import traceback
if __name__ == '__main__':
try:
main()
except Exception as e:
with open('death.log', 'w') as fh:
big_stack = '\n'.join(traceback.format_stack())
print(big_stack)
print(repr(e))
fh.write(big_stack)
fh.write(repr(e))
Related
Consider the following code snippets and the resulting printouts from the console:
Snippet 1
Behaves just fine. Everything is hunkydory.
Code
try:
raise ValueError()
finally:
print(3)
Console Output
Traceback (most recent call last):
File "D:/FILE_MGMT_PYTHON/fbzdfbhedrh.py", line 5, in <module>
raise ValueError()
ValueError
3
Snippet 2
Also behaves just fine. Everything is hunkydory.
Code
try:
raise ValueError()
except type("", (Exception,), dict()):
print("this is not supposed to print")
finally:
print(3)
Console Output
3
Traceback (most recent call last):
File "D:/FILE_MGMT_PYTHON/fbzdfbhedrh.py", line 14, in <module>
raise ValueError()
ValueError
Snippet 3
I don't understand why the following does not result in an unhandled exception being printed to the console:
Code
def e():
try:
raise ValueError()
x = y + z
L = [1, 2, 3]
print(L[9999999999999999999999999999])
except type("", (Exception,), dict()) as exc:
print("this is not supposed to print")
return "strawberries " + src(exc)
finally:
return "finally"
print(e())
Console Output
finally
Process finished with exit code 0
Build:
------------------
System Information
------------------
Time of this report: 10/25/2019, 07:22:01
Machine name: DESKTOP-U5M46TJ
Machine Id: {403D9006-3BF1-4C4B-AAF5-2AD795E00738}
Operating System: Windows 10 Pro 64-bit (10.0, Build 18362) (18362.19h1_release.190318-1202)
Language: English (Regional Setting: English)
System Manufacturer: System manufacturer
System Model: System Product Name
BIOS: BIOS Date: 10/31/12 20:41:07 Ver: 36.02 (type: BIOS)
Processor: Intel(R) Core(TM) i5-2500K CPU # 3.30GHz (4 CPUs), ~3.3GHz
Memory: 4096MB RAM
Available OS Memory: 4064MB RAM
Page File: 12606MB used, 3744MB available
Windows Dir: C:\Windows
DirectX Version: DirectX 12
DX Setup Parameters: Not found
User DPI Setting: 96 DPI (100 percent)
System DPI Setting: 144 DPI (150 percent)
DWM DPI Scaling: UnKnown
Miracast: Available, with HDCP
Microsoft Graphics Hybrid: Not Supported
DirectX Database Version: Unknown
DxDiag Version: 10.00.18362.0387 64bit Unicode
------------------
IDE Information
------------------
PyCharm 2019.1.3 (Community Edition)
Build #PC-191.7479.30, built on May 29, 2019
JRE: 11.0.2+9-b159.60 amd64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.0
------------------
Python Information
------------------
print(sys.version)
print(sys.version_info)
print(sys.hexversion)
3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:21:23) [MSC v.1916 32 bit (Intel)]
sys.version_info(major=3, minor=8, micro=0, releaselevel='final', serial=0)
50856176
Returning from a finally block discards the exception. The function can't return and raise the exception, and you're telling it that you want it to return.
The exception is going to be thrown, but before the try statement can be exited, the finally block needs to be run. Your finally block says "regardless of whatever you thought you were doing, I want to return". So the exception is discarded.
Similary, if you were returning from a try block, and the finally block raised an exception, the return value would be discarded.
See Defining Clean-up Actions in the Python docs:
If an exception occurs during execution of the try clause, the exception may be handled by an except clause. If the exception is not handled by an except clause, the exception is re-raised after the finally clause has been executed.
...
If a finally clause includes a return statement, the finally clause’s return statement will execute before, and instead of, the return statement in a try clause.
The reason is your except statement.
>>> type("", (Exception,), dict())
<class '__main__.'>
And since a "ValueError" is not an exception of that type it won't catch the exception and will only go into the finally statement.
Change the except statement to:
except ValueError as e:
pass
# or this:
except Exception as e:
pass
I am a new bee to Robot and having issue with my script execution part .
My case is failing correctly as per python's logic . However robot is still passing it . I am quite certain that i have missed out some thing on robot script end like some keyword maybe.
Any inputs and suggestion are more than welcome
I tried Run keywork if but i do not know how to use it . Please see both my python and robot code below
robot script:
*** Settings ***
Documentation TC2
Library exception
#Library common_functions
*** Keywords ***
Run Keyword if 'FAIL' in $output
*** Test Cases ***
TEST CASE TWO
validation
python code:
import os
import re
def validation():
try:
if 'loopback is up, line protocol is up' in open('1.txt').read():
print("string found")
else:
print ("string not found")
raise Exception("string not found")
except:
cleanup()
def cleanup():
print("cleanup is in progress")
validation()
Expected result :
Python script showing :
string not found
cleanup is in progress
Robot should show fail
Actual output :
Python script showing :
string not found
cleanup is in progress
Robot script showing PASS
Do not catch the exception in your Python code, let that fail the robot execution. You can do the cleanup in the test teardown section.
import os
import re
def validation():
if 'loopback is up, line protocol is up' in open('1.txt').read():
print("string found")
else:
print ("string not found")
raise RuntimeError("string not found")
def cleanup():
print("cleanup is in progress")
*** Settings ***
Documentation TC2
Library exception
*** Test Cases ***
TEST CASE TWO
validation
[Teardown] cleanup
(First guess, knowing nothing about the robot framework, but about to dive in): Your robot script is looking for FAIL in $output. Your output contains string not found cleanup is in progress (perhaps with different formatting, as you've printed twice without specifying to not print newlines). There is no FAIL in your output. Perhaps you meant to have
Run Keyword if 'string not found' in $output
in your robot script, or
print("FAIL: string not found")
in your validation code?
UPDATE: Now that I have spent some time with the documentation (they don't make it easy to learn to do something simple), I can say that Bence Kaulics answer is mostly correct, perhaps depending on what version you're running. For me, the test as shown in his answer fails with an IndentationError due to the try without except. Here's what I have that works (as in, fails for the expected reason, mostly):
.robot file:
*** Settings ***
Documentation TC2
Library exception.py
*** Test Cases ***
TEST CASE TWO
validation
exception.py:
def validation():
try:
if 'loopback is up, line protocol is up' in open('1.txt').read():
result = 'string found'
else:
result = 'string not found'
raise RuntimeError(result)
except Exception as e:
result = 'string not found'
raise RuntimeError(result)
finally:
print(result)
cleanup()
def cleanup():
print("cleanup is in progress")
With the above, I get the output:
(robottestframework) ~/s/l/robottestframework> robot -L TRACE keyword_driven.robot
==============================================================================
Keyword Driven :: TC2
==============================================================================
TEST CASE TWO | FAIL |
string not found
------------------------------------------------------------------------------
Keyword Driven :: TC2 | FAIL |
1 critical test, 0 passed, 1 failed
1 test total, 0 passed, 1 failed
==============================================================================
Note that this changes the semantics of what you are testing: instead of checking the value of stdout, you are running a keyword imported from a library, and failing if there is an exception raised. IMO, that's a better way to go than checking stdout. However, if what you really need to do is check the value of stdout, you'll need something more like:
.robot file:
*** Settings ***
Documentation TC2
Library Process
Suite Teardown Terminate All Processes kill=True
*** Test Cases ***
TEST CASE TWO
${result} = Run Process python exception.py
should not contain ${result.stdout} string not found
exception.py file:
def validation():
result = ''
try:
if 'loopback is up, line protocol is up' in open('1.txt').read():
result = 'string found'
else:
result = 'string not found'
raise RuntimeError(result)
except Exception as e:
result = 'string not found'
finally:
print(result)
cleanup()
return result
def cleanup():
print("cleanup is in progress")
validation()
With these changes, I get:
(robottestframework) ~/s/l/robottestframework> robot -L TRACE keyword_driven.robot
==============================================================================
Keyword Driven :: TC2
==============================================================================
TEST CASE TWO | FAIL |
'string not found
cleanup is in progress' contains 'string not found'
------------------------------------------------------------------------------
Keyword Driven :: TC2 | FAIL |
1 critical test, 0 passed, 1 failed
1 test total, 0 passed, 1 failed
==============================================================================
I have a program that accepts coordinates over UDP, moves some equipment around, and then replies when the job is done.
I seem to have the same issue as this guy:
Python sendto doesn't seem to send
My code is here:
import socket
import struct
import traceback
def main():
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind(('',15000))
reply_sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
try:
data,addr = sock.recvfrom(1024)
if data is not None:
try:
coords = struct.unpack('>dd',data)
#Stuff happens here
print(f'moved probe to {coords}')
reply_sock.sendto(bytearray.fromhex('B'),('10.0.0.32',15001))
except:
traceback.print_exc()
try:
reply_sock.sendto(bytearray.fromhex('D'),('10.0.0.32',15001))
except:
traceback.print_exc()
break
except:
pass
The program behaves as though the sendto call is just passed over; it accepts the packet, executes the print statements, and loops back around (It can execute the loop multiple times but never replies). I'm looking at wireshark and no packets are ever sent outbound. No errors are ever thrown.
Any ideas why this is happening?
From the documentation:
The string must contain two hexadecimal digits per byte, with ASCII
whitespace being ignored.
So this happens:
$ python3
Python 3.6.6 (default, Sep 12 2018, 18:26:19)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bytearray.fromhex('B')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: non-hexadecimal number found in fromhex() arg at position 1
>>>
Try this:
reply_sock.sendto(bytearray.fromhex('0B'),('10.0.0.32',15001))
if that's what you mean.
Note that your except is catching all the exceptions, not just the ones you're expecting, so you're not seeing the error you're causing. Consider using something like except OSError here instead.
Also, think about reducing the amount of code in your try sections:
coords = struct.unpack('>dd',data)
#Stuff happens here
print(f'moved probe to {coords}')
bytes_to_send = bytearray.fromhex('0B')
try:
reply_sock.sendto(bytes_to_send,('10.0.0.32',15001))
except IOError as e1:
print(e1)
traceback.print_exc()
bytes_to_send = bytearray.fromhex('0D')
try:
reply_sock.sendto(bytes_to_send,('10.0.0.32',15001))
except IOError as e2:
print(e2)
traceback.print_exc()
break
This way you're protecting only the code which you want to.
I am writing python code to install all the library packages required by my program in the linux environment.So the linux may contain python 2.7 or 2.6 or both so I have developed a try and except block codes that will install pip packages in linux. Try block code consists of python 2.7 version pip install and Catch block contains python 2.6 version pip install. My Problem is the peace of code is working fine, when i tried to install pandas in python 2.6 its getting me some errror. I want to catch that exception. Can you please tell me how to improve my try except blocks to catch that exception
required_libraries = ['pytz','requests','pandas']
try:
from subprocess import check_output
pip27_path = subprocess.check_output(['sudo','find','/','-name','pip2.7'])
lib_installs = [subprocess.call((['sudo',pip27_path.replace('\n',''),'install', i])) for i in required_libraries]
except:
p = subprocess.Popen(['sudo','find','/','-name','pip2.6'], stdout=subprocess.PIPE);pip26_path, err = p.communicate()
lib_installs = [subprocess.call((['sudo',pip26_path.replace('\n',''),'install', i])) for i in required_libraries]
You can catch several exceptions using one block. Let's use Exception and ArithmeticError for exceptions.
try:
# Do something
print(q)
# Catch exceptions
except (Exception, ArithmeticError) as e:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
print (message)
If you need to catch several exceptions and handle each one on its own then you'd write an except statement for each one.
try:
# Do something
print(q)
# Catch exceptions
except Exception as e:
print (1)
except ArithmeticError as e:
print (2)
# Code to be executed if the try clause succeeded with no errors or no return/continue/break statement
else:
print (3)
You can also check if the exception is of type "MyCustomException" for example using if statements.
if isinstance(e, MyCustomException):
# Do something
print(1)
As for your problem, I suggest splitting the code into two functions.
install(required_libraries)
def install(required_libraries, version='pip2.7'):
# Perform installation
try:
from subprocess import check_output
pip27_path = subprocess.check_output(['sudo','find','/','-name', version])
lib_installs = [subprocess.call((['sudo',pip27_path.replace('\n',''),'install', i])) for i in required_libraries]
except Exception as e:
backup(required_libraries)
def backup(required_libraries, version='pip2.6'):
try:
p = subprocess.Popen(['sudo','find','/','-name',version]], stdout=subprocess.PIPE);pip26_path, err = p.communicate()
lib_installs = [subprocess.call((['sudo',pip26_path.replace('\n',''),'install', i])) for i in required_libraries]
except Exception as e:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
print (message)
#Handle exception
Note: I didn't test this, I'm no expert as well so I hope I can help.
Useful links:
Built-in Exceptions
Errors and Exceptions
Compound statements
The documentation for the emit() function of Python 2.x SysLogHandler says:
The record is formatted, and then sent to the syslog server. If
exception information is present, it is not sent to the server.
...why? The source doesn't tell me much. Did I miss something?
However, when I try the following code:
import logging
from logging.handlers import SysLogHandler
rootlogger = logging.getLogger()
rootlogger.setLevel(logging.DEBUG)
syslogh = SysLogHandler('/dev/log')
rootlogger.addHandler(syslogh)
# to see what's happening too
consoleh = logging.StreamHandler()
rootlogger.addHandler(consoleh)
# this appears in the log
rootlogger.info('foobar')
try:
a = 42
a / 0
except ZeroDivisionError as e:
rootlogger.exception('myException!: {}'.format(e))
It does write the following to syslog:
May 7 16:25:59 localhost foobar
May 7 16:25:54 localhost myException!: integer division or modulo by
zero#012Traceback (most recent call last):#012 File "syslogonly.py",
line 16, in #012 a / 0#012ZeroDivisionError: integer
division or modulo by zero
I am using rsyslog 8.4.2 on a Debian wheezy system.
Also, I am aware there are issues with multiline, but is this related?
You're not calling Logger.emit(). You're calling Logger.exception(), which according to the documentation, always adds exception info to the message:
Logs a message with level ERROR on this logger. The arguments are interpreted as for debug(), except that any passed exc_info is not inspected. Exception info is always added to the logging message. This method should only be called from an exception handler.
I didn't dive into the source code to determine what calls emit() and under what circumstances, however.