Search code examples
pythondelpyvisa

Python package unloaded before __del__ is called


I am using pyvisa to communicate via USB with an instrument. I am able to control it properly. Since it is a high voltage source, and it is dangerous to forget it with high voltage turned on, I wanted to implement the __del__ method in order to turn off the output when the code execution finishes. So basically I wrote this:

import pyvisa as visa

class Instrument:
    def __init__(self, resource_str='USB0::1510::9328::04481179::0::INSTR'):
        self._resource_str = resource_str
        self._resource = visa.ResourceManager().open_resource(resource_str)
    
    def set_voltage(self, volts: float):
        self._resource.write(f':SOURCE:VOLT:LEV {volts}')
    
    def __del__(self):
        self.set_voltage(0)

instrument = Instrument()
instrument.set_voltage(555)

The problem is that it is not working and in the terminal I get

$ python3 comunication\ test.py 
Exception ignored in: <function Instrument.__del__ at 0x7f4cca419820>
Traceback (most recent call last):
  File "comunication test.py", line 12, in __del__
  File "comunication test.py", line 9, in set_voltage
  File "/home/superman/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py", line 197, in write
  File "/home/superman/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py", line 157, in write_raw
  File "/home/superman/.local/lib/python3.8/site-packages/pyvisa/resources/resource.py", line 190, in session
pyvisa.errors.InvalidSession: Invalid session handle. The resource might be closed.

I guess that what is happening is that pyvisa is being "deleted" before the __del__ method of my object is being called. How can I prevent this? How can I tell Python that pyvisa is "important" for objects of the Instrument class so it is not unloaded until all of them have been destroyed?


Solution

  • Finally I found my answer here using the package atexit. This does exactly what I wanted to do (based on my tests up to now):

    import pyvisa as visa
    import atexit
    
    class Instrument:
        def __init__(self, resource_str):
            self._resource = visa.ResourceManager().open_resource(resource_str)
            
            # Configure a safe shut down for when the class instance is destroyed:
            def _atexit():
                self.set_voltage(0)
            atexit.register(_atexit) # https://stackoverflow.com/a/41627098
        
        def set_voltage(self, volts: float):
            self._resource.write(f':SOURCE:VOLT:LEV {volts}')
        
    instrument = Instrument(resource_str = 'USB0::1510::9328::04481179::0::INSTR')
    instrument.set_voltage(555)
    

    The advantage of this solution is that it is user-independent, it does not matter how the user instantiates the Instrument class, in the end the high voltage will be turned off.