Skip to content

Commit 8001a25

Browse files
authored
Merge branch 'micropython:master' into master
2 parents 6ed6afb + fbf7e12 commit 8001a25

File tree

208 files changed

+5384
-669
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

208 files changed

+5384
-669
lines changed

.github/workflows/code_formatting.yml

-16
This file was deleted.
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Check commit message formatting
2+
3+
on: [push, pull_request]
4+
5+
concurrency:
6+
group: ${{ github.workflow }}-${{ github.ref }}
7+
cancel-in-progress: true
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
with:
15+
fetch-depth: '100'
16+
- uses: actions/setup-python@v4
17+
- name: Check commit message formatting
18+
run: source tools/ci.sh && ci_commit_formatting_run

.github/workflows/package_tests.yml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Package tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v3
10+
- uses: actions/setup-python@v4
11+
- name: Setup environment
12+
run: source tools/ci.sh && ci_package_tests_setup_micropython
13+
- name: Setup libraries
14+
run: source tools/ci.sh && ci_package_tests_setup_lib
15+
- name: Run tests
16+
run: source tools/ci.sh && ci_package_tests_run

.github/workflows/ruff.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
2-
name: Python code lint with ruff
2+
name: Python code lint and formatting with ruff
33
on: [push, pull_request]
44
jobs:
55
ruff:
66
runs-on: ubuntu-latest
77
steps:
8-
- uses: actions/checkout@v3
9-
- run: pip install --user ruff
10-
- run: ruff --format=github .
8+
- uses: actions/checkout@v4
9+
- run: pip install --user ruff==0.1.2
10+
- run: ruff check --output-format=github .
11+
- run: ruff format --diff .

.pre-commit-config.yaml

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
repos:
22
- repo: local
33
hooks:
4-
- id: codeformat
5-
name: MicroPython codeformat.py for changed files
6-
entry: tools/codeformat.py -v -f
4+
- id: verifygitlog
5+
name: MicroPython git commit message format checker
6+
entry: tools/verifygitlog.py --check-file --ignore-rebase
77
language: python
8+
verbose: true
9+
stages: [commit-msg]
810
- repo: https://github.com/charliermarsh/ruff-pre-commit
9-
rev: v0.0.280
11+
rev: v0.1.2
1012
hooks:
1113
- id: ruff
14+
id: ruff-format

micropython/aioespnow/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A supplementary module which extends the micropython `espnow` module to provide
44
`asyncio` support.
55

66
- Asyncio support is available on all ESP32 targets as well as those ESP8266
7-
boards which include the `uasyncio` module (ie. ESP8266 devices with at least
7+
boards which include the `asyncio` module (ie. ESP8266 devices with at least
88
2MB flash storage).
99

1010
## API reference
@@ -52,7 +52,7 @@ A small async server example::
5252
```python
5353
import network
5454
import aioespnow
55-
import uasyncio as asyncio
55+
import asyncio
5656

5757
# A WLAN interface must be active to send()/recv()
5858
network.WLAN(network.STA_IF).active(True)

micropython/aioespnow/aioespnow.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# aioespnow module for MicroPython on ESP32 and ESP8266
22
# MIT license; Copyright (c) 2022 Glenn Moloney @glenn20
33

4-
import uasyncio as asyncio
4+
import asyncio
55
import espnow
66

77

8-
# Modelled on the uasyncio.Stream class (extmod/stream/stream.py)
9-
# NOTE: Relies on internal implementation of uasyncio.core (_io_queue)
8+
# Modelled on the asyncio.Stream class (extmod/asyncio/stream.py)
9+
# NOTE: Relies on internal implementation of asyncio.core (_io_queue)
1010
class AIOESPNow(espnow.ESPNow):
1111
# Read one ESPNow message
1212
async def arecv(self):

micropython/aiorepl/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ To use this library, you need to import the library and then start the REPL task
2121
For example, in main.py:
2222

2323
```py
24-
import uasyncio as asyncio
24+
import asyncio
2525
import aiorepl
2626

2727
async def demo():

micropython/aiorepl/aiorepl.py

+163-24
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
import sys
77
import time
8-
import uasyncio as asyncio
8+
import asyncio
99

1010
# Import statement (needs to be global, and does not return).
1111
_RE_IMPORT = re.compile("^import ([^ ]+)( as ([^ ]+))?")
@@ -19,6 +19,13 @@
1919
_HISTORY_LIMIT = const(5 + 1)
2020

2121

22+
CHAR_CTRL_A = const(1)
23+
CHAR_CTRL_B = const(2)
24+
CHAR_CTRL_C = const(3)
25+
CHAR_CTRL_D = const(4)
26+
CHAR_CTRL_E = const(5)
27+
28+
2229
async def execute(code, g, s):
2330
if not code.strip():
2431
return
@@ -34,18 +41,16 @@ async def execute(code, g, s):
3441
code = "return {}".format(code)
3542

3643
code = """
37-
import uasyncio as asyncio
44+
import asyncio
3845
async def __code():
3946
{}
4047
4148
__exec_task = asyncio.create_task(__code())
42-
""".format(
43-
code
44-
)
49+
""".format(code)
4550

4651
async def kbd_intr_task(exec_task, s):
4752
while True:
48-
if ord(await s.read(1)) == 0x03:
53+
if ord(await s.read(1)) == CHAR_CTRL_C:
4954
exec_task.cancel()
5055
return
5156

@@ -104,7 +109,9 @@ async def task(g=None, prompt="--> "):
104109
while True:
105110
hist_b = 0 # How far back in the history are we currently.
106111
sys.stdout.write(prompt)
107-
cmd = ""
112+
cmd: str = ""
113+
paste = False
114+
curs = 0 # cursor offset from end of cmd buffer
108115
while True:
109116
b = await s.read(1)
110117
pc = c # save previous character
@@ -114,11 +121,19 @@ async def task(g=None, prompt="--> "):
114121
if c < 0x20 or c > 0x7E:
115122
if c == 0x0A:
116123
# LF
124+
if paste:
125+
sys.stdout.write(b)
126+
cmd += b
127+
continue
117128
# If the previous character was also LF, and was less
118129
# than 20 ms ago, this was likely due to CRLF->LFLF
119130
# conversion, so ignore this linefeed.
120131
if pc == 0x0A and time.ticks_diff(t, pt) < 20:
121132
continue
133+
if curs:
134+
# move cursor to end of the line
135+
sys.stdout.write("\x1B[{}C".format(curs))
136+
curs = 0
122137
sys.stdout.write("\n")
123138
if cmd:
124139
# Push current command.
@@ -135,31 +150,45 @@ async def task(g=None, prompt="--> "):
135150
elif c == 0x08 or c == 0x7F:
136151
# Backspace.
137152
if cmd:
138-
cmd = cmd[:-1]
139-
sys.stdout.write("\x08 \x08")
140-
elif c == 0x02:
141-
# Ctrl-B
153+
if curs:
154+
cmd = "".join((cmd[: -curs - 1], cmd[-curs:]))
155+
sys.stdout.write(
156+
"\x08\x1B[K"
157+
) # move cursor back, erase to end of line
158+
sys.stdout.write(cmd[-curs:]) # redraw line
159+
sys.stdout.write("\x1B[{}D".format(curs)) # reset cursor location
160+
else:
161+
cmd = cmd[:-1]
162+
sys.stdout.write("\x08 \x08")
163+
elif c == CHAR_CTRL_A:
164+
await raw_repl(s, g)
165+
break
166+
elif c == CHAR_CTRL_B:
142167
continue
143-
elif c == 0x03:
144-
# Ctrl-C
145-
if pc == 0x03 and time.ticks_diff(t, pt) < 20:
146-
# Two very quick Ctrl-C (faster than a human
147-
# typing) likely means mpremote trying to
148-
# escape.
149-
asyncio.new_event_loop()
150-
return
168+
elif c == CHAR_CTRL_C:
169+
if paste:
170+
break
151171
sys.stdout.write("\n")
152172
break
153-
elif c == 0x04:
154-
# Ctrl-D
173+
elif c == CHAR_CTRL_D:
174+
if paste:
175+
result = await execute(cmd, g, s)
176+
if result is not None:
177+
sys.stdout.write(repr(result))
178+
sys.stdout.write("\n")
179+
break
180+
155181
sys.stdout.write("\n")
156182
# Shutdown asyncio.
157183
asyncio.new_event_loop()
158184
return
185+
elif c == CHAR_CTRL_E:
186+
sys.stdout.write("paste mode; Ctrl-C to cancel, Ctrl-D to finish\n===\n")
187+
paste = True
159188
elif c == 0x1B:
160189
# Start of escape sequence.
161190
key = await s.read(2)
162-
if key in ("[A", "[B"):
191+
if key in ("[A", "[B"): # up, down
163192
# Stash the current command.
164193
hist[(hist_i - hist_b) % _HISTORY_LIMIT] = cmd
165194
# Clear current command.
@@ -175,12 +204,122 @@ async def task(g=None, prompt="--> "):
175204
# Update current command.
176205
cmd = hist[(hist_i - hist_b) % _HISTORY_LIMIT]
177206
sys.stdout.write(cmd)
207+
elif key == "[D": # left
208+
if curs < len(cmd) - 1:
209+
curs += 1
210+
sys.stdout.write("\x1B")
211+
sys.stdout.write(key)
212+
elif key == "[C": # right
213+
if curs:
214+
curs -= 1
215+
sys.stdout.write("\x1B")
216+
sys.stdout.write(key)
217+
elif key == "[H": # home
218+
pcurs = curs
219+
curs = len(cmd)
220+
sys.stdout.write("\x1B[{}D".format(curs - pcurs)) # move cursor left
221+
elif key == "[F": # end
222+
pcurs = curs
223+
curs = 0
224+
sys.stdout.write("\x1B[{}C".format(pcurs)) # move cursor right
178225
else:
179226
# sys.stdout.write("\\x")
180227
# sys.stdout.write(hex(c))
181228
pass
182229
else:
183-
sys.stdout.write(b)
184-
cmd += b
230+
if curs:
231+
# inserting into middle of line
232+
cmd = "".join((cmd[:-curs], b, cmd[-curs:]))
233+
sys.stdout.write(cmd[-curs - 1 :]) # redraw line to end
234+
sys.stdout.write("\x1B[{}D".format(curs)) # reset cursor location
235+
else:
236+
sys.stdout.write(b)
237+
cmd += b
185238
finally:
186239
micropython.kbd_intr(3)
240+
241+
242+
async def raw_paste(s, g, window=512):
243+
sys.stdout.write("R\x01") # supported
244+
sys.stdout.write(bytearray([window & 0xFF, window >> 8, 0x01]).decode())
245+
eof = False
246+
idx = 0
247+
buff = bytearray(window)
248+
file = b""
249+
while not eof:
250+
for idx in range(window):
251+
b = await s.read(1)
252+
c = ord(b)
253+
if c == CHAR_CTRL_C or c == CHAR_CTRL_D:
254+
# end of file
255+
sys.stdout.write(chr(CHAR_CTRL_D))
256+
if c == CHAR_CTRL_C:
257+
raise KeyboardInterrupt
258+
file += buff[:idx]
259+
eof = True
260+
break
261+
buff[idx] = c
262+
263+
if not eof:
264+
file += buff
265+
sys.stdout.write("\x01") # indicate window available to host
266+
267+
return file
268+
269+
270+
async def raw_repl(s: asyncio.StreamReader, g: dict):
271+
heading = "raw REPL; CTRL-B to exit\n"
272+
line = ""
273+
sys.stdout.write(heading)
274+
275+
while True:
276+
line = ""
277+
sys.stdout.write(">")
278+
while True:
279+
b = await s.read(1)
280+
c = ord(b)
281+
if c == CHAR_CTRL_A:
282+
rline = line
283+
line = ""
284+
285+
if len(rline) == 2 and ord(rline[0]) == CHAR_CTRL_E:
286+
if rline[1] == "A":
287+
line = await raw_paste(s, g)
288+
break
289+
else:
290+
# reset raw REPL
291+
sys.stdout.write(heading)
292+
sys.stdout.write(">")
293+
continue
294+
elif c == CHAR_CTRL_B:
295+
# exit raw REPL
296+
sys.stdout.write("\n")
297+
return 0
298+
elif c == CHAR_CTRL_C:
299+
# clear line
300+
line = ""
301+
elif c == CHAR_CTRL_D:
302+
# entry finished
303+
# indicate reception of command
304+
sys.stdout.write("OK")
305+
break
306+
else:
307+
# let through any other raw 8-bit value
308+
line += b
309+
310+
if len(line) == 0:
311+
# Normally used to trigger soft-reset but stay in raw mode.
312+
# Fake it for aiorepl / mpremote.
313+
sys.stdout.write("Ignored: soft reboot\n")
314+
sys.stdout.write(heading)
315+
316+
try:
317+
result = exec(line, g)
318+
if result is not None:
319+
sys.stdout.write(repr(result))
320+
sys.stdout.write(chr(CHAR_CTRL_D))
321+
except Exception as ex:
322+
print(line)
323+
sys.stdout.write(chr(CHAR_CTRL_D))
324+
sys.print_exception(ex, sys.stdout)
325+
sys.stdout.write(chr(CHAR_CTRL_D))

micropython/aiorepl/manifest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
metadata(
2-
version="0.1.1",
2+
version="0.2.0",
33
description="Provides an asynchronous REPL that can run concurrently with an asyncio, also allowing await expressions.",
44
)
55

0 commit comments

Comments
 (0)