Attempting to force the lazy importer to import everything results in memory issues

Issue #122 closed
Jeremy Chiu
created an issue

This is part question part bug.

I'm moving from pyojbc 2.5.1 to 3.0.3 or 4. We use a mocking framework as a part of testing. There are problems with mocking & stubbing objc objects because they're protected. So we wrapped our imports of pyobjc things with an abstraction that looks if we're running unit tests. If we're running normally it gives us pyobjc objects. If we're running tests it gives us mock classes which have the same public interface as the class in the framework.

To generate these mock classes we inspect the real class for its attributes for methods, create a surrogate stub method, and setattr on the mock class binding the surrogate method to the found method's name. This worked great with 2.5.1. But with lazy loading we no longer find all the methods in the original class.

I tried iterating over all the names in the class' namespace via dir and then calling getattr on each to try to force the lazy loader to load everything so that the original method traversal could would be able to find everything. That worked in small cases but I started to get exit code 139 (SIGSEGV, invalid memory access) when I tried to do that in general.

Here's a simplified version of what I tried to do to force the lazy loader to load everything:

framework = __import__('AppKit', globals(), locals(), [some list of classes I want to mock])
class_to_mock = framework.__getattr__('some class name from the above list')
for item in dir(class_to_mock):
  getattr(class_to_mock, item)

So the bug is that doing this leads to invalid memory accesses. The question would be, is this the right way to do this? If not, what is?

Comments (6)

  1. Ronald Oussoren repo owner

    Can you add a script that reproduces the problem?

    Interestingly enough the following code crashes with an uncaught Objective-C exception:

    names = ['NSApplication', 'NSWindow', 'NSButton']
    framework = __import__('AppKit', globals(), locals(), names)
    import objc
    for cls in objc.getClassList():
        for item in dir(cls):
            try:
              getattr(cls, item)
            except objc.error:
                pass
    

    The 'objc.error' handler is needed due to an internal error exception in PyObjC. Both appear to be bugs (the former definitely, I need to research the former some more).

    I don't get an invalid memory access at the moment.

  2. Jeremy Chiu reporter

    Will do. Might be a bit of delay. Busy at work. Hopefully It'll be easy to build a simple repro case since our code base is, as usual, not simple.

  3. Log in to comment