diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 000000000..688134b42 --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,13 @@ +name: Check spelling with codespell + +on: [push, pull_request] + +jobs: + codespell: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + # codespell version should be kept in sync with .pre-commit-config.yml + - run: pip install --user codespell==2.4.1 tomli + - run: codespell + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 05f5d3df0..b12b351c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,3 +13,11 @@ repos: hooks: - id: ruff id: ruff-format + + - repo: https://github.com/codespell-project/codespell + # version should be kept in sync with .github/workflows/codespell.yml & micropython repo + rev: v2.4.1 + hooks: + - id: codespell + additional_dependencies: + - tomli diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61a49101e..815d7373c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,6 +87,40 @@ specific conventions and guidelines for micropython-lib: packages](README.md#installing-packages-from-forks) in your Pull Request description. +### Module documentation + +Each package in micropython-lib is encouraged to include documentation in +the form of docstrings in the code itself. The top-level docstring in the main +module or package should provide an overview of the package's functionality, +usage examples, and any important notes or caveats. + +Note that the docstrings should be concise and relevant, even though they will +not be included in the cross-compiled bytecode and will not impact the size of +the module when installed via `mip`. + +When writing docstrings, please follow these guidelines: +* Use triple double quotes (`"""`) for docstrings. +* Multi-line docstrings should place the starting and ending triple quotes on + their own lines. +* Start with a brief summary of the module's purpose. +* Include usage examples where appropriate (indent examplecode blocks with 4 + spaces). +* Use proper indentation for multi-line docstrings to align with the code block. + +### Module versioning + +Each package in micropython-lib should include a `manifest.py` file that +specifies metadata about the package, including its version. The version +management is intentionally manual to ensure package stability and prevent +accidental version bumps. When making changes to a package that affect its +functionality or API, please update the version in `manifest.py`. The +`tools/build.py` script will detect new versions and add them to the index, but +won't increment versions automatically + +> [!NOTE] +> Changes to docstrings or comments that do not change the generated bytecode +> should not change the version. + ### Publishing packages from forks You can easily publish the packages from your micropython-lib diff --git a/micropython/aiorepl/aiorepl.py b/micropython/aiorepl/aiorepl.py index a640efcd1..15026e435 100644 --- a/micropython/aiorepl/aiorepl.py +++ b/micropython/aiorepl/aiorepl.py @@ -74,7 +74,7 @@ async def kbd_intr_task(exec_task, s): except asyncio.CancelledError: pass else: - # Excute code snippet directly. + # Execute code snippet directly. try: try: micropython.kbd_intr(3) @@ -114,6 +114,8 @@ async def task(g=None, prompt="--> "): curs = 0 # cursor offset from end of cmd buffer while True: b = await s.read(1) + if not b: # Handle EOF/empty read + break pc = c # save previous character c = ord(b) pt = t # save previous time diff --git a/micropython/aiorepl/manifest.py b/micropython/aiorepl/manifest.py index ca2fa1513..83802e1c0 100644 --- a/micropython/aiorepl/manifest.py +++ b/micropython/aiorepl/manifest.py @@ -1,5 +1,5 @@ metadata( - version="0.2.1", + version="0.2.2", description="Provides an asynchronous REPL that can run concurrently with an asyncio, also allowing await expressions.", ) diff --git a/micropython/bluetooth/aioble/README.md b/micropython/bluetooth/aioble/README.md index 83ae00209..559594850 100644 --- a/micropython/bluetooth/aioble/README.md +++ b/micropython/bluetooth/aioble/README.md @@ -93,7 +93,7 @@ async with aioble.scan(duration_ms=5000, interval_us=30000, window_us=30000, act # Either from scan result device = result.device # Or with known address -device = aioble.Device(aioble.PUBLIC, "aa:bb:cc:dd:ee:ff") +device = aioble.Device(aioble.ADDR_PUBLIC, "aa:bb:cc:dd:ee:ff") try: connection = await device.connect(timeout_ms=2000) diff --git a/micropython/bluetooth/aioble/aioble/central.py b/micropython/bluetooth/aioble/aioble/central.py index 131b1e0db..15ef8aa12 100644 --- a/micropython/bluetooth/aioble/aioble/central.py +++ b/micropython/bluetooth/aioble/aioble/central.py @@ -119,7 +119,7 @@ async def _connect( _connecting.add(device) # Event will be set in the connected IRQ, and then later - # re-used to notify disconnection. + # reused to notify disconnection. connection._event = connection._event or asyncio.ThreadSafeFlag() try: diff --git a/micropython/bluetooth/aioble/aioble/client.py b/micropython/bluetooth/aioble/aioble/client.py index 859c6e937..a3b0efb6e 100644 --- a/micropython/bluetooth/aioble/aioble/client.py +++ b/micropython/bluetooth/aioble/aioble/client.py @@ -244,7 +244,7 @@ async def read(self, timeout_ms=1000): self._register_with_connection() # This will be set by the done IRQ. self._read_status = None - # This will be set by the result and done IRQs. Re-use if possible. + # This will be set by the result and done IRQs. Reuse if possible. self._read_event = self._read_event or asyncio.ThreadSafeFlag() # Issue the read. diff --git a/micropython/bluetooth/aioble/examples/l2cap_file_server.py b/micropython/bluetooth/aioble/examples/l2cap_file_server.py index 3c3b3b44d..04afc12e5 100644 --- a/micropython/bluetooth/aioble/examples/l2cap_file_server.py +++ b/micropython/bluetooth/aioble/examples/l2cap_file_server.py @@ -1,7 +1,7 @@ # MIT license; Copyright (c) 2021 Jim Mussared # This is a BLE file server, based very loosely on the Object Transfer Service -# specification. It demonstrated transfering data over an L2CAP channel, as +# specification. It demonstrated transferring data over an L2CAP channel, as # well as using notifications and GATT writes on a characteristic. # The server supports downloading and uploading files, as well as querying @@ -33,7 +33,7 @@ _CONTROL_CHARACTERISTIC_UUID = bluetooth.UUID("0492fcec-7194-11eb-9439-0242ac130003") # How frequently to send advertising beacons. -_ADV_INTERVAL_MS = 250_000 +_ADV_INTERVAL_US = 250_000 _COMMAND_SEND = const(0) @@ -162,7 +162,7 @@ async def peripheral_task(): while True: print("Waiting for connection") connection = await aioble.advertise( - _ADV_INTERVAL_MS, + _ADV_INTERVAL_US, name="mpy-file", services=[_FILE_SERVICE_UUID], ) diff --git a/micropython/bluetooth/aioble/examples/temp_sensor.py b/micropython/bluetooth/aioble/examples/temp_sensor.py index 54580f595..9a4ec26de 100644 --- a/micropython/bluetooth/aioble/examples/temp_sensor.py +++ b/micropython/bluetooth/aioble/examples/temp_sensor.py @@ -20,7 +20,7 @@ _ADV_APPEARANCE_GENERIC_THERMOMETER = const(768) # How frequently to send advertising beacons. -_ADV_INTERVAL_MS = 250_000 +_ADV_INTERVAL_US = 250_000 # Register GATT server. @@ -50,7 +50,7 @@ async def sensor_task(): async def peripheral_task(): while True: async with await aioble.advertise( - _ADV_INTERVAL_MS, + _ADV_INTERVAL_US, name="mpy-temp", services=[_ENV_SENSE_UUID], appearance=_ADV_APPEARANCE_GENERIC_THERMOMETER, diff --git a/micropython/drivers/display/lcd160cr/lcd160cr.py b/micropython/drivers/display/lcd160cr/lcd160cr.py index 177c6fea3..1d5da2293 100644 --- a/micropython/drivers/display/lcd160cr/lcd160cr.py +++ b/micropython/drivers/display/lcd160cr/lcd160cr.py @@ -56,7 +56,7 @@ def __init__(self, connect=None, *, pwr=None, i2c=None, spi=None, i2c_addr=98): pwr(1) sleep_ms(10) # else: - # alread have power + # already have power # lets be optimistic... # set connections diff --git a/micropython/drivers/imu/bmi270/bmi270.py b/micropython/drivers/imu/bmi270/bmi270.py index 64f819ec2..1e4a940c3 100644 --- a/micropython/drivers/imu/bmi270/bmi270.py +++ b/micropython/drivers/imu/bmi270/bmi270.py @@ -21,29 +21,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Basic example usage: +Basic example usage:: + + import time + from bmi270 import BMI270 + from machine import Pin, SPI, I2C + + # Init in I2C mode. + imu = BMI270(I2C(1, scl=Pin(15), sda=Pin(14))) + + # Or init in SPI mode. + # TODO: Not supported yet. + # imu = BMI270(SPI(5), cs=Pin(10)) + + while (True): + print('Accelerometer: x:{:>6.3f} y:{:>6.3f} z:{:>6.3f}'.format(*imu.accel())) + print('Gyroscope: x:{:>6.3f} y:{:>6.3f} z:{:>6.3f}'.format(*imu.gyro())) + print('Magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) + print("") + time.sleep_ms(100) -import time -from bmi270 import BMI270 -from machine import Pin, SPI, I2C - -# Init in I2C mode. -imu = BMI270(I2C(1, scl=Pin(15), sda=Pin(14))) - -# Or init in SPI mode. -# TODO: Not supported yet. -# imu = BMI270(SPI(5), cs=Pin(10)) - -while (True): - print('Accelerometer: x:{:>6.3f} y:{:>6.3f} z:{:>6.3f}'.format(*imu.accel())) - print('Gyroscope: x:{:>6.3f} y:{:>6.3f} z:{:>6.3f}'.format(*imu.gyro())) - print('Magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) - print("") - time.sleep_ms(100) """ import array import time + from micropython import const _DEFAULT_ADDR = const(0x68) @@ -502,7 +504,7 @@ def __init__( accel_scale=4, bmm_magnet=None, ): - """Initalizes Gyro and Accelerometer. + """Initializes Gyro and Accelerometer. bus: IMU bus address: I2C address (in I2C mode). cs: SPI CS pin (in SPI mode). diff --git a/micropython/drivers/imu/bmm150/bmm150.py b/micropython/drivers/imu/bmm150/bmm150.py index a4845c961..a56c6fe78 100644 --- a/micropython/drivers/imu/bmm150/bmm150.py +++ b/micropython/drivers/imu/bmm150/bmm150.py @@ -21,22 +21,22 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Basic example usage: +Basic example usage:: -import time -from bmm150 import BMM150 -from machine import Pin, SPI, I2C + import time + from bmm150 import BMM150 + from machine import Pin, SPI, I2C -# Init in I2C mode. -imu = BMM150(I2C(1, scl=Pin(15), sda=Pin(14))) + # Init in I2C mode. + imu = BMM150(I2C(1, scl=Pin(15), sda=Pin(14))) -# Or init in SPI mode. -# TODO: Not supported yet. -# imu = BMM150(SPI(5), cs=Pin(10)) + # Or init in SPI mode. + # TODO: Not supported yet. + # imu = BMM150(SPI(5), cs=Pin(10)) -while (True): - print('magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) - time.sleep_ms(100) + while (True): + print('magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) + time.sleep_ms(100) """ import array @@ -66,7 +66,7 @@ def __init__( address=_DEFAULT_ADDR, magnet_odr=30, ): - """Initalizes the Magnetometer. + """Initializes the Magnetometer. bus: IMU bus address: I2C address (in I2C mode). cs: SPI CS pin (in SPI mode). diff --git a/micropython/drivers/imu/lsm6dsox/lsm6dsox.py b/micropython/drivers/imu/lsm6dsox/lsm6dsox.py index ca1397c66..ca7b77673 100644 --- a/micropython/drivers/imu/lsm6dsox/lsm6dsox.py +++ b/micropython/drivers/imu/lsm6dsox/lsm6dsox.py @@ -25,23 +25,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Basic example usage: +Basic example usage:: -import time -from lsm6dsox import LSM6DSOX + import time + from lsm6dsox import LSM6DSOX + + from machine import Pin, SPI, I2C + # Init in I2C mode. + lsm = LSM6DSOX(I2C(0, scl=Pin(13), sda=Pin(12))) -from machine import Pin, SPI, I2C -# Init in I2C mode. -lsm = LSM6DSOX(I2C(0, scl=Pin(13), sda=Pin(12))) + # Or init in SPI mode. + #lsm = LSM6DSOX(SPI(5), cs=Pin(10)) -# Or init in SPI mode. -#lsm = LSM6DSOX(SPI(5), cs=Pin(10)) + while (True): + print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.accel())) + print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.gyro())) + print("") + time.sleep_ms(100) -while (True): - print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.accel())) - print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.gyro())) - print("") - time.sleep_ms(100) """ import array @@ -88,7 +89,7 @@ def __init__( accel_scale=4, ucf=None, ): - """Initalizes Gyro and Accelerator. + """Initializes Gyro and Accelerator. accel_odr: (0, 1.6Hz, 3.33Hz, 6.66Hz, 12.5Hz, 26Hz, 52Hz, 104Hz, 208Hz, 416Hz, 888Hz) gyro_odr: (0, 1.6Hz, 3.33Hz, 6.66Hz, 12.5Hz, 26Hz, 52Hz, 104Hz, 208Hz, 416Hz, 888Hz) gyro_scale: (245dps, 500dps, 1000dps, 2000dps) diff --git a/micropython/drivers/imu/lsm9ds1/lsm9ds1.py b/micropython/drivers/imu/lsm9ds1/lsm9ds1.py index e5a96ad5c..5f1bfb6cc 100644 --- a/micropython/drivers/imu/lsm9ds1/lsm9ds1.py +++ b/micropython/drivers/imu/lsm9ds1/lsm9ds1.py @@ -27,21 +27,21 @@ The sensor contains an accelerometer / gyroscope / magnetometer Uses the internal FIFO to store up to 16 gyro/accel data, use the iter_accel_gyro generator to access it. -Example usage: +Example usage:: -import time -from lsm9ds1 import LSM9DS1 -from machine import Pin, I2C + import time + from lsm9ds1 import LSM9DS1 + from machine import Pin, I2C -imu = LSM9DS1(I2C(1, scl=Pin(15), sda=Pin(14))) + imu = LSM9DS1(I2C(1, scl=Pin(15), sda=Pin(14))) -while (True): - #for g,a in imu.iter_accel_gyro(): print(g,a) # using fifo - print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.accel())) - print('Magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) - print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.gyro())) - print("") - time.sleep_ms(100) + while (True): + #for g,a in imu.iter_accel_gyro(): print(g,a) # using fifo + print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.accel())) + print('Magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) + print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.gyro())) + print("") + time.sleep_ms(100) """ import array @@ -81,7 +81,7 @@ def __init__( magnet_odr=80, magnet_scale=4, ): - """Initalizes Gyro, Accelerometer and Magnetometer. + """Initializes Gyro, Accelerometer and Magnetometer. bus: IMU bus address_imu: IMU I2C address. address_magnet: Magnetometer I2C address. @@ -134,14 +134,14 @@ def __init__( mv[5] = 0x2 # ctrl9 - FIFO enabled self.bus.writeto_mem(self.address_imu, _CTRL_REG4_G, mv) - # fifo: use continous mode (overwrite old data if overflow) + # fifo: use continuous mode (overwrite old data if overflow) self.bus.writeto_mem(self.address_imu, _FIFO_CTRL_REG, b"\x00") self.bus.writeto_mem(self.address_imu, _FIFO_CTRL_REG, b"\xc0") # Configure Magnetometer mv[0] = 0x40 | (magnet_odr << 2) # ctrl1: high performance mode mv[1] = _MAGNET_SCALE.index(magnet_scale) << 5 # ctrl2: scale, normal mode, no reset - mv[2] = 0x00 # ctrl3: continous conversion, no low power, I2C + mv[2] = 0x00 # ctrl3: continuous conversion, no low power, I2C mv[3] = 0x08 # ctrl4: high performance z-axis mv[4] = 0x00 # ctr5: no fast read, no block update self.bus.writeto_mem(self.address_magnet, _CTRL_REG1_M, mv[:5]) diff --git a/micropython/drivers/sensor/ds18x20/ds18x20.py b/micropython/drivers/sensor/ds18x20/ds18x20.py index ad2d9f52c..066a05fb9 100644 --- a/micropython/drivers/sensor/ds18x20/ds18x20.py +++ b/micropython/drivers/sensor/ds18x20/ds18x20.py @@ -1,5 +1,8 @@ -# DS18x20 temperature sensor driver for MicroPython. -# MIT license; Copyright (c) 2016 Damien P. George +""" +DS18x20 temperature sensor driver for MicroPython. + +MIT license; Copyright (c) 2016 Damien P. George +""" from micropython import const diff --git a/micropython/drivers/sensor/hs3003/hs3003.py b/micropython/drivers/sensor/hs3003/hs3003.py index 003501649..332df8de7 100644 --- a/micropython/drivers/sensor/hs3003/hs3003.py +++ b/micropython/drivers/sensor/hs3003/hs3003.py @@ -22,21 +22,23 @@ THE SOFTWARE. HS3003 driver for MicroPython. +------------------------------ Example usage: -import time -from hs3003 import HS3003 -from machine import Pin, I2C + import time + from hs3003 import HS3003 + from machine import Pin, I2C -bus = I2C(1, scl=Pin(15), sda=Pin(14)) -hts = HS3003(bus) + bus = I2C(1, scl=Pin(15), sda=Pin(14)) + hts = HS3003(bus) + + while True: + rH = hts.humidity() + temp = hts.temperature() + print ("rH: %.2f%% T: %.2fC" %(rH, temp)) + time.sleep_ms(100) -while True: - rH = hts.humidity() - temp = hts.temperature() - print ("rH: %.2f%% T: %.2fC" %(rH, temp)) - time.sleep_ms(100) """ import struct diff --git a/micropython/drivers/sensor/hts221/hts221.py b/micropython/drivers/sensor/hts221/hts221.py index c6cd51f48..5be268d40 100644 --- a/micropython/drivers/sensor/hts221/hts221.py +++ b/micropython/drivers/sensor/hts221/hts221.py @@ -23,22 +23,24 @@ THE SOFTWARE. HTS221 driver driver for MicroPython. +------------------------------------- + Original source: https://github.com/ControlEverythingCommunity/HTS221/blob/master/Python/HTS221.py Example usage: -import time -import hts221 -from machine import Pin, I2C + import time + import hts221 + from machine import Pin, I2C -bus = I2C(1, scl=Pin(15), sda=Pin(14)) -hts = hts221.HTS221(bus) + bus = I2C(1, scl=Pin(15), sda=Pin(14)) + hts = hts221.HTS221(bus) -while (True): - rH = hts.humidity() - temp = hts.temperature() - print ("rH: %.2f%% T: %.2fC" %(rH, temp)) - time.sleep_ms(100) + while (True): + rH = hts.humidity() + temp = hts.temperature() + print ("rH: %.2f%% T: %.2fC" %(rH, temp)) + time.sleep_ms(100) """ import struct diff --git a/micropython/drivers/storage/sdcard/manifest.py b/micropython/drivers/storage/sdcard/manifest.py index cb4647eeb..ea30a7a39 100644 --- a/micropython/drivers/storage/sdcard/manifest.py +++ b/micropython/drivers/storage/sdcard/manifest.py @@ -1,3 +1,3 @@ -metadata(description="SDCard block device driver.", version="0.1.0") +metadata(description="SDCard block device driver.", version="0.1.1") module("sdcard.py", opt=3) diff --git a/micropython/drivers/storage/sdcard/sdcard.py b/micropython/drivers/storage/sdcard/sdcard.py index c9c991f59..3df4788a2 100644 --- a/micropython/drivers/storage/sdcard/sdcard.py +++ b/micropython/drivers/storage/sdcard/sdcard.py @@ -97,7 +97,7 @@ def init_card(self, baudrate): csd = bytearray(16) self.readinto(csd) if csd[0] & 0xC0 == 0x40: # CSD version 2.0 - self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 + self.sectors = ((csd[7] << 16 | csd[8] << 8 | csd[9]) + 1) * 1024 elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6 c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7 diff --git a/micropython/espflash/example.py b/micropython/espflash/example.py index 76e8eb84e..ea4a8af95 100644 --- a/micropython/espflash/example.py +++ b/micropython/espflash/example.py @@ -13,7 +13,7 @@ esp = espflash.ESPFlash(reset, gpio0, uart) # Enter bootloader download mode, at 115200 esp.bootloader() - # Can now chage to higher/lower baudrate + # Can now change to higher/lower baudrate esp.set_baudrate(921600) # Must call this first before any flash functions. esp.flash_attach() diff --git a/micropython/lora/lora-sx126x/lora/sx126x.py b/micropython/lora/lora-sx126x/lora/sx126x.py index 7fa4896ae..446489431 100644 --- a/micropython/lora/lora-sx126x/lora/sx126x.py +++ b/micropython/lora/lora-sx126x/lora/sx126x.py @@ -501,7 +501,7 @@ def calibrate_image(self): self._cmd(">BH", _CMD_CALIBRATE_IMAGE, args) - # Can't find anythign in Datasheet about how long image calibration + # Can't find anything in Datasheet about how long image calibration # takes or exactly how it signals completion. Assuming it will be # similar to _CMD_CALIBRATE. self._wait_not_busy(_CALIBRATE_TIMEOUT_US) diff --git a/micropython/lora/lora/lora/__init__.py b/micropython/lora/lora/lora/__init__.py index 7f8930b8c..9030c91bd 100644 --- a/micropython/lora/lora/lora/__init__.py +++ b/micropython/lora/lora/lora/__init__.py @@ -5,6 +5,12 @@ ok = False # Flag if at least one modem driver package is installed + +def _can_ignore_error(e): + """Check if ImportError can be ignored due to missing module.""" + return all(x in str(e) for x in ["no module named", "lora"]) + + # Various lora "sub-packages" try: @@ -12,7 +18,7 @@ ok = True except ImportError as e: - if "no module named 'lora." not in str(e): + if not _can_ignore_error(e): raise try: @@ -20,7 +26,7 @@ ok = True except ImportError as e: - if "no module named 'lora." not in str(e): + if not _can_ignore_error(e): raise try: @@ -28,7 +34,7 @@ ok = True except ImportError as e: - if "no module named 'lora." not in str(e): + if not _can_ignore_error(e): raise diff --git a/micropython/lora/lora/lora/modem.py b/micropython/lora/lora/lora/modem.py index 499712acf..73fd4db20 100644 --- a/micropython/lora/lora/lora/modem.py +++ b/micropython/lora/lora/lora/modem.py @@ -47,7 +47,7 @@ def __init__(self, ant_sw): self._bw_hz = 125000 # Reset value self._coding_rate = 5 self._crc_en = True # use packet CRCs - self._implicit_header = False # implict vs explicit header mode + self._implicit_header = False # implicit vs explicit header mode self._preamble_len = 12 self._coding_rate = 5 diff --git a/micropython/lora/tests/test_time_on_air.py b/micropython/lora/tests/test_time_on_air.py index 56fa1ad81..7a26e5954 100644 --- a/micropython/lora/tests/test_time_on_air.py +++ b/micropython/lora/tests/test_time_on_air.py @@ -5,7 +5,7 @@ # # ## What is this? # -# Host tests for the BaseModem.get_time_on_air_us() function. Theses against +# Host tests for the BaseModem.get_time_on_air_us() function. These test against # dummy test values produced by the Semtech "SX1261 LoRa Calculator" software, # as downloaded from # https://lora-developers.semtech.com/documentation/product-documents/ diff --git a/micropython/mip/manifest.py b/micropython/mip/manifest.py index 9fb94ebcb..a1b340670 100644 --- a/micropython/mip/manifest.py +++ b/micropython/mip/manifest.py @@ -1,4 +1,4 @@ -metadata(version="0.4.1", description="On-device package installer for network-capable boards") +metadata(version="0.4.2", description="On-device package installer for network-capable boards") require("requests") diff --git a/micropython/mip/mip/__init__.py b/micropython/mip/mip/__init__.py index 7c0fb4d3a..ab48393d6 100644 --- a/micropython/mip/mip/__init__.py +++ b/micropython/mip/mip/__init__.py @@ -1,13 +1,14 @@ # MicroPython package installer # MIT license; Copyright (c) 2022 Jim Mussared -from micropython import const -import requests import sys +import requests + +from micropython import const _PACKAGE_INDEX = const("https://micropython.org/pi/v2") -_CHUNK_SIZE = 128 +_CHUNK_SIZE = const(128) allowed_mip_url_prefixes = ("http://", "https://", "github:", "gitlab:") diff --git a/micropython/senml/docs/index.md b/micropython/senml/docs/index.md index 91ed7fe99..e849f2ca7 100644 --- a/micropython/senml/docs/index.md +++ b/micropython/senml/docs/index.md @@ -1,4 +1,4 @@ -Welcome to the API documet site for the micro-python SenML library. +Welcome to the API document site for the micro-python SenML library. The following api sections are available: diff --git a/micropython/senml/docs/senml_record.md b/micropython/senml/docs/senml_record.md index 6bac549a5..e7c8d0cd0 100644 --- a/micropython/senml/docs/senml_record.md +++ b/micropython/senml/docs/senml_record.md @@ -42,11 +42,11 @@ _parameters:_ - `kwargs:` optional parameters: - value: the value to store in the record - time: the timestamp to use (when was the value measured) - - name: the name of hte record + - name: the name of the record - unit: unit value - sum: sum value - update_time: max time before sensor will provide an updated reading - - callback: a callback function taht will be called when actuator data has been found. Expects no params + - callback: a callback function that will be called when actuator data has been found. Expects no params ### do_actuate diff --git a/micropython/senml/examples/custom_record.py b/micropython/senml/examples/custom_record.py index 1e83ea06b..90f1ddfdb 100644 --- a/micropython/senml/examples/custom_record.py +++ b/micropython/senml/examples/custom_record.py @@ -32,8 +32,8 @@ class Coordinates(SenmlRecord): def __init__(self, name, **kwargs): """overriding the init function so we can initiate the 3 senml records that will represent lat,lon, alt""" self._lat = SenmlRecord( - "lattitude", unit=SenmlUnits.SENML_UNIT_DEGREES_LATITUDE - ) # create these befor calling base constructor so that all can be init correctly from constructor + "latitude", unit=SenmlUnits.SENML_UNIT_DEGREES_LATITUDE + ) # create these before calling base constructor so that all can be init correctly from constructor self._lon = SenmlRecord("longitude", unit=SenmlUnits.SENML_UNIT_DEGREES_LONGITUDE) self._alt = SenmlRecord("altitude", unit=SenmlUnits.SENML_UNIT_METER) super(Coordinates, self).__init__( diff --git a/micropython/senml/senml/senml_pack.py b/micropython/senml/senml/senml_pack.py index 5a0554467..cb5f89fa5 100644 --- a/micropython/senml/senml/senml_pack.py +++ b/micropython/senml/senml/senml_pack.py @@ -169,7 +169,7 @@ def from_json(self, data): def _process_incomming_data(self, records, naming_map): """ - generic processor for incomming data (actuators. + generic processor for incoming data (actuators. :param records: the list of raw senml data, parsed from a json or cbor structure :param naming_map: translates cbor to json field names (when needed). :return: None diff --git a/micropython/senml/senml/senml_record.py b/micropython/senml/senml/senml_record.py index ae40f0f70..282f3e5e3 100644 --- a/micropython/senml/senml/senml_record.py +++ b/micropython/senml/senml/senml_record.py @@ -36,11 +36,11 @@ def __init__(self, name, **kwargs): :param kwargs: optional parameters: - value: the value to store in the record - time: the timestamp to use (when was the value measured) - - name: the name of hte record + - name: the name of the record - unit: unit value - sum: sum value - update_time: max time before sensor will provide an updated reading - - callback: a callback function taht will be called when actuator data has been found. Expects no params + - callback: a callback function that will be called when actuator data has been found. Expects no params """ self.__parent = None # using double __ cause it's a field for an internal property self._unit = None # declare and init internal fields @@ -106,7 +106,7 @@ def value(self): @value.setter def value(self, value): - """set the current value. Will not automatically update the time stamp. This has to be done seperatly for more + """set the current value. Will not automatically update the time stamp. This has to be done separately for more finegrained control Note: when the value is a float, you can control rounding in the rendered output by using the function round() while assigning the value. ex: record.value = round(12.2 / 1.5423, 2) @@ -188,7 +188,7 @@ def _build_rec_dict(self, naming_map, appendTo): elif isinstance(self._value, bytearray): if ( naming_map["vd"] == "vd" - ): # neeed to make a distinction between json (needs base64) and cbor (needs binary) + ): # need to make a distinction between json (needs base64) and cbor (needs binary) result[naming_map["vd"]] = binascii.b2a_base64(self._value, newline=False).decode( "utf8" ) @@ -216,7 +216,7 @@ def _build_rec_dict(self, naming_map, appendTo): def _from_raw(self, raw, naming_map): """ - extracts te data from the raw record. Used during parsing of incoming data. + extracts the data from the raw record. Used during parsing of incoming data. :param raw: a raw senml record which still contains the original field names :param naming_map: used to map cbor names to json field names :return: diff --git a/micropython/ucontextlib/ucontextlib.py b/micropython/ucontextlib/ucontextlib.py index d259f9b8f..d7d984a95 100644 --- a/micropython/ucontextlib/ucontextlib.py +++ b/micropython/ucontextlib/ucontextlib.py @@ -6,7 +6,7 @@ - redirect_stdout; - ExitStack. - closing - - supress + - suppress """ diff --git a/micropython/umqtt.simple/README.rst b/micropython/umqtt.simple/README.rst index d9d09b970..6578a1a89 100644 --- a/micropython/umqtt.simple/README.rst +++ b/micropython/umqtt.simple/README.rst @@ -47,12 +47,13 @@ Taking into account API traits described above, umqtt pretty closely follows MQTT control operations, and maps them to class methods: * ``connect(...)`` - Connect to a server. Returns True if this connection - uses persisten session stored on a server (this will be always False if + uses persistent session stored on a server (this will be always False if clean_session=True argument is used (default)). * ``disconnect()`` - Disconnect from a server, release resources. * ``ping()`` - Ping server (response is processed automatically by wait_msg()). * ``publish()`` - Publish a message. * ``subscribe()`` - Subscribe to a topic. +* ``unsubscribe()`` - Unsubscribe to a topic. * ``set_callback()`` - Set callback for received subscription messages. * ``set_last_will()`` - Set MQTT "last will" message. Should be called *before* connect(). diff --git a/micropython/umqtt.simple/manifest.py b/micropython/umqtt.simple/manifest.py index 709a27505..ce639bbad 100644 --- a/micropython/umqtt.simple/manifest.py +++ b/micropython/umqtt.simple/manifest.py @@ -1,4 +1,4 @@ -metadata(description="Lightweight MQTT client for MicroPython.", version="1.6.0") +metadata(description="Lightweight MQTT client for MicroPython.", version="1.7.0") # Originally written by Paul Sokolovsky. diff --git a/micropython/umqtt.simple/umqtt/simple.py b/micropython/umqtt.simple/umqtt/simple.py index d9cdffc47..5d52ee305 100644 --- a/micropython/umqtt.simple/umqtt/simple.py +++ b/micropython/umqtt.simple/umqtt/simple.py @@ -175,6 +175,19 @@ def subscribe(self, topic, qos=0): raise MQTTException(resp[3]) return + def unsubscribe(self, topic): + pkt = bytearray(b"\xa2\0\0\0") + self.pid += 1 + struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic), self.pid) + self.sock.write(pkt) + self._send_str(topic) + while 1: + op = self.wait_msg() + if op == 0xB0: + resp = self.sock.read(3) + assert resp[1] == pkt[2] and resp[2] == pkt[3] + return + # Wait for a single incoming MQTT message and process it. # Subscribed messages are delivered to a callback previously # set by .set_callback() method. Other (internal) MQTT diff --git a/micropython/usb/usb-device-cdc/usb/device/cdc.py b/micropython/usb/usb-device-cdc/usb/device/cdc.py index 0acea184f..4ec012bc5 100644 --- a/micropython/usb/usb-device-cdc/usb/device/cdc.py +++ b/micropython/usb/usb-device-cdc/usb/device/cdc.py @@ -228,7 +228,7 @@ def desc_cfg(self, desc, itf_num, ep_num, strs): 5, # bFunctionLength _CS_DESC_TYPE, # bDescriptorType _CDC_FUNC_DESC_CALL_MANAGEMENT, # bDescriptorSubtype - 0, # bmCapabilities - XXX no call managment so far + 0, # bmCapabilities - XXX no call management so far itf_num + 1, # bDataInterface - interface 1 ) diff --git a/micropython/usb/usb-device-hid/manifest.py b/micropython/usb/usb-device-hid/manifest.py index af9b8cb84..e844b6f01 100644 --- a/micropython/usb/usb-device-hid/manifest.py +++ b/micropython/usb/usb-device-hid/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.0") +metadata(version="0.1.2") require("usb-device") package("usb") diff --git a/micropython/usb/usb-device-hid/usb/device/hid.py b/micropython/usb/usb-device-hid/usb/device/hid.py index 9e4c70dde..07664fb02 100644 --- a/micropython/usb/usb-device-hid/usb/device/hid.py +++ b/micropython/usb/usb-device-hid/usb/device/hid.py @@ -34,6 +34,8 @@ _INTERFACE_SUBCLASS_NONE = const(0x00) _INTERFACE_SUBCLASS_BOOT = const(0x01) +# These values will only make sense when interface subclass +# is 0x01, which indicates boot protocol support. _INTERFACE_PROTOCOL_NONE = const(0x00) _INTERFACE_PROTOCOL_KEYBOARD = const(0x01) _INTERFACE_PROTOCOL_MOUSE = const(0x02) @@ -123,6 +125,7 @@ def send_report(self, report_data, timeout_ms=100): if not self.is_open(): return False self.submit_xfer(self._int_ep, report_data) + return True def desc_cfg(self, desc, itf_num, ep_num, strs): # Add the standard interface descriptor @@ -130,7 +133,9 @@ def desc_cfg(self, desc, itf_num, ep_num, strs): itf_num, 1, _INTERFACE_CLASS, - _INTERFACE_SUBCLASS_NONE, + _INTERFACE_SUBCLASS_NONE + if self.protocol == _INTERFACE_PROTOCOL_NONE + else _INTERFACE_SUBCLASS_BOOT, self.protocol, len(strs) if self.interface_str else 0, ) @@ -148,7 +153,12 @@ def desc_cfg(self, desc, itf_num, ep_num, strs): desc.endpoint(self._int_ep, "interrupt", 8, 8) self.idle_rate = 0 - self.protocol = 0 + + # This variable is reused to track boot protocol status. + # 0 for boot protocol, 1 for report protocol + # According to Device Class Definition for Human Interface Devices (HID) v1.11 + # Appendix F.5, the device comes up in non-boot mode by default. + self.protocol = 1 def num_eps(self): return 1 @@ -197,6 +207,8 @@ def on_interface_control_xfer(self, stage, request): if desc_type == _DESC_HID_TYPE: return self.get_hid_descriptor() if desc_type == _DESC_REPORT_TYPE: + # Reset to report protocol when report descriptor is requested + self.protocol = 1 return self.report_descriptor elif req_type == _REQ_TYPE_CLASS: # HID Spec p50: 7.2 Class-Specific Requests diff --git a/micropython/usb/usb-device-midi/usb/device/midi.py b/micropython/usb/usb-device-midi/usb/device/midi.py index ecb178ea4..55bfbd08b 100644 --- a/micropython/usb/usb-device-midi/usb/device/midi.py +++ b/micropython/usb/usb-device-midi/usb/device/midi.py @@ -61,7 +61,7 @@ class MIDIInterface(Interface): # Base class to implement a USB MIDI device in Python. # - # To be compliant this also regisers a dummy USB Audio interface, but that + # To be compliant this also registers a dummy USB Audio interface, but that # interface isn't otherwise used. def __init__(self, rxlen=16, txlen=16): diff --git a/micropython/usb/usb-device-mouse/usb/device/mouse.py b/micropython/usb/usb-device-mouse/usb/device/mouse.py index d3dea9c61..61345276b 100644 --- a/micropython/usb/usb-device-mouse/usb/device/mouse.py +++ b/micropython/usb/usb-device-mouse/usb/device/mouse.py @@ -77,7 +77,7 @@ def move_by(self, dx, dy): b'\xA1\x00' # Collection (Physical) b'\x05\x09' # Usage Page (Buttons) b'\x19\x01' # Usage Minimum (01), - b'\x29\x03' # Usage Maximun (03), + b'\x29\x03' # Usage Maximum (03), b'\x15\x00' # Logical Minimum (0), b'\x25\x01' # Logical Maximum (1), b'\x95\x03' # Report Count (3), diff --git a/micropython/usb/usb-device/manifest.py b/micropython/usb/usb-device/manifest.py index 025e67547..fa0d8a3f5 100644 --- a/micropython/usb/usb-device/manifest.py +++ b/micropython/usb/usb-device/manifest.py @@ -1,2 +1,2 @@ -metadata(version="0.2.0") +metadata(version="0.2.1") package("usb") diff --git a/micropython/usb/usb-device/usb/device/core.py b/micropython/usb/usb-device/usb/device/core.py index 7be09ee46..20cda94ca 100644 --- a/micropython/usb/usb-device/usb/device/core.py +++ b/micropython/usb/usb-device/usb/device/core.py @@ -446,7 +446,7 @@ def num_itfs(self): # Return the number of actual USB Interfaces represented by this object # (as set in desc_cfg().) # - # Only needs to be overriden if implementing a Interface class that + # Only needs to be overridden if implementing a Interface class that # represents more than one USB Interface descriptor (i.e. MIDI), or an # Interface Association Descriptor (i.e. USB-CDC). return 1 @@ -600,7 +600,8 @@ def submit_xfer(self, ep_addr, data, done_cb=None): # function has returned to the caller. if not self._open: raise RuntimeError("Not open") - _dev._submit_xfer(ep_addr, data, done_cb) + if not _dev._submit_xfer(ep_addr, data, done_cb): + raise RuntimeError("DCD error") def stall(self, ep_addr, *args): # Set or get the endpoint STALL state. @@ -610,7 +611,7 @@ def stall(self, ep_addr, *args): # argument to set or clear. # # Generally endpoint STALL is handled automatically, but there are some - # device classes that need to explicitly stall or unstall an endpoint + # device classes that need to explicitly stall or un-stall an endpoint # under certain conditions. if not self._open or ep_addr not in self._eps: raise RuntimeError diff --git a/pyproject.toml b/pyproject.toml index 83d29405d..736fd3a62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,24 @@ +[tool.codespell] +count = "" +ignore-regex = '\b[A-Z]{3}\b' +ignore-multiline-regex = "# codespell:ignore-begin *\n.*# codespell:ignore-end *\n" +ignore-words = "tools/ignore_words.txt" +quiet-level = 3 +skip = """ +./.git,\ +""" + [tool.ruff] -exclude = [ +extend-exclude = [ "python-stdlib", "unix-ffi", ] +line-length = 99 +target-version = "py38" # enable use of walrus operator + +[tool.ruff.lint] select = [ - "ASYNC", # flake8-comprehensions + "ASYNC", # flake8-async "C4", # flake8-comprehensions "C90", # McCabe cyclomatic complexity "DTZ", # flake8-datetimez @@ -53,42 +67,40 @@ select = [ # "TRY", # tryceratops # "UP", # pyupgrade ] -ignore = [ +extend-ignore = [ "E722", - "E741", # 'l' is currently widely used + "E741", # 'l' is currently widely used "F401", "F403", "F405", - "E501", # line length, recommended to disable + "E501", # line length, recommended to disable "ISC001", - "ISC003", # micropython does not support implicit concatenation of f-strings - "PIE810", # micropython does not support passing tuples to .startswith or .endswith + "ISC003", # MicroPython does not support implicit concatenation of f-strings + "PIE810", # MicroPython does not support passing tuples to .startswith or .endswith + "PLC0415", # conditional imports are common in MicroPython "PLC1901", - "PLR1704", # sometimes desirable to redefine an argument to save code size + "PLR1704", # sometimes desirable to redefine an argument to save code size "PLR1714", "PLR5501", "PLW0602", "PLW0603", "PLW2901", - "RUF012", - "RUF100", + "RUF012", # mutable default values in class attributes. + "RUF059", # Unpacked variable `foo` is never used + "RUF100", # duplicate noqa directives. "SIM101", - "W191", # tab-indent, redundant when using formatter + "W191", # tab-indent, redundant when using formatter ] -line-length = 99 -target-version = "py37" - -[tool.ruff.mccabe] -max-complexity = 61 +mccabe.max-complexity = 61 -[tool.ruff.pylint] +[tool.ruff.lint.pylint] allow-magic-value-types = ["bytes", "int", "str"] max-args = 14 max-branches = 58 max-returns = 13 max-statements = 166 -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "micropython/aiorepl/aiorepl.py" = ["PGH001"] # manifest.py files are evaluated with some global names pre-defined @@ -97,5 +109,3 @@ max-statements = 166 # ble multitests are evaluated with some names pre-defined "micropython/bluetooth/aioble/multitests/*" = ["F821"] - -[tool.ruff.format] diff --git a/python-ecosys/aiohttp/aiohttp/__init__.py b/python-ecosys/aiohttp/aiohttp/__init__.py index 8c5493f30..1e6d89d05 100644 --- a/python-ecosys/aiohttp/aiohttp/__init__.py +++ b/python-ecosys/aiohttp/aiohttp/__init__.py @@ -42,7 +42,9 @@ def _decode(self, data): return data async def read(self, sz=-1): - return self._decode(await self.content.read(sz)) + return self._decode( + await (self.content.read(sz) if sz == -1 else self.content.readexactly(sz)) + ) async def text(self, encoding="utf-8"): return (await self.read(int(self._get_header("content-length", -1)))).decode(encoding) @@ -66,13 +68,13 @@ async def read(self, sz=4 * 1024 * 1024): self.chunk_size = int(l, 16) if self.chunk_size == 0: # End of message - sep = await self.content.read(2) + sep = await self.content.readexactly(2) assert sep == b"\r\n" return b"" - data = await self.content.read(min(sz, self.chunk_size)) + data = await self.content.readexactly(min(sz, self.chunk_size)) self.chunk_size -= len(data) if self.chunk_size == 0: - sep = await self.content.read(2) + sep = await self.content.readexactly(2) assert sep == b"\r\n" return self._decode(data) diff --git a/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py b/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py index 6e0818c92..30fa9ab77 100644 --- a/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py +++ b/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py @@ -166,7 +166,11 @@ async def handshake(self, uri, ssl, req): async def receive(self): while True: - opcode, payload = await self._read_frame() + opcode, payload, final = await self._read_frame() + while not final: + # original opcode must be preserved + _, morepayload, final = await self._read_frame() + payload += morepayload send_opcode, data = self._process_websocket_frame(opcode, payload) if send_opcode: # pragma: no cover await self.send(data, send_opcode) @@ -189,7 +193,7 @@ async def close(self): await self.send(b"", self.CLOSE) async def _read_frame(self): - header = await self.reader.read(2) + header = await self.reader.readexactly(2) if len(header) != 2: # pragma: no cover # raise OSError(32, "Websocket connection closed") opcode = self.CLOSE @@ -197,16 +201,16 @@ async def _read_frame(self): return opcode, payload fin, opcode, has_mask, length = self._parse_frame_header(header) if length == 126: # Magic number, length header is 2 bytes - (length,) = struct.unpack("!H", await self.reader.read(2)) + (length,) = struct.unpack("!H", await self.reader.readexactly(2)) elif length == 127: # Magic number, length header is 8 bytes - (length,) = struct.unpack("!Q", await self.reader.read(8)) + (length,) = struct.unpack("!Q", await self.reader.readexactly(8)) if has_mask: # pragma: no cover - mask = await self.reader.read(4) - payload = await self.reader.read(length) + mask = await self.reader.readexactly(4) + payload = await self.reader.readexactly(length) if has_mask: # pragma: no cover payload = bytes(x ^ mask[i % 4] for i, x in enumerate(payload)) - return opcode, payload + return opcode, payload, fin class ClientWebSocketResponse: diff --git a/python-ecosys/aiohttp/manifest.py b/python-ecosys/aiohttp/manifest.py index d22a6ce11..cee6f0c10 100644 --- a/python-ecosys/aiohttp/manifest.py +++ b/python-ecosys/aiohttp/manifest.py @@ -1,6 +1,6 @@ metadata( description="HTTP client module for MicroPython asyncio module", - version="0.0.5", + version="0.0.7", pypi="aiohttp", ) diff --git a/python-ecosys/cbor2/cbor2/_decoder.py b/python-ecosys/cbor2/cbor2/_decoder.py index 965dbfd46..0ddfc31d3 100644 --- a/python-ecosys/cbor2/cbor2/_decoder.py +++ b/python-ecosys/cbor2/cbor2/_decoder.py @@ -34,7 +34,7 @@ class CBORDecodeError(Exception): break_marker = object() -class CBORSimpleValue(object): +class CBORSimpleValue(object): # noqa: PLW1641 """ Represents a CBOR "simple value". :param int value: the value (0-255) diff --git a/python-ecosys/iperf3/iperf3.py b/python-ecosys/iperf3/iperf3.py index 363d10d59..05d69f774 100644 --- a/python-ecosys/iperf3/iperf3.py +++ b/python-ecosys/iperf3/iperf3.py @@ -198,6 +198,33 @@ def make_cookie(): return cookie +def _transfer(udp, reverse, addr, s_data, buf, udp_last_send, udp_packet_id, udp_interval, stats): + if udp: + if reverse: + recvninto(s_data, buf) + udp_in_sec, udp_in_usec, udp_in_id = struct.unpack_from(">III", buf, 0) + if udp_in_id != udp_packet_id + 1: + stats.add_lost_packets(udp_in_id - (udp_packet_id + 1)) + udp_packet_id = udp_in_id + stats.add_bytes(len(buf)) + else: + t = ticks_us() + if t - udp_last_send > udp_interval: + udp_last_send += udp_interval + udp_packet_id += 1 + struct.pack_into(">III", buf, 0, t // 1000000, t % 1000000, udp_packet_id) + n = s_data.sendto(buf, addr) + stats.add_bytes(n) + else: + if reverse: + recvninto(s_data, buf) + n = len(buf) + else: + n = s_data.send(buf) + stats.add_bytes(n) + return udp_last_send, udp_packet_id + + def server_once(): # Listen for a connection ai = socket.getaddrinfo("0.0.0.0", 5201) @@ -233,13 +260,21 @@ def server_once(): s_data, addr = s_listen.accept() print("Accepted connection:", addr) recvn(s_data, COOKIE_SIZE) + udp = False + udp_packet_id = 0 + udp_interval = None + udp_last_send = None elif param.get("udp", False): # Close TCP connection and open UDP "connection" s_listen.close() s_data = socket.socket(ai[0], socket.SOCK_DGRAM) s_data.bind(ai[-1]) data, addr = s_data.recvfrom(4) - s_data.sendto(b"\x12\x34\x56\x78", addr) + s_data.sendto(struct.pack("III", buf, 0) - # print(udp_in_sec, udp_in_usec, udp_in_id) - if udp_in_id != udp_packet_id + 1: - stats.add_lost_packets(udp_in_id - (udp_packet_id + 1)) - udp_packet_id = udp_in_id - stats.add_bytes(len(buf)) - else: - # print('UDP send', udp_last_send, t, udp_interval) - if t - udp_last_send > udp_interval: - udp_last_send += udp_interval - udp_packet_id += 1 - struct.pack_into( - ">III", buf, 0, t // 1000000, t % 1000000, udp_packet_id - ) - n = s_data.sendto(buf, ai[-1]) - stats.add_bytes(n) - else: - if reverse: - recvninto(s_data, buf) - n = len(buf) - else: - # print('TCP send', len(buf)) - n = s_data.send(buf) - stats.add_bytes(n) + udp_last_send, udp_packet_id = _transfer( + udp, + reverse, + ai[-1], + s_data, + buf, + udp_last_send, + udp_packet_id, + udp_interval, + stats, + ) elif pollable_is_sock(pollable, s_ctrl): # Receive command @@ -534,7 +559,7 @@ def main(): client(opt_host, opt_udp, opt_reverse) -if sys.platform == "linux": +if sys.implementation.name != "micropython": def pollable_is_sock(pollable, sock): return sock is not None and pollable[0] == sock.fileno() @@ -544,12 +569,12 @@ def ticks_us(): def ticks_diff(a, b): return a - b - - if __name__ == "__main__": - main() else: def pollable_is_sock(pollable, sock): return pollable[0] == sock from time import ticks_us, ticks_diff + +if sys.platform == "linux" and __name__ == "__main__": + main() diff --git a/python-ecosys/iperf3/manifest.py b/python-ecosys/iperf3/manifest.py index 06964ce2a..d63c2d6d1 100644 --- a/python-ecosys/iperf3/manifest.py +++ b/python-ecosys/iperf3/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.4", pypi="iperf3", pypi_publish="uiperf3") +metadata(version="0.1.5", pypi="iperf3", pypi_publish="uiperf3") module("iperf3.py") diff --git a/python-ecosys/requests/example_xively.py b/python-ecosys/requests/example_quote.py similarity index 85% rename from python-ecosys/requests/example_xively.py rename to python-ecosys/requests/example_quote.py index 60e139b98..cfbe8ac0e 100644 --- a/python-ecosys/requests/example_xively.py +++ b/python-ecosys/requests/example_quote.py @@ -1,6 +1,6 @@ import requests -r = requests.get("http://api.xively.com/") +r = requests.get("https://dummyjson.com/quotes/1") print(r) print(r.content) print(r.text) diff --git a/python-ecosys/requests/requests/__init__.py b/python-ecosys/requests/requests/__init__.py index 4ca7489a4..68b4b18cb 100644 --- a/python-ecosys/requests/requests/__init__.py +++ b/python-ecosys/requests/requests/__init__.py @@ -56,9 +56,9 @@ def request( import binascii username, password = auth - formated = b"{}:{}".format(username, password) - formated = str(binascii.b2a_base64(formated)[:-1], "ascii") - headers["Authorization"] = "Basic {}".format(formated) + formatted = b"{}:{}".format(username, password) + formatted = str(binascii.b2a_base64(formatted)[:-1], "ascii") + headers["Authorization"] = "Basic {}".format(formatted) try: proto, dummy, host, path = url.split("/", 3) diff --git a/python-stdlib/argparse/argparse.py b/python-stdlib/argparse/argparse.py index 5c92887f9..828c6e0bc 100644 --- a/python-stdlib/argparse/argparse.py +++ b/python-stdlib/argparse/argparse.py @@ -10,8 +10,12 @@ class _ArgError(BaseException): pass +class ArgumentTypeError(BaseException): + pass + + class _Arg: - def __init__(self, names, dest, action, nargs, const, default, help): + def __init__(self, names, dest, action, nargs, const, default, help, type): self.names = names self.dest = dest self.action = action @@ -19,20 +23,26 @@ def __init__(self, names, dest, action, nargs, const, default, help): self.const = const self.default = default self.help = help + self.type = type + + def _apply(self, optname, arg): + if self.type: + try: + return self.type(arg) + except (ArgumentTypeError, TypeError, ValueError) as e: + raise _ArgError("invalid value for %s: %s (%s)" % (optname, arg, str(e))) + return arg def parse(self, optname, args): # parse args for this arg if self.action == "store": if self.nargs is None: if args: - return args.pop(0) + return self._apply(optname, args.pop(0)) else: raise _ArgError("expecting value for %s" % optname) elif self.nargs == "?": - if args: - return args.pop(0) - else: - return self.default + return self._apply(optname, args.pop(0) if args else self.default) else: if self.nargs == "*": n = -1 @@ -52,7 +62,7 @@ def parse(self, optname, args): else: break else: - ret.append(args.pop(0)) + ret.append(self._apply(optname, args.pop(0))) n -= 1 if n > 0: raise _ArgError("expecting value for %s" % optname) @@ -103,6 +113,10 @@ def add_argument(self, *args, **kwargs): dest = args[0] if not args: args = [dest] + arg_type = kwargs.get("type", None) + if arg_type is not None: + if not callable(arg_type): + raise ValueError("type is not callable") list.append( _Arg( args, @@ -112,6 +126,7 @@ def add_argument(self, *args, **kwargs): const, default, kwargs.get("help", ""), + arg_type, ) ) @@ -176,7 +191,9 @@ def _parse_args(self, args, return_unknown): arg_vals = [] for opt in self.opt: arg_dest.append(opt.dest) - arg_vals.append(opt.default) + arg_vals.append( + opt._apply(opt.dest, opt.default) if isinstance(opt.default, str) else opt.default + ) # deal with unknown arguments, if needed unknown = [] diff --git a/python-stdlib/argparse/manifest.py b/python-stdlib/argparse/manifest.py index 02bf1a22c..fcfe50a35 100644 --- a/python-stdlib/argparse/manifest.py +++ b/python-stdlib/argparse/manifest.py @@ -1,4 +1,4 @@ -metadata(version="0.4.0") +metadata(version="0.5.0") # Originally written by Damien George. diff --git a/python-stdlib/argparse/test_argparse.py b/python-stdlib/argparse/test_argparse.py index d86e53211..7548d4a92 100644 --- a/python-stdlib/argparse/test_argparse.py +++ b/python-stdlib/argparse/test_argparse.py @@ -66,3 +66,47 @@ args, rest = parser.parse_known_args(["a", "b", "c", "-b", "2", "--x", "5", "1"]) assert args.a == ["a", "b"] and args.b == "2" assert rest == ["c", "--x", "5", "1"] + + +class CustomArgType: + def __init__(self, add): + self.add = add + + def __call__(self, value): + return int(value) + self.add + + +parser = argparse.ArgumentParser() +parser.add_argument("-a", type=int) +args = parser.parse_args(["-a", "123"]) +assert args.a == 123 +parser.add_argument("-b", type=str) +args = parser.parse_args(["-b", "string"]) +assert args.b == "string" +parser.add_argument("-c", type=CustomArgType(1)) +args = parser.parse_args(["-c", "123"]) +assert args.c == 124 +try: + parser.add_argument("-d", type=()) + assert False +except ValueError as e: + assert "not callable" in str(e) +parser.add_argument("-d", type=int, nargs="+") +args = parser.parse_args(["-d", "123", "124", "125"]) +assert args.d == [123, 124, 125] +parser.add_argument("-e", type=CustomArgType(1), nargs="+") +args = parser.parse_args(["-e", "123", "124", "125"]) +assert args.e == [124, 125, 126] +parser.add_argument("-f", type=CustomArgType(1), nargs="?") +args = parser.parse_args(["-f", "123"]) +assert args.f == 124 +parser.add_argument("-g", type=CustomArgType(1), default=1) +parser.add_argument("-i", type=CustomArgType(1), default="1") +args = parser.parse_args([]) +assert args.g == 1 +assert args.i == 2 +parser.add_argument("-j", type=CustomArgType(1), default=1) +args = parser.parse_args(["-j", "3"]) +assert args.g == 1 +assert args.i == 2 +assert args.j == 4 diff --git a/python-stdlib/cmd/cmd.py b/python-stdlib/cmd/cmd.py index 447ea1489..b05fc245d 100644 --- a/python-stdlib/cmd/cmd.py +++ b/python-stdlib/cmd/cmd.py @@ -1,20 +1,21 @@ -"""A generic class to build line-oriented command interpreters. +""" +A generic class to build line-oriented command interpreters. Interpreters constructed with this class obey the following conventions: 1. End of file on input is processed as the command 'EOF'. 2. A command is parsed out of each line by collecting the prefix composed of characters in the identchars member. -3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method +3. A command 'foo' is dispatched to a method 'do_foo()'; the do_ method is passed a single argument consisting of the remainder of the line. 4. Typing an empty line repeats the last command. (Actually, it calls the - method `emptyline', which may be overridden in a subclass.) -5. There is a predefined `help' method. Given an argument `topic', it - calls the command `help_topic'. With no arguments, it lists all topics + method 'emptyline', which may be overridden in a subclass.) +5. There is a predefined 'help' method. Given an argument 'topic', it + calls the command 'help_topic'. With no arguments, it lists all topics with defined help_ functions, broken into up to three topics; documented commands, miscellaneous help topics, and undocumented commands. -6. The command '?' is a synonym for `help'. The command '!' is a synonym - for `shell', if a do_shell method exists. +6. The command '?' is a synonym for 'help'. The command '!' is a synonym + for 'shell', if a do_shell method exists. 7. If completion is enabled, completing commands will be done automatically, and completing of commands args is done by calling complete_foo() with arguments text, line, begidx, endidx. text is string we are matching @@ -23,21 +24,21 @@ indexes of the text being matched, which could be used to provide different completion depending upon which position the argument is in. -The `default' method may be overridden to intercept commands for which there +The 'default' method may be overridden to intercept commands for which there is no do_ method. -The `completedefault' method may be overridden to intercept completions for +The 'completedefault' method may be overridden to intercept completions for commands that have no complete_ method. -The data member `self.ruler' sets the character used to draw separator lines +The data member 'self.ruler' sets the character used to draw separator lines in the help messages. If empty, no ruler line is drawn. It defaults to "=". -If the value of `self.intro' is nonempty when the cmdloop method is called, +If the value of 'self.intro' is nonempty when the cmdloop method is called, it is printed out on interpreter startup. This value may be overridden via an optional argument to the cmdloop() method. -The data members `self.doc_header', `self.misc_header', and -`self.undoc_header' set the headers used for the help function's +The data members 'self.doc_header', 'self.misc_header', and +'self.undoc_header' set the headers used for the help function's listings of documented functions, miscellaneous topics, and undocumented functions respectively. @@ -48,7 +49,7 @@ One of the notable deviations is that since MicroPython strips doc strings, this means that that help by doc string feature doesn't work. -completions have also been stripped out. +Completions have also been stripped out. """ import sys diff --git a/python-stdlib/contextlib/contextlib.py b/python-stdlib/contextlib/contextlib.py index 3e598b4b6..2f6d4a928 100644 --- a/python-stdlib/contextlib/contextlib.py +++ b/python-stdlib/contextlib/contextlib.py @@ -1,10 +1,10 @@ -"""Utilities for with-statement contexts. See PEP 343. +""" +Utilities for with-statement contexts. See PEP 343. Original source code: https://hg.python.org/cpython/file/3.4/Lib/contextlib.py Not implemented: - redirect_stdout; - """ import sys @@ -15,12 +15,12 @@ class closing(object): """Context to automatically close something at the end of a block. - Code like this: + Code like this:: with closing(.open()) as f: - is equivalent to this: + is equivalent to this:: f = .open() try: diff --git a/python-stdlib/copy/copy.py b/python-stdlib/copy/copy.py index 0a9283777..9d56d6cbd 100644 --- a/python-stdlib/copy/copy.py +++ b/python-stdlib/copy/copy.py @@ -191,7 +191,7 @@ def deepcopy(x, memo=None, _nil=[]): if copier: y = copier(memo) else: - reductor = dispatch_table.get(cls) + reductor = _deepcopy_dispatch.get(cls) if reductor: rv = reductor(x) else: diff --git a/python-stdlib/copy/manifest.py b/python-stdlib/copy/manifest.py index b22ebeb90..f0849295f 100644 --- a/python-stdlib/copy/manifest.py +++ b/python-stdlib/copy/manifest.py @@ -1,4 +1,4 @@ -metadata(version="3.3.4") +metadata(version="3.3.5") require("types") diff --git a/python-stdlib/datetime/datetime.py b/python-stdlib/datetime/datetime.py index 0f2a89105..b18edf1c2 100644 --- a/python-stdlib/datetime/datetime.py +++ b/python-stdlib/datetime/datetime.py @@ -1,37 +1,22 @@ # datetime.py -import time as _tmod - -_DBM = (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334) -_DIM = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) -_TIME_SPEC = ("auto", "hours", "minutes", "seconds", "milliseconds", "microseconds") +import time as _t def _leap(y): return y % 4 == 0 and (y % 100 != 0 or y % 400 == 0) -def _dby(y): - # year -> number of days before January 1st of year. - Y = y - 1 - return Y * 365 + Y // 4 - Y // 100 + Y // 400 - - def _dim(y, m): # year, month -> number of days in that month in that year. if m == 2 and _leap(y): return 29 - return _DIM[m] + return (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[m] def _dbm(y, m): # year, month -> number of days in year preceding first day of month. - return _DBM[m] + (m > 2 and _leap(y)) - - -def _ymd2o(y, m, d): - # y, month, day -> ordinal, considering 01-Jan-0001 as day 1. - return _dby(y) + _dbm(y, m) + d + return (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)[m] + (m > 2 and _leap(y)) def _o2ymd(n): @@ -73,7 +58,7 @@ def total_seconds(self): @property def days(self): - return self._tuple(2)[0] + return self._tuple(1) @property def seconds(self): @@ -145,7 +130,7 @@ def __bool__(self): return self._us != 0 def __str__(self): - return self._format(0x40) + return self._fmt(0x40) def __hash__(self): if not hasattr(self, "_hash"): @@ -153,9 +138,9 @@ def __hash__(self): return self._hash def isoformat(self): - return self._format(0) + return self._fmt(0) - def _format(self, spec=0): + def _fmt(self, spec=0): if self._us >= 0: td = self g = "" @@ -201,8 +186,8 @@ def tuple(self): def _tuple(self, n): d, us = divmod(self._us, 86_400_000_000) - if n == 2: - return d, us + if n == 1: + return d s, us = divmod(us, 1_000_000) if n == 3: return d, s, us @@ -241,7 +226,7 @@ def fromutc(self, dt): return dt + dtdst def isoformat(self, dt): - return self.utcoffset(dt)._format(0x12) + return self.utcoffset(dt)._fmt(0x12) class timezone(tzinfo): @@ -276,7 +261,7 @@ def dst(self, dt): def tzname(self, dt): if self._name: return self._name - return self._offset._format(0x22) + return self._offset._fmt(0x22) def fromutc(self, dt): return dt + self._offset @@ -287,7 +272,11 @@ def fromutc(self, dt): def _date(y, m, d): if MINYEAR <= y <= MAXYEAR and 1 <= m <= 12 and 1 <= d <= _dim(y, m): - return _ymd2o(y, m, d) + # year -> number of days before January 1st of year. + Y = y - 1 + _dby = Y * 365 + Y // 4 - Y // 100 + Y // 400 + # y, month, day -> ordinal, considering 01-Jan-0001 as day 1. + return _dby + _dbm(y, m) + d elif y == 0 and m == 0 and 1 <= d <= 3_652_059: return d else: @@ -310,11 +299,11 @@ def __init__(self, year, month, day): @classmethod def fromtimestamp(cls, ts): - return cls(*_tmod.localtime(ts)[:3]) + return cls(*_t.localtime(ts)[:3]) @classmethod def today(cls): - return cls(*_tmod.localtime()[:3]) + return cls(*_t.localtime()[:3]) @classmethod def fromordinal(cls, n): @@ -490,7 +479,9 @@ def _iso2t(s): def _t2iso(td, timespec, dt, tz): - s = td._format(_TIME_SPEC.index(timespec)) + s = td._fmt( + ("auto", "hours", "minutes", "seconds", "milliseconds", "microseconds").index(timespec) + ) if tz is not None: s += tz.isoformat(dt) return s @@ -633,15 +624,18 @@ def fromtimestamp(cls, ts, tz=None): else: us = 0 if tz is None: - raise NotImplementedError + dt = cls(*_t.localtime(ts)[:6], microsecond=us, tzinfo=tz) + s = (dt - datetime(*_t.localtime(ts - 86400)[:6]))._us // 1_000_000 - 86400 + if s < 0 and dt == datetime(*_t.localtime(ts + s)[:6]): + dt._fd = 1 else: - dt = cls(*_tmod.gmtime(ts)[:6], microsecond=us, tzinfo=tz) + dt = cls(*_t.gmtime(ts)[:6], microsecond=us, tzinfo=tz) dt = tz.fromutc(dt) return dt @classmethod def now(cls, tz=None): - return cls.fromtimestamp(_tmod.time(), tz) + return cls.fromtimestamp(_t.time(), tz) @classmethod def fromordinal(cls, n): @@ -810,13 +804,45 @@ def astimezone(self, tz=None): return self _tz = self._tz if _tz is None: - raise NotImplementedError + ts = int(self._mktime()) + os = datetime(*_t.localtime(ts)[:6]) - datetime(*_t.gmtime(ts)[:6]) else: os = _tz.utcoffset(self) utc = self - os utc = utc.replace(tzinfo=tz) return tz.fromutc(utc) + def _mktime(self): + def local(u): + return (datetime(*_t.localtime(u)[:6]) - epoch)._us // 1_000_000 + + epoch = datetime.EPOCH.replace(tzinfo=None) + t, us = divmod((self - epoch)._us, 1_000_000) + ts = None + + a = local(t) - t + u1 = t - a + t1 = local(u1) + if t1 == t: + u2 = u1 + (86400 if self.fold else -86400) + b = local(u2) - u2 + if a == b: + ts = u1 + else: + b = t1 - u1 + if ts is None: + u2 = t - b + t2 = local(u2) + if t2 == t: + ts = u2 + elif t1 == t: + ts = u1 + elif self.fold: + ts = min(u1, u2) + else: + ts = max(u1, u2) + return ts + us / 1_000_000 + def utcoffset(self): return None if self._tz is None else self._tz.utcoffset(self) @@ -828,10 +854,10 @@ def tzname(self): def timetuple(self): if self._tz is None: - conv = _tmod.gmtime + conv = _t.gmtime epoch = datetime.EPOCH.replace(tzinfo=None) else: - conv = _tmod.localtime + conv = _t.localtime epoch = datetime.EPOCH return conv(round((self - epoch).total_seconds())) @@ -840,7 +866,7 @@ def toordinal(self): def timestamp(self): if self._tz is None: - raise NotImplementedError + return self._mktime() else: return (self - datetime.EPOCH).total_seconds() @@ -874,4 +900,4 @@ def tuple(self): return d + t + (self._tz, self._fd) -datetime.EPOCH = datetime(*_tmod.gmtime(0)[:6], tzinfo=timezone.utc) +datetime.EPOCH = datetime(*_t.gmtime(0)[:6], tzinfo=timezone.utc) diff --git a/python-stdlib/datetime/localtz.patch b/python-stdlib/datetime/localtz.patch deleted file mode 100644 index 7a2449d5d..000000000 --- a/python-stdlib/datetime/localtz.patch +++ /dev/null @@ -1,84 +0,0 @@ -localtz.patch - -The CPython's implementation of `datetime.fromtimestamp()`, -`datetime.astimezone()` and `datetime.timestamp()` for naive datetime objects -relay on proper management of DST (daylight saving time) by `time.localtime()` -for the timezone of interest. In the Unix port of MicroPython, this is -accomplished by properly setting the TZ environment variable, e.g. -`os.putenv("TZ", "Europe/Rome")`. - -Because real boards often lack a supportive `time.localtime()`, the source code -in `datetime.py` has been removed as to save precious resources. If your board -provide a proper implementation, you can restore the support to naive datetime -objects by applying this patch, e.g. `patch -p1 < localtz.patch`. - ---- a/datetime.py -+++ b/datetime.py -@@ -635,7 +635,10 @@ class datetime: - else: - us = 0 - if tz is None: -- raise NotImplementedError -+ dt = cls(*_tmod.localtime(ts)[:6], microsecond=us, tzinfo=tz) -+ s = (dt - datetime(*_tmod.localtime(ts - 86400)[:6]))._us // 1_000_000 - 86400 -+ if s < 0 and dt == datetime(*_tmod.localtime(ts + s)[:6]): -+ dt._fd = 1 - else: - dt = cls(*_tmod.gmtime(ts)[:6], microsecond=us, tzinfo=tz) - dt = tz.fromutc(dt) -@@ -812,13 +815,45 @@ class datetime: - return self - _tz = self._tz - if _tz is None: -- raise NotImplementedError -+ ts = int(self._mktime()) -+ os = datetime(*_tmod.localtime(ts)[:6]) - datetime(*_tmod.gmtime(ts)[:6]) - else: - os = _tz.utcoffset(self) - utc = self - os - utc = utc.replace(tzinfo=tz) - return tz.fromutc(utc) - -+ def _mktime(self): -+ def local(u): -+ return (datetime(*_tmod.localtime(u)[:6]) - epoch)._us // 1_000_000 -+ -+ epoch = datetime.EPOCH.replace(tzinfo=None) -+ t, us = divmod((self - epoch)._us, 1_000_000) -+ ts = None -+ -+ a = local(t) - t -+ u1 = t - a -+ t1 = local(u1) -+ if t1 == t: -+ u2 = u1 + (86400 if self.fold else -86400) -+ b = local(u2) - u2 -+ if a == b: -+ ts = u1 -+ else: -+ b = t1 - u1 -+ if ts is None: -+ u2 = t - b -+ t2 = local(u2) -+ if t2 == t: -+ ts = u2 -+ elif t1 == t: -+ ts = u1 -+ elif self.fold: -+ ts = min(u1, u2) -+ else: -+ ts = max(u1, u2) -+ return ts + us / 1_000_000 -+ - def utcoffset(self): - return None if self._tz is None else self._tz.utcoffset(self) - -@@ -842,7 +877,7 @@ class datetime: - - def timestamp(self): - if self._tz is None: -- raise NotImplementedError -+ return self._mktime() - else: - return (self - datetime.EPOCH).total_seconds() - diff --git a/python-stdlib/datetime/manifest.py b/python-stdlib/datetime/manifest.py index 017189cec..d4adce9cd 100644 --- a/python-stdlib/datetime/manifest.py +++ b/python-stdlib/datetime/manifest.py @@ -1,4 +1,4 @@ -metadata(version="4.0.0") +metadata(version="4.1.0") # Originally written by Lorenzo Cappelletti. diff --git a/python-stdlib/datetime/test_datetime.py b/python-stdlib/datetime/test_datetime.py index 98da458f9..999bfaca9 100644 --- a/python-stdlib/datetime/test_datetime.py +++ b/python-stdlib/datetime/test_datetime.py @@ -68,14 +68,6 @@ import unittest -# See localtz.patch -try: - datetime.fromtimestamp(0) - LOCALTZ = True -except NotImplementedError: - LOCALTZ = False - - if hasattr(datetime, "EPOCH"): EPOCH = datetime.EPOCH else: @@ -1619,11 +1611,8 @@ def test___init__24(self): def test_fromtimestamp00(self): with LocalTz("Europe/Rome"): ts = 1012499103.001234 - if LOCALTZ: - dt = datetime.fromtimestamp(ts) - self.assertEqual(dt, d1t1) - else: - self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts) + dt = datetime.fromtimestamp(ts) + self.assertEqual(dt, d1t1) def test_fromtimestamp01(self): ts = 1012506303.001234 @@ -1642,48 +1631,35 @@ def test_fromtimestamp04(self): dt = datetime(2010, 10, 31, 0, 30, tzinfo=timezone.utc) ts = (dt - EPOCH).total_seconds() dt = dt.replace(tzinfo=None) + 2 * td1h - if LOCALTZ: - ds = datetime.fromtimestamp(ts) - self.assertEqual(ds, dt) - self.assertFalse(ds.fold) - else: - self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts) + ds = datetime.fromtimestamp(ts) + self.assertEqual(ds, dt) + self.assertFalse(ds.fold) def test_fromtimestamp05(self): with LocalTz("Europe/Rome"): dt = datetime(2010, 10, 31, 1, 30, tzinfo=timezone.utc) ts = (dt - EPOCH).total_seconds() dt = dt.replace(tzinfo=None) + 1 * td1h - if LOCALTZ: - ds = datetime.fromtimestamp(ts) - self.assertEqual(ds, dt) - self.assertTrue(ds.fold) - else: - self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts) + ds = datetime.fromtimestamp(ts) + self.assertEqual(ds, dt) + self.assertTrue(ds.fold) def test_fromtimestamp06(self): with LocalTz("US/Eastern"): dt = datetime(2020, 11, 1, 5, 30, tzinfo=timezone.utc) ts = (dt - EPOCH).total_seconds() dt = dt.replace(tzinfo=None) - 4 * td1h - if LOCALTZ: - ds = datetime.fromtimestamp(ts) - self.assertEqual(ds, dt) - else: - self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts) + ds = datetime.fromtimestamp(ts) + self.assertEqual(ds, dt) def test_fromtimestamp07(self): with LocalTz("US/Eastern"): dt = datetime(2020, 11, 1, 7, 30, tzinfo=timezone.utc) ts = (dt - EPOCH).total_seconds() dt = dt.replace(tzinfo=None) - 5 * td1h - if LOCALTZ: - ds = datetime.fromtimestamp(ts) - self.assertEqual(ds, dt) - else: - self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts) + ds = datetime.fromtimestamp(ts) + self.assertEqual(ds, dt) - @unittest.skipIf(not LOCALTZ, "naive datetime not supported") def test_now00(self): tm = datetime(*mod_time.localtime()[:6]) dt = datetime.now() @@ -2004,46 +1980,31 @@ def test_astimezone04(self): with LocalTz("Europe/Rome"): dt1 = dt27tz2 dt2 = dt1.replace(tzinfo=None) - if LOCALTZ: - self.assertEqual(dt1, dt2.astimezone(tz2)) - else: - self.assertRaises(NotImplementedError, dt2.astimezone, tz2) + self.assertEqual(dt1, dt2.astimezone(tz2)) def test_astimezone05(self): with LocalTz("Europe/Rome"): dt1 = dt28tz2 dt2 = dt1.replace(tzinfo=None) - if LOCALTZ: - self.assertEqual(dt1, dt2.astimezone(tz2)) - else: - self.assertRaises(NotImplementedError, dt2.astimezone, tz2) + self.assertEqual(dt1, dt2.astimezone(tz2)) def test_astimezone06(self): with LocalTz("Europe/Rome"): dt1 = dt30tz2 dt2 = dt1.replace(tzinfo=None) - if LOCALTZ: - self.assertEqual(dt1, dt2.astimezone(tz2)) - else: - self.assertRaises(NotImplementedError, dt2.astimezone, tz2) + self.assertEqual(dt1, dt2.astimezone(tz2)) def test_astimezone07(self): with LocalTz("Europe/Rome"): dt1 = dt31tz2 dt2 = dt1.replace(tzinfo=None) - if LOCALTZ: - self.assertEqual(dt1, dt2.astimezone(tz2)) - else: - self.assertRaises(NotImplementedError, dt2.astimezone, tz2) + self.assertEqual(dt1, dt2.astimezone(tz2)) def test_astimezone08(self): with LocalTz("Europe/Rome"): dt1 = dt3 dt2 = dt1.replace(tzinfo=None) - if LOCALTZ: - self.assertEqual(dt1, dt2.astimezone(tz2)) - else: - self.assertRaises(NotImplementedError, dt2.astimezone, tz2) + self.assertEqual(dt1, dt2.astimezone(tz2)) def test_utcoffset00(self): self.assertEqual(dt1.utcoffset(), None) @@ -2123,77 +2084,50 @@ def test_weekday00(self): def test_timestamp00(self): with LocalTz("Europe/Rome"): - if LOCALTZ: - self.assertEqual(d1t1.timestamp(), 1012499103.001234) - else: - self.assertRaises(NotImplementedError, d1t1.timestamp) + self.assertEqual(d1t1.timestamp(), 1012499103.001234) def test_timestamp01(self): self.assertEqual(d1t1z.timestamp(), 1012506303.001234) def test_timestamp02(self): with LocalTz("Europe/Rome"): - dt = datetime(2010, 3, 28, 2, 30) # doens't exist - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1269739800.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + dt = datetime(2010, 3, 28, 2, 30) # doesn't exist + self.assertEqual(dt.timestamp(), 1269739800.0) def test_timestamp03(self): with LocalTz("Europe/Rome"): dt = datetime(2010, 8, 10, 2, 30) - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1281400200.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + self.assertEqual(dt.timestamp(), 1281400200.0) def test_timestamp04(self): with LocalTz("Europe/Rome"): dt = datetime(2010, 10, 31, 2, 30, fold=0) - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1288485000.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + self.assertEqual(dt.timestamp(), 1288485000.0) def test_timestamp05(self): with LocalTz("Europe/Rome"): dt = datetime(2010, 10, 31, 2, 30, fold=1) - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1288488600.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + self.assertEqual(dt.timestamp(), 1288488600.0) def test_timestamp06(self): with LocalTz("US/Eastern"): - dt = datetime(2020, 3, 8, 2, 30) # doens't exist - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1583652600.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + dt = datetime(2020, 3, 8, 2, 30) # doesn't exist + self.assertEqual(dt.timestamp(), 1583652600.0) def test_timestamp07(self): with LocalTz("US/Eastern"): dt = datetime(2020, 8, 10, 2, 30) - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1597041000.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + self.assertEqual(dt.timestamp(), 1597041000.0) def test_timestamp08(self): with LocalTz("US/Eastern"): dt = datetime(2020, 11, 1, 2, 30, fold=0) - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1604215800.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + self.assertEqual(dt.timestamp(), 1604215800.0) def test_timestamp09(self): with LocalTz("US/Eastern"): dt = datetime(2020, 11, 1, 2, 30, fold=1) - if LOCALTZ: - self.assertEqual(dt.timestamp(), 1604215800.0) - else: - self.assertRaises(NotImplementedError, dt.timestamp) + self.assertEqual(dt.timestamp(), 1604215800.0) def test_isoweekday00(self): self.assertEqual(dt1.isoweekday(), d1.isoweekday()) diff --git a/python-stdlib/fnmatch/test_fnmatch.py b/python-stdlib/fnmatch/test_fnmatch.py index 97ef8fff7..d7f543336 100644 --- a/python-stdlib/fnmatch/test_fnmatch.py +++ b/python-stdlib/fnmatch/test_fnmatch.py @@ -58,8 +58,8 @@ def test_fnmatchcase(self): @unittest.skip("unsupported on MicroPython") def test_bytes(self): - self.check_match(b"test", b"te*") - self.check_match(b"test\xff", b"te*\xff") + self.check_match(b"test", b"te*") # codespell:ignore + self.check_match(b"test\xff", b"te*\xff") # codespell:ignore self.check_match(b"foo\nbar", b"foo*") diff --git a/python-stdlib/heapq/heapq.py b/python-stdlib/heapq/heapq.py index b11853b8d..792497e68 100644 --- a/python-stdlib/heapq/heapq.py +++ b/python-stdlib/heapq/heapq.py @@ -1,4 +1,5 @@ -"""Heap queue algorithm (a.k.a. priority queue). +""" +Heap queue algorithm (a.k.a. priority queue). Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for all k, counting elements from 0. For the sake of comparison, @@ -7,13 +8,13 @@ Usage: -heap = [] # creates an empty heap -heappush(heap, item) # pushes a new item on the heap -item = heappop(heap) # pops the smallest item from the heap -item = heap[0] # smallest item on the heap without popping it -heapify(x) # transforms list into a heap, in-place, in linear time -item = heapreplace(heap, item) # pops and returns smallest item, and adds - # new item; the heap size is unchanged + heap = [] # creates an empty heap + heappush(heap, item) # pushes a new item on the heap + item = heappop(heap) # pops the smallest item from the heap + item = heap[0] # smallest item on the heap without popping it + heapify(x) # transforms list into a heap, in-place, in linear time + item = heapreplace(heap, item) # pops and returns smallest item, and adds + # new item; the heap size is unchanged Our API differs from textbook heap algorithms as follows: @@ -40,7 +41,7 @@ property of a heap is that a[0] is always its smallest element. The strange invariant above is meant to be an efficient memory -representation for a tournament. The numbers below are `k', not a[k]: +representation for a tournament. The numbers below are 'k', not a[k]:: 0 @@ -53,7 +54,7 @@ 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 -In the tree above, each cell `k' is topping `2*k+1' and `2*k+2'. In +In the tree above, each cell 'k' is topping '2*k+1' and '2*k+2'. In an usual binary tournament we see in sports, each cell is the winner over the two cells it tops, and we can trace the winner down the tree to see all opponents s/he had. However, in many computer applications @@ -108,7 +109,7 @@ effective! In a word, heaps are useful memory structures to know. I use them in -a few applications, and I think it is good to keep a `heap' module +a few applications, and I think it is good to keep a `heap` module around. :-) -------------------- @@ -377,7 +378,7 @@ def _siftup_max(heap, pos): def merge(*iterables): """Merge multiple sorted inputs into a single sorted output. - Similar to sorted(itertools.chain(*iterables)) but returns a generator, + Similar to `sorted(itertools.chain(*iterables))` but returns a generator, does not pull the data into memory all at once, and assumes that each of the input streams is already sorted (smallest to largest). diff --git a/python-stdlib/inspect/inspect.py b/python-stdlib/inspect/inspect.py index c16c6b3e3..f6b7d3c89 100644 --- a/python-stdlib/inspect/inspect.py +++ b/python-stdlib/inspect/inspect.py @@ -1,7 +1,10 @@ import sys +# Generator function. _g = lambda: (yield) +# Closure type. +_ct = type((lambda x: (lambda: x))(None)) def getmembers(obj, pred=None): res = [] @@ -80,3 +83,67 @@ def currentframe(): def getframeinfo(frame, context=1): return ("", -1, "", [""], 0) + + +class Signature: + pass + + +# This `signature()` function is very limited. It's main purpose is to work out +# the arity of the given function, ie the number of arguments it takes. +# +# The return value is an instance of `Signature` with a `parameters` member which +# is an OrderedDict whose length is the number of arguments of `f`. +def signature(f): + import collections + import uctypes + + s = Signature() + s.parameters = collections.OrderedDict() + + t = type(f) + if t is type(globals): + # A zero-parameter built-in. + num_args = 0 + elif t is type(abs): + # A one-parameter built-in. + num_args = 1 + elif t is type(hasattr): + # A two-parameter built-in. + num_args = 2 + elif t is type(setattr): + # A three-parameter built-in. + num_args = 3 + elif t is type(signature) or t is type(_g) or t is _ct: + # A bytecode function, work out the number of arguments by inspecting the bytecode data. + fun_ptr = id(f) + num_closed_over = 0 + if t is _ct: + # A closure, the function is the second word. + clo_ptr = uctypes.struct(fun_ptr, (uctypes.ARRAY | 0, uctypes.LONG | 3)) + fun_ptr = clo_ptr[1] + num_closed_over = clo_ptr[2] + fun_obj = uctypes.struct(fun_ptr, (uctypes.ARRAY | 0, uctypes.LONG | 4)) + bytecode = uctypes.bytearray_at(fun_obj[3], 8) + # See py/bc.h:MP_BC_PRELUDE_SIG_DECODE_INTO macro. + i = 0 + z = bytecode[i] + i += 1 + A = z & 0x3 + K = 0 + n = 0 + while z & 0x80: + z = bytecode[i] + i += 1 + A |= (z & 0x4) << n + K |= ((z & 0x08) >> 3) << n + num_args = A + K - num_closed_over + else: + raise NotImplementedError("unsupported function type") + + # Add dummy arguments to the OrderedDict. + for i in range(num_args): + a = "x{}".format(i) + s.parameters[a] = a + + return s diff --git a/python-stdlib/inspect/manifest.py b/python-stdlib/inspect/manifest.py index e99e659f2..03ede9f0a 100644 --- a/python-stdlib/inspect/manifest.py +++ b/python-stdlib/inspect/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.3") +metadata(version="0.2.1") module("inspect.py") diff --git a/python-stdlib/inspect/test_inspect.py b/python-stdlib/inspect/test_inspect.py index 29ed80f11..31650040b 100644 --- a/python-stdlib/inspect/test_inspect.py +++ b/python-stdlib/inspect/test_inspect.py @@ -1,3 +1,4 @@ +import collections import inspect import unittest @@ -10,6 +11,22 @@ def gen(): yield 1 +def make_closure(): + a = 1 + b = 2 + def closure(x): + return a + b + x + return closure + + +def make_gen_closure(): + a = 1 + b = 2 + def gen_closure(x): + yield a + b + x + return gen_closure + + class Class: def meth(self): pass @@ -58,3 +75,18 @@ def test_isclass(self): def test_ismodule(self): self._test_is_helper(inspect.ismodule, entities[6]) + + def test_signature(self): + self.assertEqual(inspect.signature(globals).parameters, collections.OrderedDict()) + self.assertEqual(len(inspect.signature(abs).parameters), 1) + self.assertEqual(len(inspect.signature(hasattr).parameters), 2) + self.assertEqual(len(inspect.signature(setattr).parameters), 3) + self.assertEqual(len(inspect.signature(lambda: 0).parameters), 0) + self.assertEqual(len(inspect.signature(lambda x: 0).parameters), 1) + self.assertEqual(len(inspect.signature(lambda *, x: 0).parameters), 1) + self.assertEqual(len(inspect.signature(lambda x, y: 0).parameters), 2) + self.assertEqual(len(inspect.signature(lambda x, y, z: 0).parameters), 3) + self.assertEqual(len(inspect.signature(lambda x, y, *, z: 0).parameters), 3) + self.assertEqual(len(inspect.signature(gen).parameters), 0) + self.assertEqual(len(inspect.signature(make_closure()).parameters), 1) + self.assertEqual(len(inspect.signature(make_gen_closure()).parameters), 1) diff --git a/python-stdlib/pathlib/tests/test_pathlib.py b/python-stdlib/pathlib/tests/test_pathlib.py index e632e1242..31e762eea 100644 --- a/python-stdlib/pathlib/tests/test_pathlib.py +++ b/python-stdlib/pathlib/tests/test_pathlib.py @@ -234,7 +234,7 @@ def test_touch(self): self.assertExists(target) # Technically should be FileExistsError, - # but thats not builtin to micropython + # but that's not builtin to micropython with self.assertRaises(OSError): path.touch(exist_ok=False) diff --git a/python-stdlib/tarfile/manifest.py b/python-stdlib/tarfile/manifest.py index 9940bb051..c379837c7 100644 --- a/python-stdlib/tarfile/manifest.py +++ b/python-stdlib/tarfile/manifest.py @@ -1,4 +1,4 @@ -metadata(description="Read-only implementation of Python's tarfile.", version="0.4.1") +metadata(description="Read-only implementation of Python's tarfile.", version="0.4.2") # Originally written by Paul Sokolovsky. diff --git a/python-stdlib/tarfile/tarfile/__init__.py b/python-stdlib/tarfile/tarfile/__init__.py index 4bb95af30..00b271137 100644 --- a/python-stdlib/tarfile/tarfile/__init__.py +++ b/python-stdlib/tarfile/tarfile/__init__.py @@ -55,9 +55,12 @@ def skip(self): if sz: buf = bytearray(16) while sz: - s = min(sz, 16) - self.f.readinto(buf, s) - sz -= s + if sz >= 16: + self.f.readinto(buf) + sz -= 16 + else: + self.f.read(sz) + sz = 0 class TarInfo: diff --git a/python-stdlib/tarfile/test.tar b/python-stdlib/tarfile/test.tar new file mode 100644 index 000000000..7fa0604d7 Binary files /dev/null and b/python-stdlib/tarfile/test.tar differ diff --git a/python-stdlib/tarfile/test_tarfile.py b/python-stdlib/tarfile/test_tarfile.py new file mode 100644 index 000000000..7acd61af7 --- /dev/null +++ b/python-stdlib/tarfile/test_tarfile.py @@ -0,0 +1,45 @@ +import tarfile +import unittest + + +test_tar_contents = ( + ("a", "file", 2), + ("b", "file", 2), + ("dir/", "dir", 0), + ("dir/c", "file", 2), + ("dir/d", "file", 2), + ("tar.tar", "file", 10240), +) + +test_sub_tar_contents = ( + ("e", "file", 2), + ("f", "file", 2), +) + + +class TestTarFile(unittest.TestCase): + def check_contents(self, expected, tf): + for i, file in enumerate(tf): + name, type, size = expected[i] + self.assertEqual(file.name, name) + self.assertEqual(file.type, type) + self.assertEqual(file.size, size) + + def test_iter(self): + tf = tarfile.TarFile("test.tar") + for _ in range(6): + self.assertIsInstance(next(tf), tarfile.TarInfo) + with self.assertRaises(StopIteration): + next(tf) + + def test_contents(self): + tf = tarfile.TarFile("test.tar") + self.check_contents(test_tar_contents, tf) + + def test_nested_tar(self): + tf = tarfile.TarFile("test.tar") + for file in tf: + if file.name == "tar.tar": + subf = tf.extractfile(file) + subtf = tarfile.TarFile(fileobj=subf) + self.check_contents(test_sub_tar_contents, subtf) diff --git a/python-stdlib/textwrap/textwrap.py b/python-stdlib/textwrap/textwrap.py index 4e9f35069..c5771bec6 100644 --- a/python-stdlib/textwrap/textwrap.py +++ b/python-stdlib/textwrap/textwrap.py @@ -1,4 +1,5 @@ -"""Text wrapping and filling. +""" +Text wrapping and filling. """ # Copyright (C) 1999-2001 Gregory P. Ward. @@ -169,7 +170,8 @@ def _split(self, text): return chunks def _fix_sentence_endings(self, chunks): - """_fix_sentence_endings(chunks : [string]) + """ + _fix_sentence_endings(chunks : [string]) Correct for sentence endings buried in 'chunks'. Eg. when the original text contains "... foo.\nBar ...", munge_whitespace() diff --git a/python-stdlib/time/README.md b/python-stdlib/time/README.md index f07517305..f3d880ad7 100644 --- a/python-stdlib/time/README.md +++ b/python-stdlib/time/README.md @@ -19,7 +19,7 @@ See [Package management](https://docs.micropython.org/en/latest/reference/packag ## Common uses -`strftime()` is used when using a loggging [Formatter +`strftime()` is used when using a logging [Formatter Object](https://docs.python.org/3/library/logging.html#formatter-objects) that employs [`asctime`](https://docs.python.org/3/library/logging.html#formatter-objects). diff --git a/python-stdlib/unittest/examples/example_subtest.py b/python-stdlib/unittest/examples/example_subtest.py index 558af0b26..40b87d45b 100644 --- a/python-stdlib/unittest/examples/example_subtest.py +++ b/python-stdlib/unittest/examples/example_subtest.py @@ -99,7 +99,7 @@ def test_sorted(self) -> None: self.assertEqual(sorted(test["unsorted"]), test["sorted"]) def test_factorial(self) -> None: - """Test that the factorial fuction correctly calculates factorials + """Test that the factorial function correctly calculates factorials Makes use of `msg` argument in subtest method to clarify which subtests had an error in the results @@ -190,7 +190,7 @@ def test_person(self) -> None: self.assertFalse(bob.has_friend(alice)) # Friendship is not always commutative, so Bob is not implicitly friends with Alice - with self.subTest("Alice and Bob should not both be friends with eachother"): + with self.subTest("Alice and Bob should not both be friends with each other"): with self.assertRaises(AssertionError): self.assertTrue(bob.has_friend(alice) and alice.has_friend(bob)) diff --git a/python-stdlib/unittest/tests/test_assertions.py b/python-stdlib/unittest/tests/test_assertions.py index b191220e6..3b9904086 100644 --- a/python-stdlib/unittest/tests/test_assertions.py +++ b/python-stdlib/unittest/tests/test_assertions.py @@ -92,7 +92,7 @@ def testFalse(self): with self.assertRaises(AssertionError): self.assertFalse(True) - def testIn(self): + def testIn(self): # codespell:ignore self.assertIn("t", "cat") with self.assertRaises(AssertionError): self.assertIn("x", "cat") diff --git a/tools/ci.sh b/tools/ci.sh index 6689e8aa4..abe83b563 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -90,6 +90,7 @@ function ci_package_tests_run { python-stdlib/pathlib \ python-stdlib/quopri \ python-stdlib/shutil \ + python-stdlib/tarfile \ python-stdlib/tempfile \ python-stdlib/time \ python-stdlib/unittest/tests \ diff --git a/tools/ignore_words.txt b/tools/ignore_words.txt new file mode 100644 index 000000000..5286cbed5 --- /dev/null +++ b/tools/ignore_words.txt @@ -0,0 +1,12 @@ +# non-words to be ignored by codespell +# shared with micropython/micropython +asend +ure + +# Specific to micropython/micropython-lib +bu +curch +ist +clen +shttp +ody diff --git a/tools/uncrustify.cfg b/tools/uncrustify.cfg index 80542b903..7a0ff0d46 100644 --- a/tools/uncrustify.cfg +++ b/tools/uncrustify.cfg @@ -1323,7 +1323,7 @@ indent_using_block = true # true/false # 2: When the `:` is a continuation, indent it under `?` indent_ternary_operator = 0 # unsigned number -# Whether to indent the statments inside ternary operator. +# Whether to indent the statements inside ternary operator. indent_inside_ternary_operator = false # true/false # If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. @@ -1779,7 +1779,7 @@ nl_func_call_args_multi_line = false # true/false # different lines. nl_func_call_end_multi_line = false # true/false -# Whether to respect nl_func_call_XXX option incase of closure args. +# Whether to respect nl_func_call_XXX option in case of closure args. nl_func_call_args_multi_line_ignore_closures = false # true/false # Whether to add a newline after '<' of a template parameter list. @@ -2570,7 +2570,7 @@ align_oc_decl_colon = false # true/false # (OC) Whether to not align parameters in an Objectve-C message call if first # colon is not on next line of the message call (the same way Xcode does -# aligment) +# alignment) align_oc_msg_colon_xcode_like = false # true/false # @@ -2916,28 +2916,28 @@ pp_define_at_level = false # true/false pp_ignore_define_body = false # true/false # Whether to indent case statements between #if, #else, and #endif. -# Only applies to the indent of the preprocesser that the case statements +# Only applies to the indent of the preprocessor that the case statements # directly inside of. # # Default: true pp_indent_case = true # true/false # Whether to indent whole function definitions between #if, #else, and #endif. -# Only applies to the indent of the preprocesser that the function definition +# Only applies to the indent of the preprocessor that the function definition # is directly inside of. # # Default: true pp_indent_func_def = true # true/false # Whether to indent extern C blocks between #if, #else, and #endif. -# Only applies to the indent of the preprocesser that the extern block is +# Only applies to the indent of the preprocessor that the extern block is # directly inside of. # # Default: true pp_indent_extern = true # true/false # Whether to indent braces directly inside #if, #else, and #endif. -# Only applies to the indent of the preprocesser that the braces are directly +# Only applies to the indent of the preprocessor that the braces are directly # inside of. # # Default: true diff --git a/unix-ffi/cgi/cgi.py b/unix-ffi/cgi/cgi.py index 550f70713..e8e91bfe4 100644 --- a/unix-ffi/cgi/cgi.py +++ b/unix-ffi/cgi/cgi.py @@ -197,7 +197,7 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): # parse query string function called from urlparse, -# this is done in order to maintain backward compatiblity. +# this is done in order to maintain backward compatibility. def parse_qs(qs, keep_blank_values=0, strict_parsing=0): diff --git a/unix-ffi/email.charset/email/charset.py b/unix-ffi/email.charset/email/charset.py index 304cc8fef..9278766c2 100644 --- a/unix-ffi/email.charset/email/charset.py +++ b/unix-ffi/email.charset/email/charset.py @@ -222,11 +222,11 @@ def __init__(self, input_charset=DEFAULT_CHARSET): # We can try to guess which encoding and conversion to use by the # charset_map dictionary. Try that first, but let the user override # it. - henc, benc, conv = CHARSETS.get(self.input_charset, (SHORTEST, BASE64, None)) + henc, benc, conv = CHARSETS.get(self.input_charset, (SHORTEST, BASE64, None)) # codespell:ignore if not conv: conv = self.input_charset # Set the attributes, allowing the arguments to override the default. - self.header_encoding = henc + self.header_encoding = henc # codespell:ignore self.body_encoding = benc self.output_charset = ALIASES.get(conv, conv) # Now set the codecs. If one isn't defined for input_charset, diff --git a/unix-ffi/email.parser/email/parser.py b/unix-ffi/email.parser/email/parser.py index 760adeff5..7053d0734 100644 --- a/unix-ffi/email.parser/email/parser.py +++ b/unix-ffi/email.parser/email/parser.py @@ -23,7 +23,7 @@ def __init__(self, _class=Message, policy=compat32): textual representation of the message. The string must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The + continuation lines, optionally preceded by a `Unix-from' header. The header block is terminated either by the end of the string or by a blank line. @@ -85,7 +85,7 @@ def __init__(self, *args, **kw): textual representation of the message. The input must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The + continuation lines, optionally preceded by a `Unix-from' header. The header block is terminated either by the end of the input or by a blank line. diff --git a/unix-ffi/html.entities/html/entities.py b/unix-ffi/html.entities/html/entities.py index 223af74aa..b5f8717c8 100644 --- a/unix-ffi/html.entities/html/entities.py +++ b/unix-ffi/html.entities/html/entities.py @@ -1,6 +1,7 @@ """HTML character entity references.""" # maps the HTML entity name to the Unicode codepoint +# codespell:ignore-begin name2codepoint = { "AElig": 0x00C6, # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 "Aacute": 0x00C1, # latin capital letter A with acute, U+00C1 ISOlat1 @@ -255,9 +256,10 @@ "zwj": 0x200D, # zero width joiner, U+200D NEW RFC 2070 "zwnj": 0x200C, # zero width non-joiner, U+200C NEW RFC 2070 } - +# codespell:ignore-end # maps the HTML5 named character references to the equivalent Unicode character(s) +# codespell:ignore-begin html5 = { "Aacute": "\xc1", "aacute": "\xe1", @@ -2491,6 +2493,7 @@ "zwj;": "\u200d", "zwnj;": "\u200c", } +# codespell:ignore-end # maps the Unicode codepoint to the HTML entity name codepoint2name = {} diff --git a/unix-ffi/html.parser/html/parser.py b/unix-ffi/html.parser/html/parser.py index 74b39d49a..571b42a34 100644 --- a/unix-ffi/html.parser/html/parser.py +++ b/unix-ffi/html.parser/html/parser.py @@ -445,7 +445,7 @@ def parse_endtag(self, i): tagname = namematch.group().lower() # consume and ignore other stuff between the name and the > # Note: this is not 100% correct, since we might have things like - # , but looking for > after tha name should cover + # , but looking for > after the name should cover # most of the cases and is much simpler gtpos = rawdata.find(">", namematch.end()) self.handle_endtag(tagname) diff --git a/unix-ffi/http.client/http/client.py b/unix-ffi/http.client/http/client.py index 856848283..3556f1ca8 100644 --- a/unix-ffi/http.client/http/client.py +++ b/unix-ffi/http.client/http/client.py @@ -1038,10 +1038,10 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): self.putheader("Accept-Encoding", "identity") # we can accept "chunked" Transfer-Encodings, but no others - # NOTE: no TE header implies *only* "chunked" + # NOTE: no 'TE' header implies *only* "chunked" # self.putheader('TE', 'chunked') - # if TE is supplied in the header, then it must appear in a + # if 'TE' is supplied in the header, then it must appear in a # Connection header. # self.putheader('Connection', 'TE') @@ -1093,7 +1093,7 @@ def _set_content_length(self, body): thelen = None try: thelen = str(len(body)) - except TypeError as te: + except TypeError as te: # codespell:ignore # If this is a file-like object, try to # fstat its file descriptor try: diff --git a/unix-ffi/socket/manifest.py b/unix-ffi/socket/manifest.py index a0a384d37..9cb582a84 100644 --- a/unix-ffi/socket/manifest.py +++ b/unix-ffi/socket/manifest.py @@ -1,4 +1,4 @@ -metadata(version="0.5.2") +metadata(version="0.5.3") # Originally written by Paul Sokolovsky. diff --git a/unix-ffi/socket/socket.py b/unix-ffi/socket/socket.py index 1b47f9840..16d661b0c 100644 --- a/unix-ffi/socket/socket.py +++ b/unix-ffi/socket/socket.py @@ -4,8 +4,6 @@ _GLOBAL_DEFAULT_TIMEOUT = 30 IPPROTO_IP = 0 -IP_ADD_MEMBERSHIP = 35 -IP_DROP_MEMBERSHIP = 36 INADDR_ANY = 0 error = OSError diff --git a/unix-ffi/ucurses/ucurses/__init__.py b/unix-ffi/ucurses/ucurses/__init__.py index 688df745c..2e3af32d6 100644 --- a/unix-ffi/ucurses/ucurses/__init__.py +++ b/unix-ffi/ucurses/ucurses/__init__.py @@ -311,7 +311,7 @@ def meta(yes): def mousemask(mask): - # Mouse reporting - X10 compatbility mode + # Mouse reporting - X10 compatibility mode _wr(b"\x1b[?9h")