Name mangling and pdb - bug or feature?

Hi,

when debugging code with name mangling, I came across this behavior

(Pdb) l
2404        # To start an ED25519 kex, we simply send a random 256-bit number as the
2405        # public key.
2406        def send_init(self, s, init_msg=SSH.Protocol.MSG_KEXDH_INIT):
2407            self.__ed25519_pubkey = os.urandom(32)
2408            breakpoint()
2409 ->         s.write_byte(init_msg)
2410            s.write_string(self.__ed25519_pubkey)
2411            s.send_packet()
2412 
2413 
2414    class KexNISTP256(KexDH):
(Pdb) self.__ed25519_pubkey
*** AttributeError: 'KexCurve25519_SHA256' object has no attribute '__ed25519_pubkey'
(Pdb) self._KexCurve25519_SHA256__ed25519_pubkey
b'\x1f\tK\x96s5\xf1\x9e\xc4|\x87\x96B\xcbI\xb4\x06\xbfe\xa2\xa0\x0e\xd0>\x92\xf9h\x8e\xdarQX'
(Pdb) 

While I know you cannot access a mangled attribute from outside with its “real name”, only with its mangled one, accessing it via self.__name seems pretty… inner :smiley:

Is this a limitation of pdb or even a bug, or is this working as intended?

Or in other words…
Why is my self.__ed25519_pubkey in pdb failing, but just the next line (2410) it is working?

Thanks for any help!

The code on line 2410 is shown as __ed25519_pubkey but also mangled to _KexCurve25512_SHA256__ed25512_pubkey on compile time. Check out _Py_Mangle() in Python/compile.c.

In this example the compiler is emitting _Example__egg for Example.__egg attribute:

>>> code = """
... class Example:
...     def __init__(self):
...         self.__egg = "spam"
... 
... """
>>> dis.dis(code)
  2           0 LOAD_BUILD_CLASS
              2 LOAD_CONST               0 (<code object Example at 0x7f2500b0ac90, file "<dis>", line 2>)
              4 LOAD_CONST               1 ('Example')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               1 ('Example')
             10 CALL_FUNCTION            2
             12 STORE_NAME               0 (Example)
             14 LOAD_CONST               2 (None)
             16 RETURN_VALUE

Disassembly of <code object Example at 0x7f2500b0ac90, file "<dis>", line 2>:
  2           0 LOAD_NAME                0 (__name__)
              2 STORE_NAME               1 (__module__)
              4 LOAD_CONST               0 ('Example')
              6 STORE_NAME               2 (__qualname__)

  3           8 LOAD_CONST               1 (<code object __init__ at 0x7f2500b0abe0, file "<dis>", line 3>)
             10 LOAD_CONST               2 ('Example.__init__')
             12 MAKE_FUNCTION            0
             14 STORE_NAME               3 (__init__)
             16 LOAD_CONST               3 (None)
             18 RETURN_VALUE

Disassembly of <code object __init__ at 0x7f2500b0abe0, file "<dis>", line 3>:
  4           0 LOAD_CONST               1 ('spam')
              2 LOAD_FAST                0 (self)
              4 STORE_ATTR               0 (_Example__egg)
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE
1 Like

@tiran, thanks a lot!

This makes a lot of sense.

For me, that’s yet another reason for not using identifiers with leading double underscores.