Search code examples
pythonsqlitexbmc

Python throwing an error - String indices must be integers


I'm working on my python script using the version 2.6 for XBMC media application.

I have got a problem with my python script, I'm trying to pull the data off from the sqlite3 database but I'm getting an error TypeError: string indices must be integers.

The error are jumping on this line:

programming = channelMap[row['channel']], row["title"], row["start_date"], row["stop_date"]

Here is the full code:

import xbmc
import xbmcgui
import xbmcaddon
import os
import urllib2
import StringIO
import sqlite3
from sqlite3 import dbapi2 as database
from xml.etree import ElementTree
import xml.etree.ElementTree as ET
from UserDict import DictMixin
import datetime
import time

class MyClass(xbmcgui.WindowXML):

    def onAction(self, action):

        #DOWNLOAD THE XML SOURCE HERE
        url = ADDON.getSetting('allchannels.url')
        req = urllib2.Request(url)
        response = urllib2.urlopen(req)
        data = response.read()
        response.close()
        profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))

        if os.path.exists(profilePath):
           profilePath = profilePath + 'source.db'
           con = database.connect(profilePath)
           cur = con.cursor()
           cur.execute('CREATE TABLE programs(channel TEXT, title TEXT, start_date TIMESTAMP, stop_date TIMESTAMP, description TEXT)')
           con.commit()
           con.close
           tv_elem = ElementTree.parse(StringIO.StringIO(data)).getroot()
           profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
           profilePath = profilePath + 'source.db'
           con = sqlite3.connect(profilePath)
           cur = con.cursor()
           channels = OrderedDict()

           # Get the loaded data
           for channel in tv_elem.findall('channel'):
                channel_name = channel.find('display-name').text
                for program in channel.findall('programme'):
                   title = program.find('title').text
                   start_time = program.get("start")
                   stop_time = program.get("stop")
                   cur.execute("INSERT INTO programs(channel, title, start_date, stop_date)" + " VALUES(?, ?, ?, ?)", [channel_name, title, start_time, stop_time])
                   con.commit()
                   print 'Channels store into database are now successfully!'

                   cur.execute('SELECT channel, title, start_date, stop_date FROM programs')
                   programList = list()
                   channelMap = dict()
                   results = cur.fetchall()
                   cur.close


                   for channel_result in results:
                      for row in channel_result:
                         programming = channelMap[row['channel']], row["title"], row["start_date"], row["stop_date"]
                         print(programming)

I keep getting a same request of error in my XBMC log.

EDIT: When I try this:

programList = list()
channelMap = dict()
for c in channels:
if c.id:
   channelMap[c.id] = c
   strCh = '(\'' + '\',\''.join(channelMap.keys()) + '\')'
   cur.execute('SELECT * FROM programs WHERE channel')
   for row in cur:
      programming = program(channelMap[row['channel']], row["title"], row["start_date"], row["stop_date"])
      programList.append(programming)  
      print(programming)

Here is the error on the xbmc log:

- NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS!
Error Type: <type 'exceptions.TypeError'>
Error Contents: tuple indices must be integers, not str
Traceback (most recent call last):
File "C:\Users\user\AppData\Roaming\XBMC\addons\script.tvguide\test.py", line 1679, in onAction
programming = program(channelMap[row['channel']], row["title"], row["start_date"], row["stop_date"])
TypeError: tuple indices must be integers, not str
-->End of Python script error report<--

Solution

  • You are looping over each row in the result, then over each column. The columns are strings:

    for channel_result in results:
        for row in channel_result:
    

    So channel_result is a row (a tuple by default), then you loop over that with for row in channel_result. This makes each row object a single column value.

    You appear to expect row to be a dictionary instead; that is not the case here. You could just print the row directly; the columns are listed in the same order as the original SELECT:

    for row in results:
        programming = (channelMap[row[0]],) + row[1:]
    

    If you really wanted a dictionary for each row, you'll have to tell sqlite3 to so by setting the row_factory attribute on the connection:

    def dict_factory(cursor, row):
        d = {}
        for idx, col in enumerate(cursor.description):
            d[col[0]] = row[idx]
        return d
    
    con = sqlite3.connect(profilePath)
    con.row_factory = dict_factory
    

    after which you use the one loop:

    for row in results:
    

    and row will be a dictionary with keys corresponding to the column names.