【问题标题】:Python: How to loop through several ini files with ConfigParser?Python:如何使用 ConfigParser 遍历多个 ini 文件?
【发布时间】:2015-09-06 12:52:10
【问题描述】:

我有点了解如何在 Python 中进行循环,似乎很容易说“对于这个目录中的每个文件......做一些事情”。我现在很难弄清楚如何遍历目录中的一系列 .ini 文件,从中读取行,并将 ini 文件中的文本用作同一 Python 脚本中的变量。例如,in this scriptsingle .ini file 提供脚本中 12 个变量的值。目前,要多次运行脚本,必须将单个 ini 文件替换为另一个包含不同 12 个变量的 ini 文件。该脚本执行在线地图服务提供商的日常维护......事情是......我有几十个服务我想用这个脚本来管理。从脚本来看,.ini 文件的名称似乎是固定的,不确定是否可以循环遍历多个 ini 文件?好消息是,脚本正在使用 ConfigParser .....我希望这是有道理的!

[FS_INFO]
SERVICENAME = MyMapService
FOLDERNAME = None
MXD = D:\nightly_updates\maps\MyMap.mxd
TAGS = points, dots, places
DESCRIPTION  = This is the description text
MAXRECORDS = 1000

[FS_SHARE]
SHARE = True
EVERYONE = true
ORG = true
GROUPS = None

[AGOL]
USER = user_name
PASS = pass_word1

下面的脚本是从上面的ini文件中读取的。

# Import system modules
import urllib, urllib2, json
import sys, os
import requests
import arcpy
import ConfigParser
from xml.etree import ElementTree as ET

class AGOLHandler(object):    

    def __init__(self, username, password, serviceName, folderName):
        self.username = username
        self.password = password
        self.serviceName = serviceName
        self.token, self.http = self.getToken(username, password)
        self.itemID = self.findItem("Feature Service")
        self.SDitemID = self.findItem("Service Definition")
        self.folderName = folderName
        self.folderID = self.findFolder()

    def getToken(self, username, password, exp=60):

        referer = "http://www.arcgis.com/"
        query_dict = {'username': username,
                      'password': password,
                      'expiration': str(exp),
                      'client': 'referer',
                      'referer': referer,
                      'f': 'json'}   

        query_string = urllib.urlencode(query_dict)
        url = "https://www.arcgis.com/sharing/rest/generateToken"

        token = json.loads(urllib.urlopen(url + "?f=json", query_string).read())

        if "token" not in token:
            print token['error']
            sys.exit()
        else: 
            httpPrefix = "http://www.arcgis.com/sharing/rest"
            if token['ssl'] == True:
                httpPrefix = "https://www.arcgis.com/sharing/rest"

            return token['token'], httpPrefix

    def findItem(self, findType):
        #
        # Find the itemID of whats being updated
        #        
        searchURL = self.http + "/search"

        query_dict = {'f': 'json',
                      'token': self.token,
                      'q': "title:\""+ self.serviceName + "\"AND owner:\"" + self.username + "\" AND type:\"" + findType + "\""}    

        jsonResponse = sendAGOLReq(searchURL, query_dict)

        if jsonResponse['total'] == 0:
            print "\nCould not find a service to update. Check the service name in the settings.ini"
            sys.exit()
        else:
            print("found {} : {}").format(findType, jsonResponse['results'][0]["id"])    

        return jsonResponse['results'][0]["id"]

    def findFolder(self):
        #
        # Find the ID of the folder containing the service
        #

        if self.folderName == "None":
            return ""

        findURL = self.http + "/content/users/{}".format(self.username)

        query_dict = {'f': 'json',
                      'num': 1,
                      'token': self.token}

        jsonResponse = sendAGOLReq(findURL, query_dict)

        for folder in jsonResponse['folders']:
            if folder['title'] == self.folderName:
                return folder['id']

        print "\nCould not find the specified folder name provided in the settings.ini"
        print "-- If your content is in the root folder, change the folder name to 'None'"
        sys.exit()


def urlopen(url, data=None):
    # monkey-patch URLOPEN
    referer = "http://www.arcgis.com/"
    req = urllib2.Request(url)
    req.add_header('Referer', referer)

    if data:
        response = urllib2.urlopen(req, data)
    else:
        response = urllib2.urlopen(req)

    return response


def makeSD(MXD, serviceName, tempDir, outputSD, maxRecords):
    #
    # create a draft SD and modify the properties to overwrite an existing FS
    #    

    arcpy.env.overwriteOutput = True
    # All paths are built by joining names to the tempPath
    SDdraft = os.path.join(tempDir, "tempdraft.sddraft")
    newSDdraft = os.path.join(tempDir, "updatedDraft.sddraft")    

    arcpy.mapping.CreateMapSDDraft(MXD, SDdraft, serviceName, "MY_HOSTED_SERVICES")

    # Read the contents of the original SDDraft into an xml parser
    doc = ET.parse(SDdraft)  

    root_elem = doc.getroot()
    if root_elem.tag != "SVCManifest":
        raise ValueError("Root tag is incorrect. Is {} a .sddraft file?".format(SDDraft))

    # The following 6 code pieces modify the SDDraft from a new MapService
    # with caching capabilities to a FeatureService with Query,Create,
    # Update,Delete,Uploads,Editing capabilities as well as the ability to set the max
    # records on the service.
    # The first two lines (commented out) are no longer necessary as the FS
    # is now being deleted and re-published, not truly overwritten as is the 
    # case when publishing from Desktop.
    # The last three pieces change Map to Feature Service, disable caching 
    # and set appropriate capabilities. You can customize the capabilities by
    # removing items.
    # Note you cannot disable Query from a Feature Service.

    #doc.find("./Type").text = "esriServiceDefinitionType_Replacement" 
    #doc.find("./State").text = "esriSDState_Published"

    # Change service type from map service to feature service
    for config in doc.findall("./Configurations/SVCConfiguration/TypeName"):
        if config.text == "MapServer":
            config.text = "FeatureServer"

    #Turn off caching
    for prop in doc.findall("./Configurations/SVCConfiguration/Definition/" +
                                "ConfigurationProperties/PropertyArray/" +
                                "PropertySetProperty"):
        if prop.find("Key").text == 'isCached':
            prop.find("Value").text = "false"
        if prop.find("Key").text == 'maxRecordCount':
            prop.find("Value").text = maxRecords

    # Turn on feature access capabilities
    for prop in doc.findall("./Configurations/SVCConfiguration/Definition/Info/PropertyArray/PropertySetProperty"):
        if prop.find("Key").text == 'WebCapabilities':
            prop.find("Value").text = "Query,Create,Update,Delete,Uploads,Editing"

    # Add the namespaces which get stripped, back into the .SD    
    root_elem.attrib["xmlns:typens"] = 'http://www.esri.com/schemas/ArcGIS/10.1'
    root_elem.attrib["xmlns:xs"] ='http://www.w3.org/2001/XMLSchema'

    # Write the new draft to disk
    with open(newSDdraft, 'w') as f:
        doc.write(f, 'utf-8')

    # Analyze the service
    analysis = arcpy.mapping.AnalyzeForSD(newSDdraft)

    if analysis['errors'] == {}:
        # Stage the service
        arcpy.StageService_server(newSDdraft, outputSD)
        print "Created {}".format(outputSD)

    else:
        # If the sddraft analysis contained errors, display them and quit.
        print analysis['errors']
        sys.exit()


def upload(fileName, tags, description): 
    #
    # Overwrite the SD on AGOL with the new SD.
    # This method uses 3rd party module: requests
    #

    updateURL = agol.http+'/content/users/{}/{}/items/{}/update'.format(agol.username, agol.folderID, agol.SDitemID)

    filesUp = {"file": open(fileName, 'rb')}

    url = updateURL + "?f=json&token="+agol.token+ \
        "&filename="+fileName+ \
        "&type=Service Definition"\
        "&title="+agol.serviceName+ \
        "&tags="+tags+\
        "&description="+description

    response = requests.post(url, files=filesUp);     
    itemPartJSON = json.loads(response.text)

    if "success" in itemPartJSON:
        itemPartID = itemPartJSON['id']
        print("updated SD:   {}").format(itemPartID)
        return True
    else:
        print "\n.sd file not uploaded. Check the errors and try again.\n"  
        print itemPartJSON
        sys.exit()        


def publish():
    #
    # Publish the existing SD on AGOL (it will be turned into a Feature Service)
    #

    publishURL = agol.http+'/content/users/{}/publish'.format(agol.username)

    query_dict = {'itemID': agol.SDitemID,
              'filetype': 'serviceDefinition',
              'overwrite': 'true',
              'f': 'json',
              'token': agol.token}    

    jsonResponse = sendAGOLReq(publishURL, query_dict)

    print("successfully updated...{}...").format(jsonResponse['services'])

    return jsonResponse['services'][0]['serviceItemId']


def enableSharing(newItemID, everyone, orgs, groups):
    #
    # Share an item with everyone, the organization and/or groups
    #

    shareURL = agol.http+'/content/users/{}/{}/items/{}/share'.format(agol.username, agol.folderID, newItemID)

    if groups == None:
        groups = ''

    query_dict = {'f': 'json',
                  'everyone' : everyone,
                  'org' : orgs,
                  'groups' : groups,
                  'token': agol.token}    

    jsonResponse = sendAGOLReq(shareURL, query_dict)

    print("successfully shared...{}...").format(jsonResponse['itemId'])    



def sendAGOLReq(URL, query_dict):
    #
    # Helper function which takes a URL and a dictionary and sends the request
    #

    query_string = urllib.urlencode(query_dict)    

    jsonResponse = urllib.urlopen(URL, urllib.urlencode(query_dict))
    jsonOuput = json.loads(jsonResponse.read())

    wordTest = ["success", "results", "services", "notSharedWith", "folders"]
    if any(word in jsonOuput for word in wordTest):
        return jsonOuput    
    else:
        print "\nfailed:"
        print jsonOuput
        sys.exit()


if __name__ == "__main__":
    #
    # start
    #

    print "Starting Feature Service publish process"

    # Find and gather settings from the ini file
    localPath = sys.path[0]
    settingsFile = os.path.join(localPath, "settings.ini")

    if os.path.isfile(settingsFile):
        config = ConfigParser.ConfigParser()
        config.read(settingsFile)
    else:
        print "INI file not found. \nMake sure a valid 'settings.ini' file exists in the same directory as this script."
        sys.exit()

    # AGOL Credentials
    inputUsername = config.get( 'AGOL', 'USER')
    inputPswd = config.get('AGOL', 'PASS')

    # FS values
    MXD = config.get('FS_INFO', 'MXD')
    serviceName = config.get('FS_INFO', 'SERVICENAME')   
    folderName = config.get('FS_INFO', 'FOLDERNAME')
    tags = config.get('FS_INFO', 'TAGS')
    description = config.get('FS_INFO', 'DESCRIPTION')
    maxRecords = config.get('FS_INFO', 'MAXRECORDS')

    # Share FS to: everyone, org, groups
    shared = config.get('FS_SHARE', 'SHARE')
    everyone = config.get('FS_SHARE', 'EVERYONE')
    orgs = config.get('FS_SHARE', 'ORG')
    groups = config.get('FS_SHARE', 'GROUPS')  #Groups are by ID. Multiple groups comma separated


    # create a temp directory under the script     
    tempDir = os.path.join(localPath, "tempDir")
    if not os.path.isdir(tempDir):
        os.mkdir(tempDir)  
    finalSD = os.path.join(tempDir, serviceName + ".sd")  

    #initialize AGOLHandler class
    agol = AGOLHandler(inputUsername, inputPswd, serviceName, folderName)

    # Turn map document into .SD file for uploading
    makeSD(MXD, serviceName, tempDir, finalSD, maxRecords)

    # overwrite the existing .SD on arcgis.com

    if upload(finalSD, tags, description):

        # publish the sd which was just uploaded
        newItemID = publish()

        # share the item
        if shared:
            enableSharing(newItemID, everyone, orgs, groups)

        print "\nfinished."

【问题讨论】:

    标签: python python-3.x configuration arcgis ini


    【解决方案1】:

    如果我正确理解您的问题,您只想在 main 中添加另一个循环,然后将 main 中的大部分内容放入一个新函数中(在我的示例中,新函数称为“process_ini”。

    所以,尝试将你的 name == main line 到最后的所有内容替换为:

    def process_ini(fileName):
        settingsFile = os.path.join(localPath, fileName)
    
        if os.path.isfile(settingsFile):
            config = ConfigParser.ConfigParser()
            config.read(settingsFile)
        else:
            print "INI file not found. \nMake sure a valid 'settings.ini' file exists in the same directory as this script."
            sys.exit()
    
        # AGOL Credentials
        inputUsername = config.get( 'AGOL', 'USER')
        inputPswd = config.get('AGOL', 'PASS')
    
        # FS values
        MXD = config.get('FS_INFO', 'MXD')
        serviceName = config.get('FS_INFO', 'SERVICENAME')
        folderName = config.get('FS_INFO', 'FOLDERNAME')
        tags = config.get('FS_INFO', 'TAGS')
        description = config.get('FS_INFO', 'DESCRIPTION')
        maxRecords = config.get('FS_INFO', 'MAXRECORDS')
    
        # Share FS to: everyone, org, groups
        shared = config.get('FS_SHARE', 'SHARE')
        everyone = config.get('FS_SHARE', 'EVERYONE')
        orgs = config.get('FS_SHARE', 'ORG')
        groups = config.get('FS_SHARE', 'GROUPS')  #Groups are by ID. Multiple groups comma separated
    
    
        # create a temp directory under the script
        tempDir = os.path.join(localPath, "tempDir")
        if not os.path.isdir(tempDir):
            os.mkdir(tempDir)
        finalSD = os.path.join(tempDir, serviceName + ".sd")
    
        #initialize AGOLHandler class
        agol = AGOLHandler(inputUsername, inputPswd, serviceName, folderName)
    
        # Turn map document into .SD file for uploading
        makeSD(MXD, serviceName, tempDir, finalSD, maxRecords)
    
        # overwrite the existing .SD on arcgis.com
    
        if upload(finalSD, tags, description):
    
            # publish the sd which was just uploaded
            newItemID = publish()
    
            # share the item
            if shared:
                enableSharing(newItemID, everyone, orgs, groups)
    
            print "\nfinished."
    
    if __name__ == "__main__":
        print "Starting Feature Service publish process"
        # Find and gather settings from the ini file
        localPath = sys.path[0]
        for fileName in ['settings.ini', 'flurb.ini', 'durf.ini']:
            process_ini(fileName)
    

    您必须将所有 ini 文件名写入我的示例倒数第二行的列表中。

    或者,您可以通过代码识别目录中的所有 .ini 文件:

    if __name__ == "__main__":
        print "Starting Feature Service publish process"
        # Find and gather settings from the ini file
        localPath = sys.path[0]
        fileNames = [os.path.join(localPath, i) for i in os.listdir(localPath) if i.endswith('.ini')]
        for fileName in fileNames:
            process_ini(fileName)
    

    它也可能有助于设置工作目录(例如,os.chdir(localPath)),但我要放弃你已经拥有的。

    【讨论】:

    • 好人,谢谢!我完全按照您的建议实施,但是 if name == "main": 中的某些内容失败:返回 Traceback(最近一次调用最后一次):文件“update.py” ,第 330 行,在 process_ini(fileName) 文件“update.py”,第 314 行,在 process_ini 如果上传(finalSD,标签,描述):文件“update.py”,第 187 行,在上传 updateURL = agol。 http+'/content/users/{}/{}/items/{}/update'.format(agol.use rname, agol.folderID, agol.SDitemID) NameError: global name 'agol' is not defined
    • 您的回答的好处在于,它成功读取了 ini 文件并创建了要上传的文件,只是循环的上传部分以某种方式爆炸。令人困惑。
    • @tpcolson,基于该错误,您只需将 agol 传递给 upload()。在脚本中出现“upload(finalSD, tags, description)”的两个地方,将其替换为“upload(finalSD, tags, description, agol)”。
    • 这产生了一些新错误: Traceback(最近一次调用最后一次):文件“X:\GIS_Final\data\basedata\basemap\Maps\ArcGIS\ARCGIS_SERVER\GRSM_DISTURBANCE\test.py”,第 331 行,在 process_ini(fileName) 文件“X:\GIS_Final\data\basedata\basemap\Maps\ArcGIS\ARCGIS_SERVER\GRSM_DISTURBANCE\test.py”中,第 318 行,在 process_ini newItemID = publish() 文件“X:\ GIS_Final\data\basedata\basemap\Maps\ArcGIS\ARCGIS_SERVER\GRSM_DISTURBANCE\test.py",第 216 行,在发布 publishURL = agol.http+'/content/users/{}/publish'.format(agol.username) NameError : 未定义全局名称“agol”
    • 基本上你只需要确保你将 agol 传递给每个使用它的函数。在我上次的回复中,我提到了upload(),但看起来您还需要将agol 添加到publish() enableSharing() 中。请记住,您必须通过它并“接收”它。这意味着搜索“publish(”并将“agol”添加到括号内的项目中。对“enableSharing(”执行相同的操作。参见:tutorialspoint.com/python/python_functions.htm
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 2016-09-12
    • 2011-03-27
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多