
"Tim Peters" <tim.one@home.com> writes:
I left it running overnight, and it terminated! (with a KeyError). I can't say I really understand what's going on, but I'm in Exam Hell at the moment (for the last time! Yippee!), so don't have any spare cycles to think about it hard. Anyway, this is what I was running: dict = {} # let's force dict to malloc its table for i in range(1,10000): dict[i] = i hashcode = 0 class Machiavelli2: def __eq__(self, other): global hashcode d2 = dict.copy() dict.clear() hashcode += 1 for k,v in d2.items(): dict[k] = v return 1 def __hash__(self): return hashcode dict[Machiavelli2()] = Machiavelli2() print dict[Machiavelli2()] If you thought my last test case was contrived, I look forward to you finding adjectives for this one... Cheers, M. -- (ps: don't feed the lawyers: they just lose their fear of humans) -- Peter Wood, comp.lang.lisp

[Michael Hudson, taking a break from exams]
Good luck! I really shouldn't tell you this now, but the real reason people dread turning 30, 40, 50, 60-- and so on --is that every 10th birthday starting at 30 they test you *again*! On every course you ever took. It's grueling. The penalty for failure is severe: flunk just one review exam, and they pick a date at random over the following 10 years for you to die. No point fighting it, it's just civilization's nasty little secret. This is why life expectancy correlates with education, but it does appear that the human limit for remembering both plane geometry and the names of hundreds of dead psychopaths is about 120 years. In the meantime, I built a test case to tickle stack overflow directly, and it does so quickly: class Yuck: def __init__(self): self.i = 0 def make_dangerous(self): self.i = 1 def __hash__(self): # direct to slot 4 in table of size 8; slot 12 when size 16 return 4 + 8 def __eq__(self, other): if self.i == 0: # leave dict alone pass elif self.i == 1: # fiddle to 16 slots self.__fill_dict(6) self.i = 2 else: # fiddle to 8 slots self.__fill_dict(4) self.i = 1 return 1 def __fill_dict(self, n): self.i = 0 dict.clear() for i in range(n): dict[i] = i dict[self] = "OK!" y = Yuck() dict = {y: "OK!"} z = Yuck() y.make_dangerous() print dict[z] It just arranges to move y to a different slot in a different-sized table each time __eq__ is invoked, alternating between slot 4 in a size-8 table and slot 12 in a size-16 table. However, if I stick "print self.i" at the start of __eq__, it dies with a KeyError instead! That's why I'm mentioning it -- could be the same misdirection you're seeing. I can't account for the KeyError in any rational way: under Windows, it's actually hitting a stack overflow in the bowels of the system malloc() then. Windows "recovers" from that and presses on. Everything that happens after appears to be an accident. win98-as-usual-ly y'rs - tim PS: You'll be tested on this, too <wink>.

Haven't run your example yet as my machine's not on at the moment. On Tue, 5 Jun 2001, Tim Peters wrote:
Hmm. It's quite likely that PyMem_Malloc (or whatever) crapping out and returning NULL will get turned into a MemoryError, which will then get turned into a KeyError, isn't it? I could believe that malloc would set up some fancy sigsegv-type handlers for memory management purposes which then get called when it tramples all over the end of the stack. But I'm making this up as I go along...
Well, linux seems to be similarly inscrutable here. One problem is that this is a pig to run under the debugger - setting a breakpoint on lookdict isn't terribly interesting way to spend your time. I suppose you could just set the breakpoint on the recursive call... later.
PS: You'll be tested on this, too <wink>.
Oh, piss off <wink>. Cheers, M.

[Michael Hudson, taking a break from exams]
Good luck! I really shouldn't tell you this now, but the real reason people dread turning 30, 40, 50, 60-- and so on --is that every 10th birthday starting at 30 they test you *again*! On every course you ever took. It's grueling. The penalty for failure is severe: flunk just one review exam, and they pick a date at random over the following 10 years for you to die. No point fighting it, it's just civilization's nasty little secret. This is why life expectancy correlates with education, but it does appear that the human limit for remembering both plane geometry and the names of hundreds of dead psychopaths is about 120 years. In the meantime, I built a test case to tickle stack overflow directly, and it does so quickly: class Yuck: def __init__(self): self.i = 0 def make_dangerous(self): self.i = 1 def __hash__(self): # direct to slot 4 in table of size 8; slot 12 when size 16 return 4 + 8 def __eq__(self, other): if self.i == 0: # leave dict alone pass elif self.i == 1: # fiddle to 16 slots self.__fill_dict(6) self.i = 2 else: # fiddle to 8 slots self.__fill_dict(4) self.i = 1 return 1 def __fill_dict(self, n): self.i = 0 dict.clear() for i in range(n): dict[i] = i dict[self] = "OK!" y = Yuck() dict = {y: "OK!"} z = Yuck() y.make_dangerous() print dict[z] It just arranges to move y to a different slot in a different-sized table each time __eq__ is invoked, alternating between slot 4 in a size-8 table and slot 12 in a size-16 table. However, if I stick "print self.i" at the start of __eq__, it dies with a KeyError instead! That's why I'm mentioning it -- could be the same misdirection you're seeing. I can't account for the KeyError in any rational way: under Windows, it's actually hitting a stack overflow in the bowels of the system malloc() then. Windows "recovers" from that and presses on. Everything that happens after appears to be an accident. win98-as-usual-ly y'rs - tim PS: You'll be tested on this, too <wink>.

Haven't run your example yet as my machine's not on at the moment. On Tue, 5 Jun 2001, Tim Peters wrote:
Hmm. It's quite likely that PyMem_Malloc (or whatever) crapping out and returning NULL will get turned into a MemoryError, which will then get turned into a KeyError, isn't it? I could believe that malloc would set up some fancy sigsegv-type handlers for memory management purposes which then get called when it tramples all over the end of the stack. But I'm making this up as I go along...
Well, linux seems to be similarly inscrutable here. One problem is that this is a pig to run under the debugger - setting a breakpoint on lookdict isn't terribly interesting way to spend your time. I suppose you could just set the breakpoint on the recursive call... later.
PS: You'll be tested on this, too <wink>.
Oh, piss off <wink>. Cheers, M.
participants (2)
-
Michael Hudson
-
Tim Peters