Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception AttributeError: "'Context' object has no attribute '_libudev'" in <bound method Context.__del__ of <pyudev.core.Context object at 0x6b922870>> ignored #421

Closed
rag2180 opened this issue Mar 22, 2021 · 4 comments · Fixed by #439
Assignees
Projects

Comments

@rag2180
Copy link

rag2180 commented Mar 22, 2021

I am getting the following exception in my code randomly. This error was captured using sentry.

Exception AttributeError: "'Context' object has no attribute '_libudev'" in <bound method Context.__del__ of <pyudev.core.Context object at 0x6b922870>> ignored

Following is my code:

    import pyudev
    def find_hard_port(vendor_id, product_id):
        """
        This is used to find port number from among 1.1, 1.2, 1.3, 1.4
        :return:
        """
        _p = None
        context = pyudev.Context()
        for device in context.list_devices(subsystem='tty', ID_BUS='usb'):
            if vendor_id == device['ID_VENDOR_ID'] and product_id == device['ID_MODEL_ID']:
                _p = re.findall(r'1\.[1-9]', device['DEVPATH'])[-1]

        return _p
@mulkieran
Copy link
Contributor

I can't really diagnose from here. Once the context is created, it must have a _libudev attribute. And the __del__ method should only be called by the garbage collector. And there is no code that I can find that explicitly remove the _libudev attribute. Right now, the behavior is not inconsistent with the garbage collector trying to collect the Context object twice, somehow.

@parthgoel06
Copy link

parthgoel06 commented Jun 20, 2021

I also faced this issue which @rag2180 mentioned and I found this.

Source - https://www.studytonight.com/python/destructors-in-python
Description-
In object-oriented programming, a destructor is only called in case an object is successfully created, because if any exception occurs in the constructor then the constructor itself destroys the object.
But in python, if any exception occurs in the init method while initializing the object, in that case too, the method del gets called.
Hence, even though the object was never initialized correctly, the del method will try to empty all the resources and variables and in turn, may lead to another exception.

Example code to reproduce the error-

class Example():
	def __init__(self, x):
	    # for x = 0, raise exception
		if x == 0:
			raise Exception();
		self.x = x;
		
	def __del__(self):
		print (self.x)

# creating an object
myObj = Example();
# to delete the object explicitly
del myObj

Error-

Exception exceptions.AttributeError: "'Example' object has no attribute 'x'" in <bound
 method Example.__del__ of <__main__.Example object at 0x02449570>> ignored

Context class of pyudev.core-

class Context(object):
    def __init__(self):
        """
        Create a new context.
        """
        self._libudev = load_ctypes_library('udev', SIGNATURES, ERROR_CHECKERS)
        self._as_parameter_ = self._libudev.udev_new()

    def __del__(self):
        self._libudev.udev_unref(self)

We can modify this class as per the solution given below.

Solution I found on StackOverflow-
Source - https://stackoverflow.com/questions/35327164/python-destructor-for-failed-constructor

Solution -
Test if the attribute is actually set, or provide a default by also making it a class attribute.
For example, setting a class attribute makes your current code work:

class MyClass:
    _obj = None  # class attribute

    def __init__(self, obj):
        self._obj = obj

    def __del__(self):
        if self._obj:
            print(self._obj)

or use getattr() with a default:

def __del__(self):
    if getattr(self, '_obj', None):
        print(self._obj)

I also found this - https://stackoverflow.com/questions/18058730/python-attributeerror-on-del

These are my findings for the mentioned issue. I haven't tested this solution thoroughly so there can be something that I'm missing here. Please verify if this is a viable solution or not.

@parthgoel06
Copy link

Also in one of the above StackOverflow link, I found a warning from the del hook documentation if it helps-

Warning: Due to the precarious circumstances under which del() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead. Also, when del() is invoked in response to a module being deleted (e.g., when execution of the program is done), other globals referenced by the del() method may already have been deleted or in the process of being torn down (e.g. the import machinery shutting down). For this reason, del() methods should do the absolute minimum needed to maintain external invariants. Starting with version 1.5, Python guarantees that globals whose name begins with a single underscore are deleted from their module before other globals are deleted; if no other references to such globals exist, this may help in assuring that imported modules are still available at the time when the del() method is called.

@mulkieran mulkieran self-assigned this Jun 22, 2021
@mulkieran
Copy link
Contributor

Ok. This looks like a reasonable explanation for why the exception is occurring. I think hasattr is the best choice and raising an exception indicating the real problem, in the case the attribute has not been set.

@mulkieran mulkieran added this to To do in 2022 Jan 27, 2022
2022 automation moved this from To do to Done Jan 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
2022
Done
Development

Successfully merging a pull request may close this issue.

3 participants