Pass instance attribute as a decorator parameter - python

I am creating a class Encuesta to create multiple surveys via user commands for a discord bot using discord.py.
To achieve this, I have to pass the attribute "self.buttonid" as the 'custom_id' parameter for the decorator #discord.ui.button (already defined by discord.py).
class Encuesta(discord.ui.View):
def __init__(self, pregunta, buttonid):
super().__init__(timeout=None)
self.pregunta = pregunta
self.buttonid = buttonid
#discord.ui.button(label="Realizar encuesta", style=discord.ButtonStyle.grey, custom_id=self.buttonid)
async def encuesta(self, interaction: discord.Interaction, button: discord.ui.Button):
await interaction.response.send_modal(await crear_modal(self.pregunta, interaction.user))
But I get the error "self is not defined", since 'self' only exists inside class methods, and cant access it from the decorator calling.
I just need to get access to self.buttonid while calling the decorator.
I tried directly calling self.buttonid, without success. Other answers rely on modifyng the decorator, and I would want to avoid that.

Since a discord View class is meant to be instantiated only once, one viable workaround would be to use a class factory function to create a singleton class with buttonid preserved in the closure of the factory function rather than storing it as an instance attribute:
def Encuesta(pregunta, buttonid):
class _Encuesta(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
#discord.ui.button(
label="Realizar encuesta",
style=discord.ButtonStyle.grey,
custom_id=buttonid)
async def encuesta(self, interaction: discord.Interaction, button: discord.ui.Button):
await interaction.response.send_modal(await crear_modal(pregunta, interaction.user))
return _Encuesta()

Related

Implement async abstract method synchronously

I have an abstract Python class that defined an abstract async method:
class ConversionResultsReporter(ABC):
#abstractmethod
async def report(
self, conversion_spec: ConversionSpec, results: Sequence[PositiveFloat]
) -> None:
pass
I'd like to implement a derived class where the implementation is synchronous, doesn't need to use await - for simplicity let's say it should do print "Hello World" and exit, how would it be written?

Unable to access instantiated data member in python

I'm having problem accessing the data member in python. Not sure why.
These are my files:
# main.py
from myprocess import DataProcess as dp
myprocess = dp('apple')
myprocess.process_files()
Now for the file where i have a class
#myprocess.py
class DataProcess:
def __init__(self, file1):
self.file1=file1
#classmethod
def process_files():
print(self.file1)
In the process_files method, I have an error :
Class 'DataProcess' has no 'file1' member
Where have I gone wrong?
You can't use self parameter inside a classmethod. The idea of class method is that the method belongs to the class itself, and not to an instance of the class. Hence if you delete the #classmethod decorator, your code should work

Python mocking class instantiation and accessing private member variable

I want to test a method inside a class which updates the value of a member variable. However class takes constructor arguments which are not required in the method to test.
class SimpleClass(Database):
count = None
def intoTwo(self, v):
self.count = 2*v
Now I am testing method as follows
import unittest
import mock
class TestSimpleClass(unittest.TestCase):
#mock.patch('SimpleClass', autospec=True)
def test_intoTwo(self, mock_simpleclass):
mock_instance = mock_simpleclass.return_value
mock_instance.intoTwo(2)
self.assertEqual(mock_instance.count,4)
I am getting following error:
<NonCallableMagicMock name='SimpleClass().count' id='139921148836112'>
Please suggest the solution for this. I count not find something relevant in other posts or blogs.

Mock instance created from class stored in variable

I have the following code:
class Messenger(object):
def __init__(self):
# Class Type of what messages will be created as.
message_class = Message
def publish(self, body):
# Instantiate object of type stored in `message_class`
message = message_class(body)
message.publish()
I want to assert that the Message.publish() method is called. How do I achieve this?
I've already tried the following ways:
Assign message_class to Mock or Mock(). If I debug what message_class(body) returns, it is a Mock, but I don't seem to be able to get the instance and assert it (because the Mock I assign in my test is not the instance used, it is the Type).
Patch Message class with decorator. Whenever I do this it seems like it does not catch it. When I debug what message_class(body) returns its of Message type, not Mock.
Try to mock the __init__ method of message_class in hopes that I can set the instance that is returned whenever the code tries to Instantiate the message. Does not work, throws errors because the __init__ method is not suppose to have a return value.
If you were storing the actual instance, I'd say you could do something like messenger.message.publish.assert_called_once, but since message_class is being stored, it makes it slightly trickier. Given that, you can pull the return_value from the mocked class and check the call that way. Here's how I did it:
Messenger. Note the slight modification to assign message_class to self. I'm assuming you meant to do that, otherwise it wouldn't work without some global funkiness:
'''messenger.py'''
class Message(object):
def __init__(self, body):
self.body = body
def publish(self):
print('message published: {}'.format(self.body))
class Messenger(object):
def __init__(self):
# Class Type of what messages will be created as.
self.message_class = Message
def publish(self, body):
# Instantiate object of type stored in `message_class`
message = self.message_class(body)
message.publish()
Test:
'''test_messenger.py'''
from unittest import mock, TestCase
from messenger import Messenger
class TestMessenger(TestCase):
#mock.patch('messenger.Message')
def test_publish(self, mock_message):
messenger = Messenger()
messenger.publish('test body')
# .return_value gives the mock instance, from there you can make your assertions
mock_message.return_value.publish.assert_called_once()

How do I refer to a class method outside a function body in Python?

I want to do a one time callback registration within Observer. I don't want to do the registration inside init or other function. I don't know if there is a class level equivalent for init
class Observer:
#classmethod
def on_new_user_registration(new_user):
#body of handler...
# first I try
NewUserRegistered().subscribe \
(Observer.on_new_user_registration) #gives NameError for Observer
#so I try
NewUserRegistered().subscribe(on_new_user_registration) #says not callable
#neither does this work
NewUserRegistered().subscribe(__metaclass__.on_new_user_registration)
class BaseEvent(object):
_subscriptions = {}
def __init__(self, event_info = None):
self.info = event_info
def fire(self):
for callback in self._subscriptions[event_type]:
callback(event_info)
def subscribe(self, callback):
if not callable(callback):
raise Exception(str(callback) + 'is not callable')
existing = self._subscriptions.get(self.__class__, None)
if not existing:
existing = set()
self._subscriptions[self.__class__] = existing
existing.add(callback)
class NewUserRegistered(BaseEvent):
pass
I suggest to cut down on the number of classes -- remember that Python isn't Java. Every time you use #classmethod or #staticmethod you should stop and think about it since these keywords are quite rare in Python.
Doing it like this works:
class BaseEvent(object):
def __init__(self, event_info=None):
self._subscriptions = set()
self.info = event_info
def fire(self, data):
for callback in self._subscriptions:
callback(self.info, data)
def subscribe(self, callback):
if not callable(callback):
raise ValueError("%r is not callable" % callback)
self._subscriptions.add(callback)
return callback
new_user = BaseEvent()
#new_user.subscribe
def on_new_user_registration(info, username):
print "new user: %s" % username
new_user.fire("Martin")
If you want an Observer class, then you can do it like this:
class Observer:
#staticmethod
#new_user.subscribe
def on_new_user_registration(info, username):
print "new user: %s" % username
But note that the static method does not have access to the protocol instance, so this is probably not very useful. You can not subscribe a method bound to an object instance like this since the object wont exist when the class definition is executed.
But you can of course do this:
class Observer:
def on_new_user_registration(self, info, username):
print "new user: %s" % username
o = Observer()
new_user.subscribe(o.on_new_user_registration)
where we use the bound o.on_new_user_registration as argument to subscribe.
I've come to accept that python isn't very intuitive when it comes to functional programming within class definitions. See this question. The problem with the first method is that Observer doesn't exist as a namespace until the class has been built. The problem with the second is that you've made a class method that doesn't really do what it's supposed to until after the namespace has been created. (I have no idea why you're trying the third.) In both case neither of these things occurs until after the class definition of Observer has been populated.
This might sound like a sad constraint, but it's really not so bad. Just register after the class definition. Once you realize that it's not bad style to perform certain initialization routines on classes in the body of the module but outside the body of the class, python becomes a lot friendlier. Try:
class Observer:
# Define the other classes first
class Observer:
#classmethod
def on_new_user_registration(new_user):
#body of handler...
NewUserRegistered().subscribe(Observer.on_new_user_registration)
Because of the way modules work in python, you are guaranteed that this registration will be performed once and only once (barring process forking and maybe some other irrelevant boundary cases) wherever Observer is imported.
oops. sorry about that.
All I had to do was to move the subscription outside the class definition
class Observer:
#classmethod
def on_new_user_registration(new_user):
#body of handler...
#after end of class
NewUserRegistered().subscribe(Observer.on_new_user_registration)
Guess it is a side-effect of too much Java that one doesn't immediately think of this.
What you're doing should work:
>>> class foo:
... #classmethod
... def func(cls):
... print 'func called!'
...
>>> foo.func()
func called!
>>> class foo:
... #classmethod
... def func(cls):
... print 'func called!'
... foo.func()
...
func called!
One thing to note though, class methods take a cls argument instead of a self argument. Thus, your class definition should look like this:
class Observer:
#classmethod
def on_new_user_registration(cls, new_user):
#body of handler...

Categories

Resources