I recognize this may be a very 101 type question, but I'm still having trouble understanding functional programming in general, and have a particular code snippet that I can't make sense of:
Full code, but leaving out most of the function definitions:
import blpapi
import sys
SESSION_STARTED = blpapi.Name("SessionStarted")
SESSION_STARTUP_FAILURE = blpapi.Name("SessionStartupFailure")
SERVICE_OPENED = blpapi.Name("ServiceOpened")
SERVICE_OPEN_FAILURE = blpapi.Name("ServiceOpenFailure")
ERROR_INFO = blpapi.Name("ErrorInfo")
GET_FILLS_RESPONSE = blpapi.Name("GetFillsResponse")
d_service="//blp/emsx.history"
d_host="localhost"
d_port=8194
bEnd=False
class SessionEventHandler():
def processEvent(self, event, session):
try:
if event.eventType() == blpapi.Event.SESSION_STATUS:
self.processSessionStatusEvent(event,session)
elif event.eventType() == blpapi.Event.SERVICE_STATUS:
self.processServiceStatusEvent(event,session)
elif event.eventType() == blpapi.Event.RESPONSE:
self.processResponseEvent(event)
else:
self.processMiscEvents(event)
except:
print ("Exception: %s" % sys.exc_info()[0])
return False
def processSessionStatusEvent(self,event,session):
print ("Processing SESSION_STATUS event")
for msg in event:
pass
def processServiceStatusEvent(self,event,session):
print ("Processing SERVICE_STATUS event")
for msg in event:
pass
def processResponseEvent(self, event):
print ("Processing RESPONSE event")
for msg in event:
global bEnd
bEnd = True
def processMiscEvents(self, event):
print ("Processing " + event.eventType() + " event")
for msg in event:
print ("MESSAGE: %s" % (msg.tostring()))
def main():
sessionOptions = blpapi.SessionOptions()
sessionOptions.setServerHost(d_host)
sessionOptions.setServerPort(d_port)
print ("Connecting to %s:%d" % (d_host,d_port))
eventHandler = SessionEventHandler()
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
if not session.startAsync():
print ("Failed to start session.")
return
global bEnd
while bEnd==False:
pass
session.stop()
I can follow the code up to here:
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
Here, I see I'm calling "Session" from the blpapi library, and passing it some options as well as my eventHandler.processEvent. Here is where I get lost. I look at that particular function, and see:
def processEvent(self, event, session):
try:
if event.eventType() == blpapi.Event.SESSION_STATUS:
self.processSessionStatusEvent(event,session)
elif event.eventType() == blpapi.Event.SERVICE_STATUS:
self.processServiceStatusEvent(event,session)
elif event.eventType() == blpapi.Event.RESPONSE:
self.processResponseEvent(event)
else:
self.processMiscEvents(event)
except:
print ("Exception: %s" % sys.exc_info()[0])
return False
I see that the function is attempting to discern what type of event has been passed in, and will execute a different function within the class depending on that event type. The trouble is, I can't figure out where the event is ever specified! Where does "event" come from? I see it as an argument in that particular function, but no event argument was passed to:
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
So how does it know what to do at this point? How did this "event" object magically appear?
Thanks for entertaining my dumb questions
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
Note that processEvent here lacks parentheses () after it. This means you are passing the function itself as a parameter to the Session class. This class will later call processEvent with appropriate parameters.
Side Note:
I'm still having trouble understanding functional programming
"Functional programming" has a very specific definition and this example isn't it. If you are interested, you can google "functional programming" or read the Wikipedia article to find out more. However, this isn't really important at this stage in your learning process.
Related
New to click here so I'm still learning. How do I store the click arguments/options in an object for later reference within my application? I thought of just creating an object and returning it from the setup function, but it doesn't seem to work. Here is what I did:
import click
import sys
class Cfg(object):
component = ""
command = ""
obj = ""
my_cfg = Cfg()
#click.command()
#click.argument('component')
#click.argument("command")
#click.argument("obj")
def set_args(component, command, obj):
cfg = Cfg()
if component != "optdata":
sys.stderr.write("Invalid option")
sys.exit(1)
else:
cfg.component = component
cfg.command = command
cfg.obj = obj
return cfg
if __name__ == "__main__":
app_cfg = Cfg()
app_cfg = set_args() # Never actually completes here.
print("Component = ", app_cfg.component, "Command = ", app_cfg.command, "Obj = ", app_cfg.obj)
There is some sort of exception raised in core.py which just does a sys.exit and doesn't raise any sort of actual error.
Your design goes against the idea of Click: You're not supposed to treat "parsing the options" and "doing the work" as two separate steps:
import click
#click.command()
#click.argument("component", type=click.Choice(["optdata"]))
#click.argument("command")
#click.argument("obj")
def cli(component, command, obj):
print("Component = ", component, "Command = ", command, "Obj = ", obj)
# put your business logic here
if __name__ == "__main__":
cli()
The pattern is to call the function that processes the command line options and then have that function call any other functionality.
I am trying to implement a keep-alive that sends some data every 30 seconds to keep a telnet connection open.
My code calls reinitScore every second. This function will sometimes call calculateWinner, which sends the data through telnet via stelnet.send(data).
The problem is, when I call stelnet.send(data) inside any function, it raises a NameError: global name 'stelnet' is not defined.
My questions is: why would stelnet.send(data) work in one place, and not another?
Here is the part of my code that concerns telnet transfer and function calling:
import socket, select, string, sys
import string
import threading
leftKeyCounter = 0
rightKeyCounter = 0
frontKeyCounter = 0
backKeyCounter = 0
# function called by reinitScore
def calculateWinner(d):
scores = {}
high_score = 0
for key, value in d.items():
try:
scores[value].append(key)
except KeyError:
scores[value] = [key]
if value > high_score:
high_score = value
results = scores[high_score]
if len(results) == 1:
print results[0]
stelnet.send(results[0])
return results[0]
else:
print 'TIE'
return 'TIE', results
#called once and repeat itselfs every second
def reinitScore():
threading.Timer(1, reinitScore).start()
#globaling for changing the content
global leftKeyCounter
global rightKeyCounter
global frontKeyCounter
global backKeyCounter
values = {'left' : leftKeyCounter, 'right' : rightKeyCounter, 'front' : frontKeyCounter, 'back' : backKeyCounter}
if (leftKeyCounter != 0 or rightKeyCounter != 0 or frontKeyCounter != 0 or backKeyCounter != 0):
calculateWinner(values)
leftKeyCounter = 0
rightKeyCounter = 0
frontKeyCounter = 0
backKeyCounter = 0
print "back to 0"
reinitScore()
if __name__ == "__main__":
if(len(sys.argv) < 3) :
print 'Usage : python telnet.py hostname port'
sys.exit()
host = sys.argv[1]
port = int(sys.argv[2])
stelnet = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
stelnet.settimeout(2)
# connect to remote host
try :
stelnet.connect((host, port))
except :
print 'Unable to connect'
sys.exit()
print 'Connected to remote host'
while True:
// ... Some code that has nothing to do with telnet
while 1:
socket_list = [sys.stdin, stelnet]
read_sockets, write_sockets, error_sockets = select.select(socket_list , [], [])
for sock in read_sockets:
if sock == stelnet:
data = sock.recv(4096)
if not data :
print 'Connection closed'
sys.exit()
else :
sys.stdout.write(data)
else :
msg = sys.stdin.readline()
stelnet.send(msg)
I tried to declare stelnet as a global variable at many places, but it doesn't change anything --- I always get the "not defined" NameError.
In response to your updated code... The error message is still correct, because although you have defined stelnet at the module level, you've defined it too late. It's definition occurs after its use in the calculateWinner function.
Stripping your code down to a ridiculously minimal example, you are doing something like this:
def calculateWinner():
# A leap of faith... There is no `stelnet` defined
# in this function.
stelnet.send(results[0])
def reinitScore():
# Indirectly depends on `stelnet` too.
calculateWinner()
# But we haven't defined `stelnet` yet...
reinitScore() # Kaboom!
# These lines will never run, because the NameError has
# already happened.
if __name__ == '__main__':
stelnet = ... # Too late.
calculateWinner depends on a name that does not exist when the function is compiled. Whether it works or crashes will depend on whether some other code has defined stelnet 1) where calculateWinner can get at it, and 2) before calculateWinner is executed.
Suggestions
Functions that depend on global mutable state are hard to follow, let alone code correctly. It's not easy to tell what depends on which variables, or what's modifying them, or when. Also, coming up with an MCVE is more trouble than it should be, because functions that appear independent might not be.
Stuff as much of your module-level code as you can into a main function, and call it (and nothing else) from the body of if __name__ == '__main__': (since even that is actually at module level).
Consider something like this:
def reinit_score(output_socket, shared_scores):
# Ensuring safe concurrent access to the `shared_scores`
# dictionary is left as an exercise for the reader.
winner = ... # Determined from `shared_scores`.
output_socket.send(winner)
for key in shared_scores:
shared_scores[key] = 0
threading.Timer(
interval=1,
function=reinit_score,
args=[output_socket, shared_scores],
).start()
def main():
output_socket = ... # This was `stelnet`.
shared_scores = {...} # A dictionary with 4 keys: L/R/U/D.
reinit_score(output_socket, shared_scores)
while True:
play_game(shared_scores)
# `play_game` mutates the `shared_scores` dictionary...
if __name__ == '__main__':
main()
These functions are still connected by the shared dictionary that they pass around, but only functions that are explicitly passed that dictionary can change its contents.
Your code is not working because you are not passing stelnet to your function.
import pynotify
import gobject
def on_clicked(notification, signal_text):
print "1: " + str(notification)
print "2: " + str(signal_text)
notification.close()
def on_closed(notification):
print "on_closed"
notification.close()
def show_notification(title, body):
n = pynotify.Notification(title, body)
n.add_action("button", "Test button", on_clicked)
n.connect("closed", on_closed)
n.show()
if __name__ == '__main__':
pynotify.init('TestApp')
global loop
loop = gobject.MainLoop()
# first case
notify = pynotify.Notification("1_notify", "test")
notify.add_action("button", "Test button", on_clicked)
notify.connect("closed", on_closed)
notify.show()
# second case
show_notification("2_notify", "test")
loop.run()
Sorry for my bad English. I want to handle closing xfce4-notifyd notification. In the first case, the function "on_closed()" works. Why in the second case it does not work?
This only works well in one namespace?
It does not work because the Notification object goes out of scope when show_notification() returns and is freed. You can make it work by e.g. returning the Notification object from the function and storing it in a variable in main body.
This seems like a remedial topic, but I'm a bit unsure of how to deal with this. Every solution I think of seems messy.
I'm working with some code that builds up a message while performing several actions, then ultimately returns that msg with an http response. Currently it looks somewhat like this:
try:
pdict = parser.parseProtocol(id)
msg = "parsing worked"
except:
msg = "parsing failed"
try:
file = parser.getFile(pdict['filePath'])
msg += "file retrieved"
except:
msg += "file not found"
Say I want to encapsulate the code into functions. How could do I have a message that gets updated throughout? Strings are immutable, so I can't just pass them to a function and modify them. A super ugly solution would be:
(pdict, msg) = parseSomething()
if pdict:
(file, msg) = retrieveFile(pdict, msg)
def parseSomething():
try:
pdict = parser.parseProtocol(id)
return (pdict, "parsing worked")
except:
return (None, "parsing failed")
def retrieveFile(pdict, msg)
try:
file = parser.getFile(pdict['filePath'])
return (file, msg + "file retrieved")
except:
return (None, msg + "file not found")
Super ugly.
I could create a message class, or use a list of length 1, and that would be prettier, but still not very pythonic, right? I think I just want these functions to take a message string and modify it, without having to return it, but strings are immutable so that's not the default behavior.
There's gotta be a smooth way to do this that I'm just blanking on. Help?
Consider putting your messages in a list and appending to it as you go?
messages = []
try:
pdict = parser.parseProtocol(id)
messages.append("parsing worked")
except:
messages.append("parsing failed")
try:
file = parser.getFile(pdict['filePath'])
messages.append("file retrieved")
except:
messages.append("file not found")
print '\n'.join(messages)
If your codepath is particularly convuluted, consider embedding them in a class:
class Tests(object):
def __init__(self):
messages = []
self.pdict = None
def parsetest(self):
try:
self.pdict = parser.parseProtocol(id)
except:
self.messages.append("parsing failed")
else:
self.messages.append("parsing worked")
def retrievetest(self):
if self.pdict is None:
raise Exception("called retrievetest() before successfully parsing")
try:
file = parser.getFile(self.pdict['filePath'])
except:
self.messages.append("file not found")
else:
self.messages.append("file retrieved")
And then later:
tests = Tests()
tests.parsetest()
if condition:
tests.retrievetest()
print '\n'.join(tests.messages)
put your message in an array, pass it around, and just append each part to it.
just before sending it, do a ''.join(msg).
Make your message a member of a class, and pass around an instance of the class.
Better yet, make all these function methods on a class, and keep the message as an attribute of the object.
So, I wanted to extend the Python smtpd SMTPServer class so that it could handle SMTP AUTH connections. Seemed simple enough...
So, it looked like I could just start like this:
def smtp_EHLO(self, arg):
print 'got in arg: ', arg
# do stuff here...
But for some reason, that never gets called. The Python smtpd library calls other similar methods like this:
method = None
i = line.find(' ')
if i < 0:
command = line.upper()
arg = None
else:
command = line[:i].upper()
arg = line[i+1:].strip()
method = getattr(self, 'smtp_' + command, None)
Why won't it call my method?
After that, I thought that I could probably just override the entire found_terminator(self): method, but that doesn't seem to work either.
def found_terminator(self):
# I add this to my child class and it never gets called...
Am I doing something stupid or...? Maybe I just haven't woken up fully yet today...
import smtpd
import asyncore
class CustomSMTPServer(smtpd.SMTPServer):
def smtp_EHLO(self, arg):
print 'got in arg: ', arg
def process_message(self, peer, mailfrom, rcpttos, data):
print 'Receiving message from:', peer
print 'Message addressed from:', mailfrom
print 'Message addressed to :', rcpttos
print 'Message length :', len(data)
print 'HERE WE ARE MAN!'
return
# Implementation of base class abstract method
def found_terminator(self):
print 'THIS GOT CALLED RIGHT HERE!'
line = EMPTYSTRING.join(self.__line)
print >> DEBUGSTREAM, 'Data:', repr(line)
self.__line = []
if self.__state == self.COMMAND:
if not line:
self.push('500 Error: bad syntax')
return
method = None
i = line.find(' ')
if i < 0:
command = line.upper()
arg = None
else:
command = line[:i].upper()
arg = line[i+1:].strip()
method = getattr(self, 'smtp_' + command, None)
print 'looking for: ', command
print 'method is: ', method
if not method:
self.push('502 Error: command "%s" not implemented' % command)
return
method(arg)
return
else:
if self.__state != self.DATA:
self.push('451 Internal confusion')
return
# Remove extraneous carriage returns and de-transparency according
# to RFC 821, Section 4.5.2.
data = []
for text in line.split('\r\n'):
if text and text[0] == '.':
data.append(text[1:])
else:
data.append(text)
self.__data = NEWLINE.join(data)
status = self.__server.process_message(self.__peer,
self.__mailfrom,
self.__rcpttos,
self.__data)
self.__rcpttos = []
self.__mailfrom = None
self.__state = self.COMMAND
self.set_terminator('\r\n')
if not status:
self.push('250 Ok')
else:
self.push(status)
server = CustomSMTPServer(('127.0.0.1', 1025), None)
asyncore.loop()
You need to extend SMTPChannel -- that's where the smtp_verb methods are implemented; your extension of SMTPServer just needs to return your own subclass of the channel.
TL&DR: To add additional functionality to SMTPChannel you just need to declare a function, and then add it directly to smtpd.SMTPChannel
Explanation:
The SMTPChannel class is designed to respond to the commands that are entered by the user on the open port (typically port 25). The way it searches for which commands it can answer is based off 'Introspection' where it examines all the available attributes of the function.
Take note that the functions within SMTPChannel need to start with the "smtp_". For Example, if you wanted to respond to HELP you would create smtpd.SMTPChannel.smtp_HELP.
The Function below is from the source-code that details the introspection
class SMTPChannel(asynchat.async_chat):
method = getattr(self, 'smtp_' + command, None)
CodeThatWorks
Step 1: Declare a FUNCTION that will be called
def smtp_HELP(self,arg):
self.push("[8675309] GPT Answers to HELP")
Step 2: Add the following function to smtpd.SMTPChannel
class FakeSMTPServer(smtpd.SMTPServer):
"""A Fake smtp server"""
smtpd.SMTPChannel.smtp_HELP = smtp_HELP
Step 3: Telnet to localhost 25 and test out
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 vics-imac.fios-router.home ESMTP Sendmail 6.7.4 Sunday 17 March 2019
HELP
[8675309] GPT Answers to HELP