How to pass arguments to main function in module?

38735
7
Jump to solution
03-12-2017 05:48 AM
PeterWilson
Occasional Contributor III

I have recently started learning more about Python Packages and Modules. I'm currently busy updating my existing modules so that that can be run as script or imported as a module into my other code. I'm not sure how to construct my input arguments within my module and pass them to the main() function within my module.

I've have written my my main() function and called it under if __name__ == '__main__' passing the input arguments. The inputs are currently hard coded to show what I'm trying to achieve. Any help in how to correctly construct my input arguments that the user will pass, which will then be passed onto the main function will be appreciated.

As mentioned I'm trying to be able to use the following as a script when used directly or imported as a module into my other code and run from there. If I import it as a module would I call the main() function when importing it? Is the following structure correct in how I have written the following? Any advice is appreciated.

'''
Created on March 12, 2017

Create a new ArcHydro Schema

File Geodatabase and Rasters

Folder

@author: PeterW
'''
# import site-packages and modules
import re
from pathlib import Path
import arcpy

# set environment settings
arcpy.env.overwriteOutput = True


def archydro_rasters_folder(workspace):
    """Create rasters folder directory
    if it doens't already exist"""
    model_name = Path(workspace).name
    layers_name = re.sub(r"\D+", "Layers", model_name)
    layers_folder = Path(workspace, layers_name)
    if layers_folder.exists():
        arcpy.AddMessage("Rasters folder: {0} exists".format(layers_name))
    else:
        layers_folder.mkdir(parents=True)
        arcpy.AddMessage("Rasters folder {0} created".format(layers_name))


def archydro_fgdb_schema(schema, dem, workspace):
    """Create file geodatabase using XML
    schema and set coordinate system based
    on input DEM if it doesn't already exist"""
    model_name = Path(workspace).name
    fgdb = "{0}.gdb".format(model_name)
    if arcpy.Exists(str(Path(workspace, fgdb))):
        arcpy.AddMessage("{0} file geodatabase exists".format(fgdb))
    else:
        new_fgdb = arcpy.CreateFileGDB_management(str(workspace), fgdb)
        import_type = "SCHEMA_ONLY"
        config_keyword = "DEFAULTS"
        arcpy.AddMessage("New {0} file geodatabase created".format(fgdb))
        arcpy.ImportXMLWorkspaceDocument_management(new_fgdb, schema,
                                                    import_type,
                                                    config_keyword)
        arcpy.AddMessage("ArcHydro schema imported")
        projection = arcpy.Describe(dem).spatialReference
        projection_name = projection.PCSName
        feature_dataset = Path(workspace, fgdb, "Layers")
        arcpy.DefineProjection_management(str(feature_dataset),
                                          projection)
        arcpy.AddMessage("Changed projection to {0}".format(projection_name))


def main(workspace, dem, schema):
    """main function to create rasters folder
    and file geodatabase"""
    archydro_rasters_folder(workspace)
    archydro_fgdb_schema(schema, dem, workspace)


if __name__ == '__main__':
    main(workspace = r"E:\Projects\2016\01_Bertrand_Small_Projects\G113268\ArcHydro\Model04",
         dem = r"E:\Projects\2016\01_Bertrand_Small_Projects\G113268\ArcHydro\DEM2\raw",
         schema = r"E:\Python\Masters\Schema\ESRI_UC12\ModelBuilder\Schema\Model01.xml")
0 Kudos
1 Solution

Accepted Solutions
DanPatterson_Retired
MVP Emeritus

Peter...off the top of my head.... underneath ... if __name__... you should have a call to your function... it doesn't have to be called main, I use _demo() and when I am working standalone, it will run whatever is under the  if __name... line

def runme():
    do stuff here
# -------------------------------------------------------------------------
if __name__ == "__main__":
    """ No demo  """
    runme()‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

then if you want to import the module

from mypackage import runme‍‍

Then you can use runme whenever you want.

To test... try something simple.  But your 'runme' script should have preset arguments or sys.argv[1] of its getparameteras text equivalents.  In the above example, runme doesn't need any parameters.  If you want to have those to be passed or read, then you need to modify the code a bit

def runme(input_featureclass):
    do stuff here
# -------------------------------------------------------------------------
if __name__ == "__main__":
    """ No demo  """
    in_fc = r"\c:temp\something"
    runme(input_featureclass=in_fc)‍‍‍‍‍‍‍‍‍‍‍‍‍‍

then when you call runme from your main program it would be

runme(in_fc)    or runme(sys.argv[1])  or runme(GetParameterAsText(0))

give it a shot

View solution in original post

7 Replies
DanPatterson_Retired
MVP Emeritus

Peter...off the top of my head.... underneath ... if __name__... you should have a call to your function... it doesn't have to be called main, I use _demo() and when I am working standalone, it will run whatever is under the  if __name... line

def runme():
    do stuff here
# -------------------------------------------------------------------------
if __name__ == "__main__":
    """ No demo  """
    runme()‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

then if you want to import the module

from mypackage import runme‍‍

Then you can use runme whenever you want.

To test... try something simple.  But your 'runme' script should have preset arguments or sys.argv[1] of its getparameteras text equivalents.  In the above example, runme doesn't need any parameters.  If you want to have those to be passed or read, then you need to modify the code a bit

def runme(input_featureclass):
    do stuff here
# -------------------------------------------------------------------------
if __name__ == "__main__":
    """ No demo  """
    in_fc = r"\c:temp\something"
    runme(input_featureclass=in_fc)‍‍‍‍‍‍‍‍‍‍‍‍‍‍

then when you call runme from your main program it would be

runme(in_fc)    or runme(sys.argv[1])  or runme(GetParameterAsText(0))

give it a shot

PeterWilson
Occasional Contributor III

Hi Dan

Thanks for the following, how would I set up the input arguments that need to be passed into my main() function. The reason I'm asking is that I'm calling two functions that both use the same input argument workspace.

0 Kudos
DanPatterson_Retired
MVP Emeritus

I would assume that you would pass that to the called

def runme(folder):
    
    may_return_stuff = da_func1(folder)
    big_output = da_func2(folder, may_return_stuff)
    return big_output

# -------------------------------------------------------------------------
if __name__ == "__main__":
    """ No demo  """
    folder = r"\c:temp\something"
    runme(in_folder=folder)

you import, use runme, which requires a folder which you pass to it, da_func1 need the folder to do stuff.  Then da)func2, needs the result of da_func1 as well as the folder.  play with a simple example and don't try to do it with your 'real' scripts/module until you get the flow you want, then emulate what works.

0 Kudos
PeterWilson
Occasional Contributor III

Thanks Dan for all your help, I'll post my code once completed, that it might help others.

0 Kudos
PeterWilson
Occasional Contributor III

Hi Dan \ Joshua

As promised I've attached my updated module based on your suggestions as well as those that I received from my Stack Overflow post:

'''
Created on March 12, 2017

Create a new ArcHydro Schema

File Geodatabase and Rasters

Folder

@author: PeterW
'''
# import site-packages and modules
import re
from pathlib import Path
import arcpy
import argparse

# set environment settings
arcpy.env.overwriteOutput = True


def rasters_directory(workspace):
    """Create rasters folder directory
    if it doens't already exist"""
    model_name = Path(workspace).name
    layers_name = re.sub(r"\D+", "Layers", model_name)
    layers_folder = Path(workspace, layers_name)
    if layers_folder.exists():
        arcpy.AddMessage("Rasters folder: {0} exists".format(layers_name))
    else:
        layers_folder.mkdir(parents=True)
        arcpy.AddMessage("Rasters folder {0} created".format(layers_name))


def fgdb_schema(workspace, schema, dem):
    """Create file geodatabase using XML
    schema and set coordinate system based
    on input DEM if it doesn't already exist"""
    model_name = Path(workspace).name
    fgdb = "{0}.gdb".format(model_name)
    if arcpy.Exists(str(Path(workspace, fgdb))):
        arcpy.AddMessage("{0} file geodatabase exists".format(fgdb))
    else:
        new_fgdb = arcpy.CreateFileGDB_management(str(workspace), fgdb)
        import_type = "SCHEMA_ONLY"
        config_keyword = "DEFAULTS"
        arcpy.AddMessage("New {0} file geodatabase created".format(fgdb))
        arcpy.ImportXMLWorkspaceDocument_management(new_fgdb, schema,
                                                    import_type,
                                                    config_keyword)
        arcpy.AddMessage("ArcHydro schema imported")
        projection = arcpy.Describe(dem).spatialReference
        projection_name = projection.PCSName
        feature_dataset = Path(workspace, fgdb, "Layers")
        arcpy.DefineProjection_management(str(feature_dataset),
                                          projection)
        arcpy.AddMessage("Changed projection to {0}".format(projection_name))


def model_schema(workspace, schema, dem):
    """Create model schema: rasters folder
    and file geodatabase"""
    rasters_directory(workspace)
    fgdb_schema(schema, dem, workspace)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Create a ArcHydro schema')
    parser.add_argument('--workspace', metavar='path', required=True,
                        help='the path to workspace')
    parser.add_argument('--schema', metavar='path', required=True,
                        help='path to schema')
    parser.add_argument('--dem', metavar='path', required=True,
                        help='path to dem')
    args = parser.parse_args()
    model_schema(workspace=args.workspace, schema=args.schema, dem=args.dem)
JoshuaBixby
MVP Esteemed Contributor

If you are going to the effort to formalize modules and create packages, I encourage you to review the PEP 8 -- Style Guide for Python Code.  There are suggested naming conventions for functions, modules, packages, and more.  It would be a shame to put all this effort in and have it be difficult for others to read or maintain.

Looking at the PEP 8 Naming Conventions:

Overriding Principle

Names that are visible to the user as public parts of the API should follow conventions that reflect usage rather than implementation.

Looking at your code specifically, a name like main() is reflecting the implementation and not the usage or purpose of a function.

In terms of understanding the role of if __name__ == '__main__': , I encourage you to peruse the Command line and environment documentation if you haven't already.  Also, there is a good discussion on the topic at StackOverflow:  What does if __name__ == "__main__": do?

PeterWilson
Occasional Contributor III

Hi Joshua,

Thanks so much for your suggestions, I'll have a look at the following links that you have provided and post my completed code afterwards.

0 Kudos