pass argument when opening another python file as a module? - python

I'm remaking a game in python and the menu file and the game file are separate. The menu imports the game file as a module and calls it when requested. However I want variables that determine the sound and difficulty to be passed through from the menu file to the game file, as these options are chosen by the user in the menu.
I'm not sure how to do this at all, so here is how i import and call the python game file:
import SpaceInvaders
SpaceInvaders.SpaceInvaders().mainLoop()
and I want to pass the arguments 'sound' and 'difficulty' through, whose values are strings.
also i call the main loop function but the variables need to be usable in the SpaceInvaders class so i can assign them to self.sound and self.difficulty, they aren't used in the main loop.

If you want to pass sound and difficulty to the mainLoop of SpaceInvaders, then make it so that mainLoop takes them as arguments, and then you will be able to send them:
SpaceInvaders.SpaceInvaders().mainLoop(sound, difficulty)
To answer the additional question "That's the issue, I don't want them in main loop, I want them in the SpaceInvaders class :(" - do this:
SpaceInvaders.SpaceInvaders(sound, difficulty).mainLoop()

example usage:
python somegame.py hello args --difficulty medium
how to use sys.argv create an example:
echo 'import sys;print(sys.argv)' >> test.py
python test.py okay okay --diff med
output:
['1.py', 'okay', 'okay', '--diff', 'med']
very basic example using sys.argv:
import sys
import SpaceInvaders
print(sys.argv)
# step through using pdb
# see: https://docs.python.org/3/library/pdb.html
import pdb;pdb.set_trace() #print(dir(), dir(SpaceInvaders))
# 'n' enter, 'n' enter
# set self.stuff
# create the class
invade = SpaceInvaders.SpaceInvaders(sys.argv)
# pass argv to mainLoop
invade.mainLoop(sys.argv)
and in SpaceInvaders.py:
class SpaceInvaders(object):
def __init__(self, args):
print(args)
self.difficulty = # some var from args
import pdb;pdb.set_trace()
def mainLoop(self, args):
print(args)
if you want to get really fancy checkout:
https://github.com/pallets/click
https://github.com/google/python-fire
and of course checkout examples of argparse

Related

How to integrate python scripting in my python code

I am writing Python code (based on PyQt5 signals and slots). I need to make my code "Scriptable". By scriptable I mean the user can utilize the internal objects himself in a user-defined python script to develop/automate some functions,..etc. But I have no idea of how to implement it clearly.
I have tried using (exec) function in python in the following way:
user-def.py
def script_entry(main_object_in_my_code):
# Connecting signal from main_object_in_my_code (inherited from QObject) to other functions in this
# file. example:
main_object_in_my_code.event_1.connect(function_1)
#QtCore.pyqtSlot(str)
def function_1 (args):
#do user-defined logic using those args.
then in my script when user want to execute it, he inputs (as example)
source user-def.py
the main script reads the script and uses exec as the following:
with open(script_path) as f:
script = f.read()
exec(script, globals())
the problem is that events are triggered but function function_1 is not executed.
I am sure this is not the right way to do this. So, How can I implement my code to be (scriptable) using user defined scripts?
I would recomend to create a class and extend from it, let the 'user' call the functions when s/he needs.
If you are not in touch with class inheritance check this tutorial
source_def.py
class Main:
def __init__(self):
super(Main, self).__init__()
def script_entry(self, main_object_in_my_code):
main_object_in_my_code.event_1.connect( function_1 )
#QtCore.pyqtSlot(str)
def function_1( self, args ):
#this checks if the function is set
invert_op = getattr(self, "user_function", None)
if callable(user_function):
eval('self.user_function( args )')
user_def.py
from source_def import Main
class UserClass( Main ):
def __init__(self):
super(UserClass, self).__init__()
def user_function(self , args ):
print( args )
Try this

Using Python Click within a class

I've got an old flashcards app that I made that I've repurposed for some aws cert study. I wrote this years ago and overhauled that code, and one of those changes involved using click over argparse, but I'm having some issues using click. My code is below (minus a couple of extra functions):
The idea is that load_deck() needs to read the file argument. The way that I was trying to do it is pass load_deck() the file argument through main()
Finally, running this code states that within the Flashcards().main() call, main() is missing a positional argument self. I think this is a problem with the way I'm using click.
import os
import random
import sys
import click
import keyboard
import ruamel.yaml
class Flashcards:
"""
pass
"""
def __init__(self):
self.deck = ruamel.yaml.YAML()
self.card = ['key', 'value']
def load_deck(self, file):
"""
pass
"""
with open(file, mode='r') as deck:
self.deck = self.deck.load(deck)
#click.command()
#click.option('-f', '--file', default='aws.yaml', help='specifies yaml file to use')
#click.option('-r', '--reverse', default=False, help='displays values prompting user to guess keys')
def main(self, file, reverse):
"""
pass
"""
self.load_deck(file)
if reverse is True:
self.deck = [
[card[1], card[0]
] for card in self.deck]
os.system('clear')
print('Press [SPACEBAR] to advance. Exit at anytime with [CTRL] + [C]\n'
'There are {} cards in your deck.\n'.format(len(self.deck)))
try:
while True:
self.read_card()
self.flip_card()
except KeyboardInterrupt:
# removes '^C' from terminal output
os.system('clear')
sys.exit(0)
if __name__ == "__main__":
Flashcards().main()
The program reads a yaml file in the following format (it was previously a spanish flashcard app):
bajar: to descend
borrar: to erase
contestar: to answer
Click isn't naturally designed to work this way, see for example this issue which also includes some workarounds by folks in the comments if you want to go that route.
In your case, you could just yoink main out of the class and have it instantiate Flashcards for you:
#click.etc.
def main(file, reverse):
f = Flashcards()
f.load_deck(file)
# And so on, using 'f' instead of 'self'
I found this workaround:
import click
class Stub:
pass
viking = click.make_pass_decorator(Stub,ensure=True)
#click.group()
#viking
#click.pass_context
def cli(ctx,_):
ctx.obj = Viking()
class Viking(Stub):
def __init__(self):
self.name = "Thor"
#cli.command()
#viking
def plunder(self):
print(f"{self.name} attacks")
#cli.command()
#viking
def sail(self):
print("whoosh")
cli()

python click usage of standalone_mode

This question is about the Python Click library.
I want click to gather my commandline arguments. When gathered, I want to reuse these values. I dont want any crazy chaining of callbacks, just use the return value. By default, click disables using the return value and calls sys.exit().
I was wondering how to correctly invoke standalone_mode (http://click.pocoo.org/5/exceptions/#what-if-i-don-t-want-that) in case I want to use the decorator style. The above linked doc only shows the usage when (manually) creating Commands using click.
Is it even possible? A minimal example is shown below. It illustrates how click calls sys.exit() directly after returning from gatherarguments
import click
#click.command()
#click.option('--name', help='Enter Name')
#click.pass_context
def gatherarguments(ctx, name):
return ctx
def usectx(ctx):
print("Name is %s" % ctx.params.name)
if __name__ == '__main__':
ctx = gatherarguments()
print(ctx) # is never called
usectx(ctx) # is never called
$ python test.py --name Your_Name
I would love this to be stateless, meaning, without any click.group functionality - I just want the results, without my application exiting.
Just sending standalone_mode as a keyword argument worked for me:
from __future__ import print_function
import click
#click.command()
#click.option('--name', help='Enter Name')
#click.pass_context
def gatherarguments(ctx, name):
return ctx
def usectx(ctx):
print("Name is %s" % ctx.params['name'])
if __name__ == '__main__':
ctx = gatherarguments(standalone_mode=False)
print(ctx)
usectx(ctx)
Output:
./clickme.py --name something
<click.core.Context object at 0x7fb671a51690>
Name is something

In Python, can I call the main() of an imported module?

In Python I have a module myModule.py where I define a few functions and a main(), which takes a few command line arguments.
I usually call this main() from a bash script. Now, I would like to put everything into a small package, so I thought that maybe I could turn my simple bash script into a Python script and put it in the package.
So, how do I actually call the main() function of myModule.py from the main() function of MyFormerBashScript.py? Can I even do that? How do I pass any arguments to it?
It's just a function. Import it and call it:
import myModule
myModule.main()
If you need to parse arguments, you have two options:
Parse them in main(), but pass in sys.argv as a parameter (all code below in the same module myModule):
def main(args):
# parse arguments using optparse or argparse or what have you
if __name__ == '__main__':
import sys
main(sys.argv[1:])
Now you can import and call myModule.main(['arg1', 'arg2', 'arg3']) from other another module.
Have main() accept parameters that are already parsed (again all code in the myModule module):
def main(foo, bar, baz='spam'):
# run with already parsed arguments
if __name__ == '__main__':
import sys
# parse sys.argv[1:] using optparse or argparse or what have you
main(foovalue, barvalue, **dictofoptions)
and import and call myModule.main(foovalue, barvalue, baz='ham') elsewhere and passing in python arguments as needed.
The trick here is to detect when your module is being used as a script; when you run a python file as the main script (python filename.py) no import statement is being used, so python calls that module "__main__". But if that same filename.py code is treated as a module (import filename), then python uses that as the module name instead. In both cases the variable __name__ is set, and testing against that tells you how your code was run.
Martijen's answer makes sense, but it was missing something crucial that may seem obvious to others but was hard for me to figure out.
In the version where you use argparse, you need to have this line in the main body.
args = parser.parse_args(args)
Normally when you are using argparse just in a script you just write
args = parser.parse_args()
and parse_args find the arguments from the command line. But in this case the main function does not have access to the command line arguments, so you have to tell argparse what the arguments are.
Here is an example
import argparse
import sys
def x(x_center, y_center):
print "X center:", x_center
print "Y center:", y_center
def main(args):
parser = argparse.ArgumentParser(description="Do something.")
parser.add_argument("-x", "--xcenter", type=float, default= 2, required=False)
parser.add_argument("-y", "--ycenter", type=float, default= 4, required=False)
args = parser.parse_args(args)
x(args.xcenter, args.ycenter)
if __name__ == '__main__':
main(sys.argv[1:])
Assuming you named this mytest.py
To run it you can either do any of these from the command line
python ./mytest.py -x 8
python ./mytest.py -x 8 -y 2
python ./mytest.py
which returns respectively
X center: 8.0
Y center: 4
or
X center: 8.0
Y center: 2.0
or
X center: 2
Y center: 4
Or if you want to run from another python script you can do
import mytest
mytest.main(["-x","7","-y","6"])
which returns
X center: 7.0
Y center: 6.0
It depends. If the main code is protected by an if as in:
if __name__ == '__main__':
...main code...
then no, you can't make Python execute that because you can't influence the automatic variable __name__.
But when all the code is in a function, then might be able to. Try
import myModule
myModule.main()
This works even when the module protects itself with a __all__.
from myModule import * might not make main visible to you, so you really need to import the module itself.
I had the same need using argparse too.
The thing is parse_args function of an argparse.ArgumentParser object instance implicitly takes its arguments by default from sys.args. The work around, following Martijn line, consists of making that explicit, so you can change the arguments you pass to parse_args as desire.
def main(args):
# some stuff
parser = argparse.ArgumentParser()
# some other stuff
parsed_args = parser.parse_args(args)
# more stuff with the args
if __name__ == '__main__':
import sys
main(sys.argv[1:])
The key point is passing args to parse_args function.
Later, to use the main, you just do as Martijn tell.
The answer I was searching for was answered here: How to use python argparse with args other than sys.argv?
If main.py and parse_args() is written in this way, then the parsing can be done nicely
# main.py
import argparse
def parse_args():
parser = argparse.ArgumentParser(description="")
parser.add_argument('--input', default='my_input.txt')
return parser
def main(args):
print(args.input)
if __name__ == "__main__":
parser = parse_args()
args = parser.parse_args()
main(args)
Then you can call main() and parse arguments with parser.parse_args(['--input', 'foobar.txt']) to it in another python script:
# temp.py
from main import main, parse_args
parser = parse_args()
args = parser.parse_args([]) # note the square bracket
# to overwrite default, use parser.parse_args(['--input', 'foobar.txt'])
print(args) # Namespace(input='my_input.txt')
main(args)
Assuming you are trying to pass the command line arguments as well.
import sys
import myModule
def main():
# this will just pass all of the system arguments as is
myModule.main(*sys.argv)
# all the argv but the script name
myModule.main(*sys.argv[1:])
I hit this problem and I couldn't call a files Main() method because it was decorated with these click options, eg:
# #click.command()
# #click.option('--username', '-u', help="Username to use for authentication.")
When I removed these decorations/attributes I could call the Main() method successfully from another file.
from PyFileNameInSameDirectory import main as task
task()

Reference inherited class functions

I am inheriting from both threading.Thread and bdb.Bdb. Thread requires a run function for the start function to call, and I need to user the Bdb.run function. How do I reference Bdb's run function since I can't do it with self.run? I tried super, but I'm apparently not using that right, I get TypeError: must be type, not classobj.
import sys
import os
import multiprocessing
import threading
import bdb
from bdb import Bdb
from threading import Thread
from el_tree_o import ElTreeO, _RUNNING, _PAUSED, _WAITING
from pysignal import Signal
class CommandExec(Thread, Bdb):
'''
Command Exec is an implementation of the Bdb python class with is a base
debugger. This will give the user the ability to pause scripts when needed
and see script progress through line numbers. Useful for command and
control scripts.
'''
def __init__(self, mainFile, skip=None):
Bdb.__init__(self,skip=skip)
Thread.__init__(self)
# need to define botframe to protect against an error
# generated in bdb.py when set_quit is called before
# self.botframe is defined
self.botframe = None
# self.even is used to pause execution
self.event = threading.Event()
# used so I know when to start debugging
self.mainFile = mainFile
self.start_debug = 0
# used to run a file
self.statement = ""
def run(self):
self.event.clear()
self.set_step()
super(bdb.Bdb,self).run(self.statement)
Just as you invoked Bdb's __init__ method on line 22, you can invoke its run method:
Bdb.run(self, self.statement)
super is only useful when you don't know which parent class you need to invoke next, and you want to let Python's inheritance machinery figure it out for you. Here, you know precisely which function you want to call, Bdb.run, so just call it.

Categories

Resources