Skip to content

change to ruff #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 15, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

.py text eol=lf
.rst text eol=lf
.txt text eol=lf
.yaml text eol=lf
.toml text eol=lf
.license text eol=lf
.md text eol=lf
43 changes: 11 additions & 32 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

repos:
- repo: https://github.com/python/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v1.1.2
hooks:
- id: reuse
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: v2.17.4
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.4
hooks:
- id: pylint
name: pylint (library code)
types: [python]
args:
- --disable=consider-using-f-string
exclude: "^(docs/|examples/|tests/|setup.py$)"
- id: pylint
name: pylint (example code)
description: Run pylint rules on "examples/*.py" files
types: [python]
files: "^examples/"
args:
- --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- id: pylint
name: pylint (test code)
description: Run pylint rules on "tests/*.py" files
types: [python]
files: "^tests/"
args:
- --disable=missing-docstring,consider-using-f-string,duplicate-code
- id: ruff-format
- id: ruff
args: ["--fix"]
- repo: https://github.com/fsfe/reuse-tool
rev: v3.0.1
hooks:
- id: reuse
399 changes: 0 additions & 399 deletions .pylintrc

This file was deleted.

6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
@@ -14,9 +14,9 @@ Introduction
:target: https://github.com/adafruit/Adafruit_CircuitPython_AVRprog/actions/
:alt: Build Status

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code Style: Black
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Code Style: Ruff

Program your favorite AVR chips directly from CircuitPython with this handy helper class that will let you make stand-alone programmers right from your REPL. Should work with any/all AVR chips, via SPI programming. Tested with ATmega328, ATtiny85 and ATmega2560

53 changes: 15 additions & 38 deletions adafruit_avrprog.py
Original file line number Diff line number Diff line change
@@ -33,18 +33,17 @@
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AVRprog.git"

try:
from typing import List, Optional, Tuple, Union, TypedDict
from typing_extensions import TypeAlias
from os import PathLike
from typing import List, Optional, Tuple, TypedDict, Union

from busio import SPI
from microcontroller import Pin
from typing_extensions import TypeAlias

# Technically this type should come from: from _typeshed import FileDescriptorOrPath
# Unfortunately _typeshed is only in the standard library in newer releases of Python, e.g. 3.11
# Thus have to define a placeholder
FileDescriptorOrPath: TypeAlias = Union[
int, str, bytes, PathLike[str], PathLike[bytes]
]
FileDescriptorOrPath: TypeAlias = Union[int, str, bytes, PathLike[str], PathLike[bytes]]

from io import TextIOWrapper

@@ -64,7 +63,6 @@ class FileState(TypedDict):
Dictionary representing a File State
"""

# pylint: disable=invalid-name
line: int
ext_addr: int
eof: bool
@@ -90,7 +88,6 @@ class Boards:
Some well known board definitions.
"""

# pylint: disable=too-few-public-methods
ATtiny13a = {
"name": "ATtiny13a",
"sig": [0x1E, 0x90, 0x07],
@@ -158,12 +155,11 @@ def verify_sig(self, chip: ChipDictionary, verbose: bool = False) -> bool:
sig = self.read_signature()
self.end()
if verbose:
print("Found signature: %s" % [hex(i) for i in sig])
print(f"Found signature: {[hex(i) for i in sig]}")
if sig != chip["sig"]:
return False
return True

# pylint: disable=too-many-branches
def program_file(
self,
chip: ChipDictionary,
@@ -188,12 +184,12 @@ def program_file(

# create a file state dictionary
file_state = {"line": 0, "ext_addr": 0, "eof": False, "f": None}
with open(file_name, "r") as file_state["f"]:
with open(file_name) as file_state["f"]:
page_size = chip["page_size"]

for page_addr in range(0, chip["flash_size"], page_size):
if verbose:
print("Programming page $%04X..." % page_addr, end="")
print(f"Programming page ${page_addr:04X}...", end="")
page_buffer = bytearray(page_size)
for b in range(page_size):
page_buffer[b] = 0xFF # make an empty page
@@ -214,19 +210,16 @@ def program_file(
continue

if verbose:
print("Verifying page @ $%04X" % page_addr)
print(f"Verifying page @ ${page_addr:04X}")
read_buffer = bytearray(page_size)
self.read(page_addr, read_buffer)
# print("From memory: ", read_buffer)

if page_buffer != read_buffer:
if verbose:
# pylint: disable=line-too-long
print(
"Verify fail at address %04X\nPage should be: %s\nBut contains: %s"
% (page_addr, page_buffer, read_buffer)
f"Verify fail at address {page_addr:04X}\nPage should be: {page_buffer}\nBut contains: {read_buffer}"
)
# pylint: enable=line-too-long
self.end()
return False

@@ -251,7 +244,7 @@ def verify_file(

# create a file state dictionary
file_state = {"line": 0, "ext_addr": 0, "eof": False, "f": None}
with open(file_name, "r") as file_state["f"]:
with open(file_name) as file_state["f"]:
page_size = chip["page_size"]
clock_speed = chip.get("clock_speed", _FAST_CLOCK)
self.begin(clock=clock_speed)
@@ -263,20 +256,17 @@ def verify_file(
read_hex_page(file_state, page_addr, page_size, page_buffer)

if verbose:
print("Verifying page @ $%04X" % page_addr)
print(f"Verifying page @ ${page_addr:04X}")
read_buffer = bytearray(page_size)
self.read(page_addr, read_buffer)
# print("From memory: ", read_buffer)
# print("From file : ", page_buffer)

if page_buffer != read_buffer:
if verbose:
# pylint: disable=line-too-long
print(
"Verify fail at address %04X\nPage should be: %s\nBut contains: %s"
% (page_addr, page_buffer, read_buffer)
f"Verify fail at address {page_addr:04X}\nPage should be: {page_buffer}\nBut contains: {read_buffer}"
)
# pylint: enable=line-too-long
self.end()
return False

@@ -300,7 +290,6 @@ def read_fuses(self, chip: ChipDictionary) -> Tuple[int, int, int, int]:
self.end()
return (low, high, ext, lock)

# pylint: disable=unused-argument,too-many-arguments
def write_fuses(
self,
chip: ChipDictionary,
@@ -322,7 +311,6 @@ def write_fuses(
self._busy_wait()
self.end()

# pylint: disable=too-many-arguments
def verify_fuses(
self,
chip: ChipDictionary,
@@ -413,9 +401,7 @@ def _flash_word(self, addr: int, low: int, high: int) -> None:
self._transaction((0x40, addr >> 8, addr, low))
self._transaction((0x48, addr >> 8, addr, high))

def _flash_page(
self, page_buffer: bytearray, page_addr: int, page_size: int
) -> None:
def _flash_page(self, page_buffer: bytearray, page_addr: int, page_size: int) -> None:
page_addr //= 2 # address is by 'words' not bytes!
for i in range(page_size // 2): # page indexed by words, not bytes
lo_byte, hi_byte = page_buffer[2 * i : 2 * i + 2]
@@ -448,7 +434,6 @@ def _busy_wait(self) -> None:
def read_hex_page(
file_state: FileState, page_addr: int, page_size: int, page_buffer: bytearray
) -> bool:
# pylint: disable=too-many-branches
"""
Helper function that does the Intel Hex parsing. Takes in a dictionary
that contains the file 'state'. The dictionary should have file_state['f']
@@ -486,9 +471,7 @@ def read_hex_page(
file_state["line_addr"] = line_addr
rec_type = int(line[7:9], 16)
except ValueError as err:
raise RuntimeError(
"Could not parse HEX line %d addr" % file_state["line"]
) from err
raise RuntimeError("Could not parse HEX line %d addr" % file_state["line"]) from err

if file_state["ext_addr"]:
line_addr += file_state["ext_addr"]
@@ -534,13 +517,7 @@ def read_hex_page(
byte_buffer.append(int(line[9 + i * 2 : 11 + i * 2], 16))

# check chksum now!
chksum = (
hex_len
+ (line_addr >> 8)
+ (line_addr & 0xFF)
+ rec_type
+ sum(byte_buffer)
)
chksum = hex_len + (line_addr >> 8) + (line_addr & 0xFF) + rec_type + sum(byte_buffer)
# print("checksum: "+hex(chksum))
if (chksum & 0xFF) != 0:
raise RuntimeError("HEX Checksum fail")
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@

.. If you created a package, create one automodule per module in the package.
API Reference
#############

.. automodule:: adafruit_avrprog
:members:
8 changes: 2 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# -*- coding: utf-8 -*-

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import datetime
import os
import sys
import datetime

sys.path.insert(0, os.path.abspath(".."))

@@ -45,9 +43,7 @@
creation_year = "2017"
current_year = str(datetime.datetime.now().year)
year_duration = (
current_year
if current_year == creation_year
else creation_year + " - " + current_year
current_year if current_year == creation_year else creation_year + " - " + current_year
)
copyright = year_duration + " ladyada"
author = "ladyada"
10 changes: 3 additions & 7 deletions examples/avrprog_program_mega2560.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@

import board
import busio

import adafruit_avrprog

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
@@ -54,15 +55,10 @@ def error(err):

avrprog.write_fuses(atmega2560, low=0xFF, high=0xD8, ext=0x05, lock=0x3F)
if not avrprog.verify_fuses(atmega2560, low=0xFF, high=0xD8, ext=0x05, lock=0x3F):
error(
"Failure programming fuses: "
+ str([hex(i) for i in avrprog.read_fuses(atmega2560)])
)
error("Failure programming fuses: " + str([hex(i) for i in avrprog.read_fuses(atmega2560)]))

print("Programming flash from file")
avrprog.program_file(
atmega2560, "stk500boot_v2_mega2560.hex", verbose=True, verify=True
)
avrprog.program_file(atmega2560, "stk500boot_v2_mega2560.hex", verbose=True, verify=True)

avrprog.write_fuses(atmega2560, lock=0x0F)
if not avrprog.verify_fuses(atmega2560, lock=0x0F):
1 change: 1 addition & 0 deletions examples/avrprog_program_tiny13a.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@

import board
import busio

import adafruit_avrprog

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
1 change: 1 addition & 0 deletions examples/avrprog_program_trinket85.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@

import board
import busio

import adafruit_avrprog

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
8 changes: 2 additions & 6 deletions examples/avrprog_program_uno328.py
Original file line number Diff line number Diff line change
@@ -15,16 +15,15 @@
import board
import busio
import pwmio

import adafruit_avrprog

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
avrprog = adafruit_avrprog.AVRprog()
avrprog.init(spi, board.D5)

# pylint: disable-msg=no-member
# we can generate an 6 MHz clock for driving bare chips too!
clock_pwm = pwmio.PWMOut(board.D9, frequency=6000000, duty_cycle=65536 // 2)
# pylint: enable-msg=no-member

# Each chip has to have a definition so the script knows how to find it
atmega328p = avrprog.Boards.ATmega328p
@@ -50,10 +49,7 @@ def error(err):

avrprog.write_fuses(atmega328p, low=0xFF, high=0xDE, ext=0x05, lock=0x3F)
if not avrprog.verify_fuses(atmega328p, low=0xFF, high=0xDE, ext=0x05, lock=0x3F):
error(
"Failure programming fuses: "
+ str([hex(i) for i in avrprog.read_fuses(atmega328p)])
)
error("Failure programming fuses: " + str([hex(i) for i in avrprog.read_fuses(atmega328p)]))

print("Programming flash from file")
avrprog.program_file(atmega328p, "optiboot_atmega328.hex", verbose=True, verify=True)
3 changes: 1 addition & 2 deletions examples/avrprog_read_signature_simpletest.py
Original file line number Diff line number Diff line change
@@ -9,16 +9,15 @@
import board
import busio
import pwmio

import adafruit_avrprog

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
avrprog = adafruit_avrprog.AVRprog()
avrprog.init(spi, board.D5)

# pylint: disable-msg=no-member
# we can generate an 6 MHz clock for driving bare chips too!
clock_pwm = pwmio.PWMOut(board.D9, frequency=6000000, duty_cycle=65536 // 2)
# pylint: enable-msg=no-member

avrprog.begin()
print("Signature bytes: ", [hex(i) for i in avrprog.read_signature()])
108 changes: 108 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT

target-version = "py38"
line-length = 100

[lint]
preview = true
select = ["I", "PL", "UP"]

extend-select = [
"D419", # empty-docstring
"E501", # line-too-long
"W291", # trailing-whitespace
"PLC0414", # useless-import-alias
"PLC2401", # non-ascii-name
"PLC2801", # unnecessary-dunder-call
"PLC3002", # unnecessary-direct-lambda-call
"E999", # syntax-error
"PLE0101", # return-in-init
"F706", # return-outside-function
"F704", # yield-outside-function
"PLE0116", # continue-in-finally
"PLE0117", # nonlocal-without-binding
"PLE0241", # duplicate-bases
"PLE0302", # unexpected-special-method-signature
"PLE0604", # invalid-all-object
"PLE0605", # invalid-all-format
"PLE0643", # potential-index-error
"PLE0704", # misplaced-bare-raise
"PLE1141", # dict-iter-missing-items
"PLE1142", # await-outside-async
"PLE1205", # logging-too-many-args
"PLE1206", # logging-too-few-args
"PLE1307", # bad-string-format-type
"PLE1310", # bad-str-strip-call
"PLE1507", # invalid-envvar-value
"PLE2502", # bidirectional-unicode
"PLE2510", # invalid-character-backspace
"PLE2512", # invalid-character-sub
"PLE2513", # invalid-character-esc
"PLE2514", # invalid-character-nul
"PLE2515", # invalid-character-zero-width-space
"PLR0124", # comparison-with-itself
"PLR0202", # no-classmethod-decorator
"PLR0203", # no-staticmethod-decorator
"UP004", # useless-object-inheritance
"PLR0206", # property-with-parameters
"PLR0904", # too-many-public-methods
"PLR0911", # too-many-return-statements
"PLR0912", # too-many-branches
"PLR0913", # too-many-arguments
"PLR0914", # too-many-locals
"PLR0915", # too-many-statements
"PLR0916", # too-many-boolean-expressions
"PLR1702", # too-many-nested-blocks
"PLR1704", # redefined-argument-from-local
"PLR1711", # useless-return
"C416", # unnecessary-comprehension
"PLR1733", # unnecessary-dict-index-lookup
"PLR1736", # unnecessary-list-index-lookup

# ruff reports this rule is unstable
#"PLR6301", # no-self-use

"PLW0108", # unnecessary-lambda
"PLW0120", # useless-else-on-loop
"PLW0127", # self-assigning-variable
"PLW0129", # assert-on-string-literal
"B033", # duplicate-value
"PLW0131", # named-expr-without-context
"PLW0245", # super-without-brackets
"PLW0406", # import-self
"PLW0602", # global-variable-not-assigned
"PLW0603", # global-statement
"PLW0604", # global-at-module-level

# fails on the try: import typing used by libraries
#"F401", # unused-import

"F841", # unused-variable
"E722", # bare-except
"PLW0711", # binary-op-exception
"PLW1501", # bad-open-mode
"PLW1508", # invalid-envvar-default
"PLW1509", # subprocess-popen-preexec-fn
"PLW2101", # useless-with-lock
"PLW3301", # nested-min-max
]

ignore = [
"PLR2004", # magic-value-comparison
"UP030", # format literals
"PLW1514", # unspecified-encoding
"PLR0913", # too-many-arguments
"PLR0915", # too-many-statements
"PLR0917", # too-many-positional-arguments
"PLR0904", # too-many-public-methods
"PLR0912", # too-many-branches
"PLR0916", # too-many-boolean-expressions
"PLR6301", # could-be-static no-self-use
"PLC0415", # import outside toplevel
"E501", # line too long
]

[format]
line-ending = "lf"