How to pass arguments to function in module?

744
10
Jump to solution
05-29-2019 10:15 AM
ZacharyHart
Regular Contributor

I know there's a ton of other material here and @ SE regarding this subject, but conceptually i'm just stuck. In an effort to improve the modularity of my code, I'm trying to get better at importing existing functions into scripts. I'm stuck on successfully passing arguments captured within the script to the imported module. Ideally I just want to use the script and pass it a handful of client identifiers (arguments) and have those cascade down into several modules called up in  the script. Like: functiontest2('identifier')

some crude code:

functiontest2.py

from HarvestTable import HarvestTableReport
import sys
HarvestThing = sys.argv[1]

HarvestTableReport(HarvestThing)
‍‍‍‍‍

HarvestTable.py

import numpy as np
import pandas as pd
import arcpy
    
def HarvestTableReport(HarvestID):
    table = "\\\\fileshare\SDE_CONNECTIONS\\LV_NEXUS.sde\\LV_NEXUS.DATAOWNER.NORTHEAST\\clientGDB.featureclass"
    HUID = HarvestID
    whereClause = """ "LV_HARVEST_UNIT_ID" = '{0}' """.format(HUID)
    tableArray = arcpy.da.TableToNumPyArray(table, ['SUPER_TYPE','STRATA', 'OS_TYPE', 'STAND_NUMB', 'SILV_PRES', 'ACRES'], where_clause = whereClause)
    df = round(pd.DataFrame(tableArray),1)
    report = df.groupby(['SUPER_TYPE']).apply(lambda sub_df:  sub_df.pivot_table(index=['STRATA', 'OS_TYPE', 'STAND_NUMB', 'SILV_PRES'], values=['ACRES'],aggfunc=np.sum, margins=True,margins_name= 'TOTAL'))
    # this is creating a new row at level(1) called grand total
    # set it equal to the sum of ACRES where level(1) != 'Total' so you are not counting the  calculated totals in the total sum
    report.loc[('', 'GRAND TOTAL','','',''), :] = report[report.index.get_level_values(1) != 'TOTAL'].sum()
    print(whereClause)
    return np.round(report,1)
    
if __name__ == '__main__':
    HarvestTableReport()‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1 Solution

Accepted Solutions
JoshuaBixby
MVP Esteemed Contributor

The line

HarvestThing = sys.argv[1]

is saying to take the first command line argument and pass it to HarvestThing.  Since you are importing the module and not calling it from the command line with arguments, you are getting an IndexError.

View solution in original post

10 Replies
DanPatterson_Retired
MVP Esteemed Contributor

line 5, first script... you passed to the imported function, and it tried to return something, but you didn't do anything with it

i_m_Back = HarvestTableReport(HarvestThing) # ---- returned from your imported

print("Returned to you {}".format(i_m_Back))
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

So what errors or unexpected behaviors are you getting?  If errors, please post specific errors with traceback.  If unexpected results, please post what you expect and what you are actually getting.

ZacharyHart
Regular Contributor

I've had so many  different tweaks to this it's been hard to keep track (kinda banging my head against the wall here). In the current permutation shown above if I attempt to import functiontest2 into the python window in Pro I receive the following error:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "\\fileshare\SCRIPTS\functiontest2.py", line 3, in <module>
    HarvestThing = sys.argv[1]
IndexError: list index out of range
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

The line

HarvestThing = sys.argv[1]

is saying to take the first command line argument and pass it to HarvestThing.  Since you are importing the module and not calling it from the command line with arguments, you are getting an IndexError.

ZacharyHart
Regular Contributor

ok, that makes way more sense and I had success using the python cmd prompt and supplying an arrangement. thanks so much!

So is there a way to run a script from the interactive python window, or is my solution more like creating a master module that just calls up a bunch of my other single-purpose modules?  Let me try to explain what I'm hoping to achieve. Often while working along in Pro (although I could be doing the same in Jupyter Lab I guess) I want to pull some info for a given client or property. So each module might have a single function that generates a report. Rather than run each module, I want to run all of them in one script/master module. The only argument that needs to be passed to each module is like a client identifier so this should be pretty straightforward. I'd prefer to not have to jump out to the python command prompt but also don't want the overhead of rolling these up into a script tool in a Pro toolbox.

0 Kudos
JoshuaBixby
MVP Esteemed Contributor
ZacharyHart
Regular Contributor

Got pulled back to project work. This is all very helpful. As I start to wrap my head around this more I'm understanding a bit better the need for modularity and organization...it's just applying that which is still the challenge for me.

Based on everything I've read in this thread, so far it seems like what will benefit me the most in this situation is to organize several related functions into one module. I can then call them up as needed.

Thanks so much to everyone!

EDIT: I recently saw this article in my inbox regarding building command line interfaces with Python if anyone is interested.

0 Kudos
JoeBorgione
MVP Esteemed Contributor

If I understand it correctly, you want to call a script from another, passing variables set up in the first script?  If so, here is the basic template with which to do so.

# -*- coding: utf-8 -*-
"""
Created on Thu May 16 14:20:43 2019
@author: JBorgione
save  this as script1.py
"""
var1 = 'A'
var2 = 'B'
var3 = 'C'

import Script2‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

# -*- coding: utf-8 -*-
"""
Created on Thu May 16 14:31:01 2019
@author: JBorgione
script2.py  is called from script1.py
""" 
from __main__ import *

def main(): 
    print(var3) 
    print(var2) 
    print(var1) 

if __name__ == 'Script2': 
    main() ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

It's my understanding that when you call a second script from a first script, __name__ = main in the first script but not in the second script:  __name__ = the script name itself (sans .py extension) and both of them need to be in the same directory.

See a post of mine earlier this spring which directs you to this site

That should just about do it....
ZacharyHart
Regular Contributor

It's my understanding that when you call a second script from a first script, __name__ = main in the first script but not in the second script:  __name__ = the script name itself (sans .py extension) and both of them need to be in the same directory.

This right here is something I really need to get my head around! I keep getting confused on this.

0 Kudos