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 there an SVN crawler, that can walk thru an SVN repo and spitt out all existing branches, or tags?
Preferably in Perl or Python ...
SVN tags and branches are just directories, usually following a particular naming convention. You can easily get them in perl like:
my #branches = `svn ls YourRepoBaseURL/branches`;
chomp #branches; # remove newlines
chop #branches; # remove trailing /
my #tags = `svn ls YourRepoBaseURL/tags`;
chomp #tags;
chop #tags;
Here is a little snippet to print information about files in a SVN repository in python:
# svncrawler.py
import os
import sys
import pysvn
svn_client = pysvn.Client()
for file_status in svn_client.status(sys.argv[1]):
print u'SVN File %s %s' % (file_status, file_status.text_status)
Call it like this:
python svncrawler.py my_repository
It should be easy to modify it to just print the tags and branches.
Thanks for all the help, here is what I came up with in python with your help:
# -*- coding: utf-8 -*-
import os
import sys
import pysvn
svnclient = pysvn.Client()
projects = svnclient.list(sys.argv[1])
for project_path, project_info in projects:
try:
project_branches = svnclient.list(project_path.path + '/branches/')
if ( len(project_branches)>2 ):
for branch, info in project_branches:
print branch.path
except:
pass
I have a python script that I run to populate my database. I usually run the script inside shell_plus because of the dependencies required. Is there a way to load the script into shell_plus and run everything from my linux command line without actually opening the shell_plus interface?
"Standalone Django scripts"
You bet!
I don't even recommend using shell_plus. I tend to store my utilities scripts in my app utility folder. Then I simply call them from a cron job or manually as needed. Here is framework script I base this off of. (Somewhat simplified)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import logging
import time
import time
import optparse
# DO NOT IMPORT DJANGO MODELS HERE - THIS NEED TO HAPPEN BELOW!!
# This needs to be able to be run when django isn't in the picture (cron) so we need
# to be able to add in the django paths when needed.
def getArgs():
"""
Simply get the options for running update people.
"""
p = optparse.OptionParser()
help = "The Python path to a settings module, e.g. "
help += "\"myproject.settings.main\". If this isn't provided, the "
help += "DJANGO_SETTINGS_MODULE environment variable will be used."
p.add_option("-s", "--settings", action = "store", type = "string",
dest = "settings", help = help)
help = "A directory to add to the Python path, e.g."
help += " \"/home/djangoprojects/myproject\"."
p.add_option("-y", "--pythonpath", action = "store", type = "string",
dest = "pythonpath", help = help)
p.add_option("-v", "--verbose", action = "count", dest = "verbose",
help = "Turn on verbose debugging")
p.set_defaults(settings = os.environ.get("DJANGO_SETTINGS_MODULE",
"settings"),
pythonpath = "", verbose = 0,
)
return p.parse_args()
def update(opt, loglevel=None):
"""
This is the main script used for updating people
"""
start = time.time()
# This ensures that our sys.path is ready.
for path in opt.pythonpath.split(":"):
if os.path.abspath(path) not in sys.path and os.path.isdir(path):
sys.path.append(os.path.abspath(path))
os.environ['DJANGO_SETTINGS_MODULE'] = opt.settings
from django.conf import settings
try:
if settings.SITE_ROOT not in sys.path: pass
except ImportError:
return("Your setting file cannot be imported - not in sys.path??")
# IMPORT YOUR CODE MODELS HERE
from apps.core.utility.ExampleExtractor import ExampleExtractor
# YOUR DJANGO STUFF GOES HERE..
example = ExampleExtractor(loglevel=loglevel, singleton=not(opt.multiple))
raw = example.get_raw()
results = example.update_django(raw)
log.info("Time to update %s entries : %s" % (len(results), time.time() - start))
return results
if __name__ == '__main__':
logging.basicConfig(format = "%(asctime)s %(levelname)-8s %(module)s \
%(funcName)s %(message)s", datefmt = "%H:%M:%S", stream = sys.stderr)
log = logging.getLogger("")
log.setLevel(logging.DEBUG)
opts, args = getArgs()
sys.exit(update(opts))
HTH!
I currently have multiple Django sites running from one Apache server through WSGI and each site has their own virtualenv with possibly slight Python and Django version difference. For each site, I want to display the Python and Django version it is using as well as from which path it's pulling the Python binaries from.
For each Django site, I can do:
import sys
sys.version
but I'm not sure if it's showing the Python that the site is using or the system's Python. Any help on that?
To find out which Python is being used, log the value of sys.executable. It should contain the path to the interpreter being used.
but I'm not sure if it's showing the Python that the site is using or the system's Python. Any help on that?
Nope.
However, if you want to know something about your Django app, do this.
Use logging. Write to sys.stderr that's usually routed to errors_log by mod_wsgi. Or look inside the request for the wsgi.errors file.
Update your top-level urls.py to write a startup message, including sys.version to your log.
This is a bit more than what you asked for, but we have a similar situation where different directories are (possibly) using different Python/Django code. We use following to build an easy to look at list (at least for me) of Python & Django versions plus all modules and where they were loaded from. It has sometimes helped save what little there is left of my hair. Modify to taste.
import sys, re, os
import django
def ModuleList():
ret = []
# The double call to dirname() is because this file is in our utils/
# directory, which is one level down from the top of the project.
dir_project = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
project_name = os.path.basename(dir_project)
for k,v in sys.modules.items():
x = str(v)
if 'built-in' in x:
ret.append((k, 'built-in'))
continue
m = re.search(r"^.*?'(?P<module>.*?)' from '(?P<file>.*?)'.*$", x)
if m:
d = m.groupdict()
f = d['file']
# You can skip all of the re.sub()'s if you want raw paths
f = re.sub(r'/usr/.*?/lib/python[.0-9]*/site-packages/django/', 'system django >> ', f)
f = re.sub(r'/usr/.*?/lib/python[.0-9]*/site-packages/', 'site-packages >> ', f)
f = re.sub(r'/usr/.*?/lib/python[.0-9]*/', 'system python >> ', f)
f = re.sub(dir_project+'.*python/', 'local python >> ', f)
f = re.sub(dir_project+'.*django/', 'local django >> ', f)
f = re.sub(dir_project+r'(/\.\./)?', project_name + ' >> ', f)
ret.append((d['module'], f))
ret.sort( lambda a,b: cmp(a[0].lower(), b[0].lower()) )
ret.insert(0, ('Python version', sys.version) )
ret.insert(0, ('Django version', django.get_version()) )
return ret
# ModuleList
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