Python module arguments?

Hey all,

I’d love to be able to call on my module and have it pass arguments to the internal functionals, but so far I’ve been running into walls trying to get it working. Such functionality is inherent to Python when running scripts through the command line (by accessing the sys.argv list), but it seems to be lost when working within Maya?

Let’s say I’ve written an exporter: exporter.py. What I’m looking for is letting the user simply write “exporter” in the script editor to get going. He could also write “exporter --help” to print out possible arguments. Just like applications usually behave in an OS.

Maybe I’m chasing an imaginary tree here, but it just feels klunky to have to write “exporter.start()” or “.main()” or “.init()” all the time and not having an easy way to know which arguments to pass until pouring through the docstrings. I’d love to be able to write more elegant “applications” that behave in a stable, predictable and versatile manner.

What approach do you take when writing tools that grow byond individual scripts and starts being used in lots of different ways?

Cheers,
Jon

I think you have a few options.

If you have a Python script that needs to behave like an application, then one approach is to execute it like one instead of importing it as a module.

execfile( script_filename )

[LEFT][FONT=Venus][SIZE=1][SIZE=2]Or, if you’re on Python 3 already (thinking not, since it’s Maya):[/SIZE][/LEFT]

[LEFT][FONT=Courier New][SIZE=2]

with open( script_filename ) as file:[/LEFT]
exec( file.read() )

If you want command-line args, you’re better off with a subprocess:

import subprocess
return_code = subprocess.Popen( ‘%s -arg1 -arg2’ % script_filename, shell=True ).wait( )

Now another approach would be to import your module when Maya starts, and assign some entry function as an object in the main namespace:

import my_tool_module
my_tool = my_tool_module.start

Then you can do this:

my_tool( arg1, arg2, arg3=‘whatever’ )

You can have that start function handle arbitrary arguments. Here’s an example how to do that. Required args come in as a list, keyword args are a dictionary:

def start( *args, **kwargs ):
print ‘args list =’, args
print ‘keyword args dict =’, kwargs

If you prefer, you could do something similar with your tool as a class, rather than a function. Class instances can be made callable just like a function. Basic example:

class My_Tool( object ):
def call( self, arg1, arg2, arg3=None ):

Tool does its thing here

Then somewhere in your Maya startup sequence, create one global instance of that class:

my_tool = My_Tool( )

You can then call that class instance just like the function example above:

my_tool( arg1, arg2, arg3=‘whatever’ )
[SIZE=2]Of course you can add an init to that class and additional methods as desired.

That all said, I’ve done precious little with Python in Maya. Some/all of the above might not work like it does in standalone Python.

[/SIZE][/FONT][/SIZE][/SIZE][/FONT]

I think you have a few options.

If you have a Python script that needs to behave like an application, then one approach is to execute it like one instead of importing it as a module.


execfile( script_filename )

Or, if you’re on Python 3 already (thinking not, since it’s Maya):


with open( script_filename ) as file:
   exec( file.read() )

If you want command-line args, you’re better off with a subprocess:


import subprocess
return_code = subprocess.Popen( '%s -arg1 -arg2' % script_filename, shell=True ).wait( )

Now another approach would be to import your module when Maya starts, and assign some entry function as an object in the main namespace:


import my_tool_module
my_tool = my_tool_module.start

Then you can do this:


my_tool( arg1, arg2, arg3='whatever' )

You can have that start function handle arbitrary arguments. Here’s an example how to do that. Required args come in as a list, keyword args are a dictionary:


def start( *args, **kwargs ):
   print 'args list =', args
   print 'keyword args dict =', kwargs

If you prefer, you could do something similar with your tool as a class, rather than a function. Class instances can be made callable just like a function. Basic example:


class My_Tool( object ):
   def __call__( self, arg1, arg2, arg3=None ):
      # Tool does its thing here

Then somewhere in your Maya startup sequence, create one global instance of that class:


my_tool = My_Tool( )

You can then call that class instance just like the function example above:


my_tool( arg1, arg2, arg3='whatever' )

Of course you can add an init to that class and additional methods as desired.

That all said, I’ve done precious little with Python in Maya. Some/all of the above might not work like it does in standalone Python.

[QUOTE=Adam Pletcher;4988]
That all said, I’ve done precious little with Python in Maya. Some/all of the above might not work like it does in standalone Python.[/QUOTE]

Most of these actually do work as expected on the Maya side, we handle some of our more frequently used stuff this way. The quick dirty way to do it is do all your mappings/setup as described in Adam’s post in your userSetup.py. The execfile stuff in Maya can get you in some trouble, but i guess it depends really on what you’re trying to do.

Python is not designed to have flags like a shell language so forcing it to do so is not necessarily equivalent to writing more elegant applications.
In fact having the module run some code in the background would generally be considered bad coding practice.
The import statement will now allow you to pass in arguments, neither will the execfile. You’d probably have to run some sort of a shell process … but really I don’t see a need to go through all this, it’s definitely not going to improve your application one bit.

Why not have start function that will handle all your arguments for you. You can alias it to something more convenient if you so please if you want to obfuscate where it comes from to the user.

Thanks guys, excellent tips. I’ll try function aliasing first and see where that takes me, it sounds reasonably elegant.

[QUOTE=gaggle;4983]it just feels klunky to have to write “exporter.start()” or “.main()” or “.init()” all the time and not having an easy way to know which arguments to pass until pouring through the docstrings.[/QUOTE]

beauty is in the eye of the beholder. python has its own syntax and its own way of doing things: its best to avoid trying to force it to look or work how you expect it to, and instead learn how it is intended to be used.

[QUOTE=gaggle;4983]What I’m looking for is letting the user simply write “exporter” in the script editor to get going. He could also write “exporter --help” to print out possible arguments.[/QUOTE]

the python help() command combined with descriptive argument names, intelligent argument defaults, and well-written docstrings is how python solves the problem of argument transparency. also, try/except clauses with concise, but informative error messages on how a user can correct their mistake. these are the “pythonic” ways of addressing the very real issues you’ve brought up.