Python Forum
calling external function with arguments
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
calling external function with arguments
#1
I am new to python...

I am trying to call a function defined in a separate program, but I cannot figure out how to pass an argument. How to do?

The function I want to call is in this program:
testren.py:
import sys, getopt
global old_name
def renameit(argv):
   global old_name
   old_name = ''
   opts, args = getopt.getopt(argv,"ho:",["oldname="])
   for opt, arg in opts:
      if opt in ("-o", "--old_name"):
         old_name = arg  
   print ('in testren after - old_name =', old_name )

if __name__ == "__main__":
   print ('sys.argv ', sys.argv[1:])
   renameit(sys.argv[1:])
If I call testren.py directly, I get the desired results:
Output:
C:\python>python testren.py -o "my1.mp3" sys.argv ['-o', 'my1.mp3'] in testren after - old_name = my1.mp3
If I call it from another python program the arguments are not found, I cannot figure out how to pass the arguments. How to do?

calltestren.py:
import testren
if __name__ == '__main__':
    renargs = '-o "my1.mp3"'
    print('calling rename', renargs)
    testren.renameit(renargs)
    print('old_name<', old_name, '>')
I just get blanks where I expect a file name to be:
Output:
C:\python>python calltestren.py calling rename -o "my1.mp3" in testren after - old_name = old_name< >
Reply
#2
Some remarks first:
  1. Avoid using the global keyword as much as possible, it makes better code. Seasoned pythonistas use this keyword very infrequently.
  2. Don't use the getopt module, use the more flexible argparse module instead. Getopt just mimics the old getopt library of the C language. I think it is still here mostly for backward compatibility.
  3. The function renameit expects its first argument to be a list of strings, don't give it a simple string.
Here is the corrected code
# testren2.py

import argparse
import sys

def renameit(argv):
   parser = argparse.ArgumentParser()
   parser.add_argument(
      '-o', '--old-name', help="old name", dest='old_name', required=True)
   args = parser.parse_args(argv)
   print ('in testren after - old_name =', args.old_name )

if __name__ == "__main__":
   print ('sys.argv ', sys.argv)
   renameit(sys.argv[1:])
and the code that calls it
# calltestren2.py

import testren2 as testren

if __name__ == '__main__':
    renargs = ['-o',  'my1.mp3']  # note this is a list of strings, not a single string!
    print('calling rename', renargs)
    testren.renameit(renargs)
snippsat likes this post
Reply
#3
(Jun-23-2023, 04:43 AM)Wimpy_Wellington Wrote: If I call it from another python program the arguments are not found, I cannot figure out how to pass the arguments. How to do?
I may think there is confusion about using command line tool sys.argv, getopt(do not use as mention).
Because you don't use command line here at all,but try to call it from a other module using a string.

argparse Gribouillis show is ok,but can show a example with Typer which is really cool.
Here i also do rename on files on disk,not just show there names.
from pathlib import Path
import typer

app = typer.Typer()

@app.command()
def renameit(
    old_name: str = typer.Option(..., '-o', '--old-name'),
    new_name: str = typer.Option(..., '-n', '--new-name'),
):
    old_path = Path(old_name)
    if old_path.is_file():
        old_path.rename(Path(new_name))
        typer.echo(f'File renamed from {old_name} to {new_name}')
    else:
        typer.echo('File not found.')

if __name__ == '__main__':
    app() 
Using it,see that help and colors get(use Rich under the hood) get generated automatic.
[Image: ATaLiF.png]
Also using this in a other module only need to pass app,and it will work.
from rename_files import app

if __name__ == '__main__':
    app()
Reply
#4
(Jun-24-2023, 01:11 PM)snippsat Wrote:
(Jun-23-2023, 04:43 AM)Wimpy_Wellington Wrote: If I call it from another python program the arguments are not found, I cannot figure out how to pass the arguments. How to do?
I may think there is confusion about using command line tool sys.argv, getopt(do not use as mention).
Because you don't use command line here at all,but try to call it from a other module using a string. tiny fishing

argparse Gribouillis show is ok,but can show a example with Typer which is really cool.
Here i also do rename on files on disk,not just show there names.
from pathlib import Path
import typer

app = typer.Typer()

@app.command()
def renameit(
    old_name: str = typer.Option(..., '-o', '--old-name'),
    new_name: str = typer.Option(..., '-n', '--new-name'),
):
    old_path = Path(old_name)
    if old_path.is_file():
        old_path.rename(Path(new_name))
        typer.echo(f'File renamed from {old_name} to {new_name}')
    else:
        typer.echo('File not found.')

if __name__ == '__main__':
    app() 
Using it,see that help and colors get(use Rich under the hood) get generated automatic.
[Image: ATaLiF.png]
Also using this in a other module only need to pass app,and it will work.
from rename_files import app

if __name__ == '__main__':
    app()
Thanks, it works.
Reply
#5
I don't know about the theoretical aspects of programming, or technical stuff. For me Python is just a practical tool. Get the job done as simply as possible.

I have a folder: /home/pedro/myPython/myModules/ where, unsurprisingly, I keep my homemade modules.

If I make this module and save it in /home/pedro/myPython/myModules/ as change_file_names.py:

import os

def change_name(a,b):
    os.rename(a, b)
    print('Changed the name of', a, 'to', b)
Then I can call it anytime:

#! /usr/bin/python3
# do this because I don't add the path to my modules to the system PATH variable
import sys
sys.path.append('/home/pedro/myPython/myModules/')
# if you add the path to your homemade modules to your system PATH, you can forget the 2 lines above
import change_file_names as cfns

if __name__ == "__main__":
    cfns.change_name('/home/pedro/temp/a.txt', '/home/pedro/temp/b.txt')
I'm sure the suggestions from Gribouillis and snippsat are technically much better, but it's hard for me to understand what's going on, it's all very cryptic.

Also, if the trusty old steed os can do the job, why complicate matters? Just use os.
Reply
#6
(Jul-04-2023, 11:18 PM)Pedroski55 Wrote: sys.path.append('/home/pedro/myPython/myModules/')
@Pedroski55 you could write this line in a (new?) file named usercustomize.py in the directory returned by site.getusersitepackages()
This would spare you the effort to write this line in all your scripts.

Also the PATH variable is for executable programs, not for Python modules.
Reply
#7
Aha! Technical stuff! Thanks!

That was easier than I thought:

import site
site.addsitedir('/home/pedro/myPython/myModules/')
Then this works fine:

import change_file_names as cfns

if __name__ == "__main__":
    cfns.change_name('/home/pedro/temp/b.txt', '/home/pedro/temp/a.txt')
But I prefer

import os
os.rename('/home/pedro/temp/a.txt', '/home/pedro/temp/b.txt')
Reply
#8
Why is testren.py a module? It is not reusable code. How can a different Python program use this code? There are reusable command line parsers (typer was mentioned. I like argparse for simple command lines. There are others) that let you parse arbitrary command lines. Why write a module for doing this? I can understand writing this as a function in your main program, but it makes no sense as a module.

Never use global variables to return values from a function. Use the return statement. The less you have to know about a function/module to use it, the better. Your users should not have to call a function and then access a global variable to get the result. That forces them to know 3 things (function, variable, connection between the two). Your function should return the value directly, so your users only need to know about the function.

Maybe I am reading this wrong and renameit() is supposed to be a reusable function that does something useful (like rename a file from old_name to new_name). If that is the case, you should pass function arguments, not command line arguments. You can also write your module so it lets you call renameit() from the command line. This is a simple example.

renameit.py
import sys

# This is the reusable function I want to export.
def renameit(old_name, new_name):
    """Does something interesting or useful with old_name and new_name."""
    print(f"renameit({old_name}, {new_name})")

# This lets me execute the function from the command line.
if __name__ == "__main__":
    args = sys.argv[1:]
    if len(args) != 2:
        print("Missing arguments for renameit file new_name")
    else:
        renameit(*args)
Importing and using the renameit function.
from renamit import renameit
renamit("name of old thing", "the new name")
Here I added an optional argument and use argparse to parse the command line.
import argparse

def renameit(old_name, new_name, version=None):
    """Does something interesting or useful with old_name and new_name."""
    print(f"renameit({old_name}, {new_name}, version={version})")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Give an old thing a new name.")
    parser.add_argument("old_name", type=str, help="name of existing thing")
    parser.add_argument("new_name", type=str, help="new name for thing")
    parser.add_argument(
        "-v",
        "--version",
        action="store_true",
        help="add version number of new name clashes with existing thing",
    )

    args = parser.parse_args()
    renameit(args.old_name, args.new_name, args.version)
Now I can ask for help on how to use the function from the commandline.
Output:
: python renameit -h usage: renameit.py [-h] [-v] old_name new_name Give an old thing a new name. positional arguments: old_name name of existing thing new_name new name for thing options: -h, --help show this help message and exit -v, --version add version number of new name clashes with existing thing
And if I mess up entering the arguments I get an informative error message.
Output:
: python renameit.py -v "name of existing thing" usage: renameit.py [-h] [-v] old_name new_name junk.py: error: the following arguments are required: new_name
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Why doesn't calling a parent constructor work with arbitrary keyword arguments? PurposefulCoder 4 955 Jun-24-2023, 02:14 PM
Last Post: deanhystad
  Calling a function (which accesses a library) from another file mouse9095 4 832 Jun-07-2023, 08:55 PM
Last Post: deanhystad
Sad Iterate randint() multiple times when calling a function Jake123 2 2,061 Feb-15-2022, 10:56 PM
Last Post: deanhystad
  Calling a class from a function jc4d 5 1,831 Dec-17-2021, 09:04 PM
Last Post: ndc85430
  'namespace' shorthand for function arguments? shadowphile 5 2,609 Aug-11-2021, 09:02 PM
Last Post: shadowphile
  Checking the number of arguments a function takes Chirumer 3 2,166 Jul-06-2021, 04:56 PM
Last Post: Chirumer
  [Solved] TypeError when calling function Laplace12 2 2,899 Jun-16-2021, 02:46 PM
Last Post: Laplace12
  Possible to dynamically pass arguments to a function? grimm1111 2 2,201 Feb-21-2021, 05:57 AM
Last Post: deanhystad
  calling a function and argument in an input phillup7 3 2,627 Oct-25-2020, 02:12 PM
Last Post: jefsummers
  How to pass multiple arguments into function Mekala 4 2,465 Jul-11-2020, 07:03 AM
Last Post: Mekala

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020