Python KeyError on second passing of .format() - python-3.x

I'm using .format() to autogenerate a menu. But I also need to format it as users run more tests to indicate those tests are already done.
Example test dict:
menuDict = {
"1":
{"testDataDict": "testDataDict1",
"testName": "testName1",
"testGroupName":"testGroupName1"},
"2":
{"testDataDict": "testDataDict2",
"testName": "testName2",
"testGroupName":"testGroupName2"
},
"3":
{"testDataDict": "testDataDict3",
"testName": "testName3",
"testGroupName":"testGroupName3"
},
"4":
{"testDataDict": "testDataDict4",
"testName": "testName4",
"testGroupName":"testGroupName3"
}
}
Actual code:
def menuAutoCreate(menuDict):
testGroupDict = {}
for testNum in menuDict.keys():
try:
testGroupDict[menuDict[testNum]["testGroupName"]].append(testNum)
except:
testGroupDict[menuDict[testNum]["testGroupName"]] = [testNum]
#Groups the tests under the group names
from natsort import natsorted as nt
testGroupNamesList = nt(testGroupDict.keys(), key=lambda y: y.lower())
#Naturally sorts group names so they look orderly
textDump = " "
i = 0
while i < len(testGroupNamesList):
howManyLinesEven = 0
evenList = []
howManyLinesOdd = 0
oddList = []
testGroupNameEven = testGroupNamesList[i]
textDump += "|{:44} |".format(testGroupNameEven)
howManyLinesEven = len(testGroupDict[testGroupNameEven])
evenList = nt(testGroupDict[testGroupNameEven], key=lambda y: y.lower())
#If it's an even number, it puts the menu template on the left side of the screen
if i != len(testGroupNamesList)-1:
testGroupNameOdd = testGroupNamesList[i+1]
textDump += "{:45} |".format(testGroupNameOdd) + "\n"
howManyLinesOdd = len(testGroupDict[testGroupNameOdd])
oddList = nt(testGroupDict[testGroupNameOdd], key=lambda y: y.lower())
#If it's odd, on the right side.
if i == len(testGroupNamesList)-1:
textDump += "{:45} |".format("") + "\n"
#Ensures everything is correctly whitespaced
howManyLines = max(howManyLinesEven, howManyLinesOdd)
#Checks how many lines there are, so if a group has less tests, it will have extra whitespaces
for line in range(howManyLines):
if line < howManyLinesEven:
data = {"testNum": evenList[line], "testName": menuDict[evenList[line]]["testName"]}
textDump += "|({d[testNum]}) {d[testName]:40} {{doneTests[{d[testNum]!r}]:^8}} |".format(d=data)
else:
textDump += "|{:44} |".format("")
if line < howManyLinesOdd:
data = {"testNum": oddList[line], "testName": menuDict[oddList[line]]["testName"]}
textDump += "({d[testNum]}) {d[testName]:41} {{doneTests[{d[testNum]!r}]:^8}} |".format(d=data) + "\n"
else:
textDump += "{:45} |".format("") + "\n"
#Automatically creates a menu
i += 2
print(textDump)
print("\n")
Output of this, as expected:
|testGroupName1 |testGroupName2 |
|(1) testName1 {doneTests['1']:^8} |(2) testName2 {doneTests['2']:^8} |
|testGroupName3 | |
|(3) testName3 {doneTests['3']:^8} | |
|(4) testName4 {doneTests['4']:^8} | | |
This last step will be done elsewhere, but put here for demonstration:
doneTests = {}
for testNum in menuDict.keys():
doneTests[testNum] = "(-)"
print(doneTests)
#textDump.format(**doneTests)
#This doesn't work for some reason?
textDump.format(doneTests = doneTests)
#This step will be repeated as the user does more tests, as an indicator of
which tests are completed.
The expected output would be this:
|testGroupName1 |testGroupName2 |
|(1) testName1 (-) |(2) testName2 (-) |
|testGroupName3 | |
|(3) testName3 (-) | |
|(4) testName4 (-) | | |
But here it throws a:
KeyError: "'1'"
If you remove !r from:
{{doneTests[{d[testNum]!r}]:^8}}
It throws a
KeyError: 1
instead.
I tried formatting with !s. Using lists/tuples. Adding and removing brackets. Out of ideas at this point...

Just tried your example.
I used the function sorted() instead of natsorted() and added the line
textDump = ''
to initialize the textDump variable before the line
i = 0
As a result I got no errors and got the expected output.
EDIT
Now I reproduced your error. I removed !r from {{doneTests[{d[testNum]!r}]:^8}} and used integer keys in doneTests variable
doneTests[int(testNum)] = "(-)"
to solve the problem. I guess the origin of the problem is how format() method works.

Related

Return a summary of games and results

I have a list such as this one:
games = [
{
"id": "5559cafd-6966-4465-af6f-67a784016b41",
"date": "2021-01-23 11:58:20",
"players": ["jowic42", "robot"],
"winner": None
},
...
{
"id": "80a0a0d2-059d-4539-9d53-78b3f6045943",
"date": "2021-01-24 14:23:59",
"players": ["jowic42", "robot"],
"winner": "jowic42"
}
]
and I have to make a function that returns a list summary of the games like this :
1 : 2021-01-23 11:58:20, jowic42 vs robot
2 : 2021-01-24 14:23:59, jowic42 vs robot, winner: jowic42
Here is the code I came up with:
def games_list(games):
for i in range(len(games)):
no = i + 1
date = games[i].get('date')
winner = games[i].get('winner')
player1 = games[i].get('players')[0]
player2 = games[i].get('players')[1]
return (f'{no} : {date}, {player1} vs {player2}, winner:{winner}\n')
The problem is that it only returns the first game and the date is in the form {'2021-01-23 11:58:20'} instead of just the text 2021-01-23 11:58:20. I also want the winner part of the return to not get displayed if it's a None. How could I do this?
You could use list to which data is appended in every iteration and use a conditional operator to add winner based on its value. See below code.
def games_list(games):
no = 0
results = []
for game in games:
no = no + 1
date = game['date']
winner = game['winner']
player1 = game['players'][0]
player2 = game['players'][1]
line = str(no) + ':' + date + ' ' + player1 + ' vs ' + player2 + (' winner:' + winner if winner else '') + '\n'
results.append(line)
return results
print(''.join(games_list(games)))
Output:
1:2021-01-23 11:58:20 jowic42 vs robot
2:2021-01-24 14:23:59 jowic42 vs robot winner:jowic42
Note: used #danh's code and updated it as danh's code looked more readable.
My python is rusty, but...
def games_list(games):
i = 0
results = []
for game in games:
no = i + 1
date = game['date']
winner = game['winner']
player1 = game['players'][0]
player2 = game['players'][1]
results.append((f'{no} : {date}, {player1} vs {player2}, winner:{winner}\n')
return results

why Lists in a class were changed without my operate?

i run a class in a for cycle.
the code is:
graph_all = []
graph_now = graph_exc(-1)
for lin in res_str:
if lin.strip():
print(lin)
if lin[0] == 't' :
if graph_now.numbering != (-1) :
graph_all.append(graph_now)
graph_now = graph_exc(int(lin[4:-1]))
if lin[0] == 'v' :
graph_now.add_vertex(lin[2],lin[4])
if lin[0] == 'e' :
graph_now.add_edges(lin[2],lin[4],lin[6])
else:
continue
res_str is a list full of sentences.
I use graph_all to accommdate all the class.
when the first letter is 'v' or 'e',i put something in graph_now's lists.
when the first letter is 't',i put graph_now to graph_all and re-define the graph_all.but i find a problem:when i define graph_all again,the lists in class in graph_all will be nothing,too!
the code of graph_exc is :
class graph_exc:
edges_on_1 = []
edges_on_2 = []
edges_on_edges = []
vertex_on = {}
def __init__(self,numbering):
self.num_edge = 0
self.num_vertex = 0
self.numbering = numbering
self.edges_on_edges.clear()
self.edges_on_2.clear()
self.edges_on_1.clear()
self.vertex_on.clear()
def add_vertex(self,ver,ver_num):
self.vertex_on[ver] = ver_num
self.num_vertex += 1
def add_edges(self,point_1,edges,point_2):
self.edges_on_1.append(point_1)
self.edges_on_edges.append(edges)
self.edges_on_2.append(point_2)
self.num_edge += 1
the problem is : edges_on_1,edges_on_edges,edges_on_2 and vertex will be influenced.the data in here will disappear.but the num_edge and num_vertex are not.
I try to use deepcopy method to solve it.but it does not work.

I get Key Error: "po5" after everything is done

car={"po1":50,"po2":"-","po3":15,"po4":"+","po5":12}
vocar = list(car.keys())
inter=0
def cal(car,vocar,inter):
while len(car)!=1:
for inter in range(len(car)):
if car.get(vocar[inter],0)=="+":
new=car.get(vocar[inter-1])+car.get(vocar[inter+1])
car.pop(vocar[inter])
car.pop(vocar[inter+1])
car.update({vocar[inter-1]:new})
car1=car
vocar1=list(car1.keys())
inter1=0
cal(car1,vocar1,inter1)
elif car.get(vocar[inter],0)=="-":
new=car.get(vocar[inter-1])-car.get(vocar[inter+1])
car.pop(vocar[inter])
car.pop(vocar[inter+1])
car.update({vocar[inter-1]:new})
car1=car
vocar1=list(car1.keys())
inter1=0
cal(car1,vocar1,inter1)
print(car)
cal(car,vocar,inter)
I keep getting a key error even if I get what I wanted, which is {'po1': 47}.
But after everything is done, it gives me a key error. Please help!
At first:
while len(car)!=1
You pop item from car and try to make a recursion function then you can use:
if not car:
return
for inter in range(len(car)):
# ....
When you make a loop like this:
for inter in range(len(car))
that means:
| loop | inter |
|:----:|:-----:|
| 1 | 0 | car => [X, X, X ,X ,X]
| 2 | 1 | car => [X, X, X ,X]
| 3 | 2 | car => [X, X, X] ! ERR !
| 4 | 3 |
| 5 | 4 |
in loop 3 you have an error (maybe ;))
you can use the main dict:
for inter in car:
According above, You didn't need vocar anymore:
if car.get(inter,0)=="+":
"po1" is str and "po2" is int then you can't use + operator between int and int.
dictionaries are unordered
That means, every time you run it, item arrangments may be changed! so I change it:
car = [("po1",50), ("po2","-"),("po3",15),("po4","+"),("po5",12)]
We changed car then we can do this:
if inter[1] == "+":
car[head-1] = (inter[0], car[head-1][1]+car[head+1][1])
finally, we MUST remove car[head+1] at first and then remove car[inter].
car = [("po1",50), ("po2","-"),("po3",15),("po4","+"),("po5",12)]
def cal(car):
head = 0
if not car:
return
for inter in car:
if inter[1] == "+":
car[head-1] = (inter[0], car[head-1][1]+car[head+1][1])
car.remove(car[head+1])
car.remove(inter)
cal(car)
elif inter[1] =="-":
car[head-1] = (inter[0], car[head-1][1]-car[head+1][1])
car.remove(car[head+1])
car.remove(inter)
cal(car)
head += 1
return car[0][1]
print(cal(car))
OUT:
===
47

how do i verify multiple occourences using threadname.expect with pexpect library

self.nanokdp_p.sendline(cmd)
if self.nanokdp_p.expect(match, timeout = timeout)==0:
print ("Device Sleep")
Right now i run a command on an interactive output using self.nanokdp_p.sendline(cmd)
Now i want to expect occourance of "match" string but dont want to stop at first occourence, instead i want expect or some custom function to run for a particular time and count occourances of match.
Also, this thing works if i just look and stop for first occourance.
self.thread = pexpect.spawn(cmd, logfile = my_log_file, maxread = 1)
def expectMultipleOccurrencesOfanExp(thread, expToExpect, count, timeout=120, wait = 5):
count += 1 # to match prints
for i in range(1, count):
if thread.expect(expToExpect, timeout)==0:
if i == count:
return True
elif i < count:
print (expToExpect + " found for " + str(i) + "th time")
time.sleep(wait)
continue
return False

Groovy: indexes of substrings?

How do I find the indexes of all the occurances of a substring in a large string -
(so basically ,and extension of the "indexOf" function) . Any ideas?
Current situation:
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexOf(sub)
// = 9
I want something like:
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexesOf(sub)
// = [9,15]
Is there such a function? How should I implement it otherwise? (in a non trivial way)
You could write a new addition to the String metaClass like so:
String.metaClass.indexesOf = { match ->
def ret = []
def idx = -1
while( ( idx = delegate.indexOf( match, idx + 1 ) ) > -1 ) {
ret << idx
}
ret
}
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexesOf(sub)
There is nothing I know of that exists in groovy currently that gets you this for free though
This is a relatively easy approach:
String.metaClass.indexesOf = { arg ->
def result = []
int index = delegate.indexOf(arg)
while (index != -1) {
result.add(index);
index = delegate.indexOf(arg, index+1);
}
return result;
}
Note that this will find overlapping instances (i.e. "fooo".indexesOf("oo") will return [1, 2]). If you don't want this, replace index+1 with index+arg.length().

Resources