I am aware there are several ways to deal with shell-executions in Python but I haven't really found the right one for me as it includes the following structure:

command [--maincommand.subcommand value]

I want to create a backup of a collection from my ArangoDB, using:

arangodump  --server.endpoint tcp://qohelet.at:8112  --server.username root  --server.password rootpw  --server.database verstionTestDB  --collection V2  --output-directory 'pyarangoversioning/Tests/10k-node-test/10/backup'  --overwrite true 

The code looks like the following:

def arangoDumpCollection(self, backupcol, outputdict, overwrite=True):

    dumpCollectionString="arangodump "+\
        f" --server.endpoint tcp://{self.server}:{self.port} "+\
        f" --server.username {self.username} "+\
        f" --server.password {self.password} "+\
        f" --server.database {self.database} "+\
        f" --collection {backupcol} "+\
        f" --output-directory '{outputdict}' "

    if overwrite:
        dumpCollectionString+=" --overwrite true"
    execute = Popen(dumpCollectionString, shell=True)

This here works. But is it done properly? Is there a better way to define the parameters? I read several posts that would rather advice against a usage of Popen.

Should I change this code for something else?


1 Answer 1


Appending with spaces to a string and calling it with ...(..., shell=True) is error-prone and insecure. Consider using a generator to yield the command line arguments and call it with subprocess.run()

from subprocess import CompletedProcess, run
from typing import Iterator

def get_arangodump_args(
        backupcol: str,
        outputdict: str,
        overwrite: bool = True
) -> Iterator[str]:
    """Yield arguments for arangodump."""

    yield '--server.endpoint'
    yield 'tcp://{self.server}:{self.port}'
    yield '--server.username'
    yield self.username
    yield '--server.password'
    yield self.password
    yield '--server.database'
    yield self.database
    yield '--collection'
    yield backupcol
    yield '--output-directory'
    yield outputdict

    if overwrite:
        yield '--overwrite'
        yield 'true'

def run_arango_dump(
        backupcol: str,
        outputdict: str,
        overwrite: bool = True
) -> CompletedProcess:
    """Invoke the arangodump command."""

    return run(
            *self.get_arangodump_args(backupcol, outputdict, overwrite)
  • \$\begingroup\$ Thank you for your answer. See the arangodump-reference: arangodb.com/docs/stable/programs-arangodump-examples.html - the difference is in this case mainly the different setup of the long string and calling it in a subprocess. What is the advantage over my version except that subprocess is executed instead of Popen? \$\endgroup\$
    – Qohelet
    Jul 14, 2022 at 14:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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