I've got a simple external method that returns a list containing dictionaries. If I run it from python it works fine, imported and used in Zope it raises an exception "Loop over non-sequence".
The script extracts some data from a table, containing the results from a parsed logfile, created by the game Counter-strike ( if you're interested ). I'm no expert in SQL ( as the code below probably shows )
but doing it in Python is no match ( even if the code is verbose and perhaps a bit inefficient. Tips and clues are appreciated if you can make anything out of it ).
 
I import the method in Zope, calling it PlayerStats, from the module CSPlayerStats. The file itself is the Extensions-folder under the Zope main-folder. Try to test it in the ZMI and the error pops up. Thought of using it like so  :
 
<dtml-in PlayerStats>
  <dtml-let item=sequence-item>
    <dtml-var "item['player']"><!-- more fields from the dictionary, here represented as item --><br>
  </dtml-let>
</dtml-in>
The external method :
 
import MySQLdb
 
SQL_PLAYER_INFO = """
select
    cs_attacker,
    cs_attacker_clantag,
    cs_weapon,
    cs_event,
    cs_health,
    cs_damage,
    cs_armor,
    cs_damage_armor,
    cs_victim,
    cs_team_attacker,
    cs_team_victim
from cs_log
order by
    cs_attacker,
    cs_event,
    cs_weapon
"""
 
SQL_DEATHS = """
select
   cs_victim,
   count(*) as deaths
from cs_log
where cs_event = 1
group by cs_victim
order by deaths desc
"""
 
SQL_KILLS = """
select
   cs_attacker,
   count(*) as kills
from cs_log
where cs_event = 1
group by cs_attacker
order by kills desc
"""
 
SQL_HOSTAGE_KILLS = """
select
   cs_attacker,
   count(*) as kills
from cs_log
where cs_event = 5
group by cs_attacker
order by kills desc
"""
 
SQL_TEAM_KILLS = """
select
   cs_attacker,
   count(*) as kills
from cs_log
where cs_event = 3
group by cs_attacker
order by kills desc
"""
 
SQL_RESCUED_HOSTAGE = """
select
   cs_attacker,
   count(*) as rescued_hostages
from cs_log
where cs_event = 7
group by cs_attacker
order by rescued_hostages desc
"""
 
SQL_MOST_TEAM_DAMAGE = """
select cs_attacker, sum(cs_damage) + sum(cs_damage_armor) as damage
from cs_log
where cs_event = 4
group by cs_attacker
order by damage desc
"""
 
SQL_MOST_DAMAGE = """
select cs_attacker, sum(cs_damage)+sum(cs_damage_armor) as damage
from cs_log
where cs_event = 2
group by cs_attacker
order by damage desc
"""
 
def PlayerStats(self):
    players = {}
    db = MySQLdb.connect(db='zopetest')
    cur = db.cursor()
 
    cur.execute(SQL_KILLS)
    for player, kills in cur.fetchall():
        players[player] = {'kills':kills,
                           'deaths':0,
                           'team_kills':0,
                           'team_damage':0,
                           'hostage_kills':0,
                           'hostage_rescued':0}
 
    cur.execute(SQL_DEATHS)
    res = cur.fetchall()
    for player, deaths in res:
        if not players.has_key(player):
            players[player] = {}
        players[player]['deaths'] = deaths
 
    cur.execute(SQL_TEAM_KILLS)
    res = cur.fetchall()
    for player, team_kills in res:
        if not players.has_key(player):
            players[player] = {}
        players[player]['team_kills'] = team_kills
 
    cur.execute(SQL_RESCUED_HOSTAGE)
    res = cur.fetchall()
    for player, hostage_rescued in res:
        if not players.has_key(player):
            players[player] = {}
        players[player]['hostage_rescued'] = hostage_rescued
 
    cur.execute(SQL_MOST_TEAM_DAMAGE)
    res = cur.fetchall()
    for player, team_damage in res:
        if not players.has_key(player):
            players[player] = {}
        players[player]['team_damage'] = team_damage
 
    cur.execute(SQL_MOST_DAMAGE)
    res = cur.fetchall()
    for player, damage in res:
        if not players.has_key(player):
            players[player] = {}
        players[player]['damage'] = damage
 
    cur.execute(SQL_HOSTAGE_KILLS)
    res = cur.fetchall()
    for player, hostage_kills in res:
        if not players.has_key(player):
            players[player] = {}
        players[player]['hostage_kills'] = hostage_kills
 
    result = []
    for player in players:
        kill_death_ratio = float(players[player]['kills']) / float(players[player]['deaths'])
        damage_team_damage_ratio = float(players[player]['damage']) / float(players[player]['team_damage'])
        score = kill_death_ratio * damage_team_damage_ratio
       
        result.append((score, player, players[player]['kills'], players[player]['deaths'], players[player]['damage'], players[player]['team_damage'], kill_death_ratio, damage_team_damage_ratio))
 
    result.sort()
    result.reverse()
    stats = []
    for piece in result:
        score, player, kills, deaths, damage, team_damage, kd_ratio, dt_ratio = piece
        stats.append({'player':player, 'score':score, 'kills': kills, 'deaths': deaths, 'damage': damage, 'team_damage': team_damage, 'kd_ratio':kd_ratio, 'dt_ratio':dt_ratio})
 
    return stats
 
Doing something simple like this :
 
def AList(self):
    return [{'fname':'Thomas'}, {'fname':'Roger'}]
 
in a module and importing it works just fine. Testing is ok and the same DTML-piece fitted to the simple method works like a charm.
 
What am I doing wrong???
 
Appreciate any help you can provide.
 
Best regards,
Thomas Weholt, not yet Zope-meister but want to be ;-)