How to prevent commands execution in Pythonista console
Hi, do you know a way (with some esoteric objc maybe?) to bypass (prevent the execution of) the Pythonista internal python interpreter when user presses the key return in built-in keyboard in the Python console after writing a command, in order to run other external scripts written by user?
For example: when I'm in the Pythonista console and I write:
>>> print('hello') ## and press "return" key in the keyboard
I'd like to force Pythonista to execute a script X instead of the normal execution (that is the execution of the command "print('hello')" by the internal python interpreter of Pythonista). So, following this example, I don't want the common output that is:
but I'd like to have, as output and as a simple example, the number of single characters in the string of the command "print('hello')" (if the script X is a script that reads the string written by user and counts its characters). So when I press return key, I'd like to see in console the output:
that are the number of characters of the input string "print('hello')".
The script X that counts the characters of the user input string in console is only an example, only to explain better my question.
I don't think
sys.displayhookwould be very helpful here - it only affects how the final evaluated object is displayed, it does not change anything about how the typed-in Python code is executed. In particular,
If you want to affect
sys.stdoutwith a custom file-like object.
If you want to affect the code execution even more than that, you're probably better off writing your own REPL, perhaps based on the standard
codemodule or simply
eval. This lets you run the entered code in a different environment (by passing a dict to
eval) and you can even analyze and modify the code before it gets run (although that might be overkill depending on what you want to do).
You are right, I misread the post.
I would think perhaps the
cmdmodule might be useful here -- for instance if one wanted to write a sagecell console that sends commands to a remote sagecell instance, then prints the responses. Does
codelet you subclass to do pre/post hooks?
@JonB and @dgelessus, thank you for reply, yes my ultimate target is to write something (I don't know if I will have something working) for the Pythonista console able to send strings to sagemathcell server instead of execute them with internal python interpreter and to post-process the output in order to print the output from sage as string in the console (as described in my first example above).
Example of possible usage:
- user enables the by-pass of internal interpreter with the execution of a specific script A as a wrench script
- in this way the return key, when touched on the keyboard, doesn't call the internal python interpreter but reads the full string (the command written by user) and sends it to sage with all required arguments, waits for output and prints any output (output, warnings, errors, etc...) as string with images if existing.
- user can decide if evaluate the output in order to convert it as a number, array, etc...
- when user wants to return to use internal interpreter, a second script B should be executed always through the wrench menu (or the @JonB's StatusBarOverlay.py).
Like the following command in Pythonista console (as example):
>>> minimize(fun,x0,...) ## from scipy.optimize
and the touch of the return key should send the string "minimize(fun,x0,...)" to server with all explicit arguments (fun, X0, args, method, etc...) after entering in the cell string the string "from scipy.optimize import minimize" for proper importing of the required module.
I asked about the existence of some objc commands to by-pass internal interpreter when user touches the return key after inserting the command in the console. Other option is to be able to install in Pythonista some specific library like fire, for example (but it seems to me not pure python, even if GitHub says it is 100% python: I tried to install it in Pythonista but it doesn't work).
Other option is to program a user key with @JonB StatusBarOverlay.py, it is what I will do. So (I hope) I will have a user defined key in upper status bar that simulates the return key of the built-in keyboard but with a specific pre-processing and post-processing action.
But I still have the curiosity to know if with Pythonista (and its several surprises) user can customize the effect of the return key touching in console environment.
Thank you for numerous tips and explanations.
One thing you might consider is making a stash extension. Stash takes care of the history buttons (which otherwise you have to manually manage in a cmd loop).
I think what you might want to do is set up the wrench menu to essentially put you into this mode when you finish a script -- that way you can hang onto the session, etc, and start exploring the data you just manipulated. Then you would use the same response handling from the wrench script to, e.g, show returned images.
@JonB ok with scripts, but I'm searching for a way to use only Pythonista console with sage sarver (it is sometimes more convenient and fast to stay in console environment than to swipe from/to console and script editor).
Sorry for patience, I ask if Pythonista console module (ore some extra objc code) can be used by a wrench action to read console input written by user without pressing the return key on keyboard. I mean:
- I write in console the following:
- I don't press return key on keyboard but I press a wrench action C that reads the input and save it as a string in a variable (for example named "console_input"), so regardless of whether or not I pressed the return button on keyboard, if I write in console:
and press return, I'd like to see the output as a string (not evaluated, like if I do print('a=1.5')):
Can it be possible?
Hi, I’ve found something related to my question in this forum and I’ve tried to search in the web something about objc, but without success.
We know that the following code (found thanks to this forum) works for editor environment (as a common wrench action or user key defined with StatusBarMenu by JonB):
import editor, clipboard i=editor.get_line_selection() t=editor.get_text() clipboard.set(t[i:i-len(t)+len(editor.get_text())])
So I can, with one single click (two clicks if built-in wrench action is used), to copy in clipboard the entire line within the active cursor. How can I do the same in console environment?
I mean: in console environment I’m interested in searching a way (sequence of objc commands) to simulate in one single action the following actions:
- the touch with finger on the active text field in console (where user inserts the command to be launched);
- the selection of all text when pop-up is shown (with pop-up key
- the copy of the text into clipboard (with pop-up key
Copy) in order to post-process the string.
Thanks for any help about programmatic use of the built-in keys in the Pythonista pop-up (see here) when user touches screen on text in editor and console (probably through the use of objc).
@Matteo I think this code, as a tool, does what you want, try and tell me
from objc_util import * import clipboard def GetConsoleText(): win = ObjCClass('UIApplication').sharedApplication().keyWindow() main_view = win.rootViewController().view() ret = '' next_is_console = False def analyze(v,indent): global next_is_console ret = None for sv in v.subviews(): #print(indent,sv._get_objc_classname()) if 'UILabel' in str(sv._get_objc_classname()): if str(sv.text()) == '>': next_is_console = True else: next_is_console = False elif 'OMTextView' in str(sv._get_objc_classname()): #print(sv.text(),next_is_console) if next_is_console: return str(sv.text()) ret = analyze(sv,indent+' ') if ret: return ret ret = analyze(main_view,'') return ret r = GetConsoleText() clipboard.set(r)
@Matteo Still a better script: you run it once as a tool or not and it installs a "copy" button at left of the console
from objc_util import * import clipboard import ui def GetConsoleText(): win = ObjCClass('UIApplication').sharedApplication().keyWindow() main_view = win.rootViewController().view() ret = '' next_is_console = False def analyze(v,indent): global next_is_console ret = None for sv in v.subviews(): #print(indent,sv._get_objc_classname()) if 'UILabel' in str(sv._get_objc_classname()): #print(indent,sv.text()) if str(sv.text()) == '>': next_is_console = sv else: next_is_console = False elif 'OMTextView' in str(sv._get_objc_classname()): #print(sv.text(),next_is_console) if next_is_console: su = next_is_console.superview() for ssv in su.subviews(): if 'SUIButton_PY3'in str(ssv._get_objc_classname()): # rerun of this script, remove previous button ssv.removeFromSuperview() b = ui.Button(name='clipboard') b.tint_color ='red' b.image = ui.Image.named('iob:ios7_copy_outline_32') b.background_color = 'white' h = su.frame().size.height b.frame = (2,2,h-4,h-4) def test(sender): t = str(sender.console.text()) if t[-1] == '\n': t = t[:-1] clipboard.set(t) b.action = test b.console = sv retain_global(b) su.addSubview(ObjCInstance(b)) return str(sv.text()) ret = analyze(sv,indent+' ') if ret: return ret ret = analyze(main_view,'') return ret if __name__ == '__main__': r = GetConsoleText()
Hi @cvp, WOW, I am always fascinated when some people (like you) can write certain codes, here I feel like a child in a toy store! This forum is really full of creative people!
Sorry for delay of my response, your first script works very well, I was trying to understand it (without much success), I added it in @JonB StatusBarMenu as an action (I use this version, slightly modified, on my old little iPhone 5s with Pythonista version 3.1 - 301016, yes I still use that version ;-) ), but it is really too advanced for me, it is enough for me it works very well.
Then I tried your second script and I didn't think such that thing was possible: that is the customization of the input field in console environment!
Now (after some dive in objc) it is possible to process with one single touch each console input.
My next step is to understand how to recover functions, definitions and global variables like numbers, strings, arrays, etc... created in built-in Pythonista interpreter in order to pass them in the string in console as arguments of any function that must be executed by a remote interpreter (for example when the function is not available in Pythonista, I use a lot Scipy for example).
Many thanks for your help!
@Matteo Happy to help.
My last code has perhaps some lines too much because I did not remove (old) part for copying the console line in the clipboard without tapping the icon.
The action is actually named "test" but you can of course write there your own code
def test(sender): t = str(sender.console.text()) if t[-1] == '\n': t = t[:-1] sender.console.setText_('console_input="'+t+'"') clipboard.set(t)
You type a=1.5, then tap the "copy" icon, and you get
And you still have to type the return key...
Thank you @cvp , I think
GetConsoleText(), based on your previous post. Am I right? And I can't understand
console.setText_: what is it?
@Matteo no, no.
When I create the button b, I save as an attribute b.console, the associated textview sv.
When the button is pressed, its action is executed.
Sender is the button, thus sender.console is the associated textview.
sender.console.text is thus textview.text
We change the text and setText_() is a method of the textview to change its content
That's all folks 😀
Edit: this code is IN GetConsoleText thus I can't call it-self
@cvp thank you for explanation, unfortunately I have not familiarity with words like "attribute" and "method" (object-oriented programming). Anyway your code works so it is enough for me :-)