Pass commandline arguments to a Python script installed with Poetry
Asked Answered
A

3

15

The poetry documentation says that the script section can be used to install scripts or executable when the package is installed. But it does not show any example of how to pass arguments to the script.

How can you do to receive with argparse the arguments in the function?

Amyamyas answered 10/5, 2021 at 18:55 Comment(1)
Do you know what we mean by commandline arguments? Or how to run a script from a shell (linux or windows)? If using an IDE (e.g sypder) or jupyter-notebook be sure to set that context.Arvad
A
19

First a little project setup:

Starting from a new poetry project with poetry new example_script (and creating a main.py file inside example_script dir) with a structure like this:

├── example_script
│   ├── __init__.py
│   ├── main.py
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_poetry_example.py

And adding in the pyproject.toml the config (in the section [tool.poetry.scripts]) of the script that we are going to install:

# pyproject.toml

[tool.poetry]
name = "example_script"

# some lines excluded

[tool.poetry.scripts]
my-script = "example_script.main:start"

# some lines excluded

And finally the main.py file, which has to have a start function inside (as we passed it in the toml). The arguments parser goes inside this function, since this function is the one that will end up executing when we run the script:

import argparse


def some_function(target, end="!"):
    """Some example funcion"""
    msg = "hi " + target + end
    print(msg)


def start():
    # All the logic of argparse goes in this function
    parser = argparse.ArgumentParser(description='Say hi.')
    parser.add_argument('target', type=str, help='the name of the target')
    parser.add_argument('--end', dest='end', default="!",
                    help='sum the integers (default: find the max)')

    args = parser.parse_args()
    some_function(args.target, end=args.end)

We can run the script with poetry, or install and run it directly:

# run with poetry
$ poetry run my-script

# install the proyect (this will create a virtualenv if you didn't have it created)
$ poetry install
# activate the virtualenv
$ poetry shell
# run the script
$ my-script --help
usage: my-script [-h] [--end END] target

Say hi.

positional arguments:
  target      the name of the target

optional arguments:
  -h, --help  show this help message and exit
  --end END   sum the integers (default: find the max)


$ my-script "spanish inquisition" --end "?"
hi spanish inquisition?
Amyamyas answered 10/5, 2021 at 18:55 Comment(2)
How do I test this script? I have difficulty with passing the command line arguments from a call to start() from test_poetry_example.py. There must be a way to import the module with start(), but start() doesn't take any arguments. My calling script can provide test arguments to the function.Outskirts
Following up, I found this great blog post by Jonathan Bowman (and not much else). The difficulty is the executable or script take arguments parsed from command line via argparse, but to run tests with pytest we must import the method and call it with function arguments. Excellent solution, though I didn't need to use sys.argv[1:].Outskirts
L
10

This question is really two separate questions:

  1. How do I pass arguments into a script that is run using Poetry
  2. How do I access and parse those arguments, in particular, using argparse

The initial answer (by Lucas), addresses parts of each, especially about argparse, but I'm answering to fill in some additional details and explain how to directly access the args.

Access arguments directly in any function or script

As an alternative to argparse, arguments can be directly accessed in Python at any time using sys.argv, which is a list of strings, each one is one of the arguments. Python splits up the arguments based on spaces, unless the spaces are enclosed in quotes (either single or double quotes).

This method is more direct and lightweight than argparse, with a lot less functionality.

args.py setup as a main script file with a start() function:

import sys

def start(args=sys.argv):
  for i, arg in enumerate(args):
    print(f'Arg #{i}: {arg}')

if __name__ == '__main__':
  start()

Run it at the command-line with a variety of argument types:

$ python args.py "item 1" 'Hello Arguments!!' "i 3" 4 5 6
Arg #0: args.py
Arg #1: item 1
Arg #2: Hello Arguments!!
Arg #3: i 3
Arg #4: 4
Arg #5: 5
Arg #6: 6

The first argument is always the script that was called, in exactly the way it was called (i.e. relative or absolute path to the script file or other reference).

Adding arguments when calling with poetry run

NOTE: In the v2.X versions of Poetry, arguments may work differently for poetry run commands

While you can run scripts with Poetry by activating the virtual environment with poetry shell and then running the script as normal with python script.py arg1 arg2 arg3, you can also add arguments directly to the poetry run command:

At the command-line, directly running the script:

$ poetry run python args.py arg1 arg2 arg3
Arg #0: <some_path>/args.py
Arg #1: arg1
Arg #2: arg2
Arg #3: arg3

Running a python file as an installed Poetry script

Or, run it as a script, installed by Poetry. In this case the script name we assign is arg_script, and you just run it directly at a terminal prompt with the virtual environment activated (i.e. do not invoke with python):

In pyproject.toml:

[tool.poetry.scripts]
arg_script = 'args:start' # run start() function from ./args.py

After updating pyproject.toml, run poetry install at a terminal prompt to install the script in the virtual environment named as arg_script.

With Poetry, you can run a command in the virtual environment by using poetry run:

$ poetry run arg_script arg1 arg2 arg3
Arg #0: arg_script
Arg #1: arg1
Arg #2: arg2
Arg #3: arg3

Any arguments added after poetry run, act just like you were typing them into a terminal that has the virtual environment already activated. i.e. The equivalent is:

$ poetry shell
$ args_script arg1 arg2 arg3
Libelous answered 17/3, 2022 at 3:35 Comment(0)
A
1

If your use case is distributing a CLI for users who install your package then Lucas' answer is best.

But if you actually want to expose scripts with CLIs to run within your project then you'd be better off using a task runner that directly supports this. I am the author of a tool called PoeThePoet, which can be used to achieve this as a poetry plugin or stand-alone tool.

Declare a task in your pyproject.toml including the desired arguments:

[tool.poe.tasks.serve]
script = "my_pkg.app:serve(host, port)"
args = ["host", "port"]

This task can then be run as poetry poe serve --host 0.0.0.0 --port 8000 when using the poetry plugin.

Poe support most of the basic functionality of argparse, including aliases, positional argument, types, and default values for arguments. Check the docs for details.

Argybargy answered 3/5, 2024 at 17:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.