@@ -2526,6 +2526,19 @@ def __init__(self, sockfile, owns_sockfile=True, **kwargs):
25262526 self ._write_failed = False
25272527 super ().__init__ (** kwargs )
25282528
2529+ @staticmethod
2530+ def protocol_version ():
2531+ # By default, assume a client and server are compatible if they run
2532+ # the same Python major.minor version. We'll try to keep backwards
2533+ # compatibility between patch versions of a minor version if possible.
2534+ # If we do need to change the protocol in a patch version, we'll change
2535+ # `revision` to the patch version where the protocol changed.
2536+ # We can ignore compatibility for pre-release versions; sys.remote_exec
2537+ # can't attach to a pre-release version except from that same version.
2538+ v = sys .version_info
2539+ revision = 0
2540+ return int (f"{ v .major :02X} { v .minor :02X} { revision :02X} F0" , 16 )
2541+
25292542 def _send (self , ** kwargs ) -> None :
25302543 json_payload = json .dumps (kwargs )
25312544 try :
@@ -2926,7 +2939,7 @@ def complete(self, text, state):
29262939 return None
29272940
29282941
2929- def _connect (host , port , frame ):
2942+ def _connect (host , port , frame , commands , version ):
29302943 with closing (socket .create_connection ((host , port ))) as conn :
29312944 sockfile = conn .makefile ("rwb" )
29322945
@@ -2935,19 +2948,39 @@ def _connect(host, port, frame):
29352948
29362949 if Pdb ._last_pdb_instance is not None :
29372950 remote_pdb .error ("Another PDB instance is already attached." )
2951+ elif version != remote_pdb .protocol_version ():
2952+ target_ver = f"0x{ remote_pdb .protocol_version ():08X} "
2953+ attach_ver = f"0x{ version :08X} "
2954+ remote_pdb .error (
2955+ f"The target process is running a Python version that is"
2956+ f" incompatible with this PDB module."
2957+ f"\n Target process pdb protocol version: { target_ver } "
2958+ f"\n Local pdb module's protocol version: { attach_ver } "
2959+ )
29382960 else :
2961+ remote_pdb .rcLines .extend (commands .splitlines ())
29392962 remote_pdb .set_trace (frame = frame )
29402963
29412964
2942- def attach (pid ):
2965+ def attach (pid , commands = () ):
29432966 """Attach to a running process with the given PID."""
29442967 with closing (socket .create_server (("localhost" , 0 ))) as server :
29452968 port = server .getsockname ()[1 ]
29462969
29472970 with tempfile .NamedTemporaryFile ("w" , delete_on_close = False ) as connect_script :
29482971 connect_script .write (
2949- f'import pdb, sys\n '
2950- f'pdb._connect("localhost", { port } , sys._getframe().f_back)\n '
2972+ textwrap .dedent (
2973+ f"""
2974+ import pdb, sys
2975+ pdb._connect(
2976+ host="localhost",
2977+ port={ port } ,
2978+ frame=sys._getframe(1),
2979+ commands={ json .dumps ("\n " .join (commands ))} ,
2980+ version={ _RemotePdb .protocol_version ()} ,
2981+ )
2982+ """
2983+ )
29512984 )
29522985 connect_script .close ()
29532986 sys .remote_exec (pid , connect_script .name )
@@ -3087,7 +3120,7 @@ def main():
30873120 if opts .pid :
30883121 if opts .module :
30893122 parser .error ("argument -m: not allowed with argument --pid" )
3090- attach (opts .pid )
3123+ attach (opts .pid , opts . commands )
30913124 return
30923125
30933126 if opts .module :
0 commit comments