I am a beginner in Python, so please be... kind?
Anyway, I need use a static method to call another method, which requires the use of "self" (and thus, a normal method I believe). I am working with Telethon, a Python implementation of Telegram. I have tried other questions on SO, but I just can't seem to find a solution to my problem.
An overview of the program (please correct me if I'm wrong):
1) interactive_telegram_client is a child class of telegram_client, and it creates an instance.
#interactive_telegram_client.py
class InteractiveTelegramClient(TelegramClient):
super().__init__(session_user_id, api_id, api_hash, proxy)
2) When the InteractiveTelegramClient runs, it adds an update_handler self.add_update_handler(self.update_handler) to constantly check for messages received/sent, and prints it to screen
#telegram_client.py
def add_update_handler(self, handler):
"""Adds an update handler (a function which takes a TLObject,
an update, as its parameter) and listens for updates"""
if not self.sender:
raise RuntimeError(
"You should connect at least once to add update handlers.")
self.sender.add_update_handler(handler)
#interactive_telegram_client.py
#staticmethod
def update_handler(update_object):
try:
if type(update_object) is UpdateShortMessage:
if update_object.out:
print('You sent {} to user #{}'.format(update_object.message,
update_object.user_id))
else:
print('[User #{} sent {}]'.format(update_object.user_id,
update_object.message))
Now, my aim here is to send back an auto-reply message upon receiving a message. Thus, I think that adding a call to method InteractiveTelegramClient.send_ack(update_object) in the update_handler method would serve my needs.
#interactive_telegram_client.py
def send_ack(self, update_object):
entity = update_object.user_id
message = update_object.message
msg, entities = parse_message_entities(message)
msg_id = utils.generate_random_long()
self.invoke(SendMessageRequest(peer=get_input_peer(entity),
message=msg,random_id=msg_id,entities=entities,no_webpage=False))
However, as you can see, I require the self to invoke this function (based on the readme, where I assume client to refer to the same thing as self). Since the method update_handler is a static one, self is not passed through, and as such I cannot invoke the call as such.
My possible strategies which have failed include:
1) Instantiating a new client for the auto-reply
- Creating a new client/conversation for each reply...
2) Making all the methods non-static
- Involves a tremendous amount of work since other methods modified as well
3) Observer pattern (sounds like a good idea, I tried, but due to a lack of skills, not succeeded)
I was wondering if there's any other way to tackle this problem? Or perhaps it's actually easy, just that I have some misconception somewhere?
Forgot to mention that due to some restrictions on my project, I can only use Telethon, as opposed to looking at other alternatives. Adopting another library (like an existing auto-reply one) is allowed, though I did not really look into that since merging that and Telethon may be too difficult for me...
based on the readme, where I assume client to refer to the same thing as self
Correct, since the InteractiveTelegramClient subclasses the TelegramClient and hence, self is an instance of the extended client.
Instantiating a new client for the auto-reply - Creating a new client/conversation for each reply
This would require you to create another authorization and send another code request to login, because you can't work with the same *.session at the same time.
Making all the methods non-static - Involves a tremendous amount of work since other methods modified as well
It doesn't require such amount of work. Consider the following example:
class Example:
def __init__(self, a):
self.a = a
def do_something(self):
Example.other_method()
#staticmethod
def other_method():
print('hello, world!')
Is equivalent to:
class Example:
def __init__(self, a):
self.a = a
def do_something(self):
self.other_method()
#staticmethod
def other_method():
print('hello, world!')
It doesn't matter whether you use self. or the class name to refer to a static method from within the class. Since the InteractiveClientExample already uses self., all you would have to do would be changing:
#staticmethod
def update_handler(update_object):
for
def update_handler(self, update_object):
For more on the #staticmethod decorator, you can refer to the docs.
Related
I am currently trying to make some decorators helping users of my software to create code which will inform them about some issues.
Doing classes, I sometimes work with methods which I would like to use only within the class, but not to be called in instance. I know this can be worked out with underscores and dunders, but I don't want to make user's experience a hell, just a little nudge with warning that they used it in scope which is not intended to have such method used.
Let me explain on code block:
class Example:
def __init__(self):
self.sth = 0
def callableMethod(self, is_true):
if is_true:
self.otherMethod()
print (self.sth)
#NoInstanceMethod
def otherMethod(self):
self.sth = 1
Basically what I would like to achieve is that user can create object and use both methods, but when they try to use otherMethod on instance, like that:
i = Example()
i.otherMethod()
I would be able to recognise it and do something to warn the user (through print or logging message, it doesn't matter).
Is there a way to recognise that the method is used on instance in such a way, but not raise the warning on callableMethod (as it is correctly used scope)?
I am often writing scripts with boto3 and usually when writing functions I end up passing the boto3 client for the service(s) I need around my functions. So, for example
def main():
ec2 = create_client
long_function_with_lots_of_steps(ec2, ....)
def long_function_with_lots_of_steps(client):
....
This is not too bad, but it often feels repetitive and sometimes I will need to create a new client for a different service in the other function, for which I would like to use the original aws_session object.
Is there a way to do this more elegantly? I thought to make a class holding a boto3.session.Session() object but then you end up just passing that around.
How do you usually structure boto3 scripts?
I think you might have had some C or C++ programming experience. You are definitely getting language constructs confused. In Python function call arguments are passed by reference. So passing a reference is quick. You aren't passing the whole object.
This is in fact one of the better ways to pass in session info. Why is it better you may ask? Because of testing. You will need to test the thing and you don't always want to test the connections to 3rd party services. So you can do that with Mocks.
Try making a test where you are mocking out any one of those function arguments. Go ahead... I'll wait.
Easier... right?
Since you are basically asking for an opinion:
I usually go with your second approach. I build a base class with the session object, and build off of that. When working with a large program where I must maintain some "global" state, I make a class to house those items, and that becomes a member of my base class.
class ProgramState:
def __init__(self):
self.sesson = boto3.session.Session()
class Base:
def __init__(self, state: ProgramState):
self.state = state
class Firehose(Base):
def __init__(self, state: ProgramState):
Base.__init__(self, state)
self.client = self.state.session.client("firehose")
def do_something():
pass
class S3(Base):
def __init__(self, state: ProgramState):
Base.__init__(self, state)
self.client = self.state.session.client("s3")
def do_something_else():
pass
def main():
state = ProgramState()
firehose = Firehose(state)
s3 = S3(state)
firehose.do_something()
s3.do_something_else()
Full disclosure: I dislike Python.
I am following this explanation, and I don't quite get how Python interpreter arrives at the following. In the first example, is Python seeing #implementer(IAmericanSocket) is not implemented by UKSocket, then it decides to make it a AdaptToAmericanSocket because that is the only implementation of IAmericanSocket with one argument? What if there is another class instance implementing IAmericanSocket with one argument? In the second example, why is IAmericanSocket not overriding AmericanSocket's voltage method?
>>> IAmericanSocket(uk)
<__main__.AdaptToAmericanSocket instance at 0x1a5120>
>>> IAmericanSocket(am)
<__main__.AmericanSocket instance at 0x36bff0>
with the code below:
from zope.interface import Interface, implementer
from twisted.python import components
class IAmericanSocket(Interface):
def voltage():
"""
Return the voltage produced by this socket object, as an integer.
"""
#implementer(IAmericanSocket)
class AmericanSocket:
def voltage(self):
return 120
class UKSocket:
def voltage(self):
return 240
#implementer(IAmericanSocket)
class AdaptToAmericanSocket:
def __init__(self, original):
self.original = original
def voltage(self):
return self.original.voltage() / 2
components.registerAdapter(
AdaptToAmericanSocket,
UKSocket,
IAmericanSocket)
You can see the full documentation for zope.interface here: http://docs.zope.org/zope.interface/ - it may provide a more thorough introduction than Twisted's quick tutorial.
To answer your specific question, the registerAdapter call at the end there changes the behavior of calling IAmericanSocket.
When you call an Interface, it first checks to see if its argument provides itself. Since the class AmericanSocket implements IAmericanSocket, instances of AmericanSocket provide IAmericanSocket. This means that when you call IAmercianSocket with an argument of an AmericanSocket instance, you just get the instance back.
However, when the argument does not provide the interface already, the interface then searches for adapters which can convert something that the argument does provide to the target interface. ("Searches for adapters" is a huge oversimplification, but Twisted's registerAdapter exists specifically to allow for this type of simplification.)
So when IAmericanSocket is called with an instance of a UKSocket, it finds a registered adapter from instances of UKSocket. The adapter itself is a 1-argument callable that takes an argument of the type being adapted "from" (UKSocket) and returns a value of the type being adapted "to" (provider of IAmericanSocket). AdaptToAmericanSocket is a class, but classes are themselves callable, and since its constructor takes a UKSocket, it fits the contract of thing-that-takes-1-argument-of-type-UKSocket-and-returns-an-IAmericanSocket.
The existence of another class would not make a difference, unless it were registered as an adapter. If you register two adapters which might both be suitable their interactions are complicated, but since they both do the job, in theory you shouldn't care which one gets used.
I would like to construct a class in python that supports dynamic updating of methods from user supplied source code.
Instances of class Agent have a method go. At the time an instance is constructed, its .go() method does nothing. For example, if we do a=Agent(), and then a.go() we should get a NotImplementedError or something like that. The user then should be able to interactively define a.go() by supplying source code. A simple source code example would be
mySourceString = "print('I learned how to go!')"
which would be injected into a like this
a.update(mySourceString)
Further invokations of a.go() would then result in "I learned how to go!" being printed to the screen.
I have partially figured out how to do this with the following code:
import types
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class NotImplementedError(Error):
pass
class Agent(object):
def go(self):
raise NotImplementedError()
def update(self,codeString):
#Indent each line of user supplied code
codeString = codeString.replace('\n','\n ')
#Turn code into a function called func
exec "def func(self):\n"+' '+codeString
#Make func a bound method on this instance
self.go = types.MethodType(func, self)
QUESTIONS
Is this implementation sensible?
Will this implementation incur unexpected scope issues?
Is there an obvious way to sandbox the user supplied code to prevent it from touching external objects? I can think of ways to do this by supplying sets of allowed external objects, but this seems not pythonic.
Possibly useful SO posts
What's the difference between eval, exec, and compile in Python?
Adding a Method to an Existing Object
(I am working in python 2.6)
I have a POST method which calls a few tasklets. These tasklets do have yields in them, and I do have some x.put_async() in my code. So I don't want it to return before all the async stuff is done. So I decorated all my tasklets, which are just small functions with #ndb.tasklet. Also, on top of my POST method, I have:
#ndb.toplevel
def post(self):
However, in the documentation it states:
But if a handler method uses yield, that method still needs to be
wrapped in another decorator, #ndb.synctasklet; otherwise, it will
stop executing at the yield and not finish.
Indeed my method has a yield. It's already wrapped in #ndb.tasklet. Do I replace this with #ndb.synctasklet or do I use both (if so how would I use both)?
Also, see this thread which has some relevance. I too noticed an issue where my request would return without any output, but is un-reproducible. It happens every 15 minutes or so of constant use. I had app = ndb.toplevel(webapp2.WSGIApplication([..]) only, but now I've added #ndb.toplevel to the main POST methods, but the issue still persists.
Should I put #ndb.tasklet on top of methods that have just put_async()'s too? (Should I put it on top of every method just to be safe? What are the downsides to this?)
Regarding the handler and using #ndb.toplevel and #ndb.synctasklet:
The way I understood it was that you need to use both #ndb.synctasklet and #ndb.toplevel on the handler. All the sub-tasklets only need the #ndb.tasklet decorator. e.g.
class Foo(ndb.Model):
name = ndb.StringProperty()
#ndb.tasklet
def my_async(self):
....
#do something else that yields
raise ndb.Return("some result")
#ndb.toplevel
#ndb.synctasklet
def post(self):
foo = Foo(name="baz")
yield foo.put_async()
yield foo.my_async()
....
However. looking at the source, it appears that #ndb.toplevel is actually a synctasklet anyway:
def toplevel(func):
"""A sync tasklet that sets a fresh default Context.
Use this for toplevel view functions such as
webapp.RequestHandler.get() or Django view functions.
"""
Running a small test with yields in the handler and decorated with #ndb.toplevel still seems to work, and appears that you can remove #ndb.synctasklet from the handler.
Regarding whether you should include #ndb.tasklet on methods that call put_async():
If you're not yielding on the put_async(), then you don't need to include #ndb.tasklet on the surrounding method (#ndb.toplevel will handle getting the results from the put_async())