arcpy 错误后重启 python 脚本,直到成功

Restart python script after arcpy error, until successful

我在 Python (2.7) 中使用 ArcGIS 的 arcpy 模块来处理许多多边形形状文件,使用许多不同的工具。每隔一段时间它就会抛出一个随机错误,我捕获了一个异常,但所有后续的 shapefile 都会受到相同错误的影响。我真的不明白是什么导致了这个错误(ERROR 010088),我唯一的解决方法是从最后一个成功处理的文件重新启动脚本。

我的问题是:如何在每次遇到此错误时重新启动脚本,然后在成功处理所有文件后停止?

我查看了各种不同的问题(例如 Restarting a self-updating python script) but nothing quite does the job, or I can't understand how to apply it to my situation because I'm still very much a Python beginner. The closest I've come is the example below, based on this blog post: https://www.alexkras.com/how-to-restart-python-script-after-exception-and-run-it-forever/

脚本调用 test.py:

import arcpy
import sys

try:
    arcpy.Buffer_analysis(r"E:\temp\boundary.shp",
                          r"E:\temp\boundary2.shp",
                          "100 Feet")
# Print arcpy execute error
except arcpy.ExecuteError as e:
    # Print
    print(e)
# Pass any other type of error
except:
    pass

脚本名为 forever.py,在同一目录中:

from subprocess import Popen
import sys

filename = sys.argv[1]
while True:
    print("\nStarting " + filename)
    p = Popen("python " + filename, shell=True)
    p.wait()

(请注意 boundary.shp 只是一个随机边界多边形 - 此处可用:https://drive.google.com/open?id=1LylBm7ABQoSdxKng59rsT4zAQn4cxv7a)。

我在 Windows 机器上,所以我 运行 所有这些都在命令行中使用:

python.exe forever.py test.py

正如预期的那样,此脚本第一次 运行 没有错误,之后它遇到错误,因为输出文件已经存在(错误 000725)。问题是,最终我希望脚本在遇到错误 010088 时 重新启动,而绝对不是在脚本成功完成时重新启动。所以在这个例子中,它根本不应该重新启动,因为脚本第一次应该是成功的 运行。我事先知道有多少文件要处理,所以我知道脚本在到达最后一个文件时已成功完成。

回答你的问题:

对于force restart任何没有循环的python脚本,您可以调用下面的函数(在python 2.7/windows 10上测试)。

import os, sys

def force_restart_script():
    python = sys.executable
    os.execl(python, python, * sys.argv)

然而:

由于您使用批处理调用 python 脚本,因此您问题的答案并不能解决您最初的问题(经典 XY-Problem)。我的建议是在 python 中完成所有操作。如果没有理由,请不要使用批处理。

解决方案:

  • test.py 包装在一个函数中
  • 创建一个list of all input files并提交给函数
  • 创建一个 for 循环,为每个文件调用一次函数
  • 如有异常,跳过文件
  • 通过调查 error message string
  • 发现错误
  • 否则,表示处理给定文件
  • for 循环包裹在无限 while 循环中,直到所有文件都 已处理
  • 在一个 python 脚本中完成所有这些操作,以便能够使用 force_restart_script() 功能

代码:

import sys, os, arcpy
from time import sleep
# put force_restart_script() here

def arcpy_function(shapefile)       # Formerly called test.py
    try: 
       # arcpy stuff                # Your processing happens here
       return shapefile             # Return name of processed file
    except arcpy.ExecuteError as e: # In case ExecuteError:
       print(e)                     # Print error FYI
       return None                  # Return None
    except Exception as e:          # For all other errors, check message
       if 'ERROR 010088' in str(e): # restart in case of ERROR 010088
           print str(e), "hard restart"
           time.sleep(10)           # Wait so you can read what happened
           force_restart_script()   # Call the restart function
       else:                        # If not 010088, pass
           print(e)                 # Print any other error FYI
           return None              # Return None

if __name__ == "__main__":
    files_to_process = ['file1', 'file2', 'file3']       # Use glob, see link above   
    completed_files = []                                 # Processed files
    while len(completed_files) < len(files_to_process):  # Work until all are processed
        for shapefile in files_to_process:               # Process file by file
            if shapefile in completed_files:             # If the file is processed already
               os.rename(shapefile, "processed_" + shapefile)    # Rename
               continue                                  # Go to next one
            else:                                        # Otherwise
               finished_file = arcpy_function(shapefile) # Process the file
               if finished_file is not None:             # If processing worked, remember
                  completed_files.append(finished_file)
               else:                                     # If not, continue with next file
                   pass  
    else:
         print "all files processed"

请注意,如果脚本在 ERROR 010088 后强制重新启动,则需要 os.rename 来防止对输入文件进行双重处理。

此外,人们似乎 found a workaround 对在其他方面看起来类似的问题有看法。

在那种情况下,我将 运行 一个守护脚本,每分钟执行该脚本。

import datetime
import traceback
def main():
    var filename = '/tmp/running.pid'  # a file indicate whether the script is running or not
    if os.path.isfile(filename):
        log.warning("the last script is still running perfectly, so i don't run")
        return
    else:
        try:
            with open(filename, "w") as f:
                # I choose to write the time so I can see when the script has been started.
                # you can also write the thread id like .pid file
                f.write(datetime.datetime.strftime("%Y-%m-%d %H:%M:%S"))
            run_your_script_here()  # here you run the script
        except Exception  as e:
            os.remove(filename)  # if an error occurs, delete the file so the script will be run next time in 1 minute
            log.error(e)
            log.error(traceback.format_exc())

该脚本有一个缺点,如果一个脚本失败,在最坏的情况下它会等待 1 分钟来启动下一个脚本。如果你需要减少间隔,你应该在脚本中添加一些循环并设置循环之间的时间间隔或使用其他库,如芹菜。