sbpy
sbpy copied to clipboard
Allow @cite decorator on base class?
When subclass a class that has a @cite decorator directly on the class definition, an exception is raised:
from sbpy.bib import cite
@cite({'ref': 'paper 1'})
class A():
pass
class B(A):
pass
Exception:
TypeError: function() argument 1 must be code, not str
Oh, A
is not a class after being decorated!
As far as I understand, what needs to happen is that @cite
creates a class that is a subclass of A
, decorates its __init__
function, then returns the result. This is going to obfuscate things at the command line:
>>> from sbpy.bib import cite
>>>
>>>
>>> def cite_class(citations):
... def class_decorator(cls):
... class ClassWrapper(cls):
... @cite(citations)
... def __init__self(self, *args, **kwargs):
... return super().__init__(*args, **kwargs)
... return ClassWrapper
... return class_decorator
...
>>>
>>> @cite_class({'ref': 'paper 1'})
... class A():
... pass
...
>>>
>>> class B(A):
... pass
...
>>>
>>> print(issubclass(A, A))
True
>>> print(issubclass(B, A))
True
>>>
>>> print(A)
<class '__main__.cite_class.<locals>.class_decorator.<locals>.ClassWrapper'>
>>> print(B)
<class '__main__.B'>
Notice the horrendous class name for A
? I don't know how to get around that.
This would make the citation report looking ugly. Is there a way to fix that? If that can be fixed, then I don't think this is a problem.
I've gotten this far, but notice how the signature of __init__
is still based on the true class name:
from sbpy import bib
def cite_class(citations):
def class_decorator(cls):
class ClassWrapper(cls):
@bib.cite(citations)
def __init__(self, *args, **kwargs):
return super().__init__(*args, **kwargs)
__module__ = cls.__module__
__name__ = cls.__name__
__qualname__ = cls.__qualname__
__doc__ = cls.__doc__
return ClassWrapper
return class_decorator
@cite_class({'ref': 'paper 1'})
class A():
pass
class B(A):
pass
print(issubclass(A, A))
print(issubclass(B, A))
print(A)
print(B)
bib.track()
print('\nbib tracking on')
a = A()
b = B()
print(bib.show())
bib.stop()
bib.reset()
notwo /tmp $ python3 test.py
True
True
<class '__main__.A'>
<class '__main__.B'>
bib tracking on
__main__.cite_class.<locals>.class_decorator.<locals>.ClassWrapper.__init__:
ref:
paper 1
Well, looks like we need to explore other approaches. I was thinking of engineering the bib.register
function to remove the horrendous class names, but the problem is the real class name is now <local>
and no longer visible. Let's leave this for the future.
For now, although function decorator @cite
works on class, we should only use it on functions because decorated class is no longer class. The consequences are:
-
isinstance()
andissubclass()
no longer work. - Static method no longer works
I think we should include this note in the documentation.