Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
[HELP] TypeError: Expected callable/function
-
I was trying to implement a live updating timer. Well, a love updating label that will ultimately be proportionate to the time passed. I was able to implement it successfully in a test script, but when I popped the previously working function into the actual script where I wanted to use it, I get the Type Error in the title of this post.
I can't seem to figure it out. Does it have something to do with it being inside a main() function?
def main(): def timerCount(): lastTimeSince=0 arbitrary = 345 for i in xrange(arbitrary): print ">>>>>>=======true" timenow = time.time() timeThen= 1421929025.21 timeSince = timenow-timeThen if lastTimeSince <= round(timeSince,0): changingLabel1.text=str(int(round(timeSince,0))) lastTimeSince = round(timeSince,0) return lastTimeSince
The error shows up right at the print command.
-
I'd suggest you paste the actual traceback that you are getting, and/or a working example that shows the problem. Your example won't produce any Exceptions, since you haven't called timerCount(), or main() for that matter!
-
I was also unable to make the above code to fail even when adding calls to main(), timeCount(), etc. More context is is needed to understand how it is failing.
-
A few weeks ago I was getting a similar weird issue where I was testing an isolated snippet of code involving the <code>time.sleep()</code> command for inclusion in my actual program. I found that the code ran perfectly when tested as an isolated snippet but as soon as I threw it into the main program I ran into the problem of <code>time.sleep(1)</code> throwing a <code>TypeError: Expected callable/function</code> even though I was providing it with the correct argument type; an integer. So not knowing what was causing the issue, I decided just to play along and implement a hacky fix by providing a lambda returning the value I wanted. Then another TypeError popped up, this time asking for a float (?) instead of a function. So I just wrote a try: .. except: .. tree to handle the type errors. Mine ending up looking something like this:
from time import sleep try: sleep(1) except: try: sleep(lambda: 1) except: sleep(float(1))
This ended up fixing my issue and after I provided it with a float it didn't throw any more type errors.
TLDR;
Not sure how to fix it for yours since passing <code>lambda: ">>>>>>=======true"</code> to the print statement will just make it print the function object but maybe you could try using the <code>print()</code> function from the <code>future</code> module. Something like this?from __future__ import print_function def main(): def timerCount(): lastTimeSince=0 arbitrary = 345 for i in xrange(arbitrary): print(">>>>>>=======true") timenow = time.time() timeThen= 1421929025.21 timeSince = timenow-timeThen if lastTimeSince <= round(timeSince,0): changingLabel1.text=str(int(round(timeSince,0))) lastTimeSince = round(timeSince,0) return lastTimeSince
-
Hi guys. Thanks for your responses. I apologize for the half baked question - I was tired and slightly frustrated. The reason that I didn't post more is because the entire script is....huge. You see, I'm a noob, and despite my best efforts to put stuff in modules and organize sanely, the vast majority of my functionality exists in this one script. (Every time I would try to modularize something, problems would manifest!!)
So the problem sorted itself out, and I'm not exactly sure what, specifically, it was. I believe it had something to do with the ordering of different functions/parts of my script. I moved stuff around. I think.
I am not familiar with the future module or its purpose, but will look into it now.
But perhaps we can salvage this thread with 2 related questions:
1.) I would love some high-level guidance as to how you organize projects in Pythonista, with the intent of ultimately putting it into xCode. Do you put all function definitions in a separate file? How do you deal with global variables across files?? What's your particular preference?2.) Also, my timerCount() function has ended up as the central... hub for all aspects of my UI that change over time. In my actual code it's a while loop that basically executes as fast fast it can, but only updates labels if the time passed since the last update is some specified amount (seconds for the timer). Point being - is that normal? ideal? To have a constantly executing loop that does so as fast as it can executing a millisecond precision timer when in my UI I don't even utilize that precision?
When I began this project (a driver-side app for Uber drivers to get analytics), it was a down and dirty script. As it grew into something slightly more refined I decided I would try to do anything I added to it the "right" way to expand my abilities but not have to refactor everything or start over. I find myself compromising on that premise as it's proved difficult.
Also, just wanted to say that I truly appreciate all the generosity of the members of this community in spending their time to help others learn.
-
Re globals, they are okay for quick and dirty scripts, but should be avoided for big complicated things. They are hard to debug, easy to get confused (I.e globals which are not declared as global are readonly), and can have problems with other modules using the same names. At the very least, put everything into a class as instance variables to encapsulate them. Or, pass things in/out of functions.
Re timers.... If you have specific things that get updated at specific intervals, consider ui.delay loops(which are basically equivalent to threading.Timers). Basically, a function calls itself after a delay, in a new thread. While it is waiting, other threads run. You could have the delay be a fixed time, or dependent on some other condition. Just be careful if there are interactions between threads, i.e one thread is modifying values used by another, in which case you have to synchronize using locks or another threadsafe mechanism. Also, be sure to include a stopping condition such as
on_screen
, otherwise it will try to keep running even after errors occur.See a very simple example below, which updates two labels at different rates, using two independent delay loops.
import ui,time class stopwatch(ui.View): def __init__(self): self.frame=(0,0,768,768) self.bg_color='white' fast=ui.Label(frame=(10,10,200,50),name='fast') slow=ui.Label(frame=(10,70,200,50),name='slow') stop=ui.Button(frame=(150,150,80,80),bg_color='red',name='stop') go=ui.Button(frame=(250,150,80,80),bg_color='green',name='go') go.title='go' stop.title='stop' go.action=self.go stop.action=self.stop self.add_subview(fast) self.add_subview(slow) self.add_subview(stop) self.add_subview(go) self._stopped=True def stop(self,sender): self._stopped=True def go(self,sender): if self._stopped: self._stopped=False self.fast_loop() self.slow_loop() def fast_loop(self): if self.on_screen and not self._stopped: self['fast'].text='{:0.14}'.format(time.time()) r=self['fast'].bg_color[0] self['fast'].bg_color=((r+1)%2,1,1) ui.delay(self.fast_loop,0.1) def slow_loop(self): if self.on_screen and not self._stopped: self['slow'].text='{:0.14}'.format(time.time()) r=self['slow'].bg_color[0] self['slow'].bg_color=((r+1)%2,1,1) ui.delay(self.slow_loop,1.0) if __name__=='__main__': v=stopwatch() v.present()
-
So, I return to this thread, friends!
@JonB @ccc I've been refactoring my app to be less crude (one of the reasons I haven't replied to that other template thread we were hanging out in)... In doing so I'm referring back to this timer example you shared with me to craft my main run loop. I just have a couple questions.
-
While making a simpler example that I could actually understand, I ran into a couple errors and a search on stack overflow yielded a tidbit of information: Python recursion is limited to 999 recursions? The only thing is I'm blowing way past that in the example below, well into the thousands of recursions and it appears to work. Am I fundamentally misunderstanding something? (interestingly sys.getrecursionlimit() returns 256.....) - (ALSO interestingly, sys.getrecursionlimit() from pythonista's console returns 300....) (running it from OS X terminal in el capitan = 1000)
-
At the ridiculous delay rate of .00001, while the script is running, according to xCode's tools about 6% - 8% of the iPhone 5 simulator's CPU is being used. If I close the view via the standard close button but don't stop the app, the CPU usage jumps up to over 50% - why is that?
import ui,time import sys rec = sys.getrecursionlimit() print rec class COUNT(object): def __init__(self): self.recrusionsDone = 0 self.view = ui.View() self.view.name = 'Demo' self.view.background_color = 'blue' self.label = ui.Label(text = "Heyyyyyy..",text_color="red") self.label.center = (self.view.width * 0.5, self.view.height * 0.5) self.label.flex = "" self.label.width = self.view.width*2 self.label.alignment = ui.ALIGN_CENTER self.view.add_subview(self.label) self.label.text_color = "yellow" self.view.present('sheet') def crazyCounter(self): self.countRecursions() timeString = time.time() timePlus = str(timeString)+" - "+str(self.recrusionsDone) self.label.text = str(timePlus) ui.delay(self.crazyCounter,0.0001) def countRecursions(self): self.recrusionsDone = self.recrusionsDone+1 a = COUNT().crazyCounter()
-
-
Recursion is when a function calls itself. So calling
countRecursions()
fromcrazyCounter()
is not considered a recursion.import sys def looper(i=1): try: looper(i+1) except RuntimeError as err: print('{}/{} {}'.format(i, sys.getrecursionlimit(), err)) looper()
-
crazyCounter()
calls itself using ui.delay() ..
countRecursions
is just there as a bonus separate counter.. Am I missing something?
def crazyCounter(self): self.countRecursions() timeString = time.time() timePlus = str(timeString) self.label.text = str(timePlus) ui.delay(self.crazyCounter,0.0001)
PS - how are you doing inline (non-block) code snippets on the forum?
-
crazyCounter() calls ui.delay, which adds a task to the background queue, and returns immediately. crazyCounter() then exits, basically with no delay.
If you looked at a stacktrace, each call to crazyCounter() is at most one call deep. The python recursion limit is about how deep the stacktrace can go, effectively.
In contrast,
def crazyRecursion(i): if i>0 return i+crazyRecursion(i-1) else: return i crazyRecursion(5) # 5+4+3+2+1. ok crazyRecursion(1000) # will hit the recursion limit
if you printed a stacktrace inside here, it would be very deep, since each call has to wait for the next call to finish before the function exits.
I suspect the difference you say with cpu usage has to do with how ui.delay works, and shares resources with the ui thread, or else it could be that things slow down through the crazyCounter path when a ui is present (updating labels, etc migt be slower when a view is actually onscreen).
-
block inline can be done with single backticks: like
`crazyCounter` calls itself
-
Thanks for clearing that up. So I'm just trying to wrap my head around ui.delay().
A couple questions come to mind:
-
if you call multiple things using ui.delay, will they all be on the same thread?
---in another forum thread (no pun intended), Jon you suggested I use ui.delay for my button actions which were causing my app to crash. Would there be an issue if it's also used for my main runtime using pseudo-recursion as in the example above.? -
is ui.delay the same as ui.in_background just with the delay?
-
would it be a good idea to put the central runtime of my app into ui.in_background? (keeping in mind my app will have buttons that do other things)
basically.. I just want to do this right this time!!!
-
-
@Tizzy , I have used ui.delay before. I can't answer your question, but have you also looked at ui.cancel_delays. I can't see you call it anywhere
-
sorry, i misspoke above. ui.background puts everything on one thread, queued up by the order of background calls. ui.delay does kick off independant threads, though these may still be shared or managed somehow with the ui system, but I am not sure. ui.delay seems like a fine approach for updating a label with current time.
I would avoid doing anything really time critical using ui.in_background. It is good when you want to ensure the calls are executed in the order they were called, but if you are having some sort of autonomous code that is not triggered by a ui action, a Thread might be better. I think i posted an async_exec decorator somewhere on the forum which is a Thread version of ui.in_background.
Phuket2 offers good advice about using cancel_delays -- you need a way for the loop to cancel. Checking on_screen may be a good approach, you could also set a stop flag, etc. Cancel_delays is useful when you have longish delays, though in your case, you probably would never be able to catch that 0.001 between calls, so you need something inside crazyCounter() to prevent kicking off the ui.delay. Another heavy handed approach would be to redefine crazyCounter in -- your stop or will_close method, that way you don't incur the overhead of checking a flag inside your crazyCounter(), which I gather you want to run as fast as possible.