summaryrefslogtreecommitdiff
path: root/python/helpers/pydev/_pydevd_bundle/pydevd_custom_frames.py
blob: 47519f32049775dd7a93d2d8a295b202639bd986 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from _pydevd_bundle.pydevd_constants import get_current_thread_id, Null
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
from _pydev_imps._pydev_saved_modules import thread, threading
import sys

DEBUG = False

#=======================================================================================================================
# CustomFramesContainer
#=======================================================================================================================
class CustomFramesContainer:

    # Actual Values initialized later on.
    custom_frames_lock = None #: :type custom_frames_lock: threading.Lock

    custom_frames = None

    _next_frame_id = None

    _py_db_command_thread_event = None


def custom_frames_container_init(): #Note: no staticmethod on jython 2.1 (so, use free-function)

    CustomFramesContainer.custom_frames_lock = thread.allocate_lock()

    # custom_frames can only be accessed if properly locked with custom_frames_lock!
    # Key is a string identifying the frame (as well as the thread it belongs to).
    # Value is a CustomFrame.
    #
    CustomFramesContainer.custom_frames = {}

    # Only to be used in this module
    CustomFramesContainer._next_frame_id = 0

    # This is the event we must set to release an internal process events. It's later set by the actual debugger
    # when we do create the debugger.
    CustomFramesContainer._py_db_command_thread_event = Null()

#Initialize it the first time (it may be reinitialized later on when dealing with a fork).
custom_frames_container_init()


#=======================================================================================================================
# CustomFrame
#=======================================================================================================================
class CustomFrame:

    def __init__(self, name, frame, thread_id):
        # 0 = string with the representation of that frame
        self.name = name

        # 1 = the frame to show
        self.frame = frame

        # 2 = an integer identifying the last time the frame was changed.
        self.mod_time = 0

        # 3 = the thread id of the given frame
        self.thread_id = thread_id


def add_custom_frame(frame, name, thread_id):
    CustomFramesContainer.custom_frames_lock.acquire()
    try:
        curr_thread_id = get_current_thread_id(threading.current_thread())
        next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1

        # Note: the frame id kept contains an id and thread information on the thread where the frame was added
        # so that later on we can check if the frame is from the current thread by doing frame_id.endswith('|'+thread_id).
        frame_id = '__frame__:%s|%s' % (next_id, curr_thread_id)
        if DEBUG:
            sys.stderr.write('add_custom_frame: %s (%s) %s %s\n' % (
                frame_id, get_abs_path_real_path_and_base_from_frame(frame)[-1], frame.f_lineno, frame.f_code.co_name))

        CustomFramesContainer.custom_frames[frame_id] = CustomFrame(name, frame, thread_id)
        CustomFramesContainer._py_db_command_thread_event.set()
        return frame_id
    finally:
        CustomFramesContainer.custom_frames_lock.release()

addCustomFrame = add_custom_frame # Backward compatibility

def update_custom_frame(frame_id, frame, thread_id, name=None):
    CustomFramesContainer.custom_frames_lock.acquire()
    try:
        if DEBUG:
            sys.stderr.write('update_custom_frame: %s\n' % frame_id)
        try:
            old = CustomFramesContainer.custom_frames[frame_id]
            if name is not None:
                old.name = name
            old.mod_time += 1
            old.thread_id = thread_id
        except:
            sys.stderr.write('Unable to get frame to replace: %s\n' % (frame_id,))
            import traceback;traceback.print_exc()

        CustomFramesContainer._py_db_command_thread_event.set()
    finally:
        CustomFramesContainer.custom_frames_lock.release()


def get_custom_frame(thread_id, frame_id):
    '''
    :param thread_id: This should actually be the frame_id which is returned by add_custom_frame.
    :param frame_id: This is the actual id() of the frame
    '''

    CustomFramesContainer.custom_frames_lock.acquire()
    try:
        frame_id = int(frame_id)
        f = CustomFramesContainer.custom_frames[thread_id].frame
        while f is not None:
            if id(f) == frame_id:
                return f
            f = f.f_back
    finally:
        f = None
        CustomFramesContainer.custom_frames_lock.release()


def remove_custom_frame(frame_id):
    CustomFramesContainer.custom_frames_lock.acquire()
    try:
        if DEBUG:
            sys.stderr.write('remove_custom_frame: %s\n' % frame_id)
        CustomFramesContainer.custom_frames.pop(frame_id, None)
        CustomFramesContainer._py_db_command_thread_event.set()
    finally:
        CustomFramesContainer.custom_frames_lock.release()

removeCustomFrame = remove_custom_frame # Backward compatibility