Running tcl script from python with arguments - python

I'm trying to run tcl script from python,
tcl script requires command line arguments to execute, when I source the tcl file from python, it then shows the error says
tclsh.eval('source "test.tcl"' )
_tkinter.TclError: can't read "::argv": no such variable
I've done many searches, majority of them asks how to pass arguments to python in tcl.
python code
import tkinter
import sys
tclsh.eval('source "test.tcl"' )
if __name__ == '__main__':
print("hi")
tcl code
puts [lindex $::argv 0]
Is there anyway for me pass python arguments to tcl ?
or
not pass arguments and still compile ?
since if I compile tcl script only without arguments, it still compiles
Note:
In tkinter documentation it says tkinter.Tk is
The Tk class is instantiated without arguments
Is there a way to instantiated with arguments ?
Sol:
tclsh.eval('set argv [list]')
tclsh.eval('set argc 0')
I tried to set a global variable and it works for me under python 3.6

The global argv variable is, apart from being set during the startup of a standard Tcl script, not special in any way. You can therefore just set it prior to doing source. In this case, doing so with lappend in a loop is probably best as it builds the structure of the variable correctly. There are two other variables that ought to be set as well (argc and argv0); overall you do it like this (as a convenient function):
def run_tcl_script(script_name, *args):
tclsh.eval('set argv0 {{{}}}'.format(script_name))
tclsh.eval('set argv {}; set argc 0')
for a in args:
tclsh.eval('lappend argv {{{}}}; incr argc'.format(a))
tclsh.eval('source $argv0')
The {{{}}} with Python's str.format results in a single layer of braces being put around the argument, defending against most quoting issues.

Related

How do I run a python file with arguments as variables from another python file?

I'm working on cloning a Virtual Machine (VM) in vCenter environment using this code. It takes command line arguments for name of the VM, template, datastore, etc. (e.g. $ clone_vm.py -s <host_name> -p < password > -nossl ....)
I have another Python file where I've been able to list the Datastore volumes in descending order of free_storage. I have stored the datastore with maximum available storage in a variable ds_max. (Let's call this ds_info.py)
I would like to use ds_max variable from ds_info.py as a command line argument for datastore command line argument in clone_vm.py.
I tried importing the os module in ds_info.py and running os.system(python clone_vm.py ....arguments...) but it did not take the ds_max variable as an argument.
I'm new to coding and am not confident to change the clone_vm.py to take in the Datastore with maximum free storage.
Thank you for taking the time to read through this.
I suspect there is something wrong in your os.system call, but you don't provide it, so I can't check.
Generally it is a good idea to use the current paradigm, and the received wisdom (TM) is that we use subprocess. See the docs, but the basic pattern is:
from subprocess import run
cmd = ["mycmd", "--arg1", "--arg2", "val_for_arg2"]
run(cmd)
Since this is just a list, you can easily drop arguments into it:
var = "hello"
cmd = ["echo", var]
run(cmd)
However, if your other command is in fact a python script it is more normal to refactor your script so that the main functionality is wrapped in a function, called main by convention:
# script 2
...
def main(arg1, arg2, arg3):
do_the_work
if __name__ == "__main__":
args = get_sys_args() # dummy fn
main(*args)
Then you can simply import script2 from script1 and run the code directly:
# script 1
from script2 import main
args = get_args() # dummy fn
main(*args)
This is 'better' as it doesn't involve spawning a whole new python process just to run python code, and it generally results in neater code. But nothing stops you calling a python script the same way you'd call anything else.

How Tkinter font.families() function gets a list of available font?

I'm making GUI application using Tkinter and I'm using tkinter.font.families() for making pull down list of selecting fonts.
Now, I'm wondering how this function works
I tried reading the code but I can't understand it because of my lack of enough knowledge...
def families(root=None, displayof=None):
"Get font families (as a tuple)"
if not root:
root = tkinter._default_root
args = ()
if displayof:
args = ('-displayof', displayof)
return root.tk.splitlist(root.tk.call("font", "families", *args))
This is a code of the function(https://github.com/python/cpython/blob/c4928fc1a853f3f84e2b4ec1253d0349137745e5/Lib/tkinter/font.py#L180).
my qeustion is
What is the meaning of args = ('-displayof', displayof)?
Where does tk.call() function came from?(I can't find this function in Tk class)
How does tk.call("font", "families", *args) function works?
Thank you for reading my question.
What is the meaning of args = ('-displayof', displayof)?
Tkinter is a small python wrapper around a tcl interepreter which has the tk package installed. All of the actual work of creating widgets, querying fonts, etc, is done by the tcl/tk libraries.
Tcl/tk commands use a leading dash to represent an option name, and the option is typically followed by a value. This code is preparing to call a tcl function, so it is building up a list of arguments required by tcl.
In this specific case, it's adding the -displayof option if the displayof argument was passed to the function.
Where does tk.call() function came from?
It comes from a C-based library. It is defined in a file named _tkinter.c. More specifically, it's the function Tkapp_Call. It is a small wrapper that allows python to execute commands within an embedded tcl interpreter.
How does tk.call("font", "families", *args) function works?
The tk library has a command named font. From within a tcl interpreter you would call it with something like this:
font families
The code tk.call("font", "families", *args) is simply sending that command to the tcl interpreter.
The underlying tcl/tk library has platform-specific functions for dealing with fonts. See tkMacOSXFont.c, tkUnixFont.c, and tkWinFont.c.

How to pass args to embedded python sub-interpreter?

I need to interpret few files (scripts) by embedded python interpreter concurrently (to be more detailed one script executes another script as Popen and my app intercepts it and executes it itself). I've found it's called sub-interpreter and i'm going to use it. But i've read sub-interpreter does not have sys.argv:
The new environment has no sys.argv variable
I need to pass argv anyway so how can i do it?
You might find it easier to modify each of the scripts follow the pattern:
def run(*posargs, **argdict):
"""
This does the work and can be called with:
import scriptname
scriptname.run(someargs)
"""
# Code goes here and uses posargs[n] where it would use sys.argv[n+1]
if __name__ == "__main__":
import sys
run(sys.argv[1:])
Then your main script can just call each of the subscripts in turn by simply calling the run method.
You can use environment variables. Have the parent set them by updating the dict os.environ if it's in Python, or setenv() if in C or C++ etc. Then the children can read os.environ to get whatever strings they need.

Writing a Python script to execute with a console command

Research is at the bottom, read before -1'ing... Thanks.
I have to write a Python script that runs SQL queries. I made a main class and called SQLQuery. Each SQLQuery instance represents a query. The script must be structured like this:
class SQLQuery(object):
def __init___(self, string_myQuery)...
instance1 = SQLQuery(SQLQuery1)...
instance2 = SQLQuery(SQLQuery2)...
As a user requirement, the instances must be in the same file as the class (so I can't just make each instance a main and execute that file separately), and each instance must be executed with Linux console commands. I can execute the entire script with a simple python SQLQuery.py but I need to execute each instance separately. The queries will be executed every day, automatically, so I don't need a terminal UI tree. It should be executed with a command similar to this:
python SQLQuery.py -inst1
will execute instance1.
python SQLQuery.py -inst2
will execute instance2.
I have researched how to execute Python scripts with Linux commands and most of the articles are about calling commands from the Python script. However, I found this article from the Python documentation. It suggests adding -m, so:
python SQLQuery.py -m inst1
This would let me set my main with a console command, but it doesn't work since the instances aren't modules. And since the instances must be in the same file as the class, I can't just import them as a module when I execute SQLQuery.py with a console command.
Ignoring all the irrelevancies, it sounds like your problem is that you have a bunch of global objects named instance1, instance2, instance3, etc., and you want to call some method on one of them based on a command-line parameter whose value will be similar to, but not identical to, the instance names.
That's probably not a good idea… but it's not that hard:
if __name__ == '__main__':
inst = sys.argv[1] # will be 'inst1', 'inst13', etc.
inst_number = inst[5:] # so '1', '13', etc.
inst_name = 'instance' + inst_number
instance = globals()[inst_name]
instance.execute()
A much better way to do the same thing is to put the instance globals into a list or dict that you can index.
For example, let's say instead of instance1, instance2, etc., you've got an instances dict, with instances['1'], instances[2], etc. Now instead of this:
inst_name = 'instance' + inst_number
instance = globals()[inst_name]
instance.execute()
… you just do this:
instances[inst_number].execute()
Also, instead of coming up with a command-line parameter that has extra stuff in it that you have to parse and throw away, and has no more meaning for a human reader than for your code, why not just take a number?
python myscript.py 12
Or, alternatively, use argparse to create an argument that can be used in all of the obvious ways:
python myscript.py --instance=12
python myscript.py --instance 12
python myscript.py -i12
python myscript.py -i 12
Either way, your code gets the string '12', which it can then use to look up the function, as above.
You have the wrong syntax for the -m option. Suppose you have the following file named foo.py:
import sys
print 'First arg is: ', sys.argv[1]
Then you would call it like this:
$ python -m foo bar
First arg is: bar
Note that the ".py" extension is omitted. You can then use the command line argument to decide which object to use or use the argparse or optparse module to handle the argument.

Python script argument conditional

Is anyone able to tell me how to write a conditional for an argument on a python script? I want it to print "Argument2 Entered" if it is run with a second command line arguments such as:
python script.py argument1 argument2
And print "No second argument" if it is run without command line arguments, like this:
python script.py argument1
Is this possible?
import sys
if len(sys.argv)==2: # first entry in sys.argv is script itself...
print "No second argument"
elif len(sys.argv)==3:
print "Second argument"
There are many answers to this, depending on what exactly you want to do and how much flexibility you are likely to need.
The simplest solution is to examine the variable sys.argv, which is a list containing all of the command-line arguments. (It also contains the name of the script as the first element.) To do this, simply look at len(sys.argv) and change behaviour based on its value.
However, this is often not flexible enough for what people expect command-line programs to do. For example, if you want a flag (-i, --no-defaults, ...) then it's not obvious how to write one with just sys.argv. Likewise for arguments (--dest-dir="downloads"). There are therefore many modules people have written to simplify this sort of argument parsing.
The built-in solution is argparse, which is powerful and pretty easy-to-use but not particularly concise.
A clever solution is plac, which inspects the signature of the main function to try to deduce what the command-line arguments should be.
There are many ways to do this simple thing in Python. If you are interested to know more than I recommend to read this article. BTW I am giving you one solution below:
import click
'''
Prerequisite: # python -m pip install click
run: python main.py ttt yyy
'''
#click.command(context_settings=dict(ignore_unknown_options=True))
#click.argument("argument1")
#click.argument("argument2")
def main(argument1, argument2):
print(f"argument1={argument1} and argument2={argument2}")
if __name__ == '__main__':
main()
Following block should be self explanatory
$ ./first.py second third 4th 5th
5
$ cat first.py
#!/usr/bin/env python
import sys
print (len(sys.argv))
This is related to many other posts depending upon where you are going with this, so I'll put four here:
What's the best way to grab/parse command line arguments passed to a Python script?
Implementing a "[command] [action] [parameter]" style command-line interfaces?
How can I process command line arguments in Python?
How do I format positional argument help using Python's optparse?
But the direct answer to your question from the Python docs:
sys.argv -
The list of command line arguments passed to a Python script. argv[0] is the script name (it is operating system dependent whether this is a full pathname or not). If the command was executed using the -c command line option to the interpreter, argv[0] is set to the string '-c'. If no script name was passed to the Python interpreter, argv[0] is the empty string.
To loop over the standard input, or the list of files given on the command line, see the fileinput module.

Categories