0

I am converting some python 2.7 code to work with python 3.8. One thing I am stuck on is how to get a reference to im_class working. The original code looks like this:

class UpdateDbQueue():
    def __init__(self, **kwargs):
        self.job = kwargs["job"]
        self.method = self.job
        self.cls = self.job.im_class
        self.name = "{}.{}".format(self.cls.__name__, self.method.__name__)

Running a debugger shows that kwargs["job"] equals <function DateRange.get_events at 0x112f84f80>. The python2to3 utility converted the code to this:

class UpdateDbQueue():
    def __init__(self, **kwargs):
        self.job = kwargs["job"]
        self.method = self.job
        self.cls = self.job.__self__.__class__
        self.name = "{}.{}".format(self.cls.__name__, self.method.__name__)

Which returns an error:

self.cls = self.job.__self__.__class__
AttributeError: 'function' object has no attribute '__self__'

Any idea on how to fix this? I tried changed it simple to self.job.class but got an error KeyError: 'DateRange.get_events'.

Edit - this is a snipped of the DateRange class.

class DateRange(db.Model): id = db.Column(db.DateTime, primary_key=True)

def get_events(self, rows=10000):
    # gets events from database

def __repr__(self):
    return "<DateRange (starts: {})>".format(self.id)
Casey
  • 2,611
  • 6
  • 34
  • 60
  • 2
    Did you try the original? `self.cls = self.job.im_class` instead? What is `DateRange`? What is `DateRange.getevents`? Do either have an `im_class` attribute? – wwii Dec 31 '19 at 21:42
  • 1
    @wwii `im_class` is part of the data model for a bounded method in python2 https://docs.python.org/2/library/inspect.html – salparadise Dec 31 '19 at 21:56
  • Yes I tried the original but got: `AttributeError: 'function' object has no attribute 'im_class'`. I will update the code with DateRange. – Casey Dec 31 '19 at 21:58
  • How do you call `UpdateDbQueue` and pass the `job` attribute? Based on your last update, it should be something like `UpdateDbQueue(job=DateRange().get_events)` – salparadise Dec 31 '19 at 22:26
  • It is called with `update_registry.register(UpdateDbQueue( job=DateRange.get_events, action_table="date_range" ))` – Casey Dec 31 '19 at 22:32
  • Related: https://stackoverflow.com/a/25959545/2823755 – wwii Dec 31 '19 at 23:17

1 Answers1

1

Looks like im_class attribute is found in an unbounded method in python2:

>>> sys.version
'2.7.16 (default, Nov  9 2019, 05:55:08) \n[GCC 4.2.1 Compatible Apple LLVM 11.0.0 (clang-1100.0.32.4) (-macos10.15-objc-s'
>>> class S:
...    def f(s):
...      pass
...
>>> 'im_class' in dir(S.f)
True

Since unbounded methods are not a thing in python3, this does not exist in this type of object anymore:

In [9]: sys.version
Out[9]: '3.7.3 (default, Nov 15 2019, 04:04:52) \n[Clang 11.0.0 (clang-1100.0.33.16)]'

In [10]: class S:
    ...:     def f(s):
    ...:         pass
    ...:

In [11]: 'im_class' in dir(S.f)
Out[11]: False

The python2to3 conversion tool seems to be assuming you have a bounded method in python3:

In [12]: sys.version
Out[12]: '3.7.3 (default, Nov 15 2019, 04:04:52) \n[Clang 11.0.0 (clang-1100.0.33.16)]'

In [13]: class S:
    ...:     def f(s):
    ...:         pass
    ...:

In [14]: '__self__' in dir(S().f) ## bounded by S()
Out[14]: True

However this does not look like a bounded method based on your information about kwargs['job']

Running a debugger shows that kwargs["job"] equals <function DateRange.get_events at 0x112f84f80>

which is just a plain function, which is what unbounded methods are in python3.

One thing to try is to bind the function to a local method using types after assigning to an instance which could have been the intent:

import types
class UpdateDbQueue():
    def __init__(self, **kwargs):
        self.job = types.MethodType(kwargs['job'], self)

This way you have the necessary attributes:

In [26]: def f(s):
    ...:     pass
    ...:

In [27]: class S:
    ...:     def __init__(self):
    ...:         self.f = types.MethodType(f, self)
    ...:         if '__self__' in dir(self.f):
    ...:             print("I have __self__")
    ...:
    ...:

In [28]: S()
I have __self__
Out[28]: <__main__.S at 0x111711dd8>

However job needs to expect to be called as a bounded method (so the instance is passed as the first argument, etc)

Edit:

Based on your question edit, seems that if you were calling this class with the appropriate bounded method, it should work, so how are you calling the UpdateDbQueue class?

salparadise
  • 5,699
  • 1
  • 26
  • 32