Is there a simple way to use sys.stdout.write with arcpy.AddMessage?

1598
5
08-16-2016 01:47 PM
RebeccaStrauch__GISP
MVP Esteemed Contributor

I'm using

sys.stdout.write( "." )

in a a loop to print a series of dots when it has processed an .mxd that didn't fit the criteria that I am working on (if it fits criteria, then it lists the path).  It's just a very simple "progress bar" for my tool.

This works fine in a python window, but I'm not sure how to do this in a tool, without getting a bunch of newlines that come with a print or arcpy.AddMessage (which looks messy...but I could live with it)

I've read ab out many of the progress bar approaches, but this one line (or equivalent) is what I'm hoping for.  I'll include a snippet of the code..

# ...  snippet....will not run as standalone as is
updateServices = []
update = False
myMsgs("\nFinding STARTED services accesing {0} to STOP\n   Dots represent services that are OK".format(updateDS[0]))
for startedService in myServices:
     mxdFound = False
     if not update:  # will print a dot for services or df within mxd that are ok, using for progress status
          sys.stdout.write( "." )
     update = False
     #myMsgs("Reviewing service: {0}".format(startedService))
     theServicePath = os.path.join(servicesDir, startedService)
     if not arcpy.Exists(theServicePath):
          myMsgs("...we have a problem")
          exit
     else:
          # myMsgs(" {0}".format(startedService))  # Can use if want to list all services
          for root, dirs, files in os.walk(theServicePath):

               for fn in files:
                    fullPath = os.path.join(root, fn)
                    basename, extension = os.path.splitext(fn)
                    if not extension == ".mxd":
                         pass
                    else:
                         #myMsgs("  mxd file found")
                         mxd = arcpy.mapping.MapDocument(fullPath)
                         try:  
                              # Do stuff with MXD using arcpy.mapping  
                              for df in arcpy.mapping.ListDataFrames(mxd):
                                   lyrList = arcpy.mapping.ListLayers(mxd)
                                   mxdFound = True
                                   for lyr in lyrList:
                                        if lyr.isFeatureLayer and update == False:
                                             if lyr.supports("dataSource") and update == False:
                                                  theSource = lyr.dataSource
                                                  if updateDS[0] in theSource:
                                                       update = True
                                                       myMsgs(" {0} is using {1}".format(startedService, updateDS[0]))     
                                                       updateServices.append(startedService)
                                                       break

                         except AttributeError:  
                              myMsgs("\n   !!! WARNING: {0} has unreadable dataframes(s),\n       will stop as precaution...\n".format(os.path.basename(mxd.filePath)))
                              updateServices.append(startedService)
                              update = True
                              pass                                
                         del mxd
# ...

Line #8 is where the line is that works in the python window.

Thanks.

0 Kudos
5 Replies
DanPatterson_Retired
MVP Esteemed Contributor

You are going to hate me

>>> # python 2.7
>>> for i in range(5): print(i),
... 
0 1 2 3 4
>>> # python 3.x
>>> for i in range(5): print(i, end=" ")
... 
0 1 2 3 4‍‍‍‍‍‍‍‍

don't know about add message since I am on the iThingy

Oh yeah... it is the ',' for 2.7 and end="" or end=" " in 3.*

0 Kudos
RebeccaStrauch__GISP
MVP Esteemed Contributor

Thanks for the quick response Dan,   took me a while to test it out in many different configurations.

The "comma" is a nice trick for the print statement, but it doesn't work for the AddMessage either.  Also, since my for loop is a bit different, the print sill wanted to include at least a "space" with my dots.

I had seen and tried the (i,  end=" ") option in one of the thread but got a syntax error....now I know why (3.4). I'm still in 10.2.2 (and/or 10.3.x)

This is what I really want, and I am getting in the python window (from my script), but as a arcpy tool, it doesn't seem to like the stdout  and of course none of the print option above will work

Finding STARTED services accessing Master_current.gdb to STOP
   Dots represent services that are OK
........................................... wc_public//gmuLetterLandscape.MapServer is using Master_current.gdb
 wc_public//GMUSpArea.MapServer is using Master_current.gdb
 wc_public//GMUSubunits.MapServer is using Master_current.gdb
 wc_public//GMUSubunitsTest.MapServer is using Master_current.gdb
. wc_public//qryGMU.MapServer is using Master_current.gdb
 wc_public//SpecialAreas.MapServer is using Master_current.gdb
 wc_public//SpecialAreas2.MapServer is using Master_current.gdb
 wc_public//SpecialAreas3.MapServer is using Master_current.gdb

Since the tool ignores print and wants AddMessage, I always use my little script, especially when writing/testing...

# shows messages for python window or tool
def myMsgs(message):
     arcpy.AddMessage(message)
     print(message)‍‍‍‍

This may be one of those thing that I either have to write a fancy function for....or just live with it.  I'm the only one that runs it right now, so no biggie....but thinking about future and many more services (i.e., when it sites for more than 5 seconds).  I have many other options and I may just have to do one of those.   I'll leave this open for a bit...but it might end up being a "assumed answered" for lack of real answer.

-------------------------------------------------

EDIT: As a work around, I'll list the info if running from tool

myMsgs("\nFinding STARTED services accesing {0} to STOP".format(updateDS[0]))
     for startedService in myServices:
          mxdFound = False
          if not update:  # will print a dot for services or df within mxd that are ok, using for progress status
               sys.stdout.write( "." )
               arcpy.AddMessage("     Skipping...source not used in {0} ".format(startedService))
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

How aren't the built-in ArcPy progress dialogs working for you? 

0 Kudos
RebeccaStrauch__GISP
MVP Esteemed Contributor

HI Joshua, Thanks for the suggestion.

I was just trying to have is show a dot or a # for each service it didn't need to add to my list.  I thought it was getting to noisy to list the path/service out for things I was going to skip, and I thought the few second delay may look like it wasn't worked (also wanted it in the Results).  After testing what Dan had sent, I decided that not listing them was a bit silly on my part, so I modified my output format and I'm ok with it.  It's amazing what and extra space here or there can do to improve readability.  So, when all is said and done, my output doesn't look so bad.  But it would be nice if there was a way to suppress the newline after a message.  Default should be newline....but one in a while....

Re: your question of why I wasn't using arcpy.SetProgressor   One...forgot about that command, and two) guess it was because I wanted something simple/clean in the Results, not something in the status area at the bottom, where I think the SetProgressor shows, correct??

0 Kudos
RebeccaStrauch__GISP
MVP Esteemed Contributor

For the sake of closing this thread, I ended up going a different route, printing out all the services but for those that match, giving a different message.  A snippet (out of context) is

     # #####
     # ##### Finds running services, filters for those using updated fgdb 
     # #####
     StatusFilter = "STARTED"       # other options for function: "STOPPED", "all""
     updateServicesList = []     # initialize list
     update = False              # initial updae status     

     runningServices = getServiceList(siteURL, theToken, StatusFilter)
     #myMsgs("\n{0} services on {1}: \n{2}".format(StatusFilter, server, runningServices))  # will list all services with StatusFilter

     myMsgs("\nFinding STARTED services accessing {0} \n  Building list....".format(updateDS[0]))
     for startedService in runningServices:  # Checks service for fgdb use
          mxdFound = False # initialize and reset 
          update = False   # initialize and reset
          """if not update:  # prints dot for services/df w/in mxd if ok, for progress status
               sys.stdout.write( "." )
               arcpy.AddMessage("      Skipping...source not used for service: {0} ".format(startedService))
               #  tried all those below, none worked so using sys.stdout.wrtie(".")
               #     dot = "."   print(dot) ,  print(".")  myMsgs(dot)
          """
          #myMsgs("\nReviewing service: {0}".format(startedService))   # can uncomment for debugging
          theServicePath = os.path.join(servicesDir, startedService)
          if not arcpy.Exists(theServicePath):
               myMsgs("theServicePath: {0}".format(theServicePath))
               myMsgs("  ...we might have a problem, theServicePath variable doesn't exist")
               #showStatus(statusINI, scriptNum, newValue = "ERROR: not done. Fix and rerun")
               #sys.exit()
               updateStatusAndExit(statusINI, scriptNum)
          else:
               if ".MapServer" in startedService:
                    # myMsgs(" {0}".format(startedService))  # Can use if want to list all services
                    for root, dirs, files in os.walk(theServicePath):
                         for fname in files:
                              fullPath = os.path.join(root, fname)
                              basename, extension = os.path.splitext(fname)
                              if not extension == ".mxd":    # walks thru files to find .mxd
                                   pass
                              else:
                                   #myMsgs("  mxd file found")
                                   mxd = arcpy.mapping.MapDocument(fullPath)
                                   try:  
                                        # Do stuff with MXD using arcpy.mapping  
                                        for df in arcpy.mapping.ListDataFrames(mxd):
                                             lyrList = arcpy.mapping.ListLayers(mxd)
                                             mxdFound = True
                                             for lyr in lyrList:
                                                  if lyr.isFeatureLayer and update == False:
                                                       if lyr.supports("dataSource") and update == False:
                                                            theSource = lyr.dataSource
                                                            if updateDS[0] in theSource:
                                                                 update = True
                                                                 myMsgs("  +Adding {0}".format(startedService)) #, updateDS[0]))     
                                                                 updateServicesList.append(startedService)
                                                                 break
                                   except AttributeError:  
                                        myMsgs("\n   !!! WARNING: {0} has unreadable dataframes(s),\n       will stop as precaution...\n".format(os.path.basename(mxd.filePath)))
                                        update = True
                                        myMsgs("  +Adding {0}".format(startedService))
                                        updateServicesList.append(startedService)
                                        pass                                
                                   del mxd
                    if not update:  # prints dot for services/df w/in mxd if ok, for progress status
                         sys.stdout.write( "." )
                         myMsgs("      Skipping...source not used for service: {0} ".format(startedService))
                         #  tried all those below, none worked so using sys.stdout.wrtie(".")
                         #     dot = "."   print(dot) ,  print(".")  myMsgs(dot)
               elif ".GPServer" in startedService:

with lines 52-64 basically what I ended up with.  my output is similar to this...

Just in case this helps anyone else.