bash-dap 0.1__tar.gz → 0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bash_dap-0.1 → bash_dap-0.2}/PKG-INFO +8 -4
- {bash_dap-0.1 → bash_dap-0.2}/README.md +7 -3
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap/__init__.py +1 -1
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap/adapter.py +25 -4
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap/harness.sh +30 -8
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap.egg-info/PKG-INFO +8 -4
- {bash_dap-0.1 → bash_dap-0.2}/pyproject.toml +1 -1
- {bash_dap-0.1 → bash_dap-0.2}/tests/test_adapter.py +25 -0
- {bash_dap-0.1 → bash_dap-0.2}/LICENSE +0 -0
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap/__main__.py +0 -0
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap.egg-info/SOURCES.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap.egg-info/dependency_links.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap.egg-info/entry_points.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.2}/bash_dap.egg-info/top_level.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bash-dap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2
|
|
4
4
|
Summary: A debug adapter (Debug Adapter Protocol) for bash scripts, written in bash and Python
|
|
5
5
|
Author-email: Ilia Maslakov <il.smind@gmail.com>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -18,7 +18,8 @@ Dynamic: license-file
|
|
|
18
18
|
# bash-dap
|
|
19
19
|
|
|
20
20
|
A debug adapter of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
|
|
21
|
-
for bash scripts: breakpoints, steps into, over and out of functions,
|
|
21
|
+
for bash scripts: breakpoints on lines and on functions, steps into, over and out of functions,
|
|
22
|
+
the call stack, the
|
|
22
23
|
variables (arrays and associative arrays as trees), expressions, pause.
|
|
23
24
|
|
|
24
25
|
It needs nothing but bash 4.1 or newer and Python 3.8 or newer: no bashdb, no Node.js. (The bash
|
|
@@ -75,13 +76,16 @@ subshell is a process of its own, so what is said to one, a step, a breakpoint,
|
|
|
75
76
|
others through files of that directory, which every process looks at, at a test of two files a
|
|
76
77
|
command. A script under the debugger runs some 25 times slower than by itself.
|
|
77
78
|
|
|
79
|
+
A breakpoint on a function stops at its first command, at each call, in a subshell too;
|
|
80
|
+
what the script defines later, or a file it sources, is found as well, as the name is all
|
|
81
|
+
the debugger looks for.
|
|
82
|
+
|
|
78
83
|
When the session ends the adapter ends the script by its process group, the session it has to
|
|
79
84
|
itself; never by a parent, never anything else.
|
|
80
85
|
|
|
81
86
|
## Not yet
|
|
82
87
|
|
|
83
|
-
Conditional breakpoints,
|
|
84
|
-
new bash.
|
|
88
|
+
Conditional breakpoints, a script that another script runs with a new bash.
|
|
85
89
|
|
|
86
90
|
## License
|
|
87
91
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# bash-dap
|
|
2
2
|
|
|
3
3
|
A debug adapter of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
|
|
4
|
-
for bash scripts: breakpoints, steps into, over and out of functions,
|
|
4
|
+
for bash scripts: breakpoints on lines and on functions, steps into, over and out of functions,
|
|
5
|
+
the call stack, the
|
|
5
6
|
variables (arrays and associative arrays as trees), expressions, pause.
|
|
6
7
|
|
|
7
8
|
It needs nothing but bash 4.1 or newer and Python 3.8 or newer: no bashdb, no Node.js. (The bash
|
|
@@ -58,13 +59,16 @@ subshell is a process of its own, so what is said to one, a step, a breakpoint,
|
|
|
58
59
|
others through files of that directory, which every process looks at, at a test of two files a
|
|
59
60
|
command. A script under the debugger runs some 25 times slower than by itself.
|
|
60
61
|
|
|
62
|
+
A breakpoint on a function stops at its first command, at each call, in a subshell too;
|
|
63
|
+
what the script defines later, or a file it sources, is found as well, as the name is all
|
|
64
|
+
the debugger looks for.
|
|
65
|
+
|
|
61
66
|
When the session ends the adapter ends the script by its process group, the session it has to
|
|
62
67
|
itself; never by a parent, never anything else.
|
|
63
68
|
|
|
64
69
|
## Not yet
|
|
65
70
|
|
|
66
|
-
Conditional breakpoints,
|
|
67
|
-
new bash.
|
|
71
|
+
Conditional breakpoints, a script that another script runs with a new bash.
|
|
68
72
|
|
|
69
73
|
## License
|
|
70
74
|
|
|
@@ -15,7 +15,11 @@ import tempfile
|
|
|
15
15
|
import threading
|
|
16
16
|
import time
|
|
17
17
|
|
|
18
|
+
from . import __version__
|
|
19
|
+
|
|
18
20
|
HARNESS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "harness.sh")
|
|
21
|
+
# a name of a function of bash, as a breakpoint takes it: no space, no quote, no parenthesis
|
|
22
|
+
FUNCTION_NAME = re.compile(r"^[^\s()$`'\"\\]+$")
|
|
19
23
|
VARIABLES_REF = 1 # the reference of the variables of a stop
|
|
20
24
|
|
|
21
25
|
|
|
@@ -108,6 +112,7 @@ class Adapter:
|
|
|
108
112
|
self.alive = False
|
|
109
113
|
self.pause_asked = False
|
|
110
114
|
self.breaks = {}
|
|
115
|
+
self.functions = []
|
|
111
116
|
self.gen = 0
|
|
112
117
|
self.refs = {}
|
|
113
118
|
self.next_ref = VARIABLES_REF + 1
|
|
@@ -196,6 +201,8 @@ class Adapter:
|
|
|
196
201
|
for path, lines in self.breaks.items():
|
|
197
202
|
for line in lines:
|
|
198
203
|
f.write("%s:%d\n" % (path, line))
|
|
204
|
+
for name in self.functions:
|
|
205
|
+
f.write("%s()\n" % name)
|
|
199
206
|
self.gen += 1
|
|
200
207
|
open(os.path.join(self.dir, "gen.%d" % self.gen), "w").close()
|
|
201
208
|
|
|
@@ -255,6 +262,8 @@ class Adapter:
|
|
|
255
262
|
return
|
|
256
263
|
if self.pause_asked and reason == "step":
|
|
257
264
|
reason = "pause"
|
|
265
|
+
elif reason == "function":
|
|
266
|
+
reason = "function breakpoint"
|
|
258
267
|
self.pause_asked = False
|
|
259
268
|
self.event("stopped", {"reason": reason, "threadId": 1, "allThreadsStopped": True})
|
|
260
269
|
|
|
@@ -287,6 +296,7 @@ class Adapter:
|
|
|
287
296
|
self.client = request.get("arguments", {})
|
|
288
297
|
self.respond(request, {
|
|
289
298
|
"supportsConfigurationDoneRequest": True,
|
|
299
|
+
"supportsFunctionBreakpoints": True,
|
|
290
300
|
"supportsEvaluateForHovers": True,
|
|
291
301
|
"supportsTerminateRequest": True,
|
|
292
302
|
})
|
|
@@ -353,9 +363,20 @@ class Adapter:
|
|
|
353
363
|
for i, line in enumerate(lines)]})
|
|
354
364
|
|
|
355
365
|
def on_setFunctionBreakpoints(self, request):
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
366
|
+
"""The functions to stop in at their first command; a name that is no name of a
|
|
367
|
+
function of bash is not taken"""
|
|
368
|
+
answers = []
|
|
369
|
+
self.functions = []
|
|
370
|
+
for b in request.get("arguments", {}).get("breakpoints", []):
|
|
371
|
+
name = b.get("name", "")
|
|
372
|
+
if FUNCTION_NAME.match(name):
|
|
373
|
+
self.functions.append(name)
|
|
374
|
+
answers.append({"id": 1000 + len(self.functions), "verified": True})
|
|
375
|
+
else:
|
|
376
|
+
answers.append({"verified": False,
|
|
377
|
+
"message": "no name of a function of bash: %s" % name})
|
|
378
|
+
self.save_breaks()
|
|
379
|
+
self.respond(request, {"breakpoints": answers})
|
|
359
380
|
|
|
360
381
|
def on_setExceptionBreakpoints(self, request):
|
|
361
382
|
self.respond(request, {"breakpoints": []})
|
|
@@ -527,7 +548,7 @@ def main():
|
|
|
527
548
|
"on stdin and stdout")
|
|
528
549
|
return 0
|
|
529
550
|
if len(sys.argv) > 1 and sys.argv[1] == "--version":
|
|
530
|
-
print("bash-dap
|
|
551
|
+
print("bash-dap %s" % __version__)
|
|
531
552
|
return 0
|
|
532
553
|
Adapter(sys.stdin.buffer, sys.stdout.buffer).run()
|
|
533
554
|
return 0
|
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
# "replies", which it reads what the debugger says from, a line each:
|
|
10
10
|
#
|
|
11
11
|
# commands: run | step | next | finish | print EXPRESSION | vars | stack
|
|
12
|
-
# replies: stopped REASON LINE FUNCTION FILE (REASON: entry, breakpoint, step)
|
|
12
|
+
# replies: stopped REASON LINE FUNCTION FILE (REASON: entry, breakpoint, function, step)
|
|
13
13
|
# value TEXT | var NAME TEXT | frame LEVEL FUNCTION LINE FILE | error TEXT
|
|
14
14
|
# done, after the reply to a command | exited CODE
|
|
15
15
|
#
|
|
16
16
|
# TEXT is quoted as printf %q does, so that a reply is one line. A command is read only while
|
|
17
17
|
# the script is stopped. At any time the adapter may write the breakpoints to the file "breaks",
|
|
18
18
|
# FILE:LINE a line, then make the empty file "gen.N" of the next generation N; and stop the
|
|
19
|
-
# script at its next command by writing "step 0" to the file "state".
|
|
19
|
+
# script at its next command by writing "step 0" to the file "state". A line NAME() of
|
|
20
|
+
# "breaks" is a breakpoint on the function NAME: the script stops at its first command.
|
|
20
21
|
#
|
|
21
22
|
# A subshell is a process of its own: what it is told, a step, a breakpoint, goes to the others
|
|
22
23
|
# through these files. The names of the debugger begin with __bdap_.
|
|
@@ -33,11 +34,14 @@ __bdap_state=$__bdap_dir/state
|
|
|
33
34
|
__bdap_mode=run
|
|
34
35
|
__bdap_depth=0
|
|
35
36
|
# the breakpoints by FILE:LINE, and the generation of the file they came from; __bdap_names has
|
|
36
|
-
# NAME:LINE, the name of the file only, that the trap tests at each command
|
|
37
|
-
|
|
37
|
+
# NAME:LINE, the name of the file only, that the trap tests at each command; __bdap_funcs has
|
|
38
|
+
# NAME() of the functions to stop in
|
|
39
|
+
declare -A __bdap_breaks=() __bdap_names=() __bdap_funcs=()
|
|
38
40
|
__bdap_gen=0
|
|
39
41
|
__bdap_inside=
|
|
40
42
|
__bdap_prev= # the place of the command before: a line stops when it is come to, not again
|
|
43
|
+
__bdap_fdepth=0 # the depth of the calls at the command before: more is a function entered
|
|
44
|
+
__bdap_fenter= # a function to stop in is entered: its first command is next
|
|
41
45
|
|
|
42
46
|
# the variables of bash itself, and those it makes for a function: not shown
|
|
43
47
|
__bdap_known=" $(compgen -v | tr '\n' ' ') FUNCNAME BASH_ARGC BASH_ARGV BASH_LINENO BASH_SOURCE COLUMNS LINES "
|
|
@@ -93,8 +97,13 @@ __bdap_breaks_load () {
|
|
|
93
97
|
done
|
|
94
98
|
__bdap_breaks=()
|
|
95
99
|
__bdap_names=()
|
|
100
|
+
__bdap_funcs=()
|
|
96
101
|
while read -r __bdap_place; do
|
|
97
102
|
[[ -z $__bdap_place ]] && continue
|
|
103
|
+
if [[ $__bdap_place == *"()" ]]; then
|
|
104
|
+
__bdap_funcs["$__bdap_place"]=1
|
|
105
|
+
continue
|
|
106
|
+
fi
|
|
98
107
|
__bdap_breaks["$__bdap_place"]=1
|
|
99
108
|
__bdap_names["${__bdap_place##*/}"]=1
|
|
100
109
|
done <"$__bdap_dir/breaks"
|
|
@@ -126,6 +135,7 @@ __bdap_wait () {
|
|
|
126
135
|
__bdap_resume run 0
|
|
127
136
|
__bdap_breaks=()
|
|
128
137
|
__bdap_names=()
|
|
138
|
+
__bdap_funcs=()
|
|
129
139
|
trap - DEBUG
|
|
130
140
|
}
|
|
131
141
|
|
|
@@ -172,6 +182,16 @@ __bdap_check () {
|
|
|
172
182
|
if [[ -z $__bdap_reason && -n ${__bdap_breaks["$__bdap_file:$__bdap_line"]-} ]] && __bdap_on_line; then
|
|
173
183
|
__bdap_reason=breakpoint
|
|
174
184
|
fi
|
|
185
|
+
# the first command of a function to stop in: the calls are deeper than at the command
|
|
186
|
+
# before; bash comes first to the line that defines it, with the call as the command
|
|
187
|
+
if [[ -z $__bdap_reason && -n ${__bdap_funcs["${FUNCNAME[2]-}()"]-} ]]; then
|
|
188
|
+
if ((__bdap_d - 1 > __bdap_fdepth)) && [[ $__bdap_command == "${FUNCNAME[2]}"* ]]; then
|
|
189
|
+
__bdap_fenter=1
|
|
190
|
+
elif ((__bdap_d - 1 > __bdap_fdepth)) || [[ -n $__bdap_fenter ]]; then
|
|
191
|
+
__bdap_reason=function
|
|
192
|
+
fi
|
|
193
|
+
fi
|
|
194
|
+
[[ -n $__bdap_reason ]] && __bdap_fenter=
|
|
175
195
|
# the next command of a line is no new place to stop at
|
|
176
196
|
[[ -z $__bdap_reason || $__bdap_prev == "$__bdap_line:${BASH_SOURCE[2]}" ]] && return 0
|
|
177
197
|
# the lines of the frames, the one stopped in first
|
|
@@ -195,12 +215,14 @@ trap '__bdap_say "exited $?"' EXIT
|
|
|
195
215
|
shopt -s extdebug
|
|
196
216
|
set -o functrace
|
|
197
217
|
# the hook only when there may be something to do: a step, a breakpoint in a file of that name
|
|
198
|
-
# on the line, what another process has said; else a command costs a test
|
|
199
|
-
# line: a second one would have LINENO one more
|
|
200
|
-
# (with set -u of the script it runs too: nothing unset is read, BASH_SOURCE is empty at the end
|
|
218
|
+
# on the line or on the function, what another process has said; else a command costs a test
|
|
219
|
+
# of two files. One line: a second one would have LINENO one more
|
|
220
|
+
# (with set -u of the script it runs too: nothing unset is read, BASH_SOURCE is empty at the end,
|
|
221
|
+
# FUNCNAME unset out of any function)
|
|
201
222
|
__bdap_trap='[[ -n $__bdap_inside || ( $__bdap_mode == run'
|
|
202
223
|
__bdap_trap+=' && -z ${__bdap_names["${BASH_SOURCE[0]:+${BASH_SOURCE[0]##*/}}:$LINENO"]-}'
|
|
224
|
+
__bdap_trap+=' && -z ${__bdap_funcs["${FUNCNAME[0]-}()"]-}'
|
|
203
225
|
__bdap_trap+=' && ! -e $__bdap_state && ! -e $__bdap_dir/gen.$((__bdap_gen + 1)) ) ]] || __bdap_hook "$BASH_COMMAND";'
|
|
204
|
-
__bdap_trap+=' __bdap_prev=$LINENO:${BASH_SOURCE[0]-}'
|
|
226
|
+
__bdap_trap+=' __bdap_prev=$LINENO:${BASH_SOURCE[0]-} __bdap_fdepth=${FUNCNAME[0]+${#FUNCNAME[@]}}'
|
|
205
227
|
trap "$__bdap_trap" DEBUG
|
|
206
228
|
. "$0" "$@"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bash-dap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2
|
|
4
4
|
Summary: A debug adapter (Debug Adapter Protocol) for bash scripts, written in bash and Python
|
|
5
5
|
Author-email: Ilia Maslakov <il.smind@gmail.com>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -18,7 +18,8 @@ Dynamic: license-file
|
|
|
18
18
|
# bash-dap
|
|
19
19
|
|
|
20
20
|
A debug adapter of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
|
|
21
|
-
for bash scripts: breakpoints, steps into, over and out of functions,
|
|
21
|
+
for bash scripts: breakpoints on lines and on functions, steps into, over and out of functions,
|
|
22
|
+
the call stack, the
|
|
22
23
|
variables (arrays and associative arrays as trees), expressions, pause.
|
|
23
24
|
|
|
24
25
|
It needs nothing but bash 4.1 or newer and Python 3.8 or newer: no bashdb, no Node.js. (The bash
|
|
@@ -75,13 +76,16 @@ subshell is a process of its own, so what is said to one, a step, a breakpoint,
|
|
|
75
76
|
others through files of that directory, which every process looks at, at a test of two files a
|
|
76
77
|
command. A script under the debugger runs some 25 times slower than by itself.
|
|
77
78
|
|
|
79
|
+
A breakpoint on a function stops at its first command, at each call, in a subshell too;
|
|
80
|
+
what the script defines later, or a file it sources, is found as well, as the name is all
|
|
81
|
+
the debugger looks for.
|
|
82
|
+
|
|
78
83
|
When the session ends the adapter ends the script by its process group, the session it has to
|
|
79
84
|
itself; never by a parent, never anything else.
|
|
80
85
|
|
|
81
86
|
## Not yet
|
|
82
87
|
|
|
83
|
-
Conditional breakpoints,
|
|
84
|
-
new bash.
|
|
88
|
+
Conditional breakpoints, a script that another script runs with a new bash.
|
|
85
89
|
|
|
86
90
|
## License
|
|
87
91
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bash-dap"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2"
|
|
8
8
|
description = "A debug adapter (Debug Adapter Protocol) for bash scripts, written in bash and Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -198,6 +198,31 @@ class SessionTest(unittest.TestCase):
|
|
|
198
198
|
c.wait_event("terminated")
|
|
199
199
|
self.assertIn("3 files, 2800 bytes", c.output())
|
|
200
200
|
|
|
201
|
+
def test_function_breakpoints(self):
|
|
202
|
+
c = self.client = Client()
|
|
203
|
+
c.request("initialize", {"adapterID": "bash-dap", "linesStartAt1": True})
|
|
204
|
+
c.request("launch", {"program": script("backup.sh"), "cwd": SCRIPTS})
|
|
205
|
+
c.wait_event("initialized")
|
|
206
|
+
response = c.request("setFunctionBreakpoints", {"breakpoints": [
|
|
207
|
+
{"name": "size_of"}, {"name": "not a name"}]})
|
|
208
|
+
verified = [b["verified"] for b in response["body"]["breakpoints"]]
|
|
209
|
+
self.assertEqual(verified, [True, False])
|
|
210
|
+
c.request("configurationDone")
|
|
211
|
+
# at the first command of the function, in the subshell of $(...)
|
|
212
|
+
self.assertEqual(c.wait_event("stopped")["body"]["reason"], "function breakpoint")
|
|
213
|
+
self.assertEqual(c.stop_place(), [("size_of", "backup.sh", 7), ("main", "backup.sh", 15)])
|
|
214
|
+
# once a call, not at each of its commands
|
|
215
|
+
c.request("next", {"threadId": 1})
|
|
216
|
+
c.wait_event("stopped")
|
|
217
|
+
self.assertEqual(c.stop_place()[0], ("size_of", "backup.sh", 8))
|
|
218
|
+
c.request("continue", {"threadId": 1})
|
|
219
|
+
c.wait_event("stopped")
|
|
220
|
+
self.assertEqual(c.evaluate("$f")["body"]["result"], "photo.jpg")
|
|
221
|
+
# taken off, the script runs to its end
|
|
222
|
+
c.request("setFunctionBreakpoints", {"breakpoints": []})
|
|
223
|
+
c.request("continue", {"threadId": 1})
|
|
224
|
+
self.assertEqual(c.wait_event("exited")["body"]["exitCode"], 3)
|
|
225
|
+
|
|
201
226
|
def test_pipe_sourced_file_and_dollar_zero(self):
|
|
202
227
|
c = self.client = Client()
|
|
203
228
|
c.start(script("pipe.sh"), [(script("lib.sh"), 3), (script("pipe.sh"), 6)])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|