I'm trying to untar xz/bx2/gz files in the init section of my class. I'm using the following code :
class myClass(object):
def __init__(self, *args):
for i in args:
try:
f = tarfile.open(i)
print("Extracting ", i)
f.extractall()
f.close()
except tarfile.ReadError:
print("File not a tarball, or any of .xz/.bz2/.gz archives.")
exit()
if __name__ == "__main__":
<???>
The only problem here is, I'm not sure what to call after "main", in order to initialize and run the init method. I've just started out, and am a bit unclear.
If I'm to write a function named unpack() which does the untarring rather than putting it under init, i know i can do something like :
if __name__ == "__main__":
start = myClass()
start.unpack()
Since I want to do the unpacking in init itself, how would I do it in this case ?
Edit:
Sorry in case I'm not clear, I'm trying to run this script from the command line as :
# python script.py file1.tar.bz2 file2.tar.bz2 file3.tar.bz2
So the *args should be populated with the file names, and hence the code to extract it should run, atleast from what I know.
Thank you,
You just call myClass() :
if __name__ == "__main__":
start = myClass(sys.argv)
Related
I would like to create a python file that can be run from the terminal - this file will be in charge of running various other python files depending on the functionality required along with their required arguments, respectively. For example, this is the main file:
import sys
from midi_to_audio import arguments, run
files = ["midi_to_audio.py"]
def main(file, args):
if file == "midi_to_audio.py":
if len(args) != arguments:
print("Incorrect argument length")
else:
run("test","t")
if __name__ == '__main__':
sys.argv.pop(0)
file = sys.argv[0]
sys.argv.pop(0)
if file not in files:
print("File does not exist")
else:
main(file, sys.argv)
And this is the first file used in the example (midi_to_audio.py):
arguments = 2
def run(file, output_file):
print("Ran this method")
So depending on which file I've specified when running the cmd via the terminal, it will go into a different file and call its run method. If the arguments are not as required in each file, it will not run
For example: >python main.py midi_to_audio.py file_name_here output_name_here
My problem is that, as I add more files with their own "arguments" and "run" functions, I wonder if python is going to get confused with which arguments or which run function to execute. Is there a more safer/generic way of doing this?
Also, is there a way of getting the names of the python files depending on which files I've imported? Because for now I have to import the file and manually add their file name to the files list in main.py
Your runner could look like this, to load a module by name and check it has run, and check the arguments given on the command line, and finally dispatch to the module's run function.
import sys
import importlib
def main():
args = sys.argv[1:]
if len(args) < 1:
raise Exception("No module name given")
module_name = args.pop(0).removesuffix(".py") # grab the first argument and remove the .py suffix
module = importlib.import_module(module_name) # import a module by name
if not hasattr(module, 'run'): # check if the module has a run function
raise Exception(f"Module {module_name} does not have a run function")
arg_count = getattr(module, 'arguments', 0) # get the number of arguments the module needs
if len(args) != arg_count:
raise Exception(f"Module {module_name} requires {arg_count} arguments, got {len(args)}")
module.run(*args)
if __name__ == '__main__':
main()
This works with the midi_to_audio.py module in your post.
I want to call a function from another file and pass arguments from current file to that file. With below example, In file bye.py I want to call function "me" from "hi.py" file and pass "goodbye" string to function "me". How to do that ? Thank you :)
I have file hi.py
def me(string):
print(string)
me('hello')
bye.py
from hi import me
me('goodbye')
What I got:
hello
goodbye
What I would like:
goodbye
Generally when you create files to be imported you must use if __name__ == '__main__' which evaluates to false in case you are importing the file from another file. So your hi.py may look as:
def me(string):
print(string)
if __name__ == '__main__':
# Do some local work which should not be reflected while importing this file to another module.
me('hello')
How to build a console_scripts entry to script using multiple functions (not wrapped in a single main function), and you are not the author of the script?
For background read Python: migrate setup.py "scripts=" to entry_points. The answer in this previous question is successful when the script-to-be-called has all of its desirable operations in single starting function with no arguments, such as:
if '__name__' == '__main__':
main_do_all()
and the setup.py entry is:
entry_points = {
'console_scripts': ['foobar = foobartools.foobar_cli:main_do_all'],
}
However it doesn't accommodate the script which uses multiple functions at the top level to do its thing:
if '__name__' == '__main__':
prologue(sys.argv)
do_thing()
finish()
...because console_scripts can only name one function and can't use arguments (right?)
I've been thinking what is needed is to write a wrapper script with only one function - run the real script - and call that in setup.py. However reading up on exec(), execfile(), subprocess(), popen() it looks like a can of worms, and my early attempts with exec() fared poorly, making me doubt the approach.
For a test bed, the program-of-the-moment sparking this question is mutagen, though I've run into the situation other times.
What is the right way to do this?
Test case and results
(In case of copy and paste errors, also see https://gist.github.com/maphew/865c02c9143fd954e5653a8ffb1fb441)
./my_cli/single_main.py:
# This works when 1st 3 functions are in the same file, but what to do when
# they're somewhere else, and meant to be called as a commandline script?
import sys
# --- copied from 'tools/multi_main' but we really want to run in-situ ---
def prologue(argv):
print("Arguments recieved: {}".format(argv))
def do_thing():
print("Doing something here")
def finish():
print("All done now, cleaning up")
# --- end external ---
def main():
prologue(sys.argv)
do_thing()
finish()
if __name__ == '__main__':
print("Running 'single_main' script")
main()
./my_cli/cli.py:
# ./my_cli/cli.py
# wrapper created for console_scripts to run 'tools/multi_main'
import os
import sys
here = os.path.abspath(os.path.dirname(__file__))
def multi_main(argv):
fname = os.path.join(here, '../tools', 'multi_main.py')
print(fname)
with open(fname) as f:
code = compile(f.read(), fname, 'exec')
#exec(code, global_vars, local_vars)
exec(code)
if __name__ == '__main__':
multi_main(sys.argv)
./tools/multi_main:
# ./tools/multi_main
# This file from upstream source. We want to avoid editing.
import sys
def prologue(argv):
print("Arguments recieved: {}".format(argv))
def do_thing():
print("Doing something here")
def finish():
print("All done now, cleaning up")
if __name__ == '__main__':
print("Running 'multi_main' script")
prologue(sys.argv)
do_thing()
finish()
./setup.py:
#./setup.py
import os
from setuptools import setup
setup(
name="multi_main",
description="Migrating from scripts to console_scripts entry points",
# Old, 'nix-only way
scripts=[os.path.join("tools", name) for name in [
"multi_main",
]],
# new, multi-platform way (when get to working)
entry_points = {
'console_scripts': [
'my_single_main = my_cli.single_main:main',
'my_multi_main = my_cli.cli:multi_main',
],
},
)
Shell log:
[py34_x64] D:\b\code\console_script_multi
> pip install -e .
Obtaining file:///D:/b/code/console_script_multi
Installing collected packages: multi-main
Running setup.py develop for multi-main
Successfully installed multi-main-0.0.0
[py34_x64] D:\b\code\console_script_multi
> my_single_main
Arguments recieved: ['C:\\Python34_x64\\Scripts\\my_single_main-script.py']
Doing something here
All done now, cleaning up
[py34_x64] D:\b\code\console_script_multi
> my_multi_main
Traceback (most recent call last):
File "C:\Python34_x64\Scripts\my_multi_main-script.py", line 9, in <module>
load_entry_point('multi-main==0.0.0', 'console_scripts', 'my_multi_main')()
TypeError: multi_main() missing 1 required positional argument: 'argv'
[py34_x64] D:\b\code\console_script_multi
> multi_main
'multi_main' is not recognized as an internal or external command,
operable program or batch file.
[py34_x64] D:\b\code\console_script_multi
> python c:\Python34_x64\Scripts\multi_main
Running 'multi_main' script
Arguments recieved: ['c:\\Python34_x64\\Scripts\\multi_main']
Doing something here
All done now, cleaning up
[py34_x64] D:\b\code\console_script_multi
>
I am using the cmd.Cmd class in Python to offer a simple readline interface to my program.
Self contained example:
from cmd import Cmd
class CommandParser(Cmd):
def do_x(self, line):
pass
def do_xy(self, line):
pass
def do_xyz(self, line):
pass
if __name__ == "__main__":
parser = CommandParser()
parser.cmdloop()
Pressing tab twice will show possibilities. Pressing tab again does the same.
My question is, how do I get the options to cycle on the third tab press? In readline terms I think this is called Tab: menu-complete, but I can't see how to apply this to a Cmd instance.
I already tried:
readline.parse_and_bind('Tab: menu-complete')
Both before and after instantiating the parser instance. No luck.
I also tried passing "Tab: menu-complete" to the Cmd constructor. No Luck here either.
Anyone know how it's done?
Cheers!
The easiest trick would be to add a space after menu-complete:
parser = CommandParser(completekey="tab: menu-complete ")
The bind expression that is executed
readline.parse_and_bind(self.completekey+": complete")
will then become
readline.parse_and_bind("tab: menu-complete : complete")
Everything after the second space is acutally ignored, so it's the same as tab: menu-complete.
If you don't want to rely on that behaviour of readline parsing (I haven't seen it documented) you could use a subclass of str that refuses to be extended as completekey:
class stubborn_str(str):
def __add__(self, other):
return self
parser = CommandParser(completekey=stubborn_str("tab: menu-complete"))
self.completekey+": complete" is now the same as self.completekey.
Unfortunately, it seems as though the only way around it is to monkey-patch the method cmdloop from the cmd.Cmd class, or roll your own.
The right approach is to use "Tab: menu-complete", but that's overriden by the class as shown in line 115: readline.parse_and_bind(self.completekey+": complete"), it is never activated. (For line 115, and the entire cmd package, see this: https://hg.python.org/cpython/file/2.7/Lib/cmd.py). I've shown an edited version of that function below, and how to use it:
import cmd
# note: taken from Python's library: https://hg.python.org/cpython/file/2.7/Lib/cmd.py
def cmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix
off the received input, and dispatch to action methods, passing them
the remainder of the line as argument.
"""
self.preloop()
if self.use_rawinput and self.completekey:
try:
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
readline.parse_and_bind(self.completekey+": menu-complete") # <---
except ImportError:
pass
try:
if intro is not None:
self.intro = intro
if self.intro:
self.stdout.write(str(self.intro)+"\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
if self.use_rawinput:
try:
line = raw_input(self.prompt)
except EOFError:
line = 'EOF'
else:
self.stdout.write(self.prompt)
self.stdout.flush()
line = self.stdin.readline()
if not len(line):
line = 'EOF'
else:
line = line.rstrip('\r\n')
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
finally:
if self.use_rawinput and self.completekey:
try:
import readline
readline.set_completer(self.old_completer)
except ImportError:
pass
# monkey-patch - make sure this is done before any sort of inheritance is used!
cmd.Cmd.cmdloop = cmdloop
# inheritance of the class with the active monkey-patched `cmdloop`
class MyCmd(cmd.Cmd):
pass
Once you've monkey-patched the class method, (or implemented your own class), it provides the correct behavior (albeit without highlighting and reverse-tabbing, but these can be implemented with other keys as necessary).
I'll do my best to describe the issue I am having. I am building a Python program that is built on multiple classes and uses the unittest framework. In a nutshell, the Main.py file has a "ValidateDriver" class that defines a "driver" variable as an ElementTree type. If I point this directly to the XML file I need to parse, (i.e. driver = ElementTree.parse(rC:\test.xml)) then I can access it from another class. However, in reality I don't have the actual XML file that is passed in from the command-line until you get to the Main function in the ValidateDriver class. So under the ValidateDriver class driver would really be driver = ElementTree and then in the main function I would reassign that variable to ValidateDriver.driver = ElementTree.parse(args.driver). However, this is the crux. When I go to the other class and try to call ValidateDriver.driver I don't have the "findall" method/attribute available. Again, the only way it will work is to do something like: ElementTree.parse(rC:\test.xml)). If I did this in C# it would work, but I am new to Python and this is kicking my butt. Any help/suggestions is appreciated. I've included the code for both classes.
Main Function:
import sys
import argparse
import xml.etree.ElementTree as ElementTree
import unittest
import Tests.TestManufacturer
class ValidateDriver:
driver = ElementTree
def main(argv):
parser = argparse.ArgumentParser(description='Validation.')
parser.add_argument('-d', '--driver', help='Path and file name xml file', required=True)
parser.add_argument('-v', '--verbosity',
help='Verbosity for test output. 1 for terse, 2 for verbose. Default is verbose',
default=2, type=int)
#args = parser.parse_args()
args = r'C:\test.c4i'
#print ("Validate Driver: %s" % args.driver)
#print ("Verbosity Level: %s" % args.verbosity)
ValidateDriver.driver = ElementTree.parse(r'C:\test.c4i')
loader = unittest.TestLoader()
suite = loader.loadTestsFromModule(Tests.TestManufacturer)
runner = unittest.TextTestRunner(verbosity=2) # TODO Remove this...
# TODO Uncomment this...
runner = unittest.TextTestRunner(verbosity=args.verbosity)
result = runner.run(suite)
if __name__ == "__main__":
main(sys.argv[1:])
Other Class, Test Manufacturer:
import unittest
import Main
manufacturer = ['']
class Tests(unittest.TestCase):
# Test to see if Manufacturer exists.
def test_manufacturer_exists(self):
for m in Main.ValidateDriver.driver.findall('./manufacturer'):
print m.text
Producing the following error:
C:\Python27\python.exe C:\Users\test\PycharmProjects\Validator\Main.py
Traceback (most recent call last):
File "C:\Users\test\PycharmProjects\Validator\Main.py", line 22, in <module>
class ValidateDriver:
File "C:\Users\test\PycharmProjects\Validator\Main.py", line 65, in ValidateDriver
main(sys.argv[1:])
File "C:\Users\test\PycharmProjects\Validator\Main.py", line 36, in main
ValidateDriver.driver = ElementTree.parse(r'C:\test.c4i')
NameError: global name 'ValidateDriver' is not defined
Process finished with exit code 1
The main problem seems to be that your main script is wrapped in a class. There's really no reason for this, and is quite confusing.
if __name__ == "__main__":
main_object = ValidateDriver()
main_object.main(sys.argv[1:])
This should go outside the class definition
This has nothing to do with "findall" being available. The issue is that the class itself hasn't yet been completely declared at the time you try to access it. In python, the file is read top to bottom. For example, this is not allowed:
if __name__ == "__main__":
f()
def f():
...
The call to f must happen at the bottom of the file after it is declared.
What you're doing with ValidateDriver is similar, because the class isn't defined until the statements directly in its body are executed (this is different from functions, whose bodies of course aren't executed until they are called). You call main(sys.argv[1:]) inside the class body, which in turn tries to access ValidateDriver.driver, which doesn't exist yet.
Preferably, the main function, as well as the code which calls it, should be outside the class. As far as I can tell, the class doesn't need to exist at all (this isn't C# or Java -- you can put code directly at the module level without a class container). If you insist on putting it in a class as a static method, it must be defined as a class method:
#classmethod
def main(cls, argv):
...
which can then be called (outside the class definition) like:
ValidateDriver.main(sys.argv[1:])
But I stress that this is non-standard and should not be necessary.