Related
It is the first time I write as I really didn't find any solution to my issue.
I want to allow my user to launch some Python program from Excel.
So i have this VBA code at some point:
lg_ErrorCode = wsh.Run(str_PythonPath & " " & str_PythonArg, 1, bl_StopVBwhilePython)
If lg_ErrorCode <> 0 Then
MsgBox "Couldn't run python script! " _
+ vbCrLf + CStr(lg_ErrorCode)
Run_Python = False
End If
str_PythonPath = "C:\Python34\python.exe C:\Users\XXXX\Documents\4_Python\Scan_FTP\test.py"
str_PythonArg = "arg1 arg2"
After multiple testing, the row in error in Python is when I try to import another module (I precise that this VBA code is working without the below row in Python):
import fct_Ftp as ftp
The architecture of the module is as follow:
4_Python
-folder: Scan_FTP
- file: test.py (The one launch from VBA)
-file: fct_Ftp.py
(For information, I change the architecture of the file, and try to copy the file at some other position just to test without success)
The import has no problem when I launch Test.py directly with:
import sys, os
sys.path.append('../')
But from VBA, this import is not working.
So I figured out this more generic solution, that dont work as well from Excel/VBA
import sys, os
def m_importingFunction():
str_absPath = os.path.abspath('')
str_absPathDad = os.path.dirname(str_absPath)
l_absPathSons = [os.path.abspath(x[0]) for x in os.walk('.')]
l_Dir = l_absPathSons + [str_absPathDad]
l_DirPy = [Dir for Dir in l_Dir if 'Python' in Dir]
for Dir in l_DirPy:
sys.path.append(Dir)
print(Dir)
m_importingFunction()
try:
import fct_Ftp as ftp
# ftp = __import__ ("fct_Ftp")
write += 'YAAAA' # write a file YAAAA from Python
except:
write += 'NOOOOOO' # write a file NOOOOO from VBA
f= open(write + ".txt","w+")
f.close()
Can you please help me as it is a very tricky questions ?
Many thanks to you guys.
You are able to start your program from the command line?
Why not create a batch file with excel which you then start in a shell?
Is it possible for python to accept input like this:
Folder name: Download
But instead of the user typing "Download" it is already there as a initial value. If the user wants to edit it as "Downloads" all he has to do is add a 's' and press enter.
Using normal input command:
folder=input('Folder name: ')
all I can get is a blank prompt:
Folder name:
Is there a simple way to do this that I'm missing?
The standard library functions input() and raw_input() don't have this functionality. If you're using Linux you can use the readline module to define an input function that uses a prefill value and advanced line editing:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt) # or raw_input in Python 2
finally:
readline.set_startup_hook()
I'm assuming you mean from the command-line. I've never seen initial values for command line prompts, they're usually of the form:
Folder [default] :
which in code is simply:
res = raw_input('Folder [default] : ')
res = res or 'default'
Alternatively, you can try to do something using the curses module in Python.
This works in windows.
import win32console
_stdin = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
def input_def(prompt, default=''):
keys = []
for c in unicode(default):
evt = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
evt.Char = c
evt.RepeatCount = 1
evt.KeyDown = True
keys.append(evt)
_stdin.WriteConsoleInput(keys)
return raw_input(prompt)
if __name__ == '__main__':
name = input_def('Folder name: ')
print
print name
I finally found a simple alternative that works on Windows and Linux. Essentially, i'm using the pyautogui module to simulate the user's input. in practice, it looks like this:
from pyautogui import typewrite
print("enter folder name: ")
typewrite("Default Value")
folder = input()
A Word of Warning:
Theoretically, the user can insert characters in the middle of the "default" input by pressing a key before typewrite finishes.
pyautogui is notoriously unreliable on headless systems, so make sure to provide a backup solution in case the import fails. If you run into No module named 'Xlib', try to install the python3-xlib or python-xlib package (or the xlib module). Running over ssh can also be a problem.
An example fallback implementation:
Since a missing X-server can logically only happen on linux, here's an implementation that uses sth's answer as fallback:
try:
from pyautogui import typewrite
autogui = True
except (ImportError, KeyError):
import readline
autogui = False
def rlinput(prompt, prefill=''):
if autogui:
print(prompt)
typewrite(prefill)
return input()
else:
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
I think that the best (the easiest and most portable) solution is a combination of #rlotun and #Stephen answers:
default = '/default/path/'
dir = raw_input('Folder [%s]' % default)
dir = dir or default
I would like to suggest using the clipboard to solve this problem. Paste the clipboard into the input line, edit as required, press enter. Variable clpstack is used to protect existing clipboard contents. This code is for Windows. Linux could use import clipboard.
import pyperclip as clp
clpstack=clp.paste()
clp.copy("192.168.4.1")
HOST = input("Enter telnet host: ")
clp.copy(clpstack)
I found PyInquirer to be very helpful, especially when building interactive console applications frequently. Prompting a user with a default modifiable value would look as follows:
from PyInquirer import prompt
question = [
{
'type': 'input',
'name': 'first_name',
'message': 'Name please',
'default': 'Max'
}
]
answer = prompt(question)
print('Hello {}'.format(answer['first_name']))
Recently faced this problem. None of the above answers seem to be flawless. So I did some research, and found the following solution to be the easiest, and works both for Windows and Linux:
import keyboard
def input_with_default(prompt_, default_):
keyboard.write(default_)
return input(prompt_)
if __name__ == "__main__":
print(input_with_default("Please enter: ", "hello world"))
I like this, It works on window
def inputWdefault(prompt, default):
bck = chr(8) * len(default)
ret = input(prompt + default + bck)
return ret or default
I liked the approach taken by #MCO so I refactored the code. I tested it on X Windows and Microsoft Windows 10 WSL 2 using Microsoft Terminal:
def input_with_default(prompt, prefill=''):
try:
from pyautogui import typewrite
print(prompt)
typewrite(prefill)
return input()
except (ImportError, KeyError):
import readline
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
Not the best aproach but for the sake of sharing...
You could use Javascript to get all sort of inputs in IPython Notebook.
from IPython.display import HTML
newvar = ""
htm = """
<input id="inptval" style="width:60%;" type="text" value="This is an editable default value.">
<button onclick="set_value()" style="width:20%;">OK</button>
<script type="text/Javascript">
function set_value(){
var input_value = document.getElementById('inptval').value;
var command = "newvar = '" + input_value + "'";
var kernel = IPython.notebook.kernel;
kernel.execute(command);
}
</script>
"""
HTML(htm)
On the next cell you can use the new variable:
print newvar
We can use Tkinter and use a StringVar to do this. The limitation is that the input is through a Tkinter window.
from tkinter import Tk, LEFT, BOTH, StringVar
from tkinter.ttk import Entry, Frame
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Entry")
self.pack(fill=BOTH, expand=1)
self.contents = StringVar()
# give the StringVar a default value
self.contents.set('test')
self.entry = Entry(self)
self.entry.pack(side=LEFT, padx=15)
self.entry["textvariable"] = self.contents
self.entry.bind('<Key-Return>', self.on_changed)
def on_changed(self, event):
print('contents: {}'.format(self.contents.get()))
return True
def main():
root = Tk()
ex = Example(root)
root.geometry("250x100+300+300")
root.mainloop()
if __name__ == '__main__':
main()
If you are writing a CLI, you might want to consider using the python-click library for this.
You would achieve your goal with the following code:
import click
user_input = click.prompt(text="Folder name", default="Download")
print(f"{user_input=}")
If you run this code, and type in nothing, then you get:
$ python3 cli_code.py
Folder name [Download]:
user_input='Download'
If you run this code, and type in 'my-dir', then you get:
$ python3 cli_code.py
Folder name [Download]: my-dir
user_input='my-dir'
Try using an "f-string" and "or" combination, say:
default_name = "that_folder"
this_folder = input(f"Folder name: ({default_name}) ") or default_name
print(this_folder)
If you hit Return without typing in the folder name, the default_name will be assumed.
This is not a very Good Answer but it is a work around for windows. As hard as I tried, I could not get Readline or pyReadline to work on my Windows10 computer with Python Ver 3.5. So I wrote this instead. Not the best code in the world since I've only been using Python for 3 months. But it works.
import os
def note_input(defaultvalue):
#Create a textfile
txtfile = open("txtfile.txt", "w")
#
# populate it with the default value
txtfile.write(defaultvalue)
txtfile.close()
#
# call Notepad
os.system("notepad.exe txtfile.txt")
# input("Just holding until notepad is close : ") (did not need this line)
#
# get the Value Entered/Changed in Notepad
txtfile = open("txtfile.txt", "r")
func_value = txtfile.read()
txtfile.close()
return func_value
# END DEF
Notepad stopped the program from running until it was closed, so the input() line below it was not needed. Once notepad was opened the first time and placed where I wanted it on the screen, It was like a popup input window. I assume you can use any text editor like Notepad++ or Scripe or Code Writer, etc.
If you do that, the user would have to delete the existing word. What about providing a default value if the user hits "return"?
>>> default_folder = "My Documents"
>>> try: folder = input("folder name [%s]:" %default_folder)
... except SyntaxError: folder = default_folder
I am using the SWIG generated Python wrappers for GDCM (comes with gdcm.py).
I am running the following Python3 script.
import gdcm
import sys
filename="path_to_data/gdcm_test.dcm"
r = gdcm.Reader()
r.SetFileName(filename)
r.Read()
f=r.GetFile()
ds = f.GetDataSet()
csa_t1 = gdcm.CSAHeader()
t1 = csa_t1.GetCSAImageHeaderInfoTag()
csa_t1.LoadFromDataElement(ds.GetDataElement( t1))
csa_t1.Print(sys.stdout)
The relevant snippet from the gdcmswig.py file (with the function that wraps Print) is below.
def Print(self, os: 'std::ostream &') -> "void":
"""
void
gdcm::CSAHeader::Print(std::ostream &os) const
Print the CSAHeader (use only if Format == SV10 or NOMAGIC)
"""
return _gdcmswig.CSAHeader_Print(self, os)
The problem appears on the last line of my script. The call to Print(sys.stdout).
TypeError: in method 'CSAHeader_Print', argument 2 of type 'std::ostream &'
The problem, I think, is that Python’s sys.stdout is not the actual output file handle, but wraps the handle. What is the best way to solve this? Thanks in advance.
In C++, I can print debug output like this:
printf(
"FILE: %s, FUNC: %s, LINE: %d, LOG: %s\n",
__FILE__,
__FUNCTION__,
__LINE__,
logmessage
);
How can I do something similar in Python?
There is a module named inspect which provides these information.
Example usage:
import inspect
def PrintFrame():
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename) # __FILE__ -> Test.py
print(info.function) # __FUNCTION__ -> Main
print(info.lineno) # __LINE__ -> 13
def Main():
PrintFrame() # for this line
Main()
However, please remember that there is an easier way to obtain the name of the currently executing file:
print(__file__)
For example
import inspect
frame = inspect.currentframe()
# __FILE__
fileName = frame.f_code.co_filename
# __LINE__
fileNo = frame.f_lineno
There's more here http://docs.python.org/library/inspect.html
Building on geowar's answer:
class __LINE__(object):
import sys
def __repr__(self):
try:
raise Exception
except:
return str(sys.exc_info()[2].tb_frame.f_back.f_lineno)
__LINE__ = __LINE__()
If you normally want to use __LINE__ in e.g. print (or any other time an implicit str() or repr() is taken), the above will allow you to omit the ()s.
(Obvious extension to add a __call__ left as an exercise to the reader.)
You can refer my answer:
https://stackoverflow.com/a/45973480/1591700
import sys
print sys._getframe().f_lineno
You can also make lambda function
I was also interested in a __LINE__ command in python.
My starting point was https://stackoverflow.com/a/6811020 and I extended it with a metaclass object. With this modification it has the same behavior like in C++.
import inspect
class Meta(type):
def __repr__(self):
# Inspiration: https://stackoverflow.com/a/6811020
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
# print(info.filename) # __FILE__ -> Test.py
# print(info.function) # __FUNCTION__ -> Main
# print(info.lineno) # __LINE__ -> 13
return str(info.lineno)
class __LINE__(metaclass=Meta):
pass
print(__LINE__) # print for example 18
wow, 7 year old question :)
Anyway, taking Tugrul's answer, and writing it as a debug type method, it can look something like:
def debug(message):
import sys
import inspect
callerframerecord = inspect.stack()[1]
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename, 'func=%s' % info.function, 'line=%s:' % info.lineno, message)
def somefunc():
debug('inside some func')
debug('this')
debug('is a')
debug('test message')
somefunc()
Output:
/tmp/test2.py func=<module> line=12: this
/tmp/test2.py func=<module> line=13: is a
/tmp/test2.py func=<module> line=14: test message
/tmp/test2.py func=somefunc line=10: inside some func
import inspect
.
.
.
def __LINE__():
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back.f_lineno
def __FILE__():
return inspect.currentframe().f_code.co_filename
.
.
.
print "file: '%s', line: %d" % (__FILE__(), __LINE__())
Here is a tool to answer this old yet new question!
I recommend using icecream!
Do you ever use print() or log() to debug your code? Of course, you
do. IceCream, or ic for short, makes print debugging a little sweeter.
ic() is like print(), but better:
It prints both expressions/variable names and their values.
It's 40% faster to type.
Data structures are pretty printed.
Output is syntax highlighted.
It optionally includes program context: filename, line number, and parent function.
For example, I created a module icecream_test.py, and put the following code inside it.
from icecream import ic
ic.configureOutput(includeContext=True)
def foo(i):
return i + 333
ic(foo(123))
Prints
ic| icecream_test.py:6 in <module>- foo(123): 456
To get the line number in Python without importing the whole sys module...
First import the _getframe submodule:
from sys import _getframe
Then call the _getframe function and use its' f_lineno property whenever you want to know the line number:
print(_getframe().f_lineno) # prints the line number
From the interpreter:
>>> from sys import _getframe
... _getframe().f_lineno # 2
Word of caution from the official Python Docs:
CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.
In other words: Only use this code for personal testing / debugging reasons.
See the Official Python Documentation on sys._getframe for more information on the sys module, and the _getframe() function / submodule.
Based on Mohammad Shahid's answer (above).
Is it possible for python to accept input like this:
Folder name: Download
But instead of the user typing "Download" it is already there as a initial value. If the user wants to edit it as "Downloads" all he has to do is add a 's' and press enter.
Using normal input command:
folder=input('Folder name: ')
all I can get is a blank prompt:
Folder name:
Is there a simple way to do this that I'm missing?
The standard library functions input() and raw_input() don't have this functionality. If you're using Linux you can use the readline module to define an input function that uses a prefill value and advanced line editing:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt) # or raw_input in Python 2
finally:
readline.set_startup_hook()
I'm assuming you mean from the command-line. I've never seen initial values for command line prompts, they're usually of the form:
Folder [default] :
which in code is simply:
res = raw_input('Folder [default] : ')
res = res or 'default'
Alternatively, you can try to do something using the curses module in Python.
This works in windows.
import win32console
_stdin = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
def input_def(prompt, default=''):
keys = []
for c in unicode(default):
evt = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
evt.Char = c
evt.RepeatCount = 1
evt.KeyDown = True
keys.append(evt)
_stdin.WriteConsoleInput(keys)
return raw_input(prompt)
if __name__ == '__main__':
name = input_def('Folder name: ')
print
print name
I finally found a simple alternative that works on Windows and Linux. Essentially, i'm using the pyautogui module to simulate the user's input. in practice, it looks like this:
from pyautogui import typewrite
print("enter folder name: ")
typewrite("Default Value")
folder = input()
A Word of Warning:
Theoretically, the user can insert characters in the middle of the "default" input by pressing a key before typewrite finishes.
pyautogui is notoriously unreliable on headless systems, so make sure to provide a backup solution in case the import fails. If you run into No module named 'Xlib', try to install the python3-xlib or python-xlib package (or the xlib module). Running over ssh can also be a problem.
An example fallback implementation:
Since a missing X-server can logically only happen on linux, here's an implementation that uses sth's answer as fallback:
try:
from pyautogui import typewrite
autogui = True
except (ImportError, KeyError):
import readline
autogui = False
def rlinput(prompt, prefill=''):
if autogui:
print(prompt)
typewrite(prefill)
return input()
else:
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
I think that the best (the easiest and most portable) solution is a combination of #rlotun and #Stephen answers:
default = '/default/path/'
dir = raw_input('Folder [%s]' % default)
dir = dir or default
I would like to suggest using the clipboard to solve this problem. Paste the clipboard into the input line, edit as required, press enter. Variable clpstack is used to protect existing clipboard contents. This code is for Windows. Linux could use import clipboard.
import pyperclip as clp
clpstack=clp.paste()
clp.copy("192.168.4.1")
HOST = input("Enter telnet host: ")
clp.copy(clpstack)
I found PyInquirer to be very helpful, especially when building interactive console applications frequently. Prompting a user with a default modifiable value would look as follows:
from PyInquirer import prompt
question = [
{
'type': 'input',
'name': 'first_name',
'message': 'Name please',
'default': 'Max'
}
]
answer = prompt(question)
print('Hello {}'.format(answer['first_name']))
Recently faced this problem. None of the above answers seem to be flawless. So I did some research, and found the following solution to be the easiest, and works both for Windows and Linux:
import keyboard
def input_with_default(prompt_, default_):
keyboard.write(default_)
return input(prompt_)
if __name__ == "__main__":
print(input_with_default("Please enter: ", "hello world"))
I like this, It works on window
def inputWdefault(prompt, default):
bck = chr(8) * len(default)
ret = input(prompt + default + bck)
return ret or default
I liked the approach taken by #MCO so I refactored the code. I tested it on X Windows and Microsoft Windows 10 WSL 2 using Microsoft Terminal:
def input_with_default(prompt, prefill=''):
try:
from pyautogui import typewrite
print(prompt)
typewrite(prefill)
return input()
except (ImportError, KeyError):
import readline
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
Not the best aproach but for the sake of sharing...
You could use Javascript to get all sort of inputs in IPython Notebook.
from IPython.display import HTML
newvar = ""
htm = """
<input id="inptval" style="width:60%;" type="text" value="This is an editable default value.">
<button onclick="set_value()" style="width:20%;">OK</button>
<script type="text/Javascript">
function set_value(){
var input_value = document.getElementById('inptval').value;
var command = "newvar = '" + input_value + "'";
var kernel = IPython.notebook.kernel;
kernel.execute(command);
}
</script>
"""
HTML(htm)
On the next cell you can use the new variable:
print newvar
We can use Tkinter and use a StringVar to do this. The limitation is that the input is through a Tkinter window.
from tkinter import Tk, LEFT, BOTH, StringVar
from tkinter.ttk import Entry, Frame
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Entry")
self.pack(fill=BOTH, expand=1)
self.contents = StringVar()
# give the StringVar a default value
self.contents.set('test')
self.entry = Entry(self)
self.entry.pack(side=LEFT, padx=15)
self.entry["textvariable"] = self.contents
self.entry.bind('<Key-Return>', self.on_changed)
def on_changed(self, event):
print('contents: {}'.format(self.contents.get()))
return True
def main():
root = Tk()
ex = Example(root)
root.geometry("250x100+300+300")
root.mainloop()
if __name__ == '__main__':
main()
If you are writing a CLI, you might want to consider using the python-click library for this.
You would achieve your goal with the following code:
import click
user_input = click.prompt(text="Folder name", default="Download")
print(f"{user_input=}")
If you run this code, and type in nothing, then you get:
$ python3 cli_code.py
Folder name [Download]:
user_input='Download'
If you run this code, and type in 'my-dir', then you get:
$ python3 cli_code.py
Folder name [Download]: my-dir
user_input='my-dir'
Try using an "f-string" and "or" combination, say:
default_name = "that_folder"
this_folder = input(f"Folder name: ({default_name}) ") or default_name
print(this_folder)
If you hit Return without typing in the folder name, the default_name will be assumed.
This is not a very Good Answer but it is a work around for windows. As hard as I tried, I could not get Readline or pyReadline to work on my Windows10 computer with Python Ver 3.5. So I wrote this instead. Not the best code in the world since I've only been using Python for 3 months. But it works.
import os
def note_input(defaultvalue):
#Create a textfile
txtfile = open("txtfile.txt", "w")
#
# populate it with the default value
txtfile.write(defaultvalue)
txtfile.close()
#
# call Notepad
os.system("notepad.exe txtfile.txt")
# input("Just holding until notepad is close : ") (did not need this line)
#
# get the Value Entered/Changed in Notepad
txtfile = open("txtfile.txt", "r")
func_value = txtfile.read()
txtfile.close()
return func_value
# END DEF
Notepad stopped the program from running until it was closed, so the input() line below it was not needed. Once notepad was opened the first time and placed where I wanted it on the screen, It was like a popup input window. I assume you can use any text editor like Notepad++ or Scripe or Code Writer, etc.
If you do that, the user would have to delete the existing word. What about providing a default value if the user hits "return"?
>>> default_folder = "My Documents"
>>> try: folder = input("folder name [%s]:" %default_folder)
... except SyntaxError: folder = default_folder