44
$\begingroup$

Let's say we have a Python script my_script.py that does some data processing with Blender. This script accepts some arguments (e.g. arg1 arg2). How can I forward arguments to the script when using it with the CLI?

blender --background --python my_script.py

The only trick I could find was this one:

echo "arg1 arg2" > my_input.txt && blender --background --python my_script.py

Is this really the only way to do that?

Update

I found a similar question on Stack Overflow.

$\endgroup$
50
$\begingroup$

Yes, Blender's Python can read command line arguments.

In summary:

  • Python can read all arguments passed to Blender via sys.argv
    (just the same as you would in Python)
  • Blender will ignore all arguments after: --
    (double dash with no arguments, as documented in the --help message)
  • Scripts can check for -- in sys.argv and ignore all arguments beforehand.

Example:

Script: mytest.py

import sys
argv = sys.argv
argv = argv[argv.index("--") + 1:]  # get all args after "--"

print(argv)  # --> ['example', 'args', '123']

Execute like this:

blender --background test.blend --python mytest.py -- example args 123

Having spaces around -- is important, this is a signal that Blender should stop parsing the arguments and allows you to pass your own arguments to Python.


Further information:

For a more comprehensive script example, background_job.py is a Python template which comes with Blender, this uses Python's argparse module, for more flexible handling of arguments.

If you want to have comprehensive arguments for your script with a --help message, Look into argparse, general Python docs on the module can be used.


Note: if -- is not always needed, you can check for it like this.

import sys
argv = sys.argv
try:
    index = argv.index("--") + 1
except ValueError:
    index = len(argv)

argv = argv[index:]

Note that using argparse is optional, you can simply do checks such as:

if "--myarg" in argv:
    do_stuff()

... but I've found as soon as you want to pass values to arguments, this becomes a hassle and its generally better of to use argparse to begin with, unless...

  • You're making a quick test and only need primitive argument handling.
  • You have a good reason to spend time doing your own argument parsing, and argparse can't handle your use-case.
$\endgroup$
  • $\begingroup$ +1 for the solution. So you mean that if you don't add the option "--", both Python and Blender will interpret the options following after "--python"? What's the point of that? That makes it feel like this feature is either not really thought through or not an intended feature. Is it documented somewhere? $\endgroup$ – HelloGoodbye Jun 27 '18 at 11:07
4
$\begingroup$

Another way would be to set environment variables for the arguments

SET arg1=val1
blender -P script.py

Within the script the environment can be queried using os.getenv()

import os
val = os.getenv('arg1')
print(val)
$\endgroup$
  • 1
    $\begingroup$ While this works, there are a few minor downsides, 1) typos in env vars easily go unnoticed. 2) you have to do your own type checking on the contents of values (if some are numbers for eg). 3) they don't get added to a --help message automatically (as with argparse). 4) Setting them isn't the same across all platforms, so your documentation needs to account for that. $\endgroup$ – ideasman42 Jan 31 '16 at 1:16
3
$\begingroup$

thanks for the suggestions. I just managed to get a working example following the double-dash idea that retains all of argparse's functionality with minimal intrusion. Works for me, let me know if you see any issues!

my_script.py

import argparse
import sys

class ArgumentParserForBlender(argparse.ArgumentParser):
    """
    This class is identical to its superclass, except for the parse_args
    method (see docstring). It resolves the ambiguity generated when calling
    Blender from the CLI with a python script, and both Blender and the script
    have arguments. E.g., the following call will make Blender crash because
    it will try to process the script's -a and -b flags:
    >>> blender --python my_script.py -a 1 -b 2

    To bypass this issue this class uses the fact that Blender will ignore all
    arguments given after a double-dash ('--'). The approach is that all
    arguments before '--' go to Blender, arguments after go to the script.
    The following calls work fine:
    >>> blender --python my_script.py -- -a 1 -b 2
    >>> blender --python my_script.py --
    """

    def _get_argv_after_doubledash(self):
        """
        Given the sys.argv as a list of strings, this method returns the
        sublist right after the '--' element (if present, otherwise returns
        an empty list).
        """
        try:
            idx = sys.argv.index("--")
            return sys.argv[idx+1:] # the list after '--'
        except ValueError as e: # '--' not in the list:
            return []

    # overrides superclass
    def parse_args(self):
        """
        This method is expected to behave identically as in the superclass,
        except that the sys.argv list will be pre-processed using
        _get_argv_after_doubledash before. See the docstring of the class for
        usage examples and details.
        """
        return super().parse_args(args=self._get_argv_after_doubledash())

parser = ArgumentParserForBlender()


parser.add_argument("-q", "--quack",
                    action="store_true",
                    help="Quacks bar times if activated.")
parser.add_argument("-b", "--bar", type=int, default=10,
                    help="Number of desired quacks")
args = parser.parse_args()
QUACK = args.quack
BAR = args.bar

if QUACK:
    print("QUACK " * BAR)

The following calls will work as expected (tested on Ubuntu 17):

blender --python 'my_script.py'
blender --python 'my_script.py' --
blender --python 'my_script.py' -- -q
blender --python 'my_script.py' -- -b 100
blender --python 'my_script.py' -- -q -b 100
blender -b --python 'my_script.py' -- -q -b 100 # note that -b is unambiguous

Cheers!
Andres

$\endgroup$
  • $\begingroup$ Cool. If installed as addon (or via another method to place file in blenders python path) can from my_script import args in any script to get the parsed script args. $\endgroup$ – batFINGER Mar 18 at 14:39
  • $\begingroup$ How would then look the CLI call? can an addon be called with args upon blender start? $\endgroup$ – fr_andres SupportsMonicaCellio Mar 18 at 17:15
  • $\begingroup$ As for CLI blender --python somescript.py -- -q -d 100 in "somescript.py" from my_script import args I imagine that in any addon could do same, just like importing from any other module. $\endgroup$ – batFINGER Mar 18 at 17:28
  • 1
    $\begingroup$ I understand, thanks! so this way I can pack my personal utilities as module into the blender path to avoid defining them in every script. Nifty $\endgroup$ – fr_andres SupportsMonicaCellio Mar 18 at 17:30
1
$\begingroup$

I wrote something for blend_stats, which you could use:

import sys

# Get script parameters:
# all list items after the last occurence of "--"
print()
print(sys.argv)
print()

try:
    args = list(reversed(sys.argv))
    idx = args.index("--")

except ValueError:
    params = []

else:
    params = args[:idx][::-1]

print("Script params:", params)

Example call:

blender.exe --background test.blend -P ..\blend_stats.py -- foobar script_param_2 1337

Note the space between -- and foobar!

$\endgroup$
  • 1
    $\begingroup$ use of reversed and exception handling here seems unnecessary. $\endgroup$ – ideasman42 Apr 8 '14 at 20:45
  • $\begingroup$ So what is your recommended solution? There is no rindex() or rfind() for lists. I could join the list args = " ".join(sys.argv) and do args[args.rfind(" -- ")+1:].split(" "), but it's not very readable and it incorrectly returns something if the separator isn't present. And it messes up if the separator and spaces occur in the path to blender binary (sys.argv[0]). $\endgroup$ – CoDEmanX Apr 8 '14 at 21:29
  • $\begingroup$ Simply use argv.index("--"), see my answer below. (Blender stops parsing after the first --, so this is correct). $\endgroup$ – ideasman42 Apr 8 '14 at 21:50
  • $\begingroup$ But it fails if there's no "--". For blend_stats, it was my intention to use the very last occurrence of "--", not the first, so I had to come up with above code, as there's no rindex(). $\endgroup$ – CoDEmanX Apr 9 '14 at 2:10
  • $\begingroup$ Not sure why you would want rindex in this case, its incorrect. If you're not sure if -- will be passed, check for it first. if "--" in argv:, see: stackoverflow.com/questions/2132718 $\endgroup$ – ideasman42 Apr 9 '14 at 2:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.