PyOpenocdClient 0.1.0__tar.gz → 0.1.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.
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyOpenocdClient
3
+ Version: 0.1.2
4
+ Summary: Library for controlling OpenOCD from Python programs
5
+ Author-email: Jan Matyas <info@janmatyas.net>
6
+ Project-URL: Homepage, https://github.com/HonzaMat/PyOpenocdClient
7
+ Project-URL: Issues, https://github.com/HonzaMat/PyOpenocdClient/issues
8
+ Project-URL: Documentation, https://pyopenocdclient.readthedocs.io/en/latest/
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # PyOpenocdClient
18
+
19
+ [![Build documentation](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/build_doc.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/build_doc.yml)
20
+ [![Code quality checks](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/code_quality.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/code_quality.yml)
21
+ [![Unit tests](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/unit_tests.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/unit_tests.yml)
22
+ [![Integration tests](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/integration_tests.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/integration_tests.yml)
23
+
24
+ **PyOpenocdClient** is a Python library for controlling [OpenOCD](https://openocd.org)
25
+ software tool.
26
+
27
+ It allows to send TCL commands from Python programs to OpenOCD &mdash; for instance commands like halt execution of the program, view data in memory, place breakpoints, single-step, ...
28
+
29
+ Main features of PyOpenocdClient:
30
+
31
+ * allow to send any TCL command to OpenOCD and obtain its result;
32
+
33
+ * shorcuts for quick use of most common OpenOCD commands are provided;
34
+
35
+ * command failures are detected (and reported as Python exceptions by default);
36
+
37
+ * the code is fully covered via unit tests;
38
+
39
+ * automatic integration testing against multiple versions of OpenOCD;
40
+
41
+ * the code is multiplatform and portable &mdash; it does not have any dependencies except for the Python's standard library;
42
+
43
+ * fully open-source under a permissive license (MIT license).
44
+
45
+
46
+ ## Quick instructions
47
+
48
+ Install PyOpenocdClient package using Pip:
49
+
50
+ ```bash
51
+ $ python3 -m pip install PyOpenocdClient
52
+ ```
53
+
54
+ Basic usage:
55
+
56
+ ```python
57
+ from py_openocd_client import PyOpenocdClient
58
+
59
+ with PyOpenocdClient(host="localhost", port=6666) as ocd:
60
+
61
+ ocd.reset_halt()
62
+ ocd.cmd("load_image path/to/program.elf")
63
+ ocd.resume()
64
+ # ...
65
+ ```
66
+
67
+ ## Documentation
68
+
69
+ For full documentation, please visit: https://pyopenocdclient.readthedocs.io/en/latest/
70
+
71
+ &nbsp;
72
+
73
+
@@ -0,0 +1,57 @@
1
+ # PyOpenocdClient
2
+
3
+ [![Build documentation](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/build_doc.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/build_doc.yml)
4
+ [![Code quality checks](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/code_quality.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/code_quality.yml)
5
+ [![Unit tests](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/unit_tests.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/unit_tests.yml)
6
+ [![Integration tests](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/integration_tests.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/integration_tests.yml)
7
+
8
+ **PyOpenocdClient** is a Python library for controlling [OpenOCD](https://openocd.org)
9
+ software tool.
10
+
11
+ It allows to send TCL commands from Python programs to OpenOCD &mdash; for instance commands like halt execution of the program, view data in memory, place breakpoints, single-step, ...
12
+
13
+ Main features of PyOpenocdClient:
14
+
15
+ * allow to send any TCL command to OpenOCD and obtain its result;
16
+
17
+ * shorcuts for quick use of most common OpenOCD commands are provided;
18
+
19
+ * command failures are detected (and reported as Python exceptions by default);
20
+
21
+ * the code is fully covered via unit tests;
22
+
23
+ * automatic integration testing against multiple versions of OpenOCD;
24
+
25
+ * the code is multiplatform and portable &mdash; it does not have any dependencies except for the Python's standard library;
26
+
27
+ * fully open-source under a permissive license (MIT license).
28
+
29
+
30
+ ## Quick instructions
31
+
32
+ Install PyOpenocdClient package using Pip:
33
+
34
+ ```bash
35
+ $ python3 -m pip install PyOpenocdClient
36
+ ```
37
+
38
+ Basic usage:
39
+
40
+ ```python
41
+ from py_openocd_client import PyOpenocdClient
42
+
43
+ with PyOpenocdClient(host="localhost", port=6666) as ocd:
44
+
45
+ ocd.reset_halt()
46
+ ocd.cmd("load_image path/to/program.elf")
47
+ ocd.resume()
48
+ # ...
49
+ ```
50
+
51
+ ## Documentation
52
+
53
+ For full documentation, please visit: https://pyopenocdclient.readthedocs.io/en/latest/
54
+
55
+ &nbsp;
56
+
57
+
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "PyOpenocdClient"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  authors = [
5
5
  { name="Jan Matyas", email="info@janmatyas.net" },
6
6
  ]
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyOpenocdClient
3
+ Version: 0.1.2
4
+ Summary: Library for controlling OpenOCD from Python programs
5
+ Author-email: Jan Matyas <info@janmatyas.net>
6
+ Project-URL: Homepage, https://github.com/HonzaMat/PyOpenocdClient
7
+ Project-URL: Issues, https://github.com/HonzaMat/PyOpenocdClient/issues
8
+ Project-URL: Documentation, https://pyopenocdclient.readthedocs.io/en/latest/
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # PyOpenocdClient
18
+
19
+ [![Build documentation](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/build_doc.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/build_doc.yml)
20
+ [![Code quality checks](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/code_quality.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/code_quality.yml)
21
+ [![Unit tests](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/unit_tests.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/unit_tests.yml)
22
+ [![Integration tests](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/integration_tests.yml/badge.svg?event=schedule)](https://github.com/HonzaMat/PyOpenocdClient/actions/workflows/integration_tests.yml)
23
+
24
+ **PyOpenocdClient** is a Python library for controlling [OpenOCD](https://openocd.org)
25
+ software tool.
26
+
27
+ It allows to send TCL commands from Python programs to OpenOCD &mdash; for instance commands like halt execution of the program, view data in memory, place breakpoints, single-step, ...
28
+
29
+ Main features of PyOpenocdClient:
30
+
31
+ * allow to send any TCL command to OpenOCD and obtain its result;
32
+
33
+ * shorcuts for quick use of most common OpenOCD commands are provided;
34
+
35
+ * command failures are detected (and reported as Python exceptions by default);
36
+
37
+ * the code is fully covered via unit tests;
38
+
39
+ * automatic integration testing against multiple versions of OpenOCD;
40
+
41
+ * the code is multiplatform and portable &mdash; it does not have any dependencies except for the Python's standard library;
42
+
43
+ * fully open-source under a permissive license (MIT license).
44
+
45
+
46
+ ## Quick instructions
47
+
48
+ Install PyOpenocdClient package using Pip:
49
+
50
+ ```bash
51
+ $ python3 -m pip install PyOpenocdClient
52
+ ```
53
+
54
+ Basic usage:
55
+
56
+ ```python
57
+ from py_openocd_client import PyOpenocdClient
58
+
59
+ with PyOpenocdClient(host="localhost", port=6666) as ocd:
60
+
61
+ ocd.reset_halt()
62
+ ocd.cmd("load_image path/to/program.elf")
63
+ ocd.resume()
64
+ # ...
65
+ ```
66
+
67
+ ## Documentation
68
+
69
+ For full documentation, please visit: https://pyopenocdclient.readthedocs.io/en/latest/
70
+
71
+ &nbsp;
72
+
73
+
@@ -94,10 +94,35 @@ class _PyOpenocdBaseClient:
94
94
  raise ValueError("Timeout must be greater than zero")
95
95
  self._default_recv_timeout = timeout
96
96
 
97
- def _check_no_premature_recvd_bytes(self) -> None:
97
+ def _check_connection_before_command(self) -> None:
98
98
  assert self._socket is not None
99
99
  rd, _, _ = select.select([self._socket], [], [], 0) # Don't block, just poll
100
- if len(rd) > 0:
100
+
101
+ if len(rd) == 0:
102
+ # The socket is not ready for recv() now, which is the expected state.
103
+ # Success.
104
+ return
105
+
106
+ # The socket is ready for recv(). This is unexpected at this point
107
+ # and always means an error. Try receive from the socket to find out
108
+ # what happened:
109
+ try:
110
+ recvd_data = self._socket.recv(128)
111
+ except OSError as e:
112
+ # This is unlikely to happen: It would mean that the socket is ready
113
+ # for recv() but then recv() failed.
114
+ raise OcdConnectionError(
115
+ "Connection to OpenOCD broken for an unknown reason"
116
+ ) from e
117
+
118
+ if len(recvd_data) == 0:
119
+ # Empty received data means that the connection got closed by OpenOCD
120
+ # in the meanwhile.
121
+ raise OcdConnectionError("Connection closed by OpenOCD")
122
+ else:
123
+ # It looks like OpenOCD sent us some extra, unsolicited bytes (without us
124
+ # sending any command to OpenOCD). This is a violation of the communication
125
+ # protocol.
101
126
  raise OcdConnectionError(
102
127
  "Received unexpected bytes from OpenOCD before "
103
128
  "the command was even sent."
@@ -107,12 +132,27 @@ class _PyOpenocdBaseClient:
107
132
  assert self.is_connected()
108
133
  assert self._socket is not None
109
134
 
110
- # Safety:
111
- self._check_no_premature_recvd_bytes()
135
+ # Perform basic connection check before sending a command.
136
+ # Note that this is merely a safety/correctness check which itself
137
+ # does not guarantee that the subsequent send() and recv() calls
138
+ # will succeed.
139
+ self._check_connection_before_command()
112
140
 
113
141
  data = raw_cmd.encode(self.CHARSET) + self.COMMAND_DELIMITER
114
- self._socket.settimeout(self.SEND_TIMEOUT)
115
- self._socket.send(data)
142
+
143
+ try:
144
+ self._socket.settimeout(self.SEND_TIMEOUT)
145
+ except OSError as e:
146
+ raise OcdConnectionError(
147
+ "Could not send a command to OpenOCD, failed to set socket timeout"
148
+ ) from e
149
+
150
+ try:
151
+ self._socket.send(data)
152
+ except OSError as e:
153
+ raise OcdConnectionError(
154
+ "Could not send a command to OpenOCD, socket error occurred"
155
+ ) from e
116
156
 
117
157
  def _do_recv_response(self, raw_cmd: str, timeout: Optional[float] = None) -> str:
118
158
  assert self.is_connected()
@@ -123,7 +163,13 @@ class _PyOpenocdBaseClient:
123
163
  timeout if timeout is not None else self._default_recv_timeout
124
164
  )
125
165
 
126
- self._socket.settimeout(self.RECV_POLL_TIMEOUT)
166
+ try:
167
+ self._socket.settimeout(self.RECV_POLL_TIMEOUT)
168
+ except OSError as e:
169
+ raise OcdConnectionError(
170
+ "Could not receive a response from OpenOCD, "
171
+ "failed to set socket timeout"
172
+ ) from e
127
173
 
128
174
  time_start = time.time()
129
175
  while time.time() < (time_start + effective_timeout):
@@ -131,6 +177,10 @@ class _PyOpenocdBaseClient:
131
177
  d = self._socket.recv(self.RECV_BLOCK_SIZE)
132
178
  except socket.timeout:
133
179
  continue
180
+ except OSError as e:
181
+ raise OcdConnectionError(
182
+ "Could not receive a response from OpenOCD, socket error occurred"
183
+ ) from e
134
184
 
135
185
  if d == b"":
136
186
  raise OcdConnectionError("Connection closed by OpenOCD")
@@ -22,8 +22,8 @@ class PyOpenocdClient:
22
22
 
23
23
  - :meth:`cmd` method to send any TCL command to OpenOCD and obtain
24
24
  the command result,
25
- - convenience methods to issue some of the most common OpenOCD commands --
26
- :meth:`halt`, :meth:`resume`, :meth:`read_memory`, :meth:`get_reg`, ..., etc.
25
+ - convenience methods (shortcuts) to issue some of the most common OpenOCD commands
26
+ -- :meth:`halt`, :meth:`resume`, :meth:`read_memory`, :meth:`get_reg`, ..., etc.
27
27
 
28
28
  Basic usage:
29
29
 
@@ -211,25 +211,34 @@ class PyOpenocdClient:
211
211
  raw_cmd = cmd
212
212
 
213
213
  raw_cmd = "set CMD_RETCODE [ catch { " + raw_cmd + " } CMD_OUTPUT ] ; "
214
- raw_cmd += 'return "$CMD_RETCODE $CMD_OUTPUT" ; '
214
+
215
+ # Older OpenOCD versions - prior to 93f16eed4(*) - incorrectly trimmed trailing
216
+ # whitespace from the string passed to the return command. Work around this bug
217
+ # by wrapping the string by non-whitespace characters.
218
+ # (*): https://review.openocd.org/c/openocd/+/9084
219
+ raw_cmd += 'return "<$CMD_RETCODE,$CMD_OUTPUT>" ; '
215
220
 
216
221
  raw_result = self.raw_cmd(raw_cmd, timeout=timeout)
217
222
 
218
- # Verify the raw output from OpenOCD the has the expected format. It can be:
219
- #
220
- # - Command return code (positive or negative decimal number) and that's it.
221
- #
222
- # - Or, command return code (positive or negative decimal number) followed by
223
- # a space character and optionally followed by the command's textual output.
224
- if re.match(r"^-?\d+($| )", raw_result) is None:
223
+ def is_expected_raw_result(s: str) -> bool:
224
+ return (
225
+ s.startswith("<")
226
+ and s.endswith(">")
227
+ and re.match(r"^<-?\d+,", s) is not None
228
+ )
229
+
230
+ if not is_expected_raw_result(raw_result):
225
231
  msg = (
226
232
  "Received unexpected response from OpenOCD. "
227
233
  "It looks like OpenOCD misbehaves. "
228
234
  )
229
235
  raise OcdInvalidResponseError(msg, raw_cmd, raw_result)
230
236
 
231
- raw_result_parts = raw_result.split(" ", maxsplit=1)
232
- assert len(raw_result_parts) in [1, 2]
237
+ # Remove leading "<" and trailing ">"
238
+ raw_result = raw_result[1:-1]
239
+ raw_result_parts = raw_result.split(",", maxsplit=1)
240
+ assert len(raw_result_parts) == 2
241
+
233
242
  retcode = int(raw_result_parts[0], 10)
234
243
  out = raw_result_parts[1] if len(raw_result_parts) == 2 else ""
235
244
 
@@ -298,8 +307,8 @@ class PyOpenocdClient:
298
307
 
299
308
  def curstate(self) -> str:
300
309
  """
301
- Determinte the state of the currently selected target via the `currstate`
302
- command. Return the state as a string.
310
+ Determinte the state of the currently selected target via
311
+ the ``<target_name> curstate`` command. Return the state as a string.
303
312
  """
304
313
  return self.cmd("[target current] curstate").out.strip()
305
314
 
@@ -666,11 +675,24 @@ class PyOpenocdClient:
666
675
  def shutdown(self) -> None:
667
676
  """
668
677
  Shut down the OpenOCD process by sending the ``shutdown`` command to it.
669
- Then terminate the connection.
678
+ PyOpenocd client also gets immediately disconnected from OpenOCD.
670
679
  """
671
- # OpenOCD's shutdown command returns a non-zero error code (which is expected).
672
- # For that reason, throw=False is used.
673
- self.cmd("shutdown", throw=False)
680
+ # Different OpenOCD versions respond to "shutdown" command differently:
681
+ #
682
+ # - OpenOCD 0.12.0 and older:
683
+ # The "shutdown" command results in a non-zero TCL return code,
684
+ # which can be obtained normally as for any other TCL command -
685
+ # e.g. via the "catch" command.
686
+ #
687
+ # - OpenOCD 0.13.0-dev and newer (from to commit "93f16eed4"):
688
+ # The "shutdown" command immediately ends the TCL processing and
689
+ # an empty response is sent back to the TCL client.
690
+
691
+ # For the above reasons, send the shutdown command via raw_cmd() and:
692
+ # - don't wrap "shutdown" into any other TCL commands,
693
+ # - don't expect any particular response.
694
+ self.raw_cmd("shutdown")
695
+
674
696
  self.disconnect()
675
697
 
676
698
  def raw_cmd(self, raw_cmd: str, timeout: Optional[float] = None) -> str:
@@ -1,50 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: PyOpenocdClient
3
- Version: 0.1.0
4
- Summary: Library for controlling OpenOCD from Python programs
5
- Author-email: Jan Matyas <info@janmatyas.net>
6
- Project-URL: Homepage, https://github.com/HonzaMat/PyOpenocdClient
7
- Project-URL: Issues, https://github.com/HonzaMat/PyOpenocdClient/issues
8
- Project-URL: Documentation, https://pyopenocdclient.readthedocs.io/en/latest/
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.7
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
-
16
- # PyOpenocdClient
17
-
18
- **PyOpenocdClient** is a Python library for controlling [OpenOCD](https://openocd.org)
19
- software tool.
20
-
21
- It allows to send any TCL commands from Python programs to OpenOCD and receive results of these commands (for instance commands like halt execution of the program, view data in memory, place breakpoints, single-step, ...).
22
-
23
- ## Quick instructions
24
-
25
- Install PyOpenocdClient package using Pip:
26
-
27
- ```bash
28
- $ python3 -m pip install PyOpenocdClient
29
- ```
30
-
31
- Basic usage:
32
-
33
- ```python
34
- from py_openocd_client import PyOpenocdClient
35
-
36
- with PyOpenocdClient(host="localhost", port=6666) as ocd:
37
-
38
- ocd.reset_halt()
39
- ocd.cmd("load_image path/to/program.elf")
40
- ocd.resume()
41
- # ...
42
- ```
43
-
44
- ## Documentation
45
-
46
- For full documentation, please visit: https://pyopenocdclient.readthedocs.io/en/latest/
47
-
48
- &nbsp;
49
-
50
-
@@ -1,35 +0,0 @@
1
- # PyOpenocdClient
2
-
3
- **PyOpenocdClient** is a Python library for controlling [OpenOCD](https://openocd.org)
4
- software tool.
5
-
6
- It allows to send any TCL commands from Python programs to OpenOCD and receive results of these commands (for instance commands like halt execution of the program, view data in memory, place breakpoints, single-step, ...).
7
-
8
- ## Quick instructions
9
-
10
- Install PyOpenocdClient package using Pip:
11
-
12
- ```bash
13
- $ python3 -m pip install PyOpenocdClient
14
- ```
15
-
16
- Basic usage:
17
-
18
- ```python
19
- from py_openocd_client import PyOpenocdClient
20
-
21
- with PyOpenocdClient(host="localhost", port=6666) as ocd:
22
-
23
- ocd.reset_halt()
24
- ocd.cmd("load_image path/to/program.elf")
25
- ocd.resume()
26
- # ...
27
- ```
28
-
29
- ## Documentation
30
-
31
- For full documentation, please visit: https://pyopenocdclient.readthedocs.io/en/latest/
32
-
33
- &nbsp;
34
-
35
-
@@ -1,50 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: PyOpenocdClient
3
- Version: 0.1.0
4
- Summary: Library for controlling OpenOCD from Python programs
5
- Author-email: Jan Matyas <info@janmatyas.net>
6
- Project-URL: Homepage, https://github.com/HonzaMat/PyOpenocdClient
7
- Project-URL: Issues, https://github.com/HonzaMat/PyOpenocdClient/issues
8
- Project-URL: Documentation, https://pyopenocdclient.readthedocs.io/en/latest/
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.7
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
-
16
- # PyOpenocdClient
17
-
18
- **PyOpenocdClient** is a Python library for controlling [OpenOCD](https://openocd.org)
19
- software tool.
20
-
21
- It allows to send any TCL commands from Python programs to OpenOCD and receive results of these commands (for instance commands like halt execution of the program, view data in memory, place breakpoints, single-step, ...).
22
-
23
- ## Quick instructions
24
-
25
- Install PyOpenocdClient package using Pip:
26
-
27
- ```bash
28
- $ python3 -m pip install PyOpenocdClient
29
- ```
30
-
31
- Basic usage:
32
-
33
- ```python
34
- from py_openocd_client import PyOpenocdClient
35
-
36
- with PyOpenocdClient(host="localhost", port=6666) as ocd:
37
-
38
- ocd.reset_halt()
39
- ocd.cmd("load_image path/to/program.elf")
40
- ocd.resume()
41
- # ...
42
- ```
43
-
44
- ## Documentation
45
-
46
- For full documentation, please visit: https://pyopenocdclient.readthedocs.io/en/latest/
47
-
48
- &nbsp;
49
-
50
-
File without changes