From 4a550109579ca9857e574ad12eca892069827f8c Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 20 May 2024 15:42:10 +0100 Subject: [PATCH 1/7] Repackage to hatch/pyproject.toml. --- library/.coveragerc => .coveragerc | 0 .github/workflows/build.yml | 41 +++ .github/workflows/qa.yml | 39 +++ .github/workflows/test.yml | 24 +- .vscode/settings.json | 5 - library/CHANGELOG.txt => CHANGELOG.md | 0 Makefile | 94 ++++--- README.md | 26 ++ check.sh | 84 ++++++ install-legacy.sh | 256 ------------------ install.sh | 315 ++++++++++++++++------- library/LICENSE.txt | 21 -- library/MANIFEST.in | 5 - library/README.md | 47 ---- library/README.rst | 18 -- library/setup.cfg | 52 ---- library/setup.py | 33 --- library/tox.ini | 21 -- {library/lsm303d => lsm303d}/__init__.py | 0 pyproject.toml | 118 +++++++++ requirements-dev.txt | 9 + library/test.py => test.py | 0 {library/tests => tests}/test_setup.py | 0 {library/tests => tests}/tools.py | 0 tox.ini | 34 +++ uninstall.sh | 77 ++++-- 26 files changed, 689 insertions(+), 630 deletions(-) rename library/.coveragerc => .coveragerc (100%) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/qa.yml delete mode 100644 .vscode/settings.json rename library/CHANGELOG.txt => CHANGELOG.md (100%) create mode 100755 check.sh delete mode 100755 install-legacy.sh delete mode 100644 library/LICENSE.txt delete mode 100644 library/MANIFEST.in delete mode 100644 library/README.md delete mode 100644 library/README.rst delete mode 100644 library/setup.cfg delete mode 100755 library/setup.py delete mode 100644 library/tox.ini rename {library/lsm303d => lsm303d}/__init__.py (100%) create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt rename library/test.py => test.py (100%) rename {library/tests => tests}/test_setup.py (100%) rename {library/tests => tests}/tools.py (100%) create mode 100644 tox.ini diff --git a/library/.coveragerc b/.coveragerc similarity index 100% rename from library/.coveragerc rename to .coveragerc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..07620e3 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: Build + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest + strategy: + matrix: + python: ['3.9', '3.10', '3.11'] + + env: + RELEASE_FILE: ${{ github.event.repository.name }}-${{ github.event.release.tag_name || github.sha }}-py${{ matrix.python }} + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - name: Install Dependencies + run: | + make dev-deps + + - name: Build Packages + run: | + make build + + - name: Upload Packages + uses: actions/upload-artifact@v4 + with: + name: ${{ env.RELEASE_FILE }} + path: dist/ diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml new file mode 100644 index 0000000..ac672a5 --- /dev/null +++ b/.github/workflows/qa.yml @@ -0,0 +1,39 @@ +name: QA + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + name: linting & spelling + runs-on: ubuntu-latest + env: + TERM: xterm-256color + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Python '3,11' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Dependencies + run: | + make dev-deps + + - name: Run Quality Assurance + run: | + make qa + + - name: Run Code Checks + run: | + make check + + - name: Run Bash Code Checks + run: | + make shellcheck diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 213fdea..6f8cff7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,37 +1,41 @@ -name: Python Tests +name: Tests on: pull_request: push: branches: - - master + - main jobs: test: + name: Python ${{ matrix.python }} runs-on: ubuntu-latest strategy: matrix: - python: [2.7, 3.5, 3.7, 3.9] + python: ['3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v2 + - name: Checkout Code + uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} + - name: Install Dependencies run: | - python -m pip install --upgrade setuptools tox + make dev-deps + - name: Run Tests - working-directory: library run: | - tox -e py + make pytest + - name: Coverage + if: ${{ matrix.python == '3.9' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - working-directory: library run: | python -m pip install coveralls coveralls --service=github - if: ${{ matrix.python == '3.9' }} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 9fadd3a..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "python.linting.pylintEnabled": false, - "python.linting.flake8Enabled": true, - "python.linting.enabled": true -} \ No newline at end of file diff --git a/library/CHANGELOG.txt b/CHANGELOG.md similarity index 100% rename from library/CHANGELOG.txt rename to CHANGELOG.md diff --git a/Makefile b/Makefile index 0813324..56cf0df 100644 --- a/Makefile +++ b/Makefile @@ -1,70 +1,66 @@ -LIBRARY_VERSION=$(shell grep version library/setup.cfg | awk -F" = " '{print $$2}') -LIBRARY_NAME=$(shell grep name library/setup.cfg | awk -F" = " '{print $$2}') +LIBRARY_NAME := $(shell hatch project metadata name 2> /dev/null) +LIBRARY_VERSION := $(shell hatch version 2> /dev/null) -.PHONY: usage install uninstall +.PHONY: usage install uninstall check pytest qa build-deps check tag wheel sdist clean dist testdeploy deploy usage: +ifdef LIBRARY_NAME @echo "Library: ${LIBRARY_NAME}" @echo "Version: ${LIBRARY_VERSION}\n" +else + @echo "WARNING: You should 'make dev-deps'\n" +endif @echo "Usage: make , where target is one of:\n" - @echo "install: install the library locally from source" - @echo "uninstall: uninstall the local library" - @echo "check: peform basic integrity checks on the codebase" - @echo "python-readme: generate library/README.md from README.md + library/CHANGELOG.txt" - @echo "python-wheels: build python .whl files for distribution" - @echo "python-sdist: build python source distribution" - @echo "python-clean: clean python build and dist directories" - @echo "python-dist: build all python distribution files" - @echo "python-testdeploy: build all and deploy to test PyPi" - @echo "tag: tag the repository with the current version" + @echo "install: install the library locally from source" + @echo "uninstall: uninstall the local library" + @echo "dev-deps: install Python dev dependencies" + @echo "check: perform basic integrity checks on the codebase" + @echo "qa: run linting and package QA" + @echo "pytest: run Python test fixtures" + @echo "clean: clean Python build and dist directories" + @echo "build: build Python distribution files" + @echo "testdeploy: build and upload to test PyPi" + @echo "deploy: build and upload to PyPi" + @echo "tag: tag the repository with the current version\n" + +version: + @hatch version install: - ./install.sh + ./install.sh --unstable uninstall: ./uninstall.sh -check: - @echo "Checking for trailing whitespace" - @! grep -IUrn --color "[[:blank:]]$$" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO - @echo "Checking for DOS line-endings" - @! grep -lIUrn --color " " --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile - @echo "Checking library/CHANGELOG.txt" - @cat library/CHANGELOG.txt | grep ^${LIBRARY_VERSION} - @echo "Checking library/${LIBRARY_NAME}/__init__.py" - @cat library/${LIBRARY_NAME}/__init__.py | grep "^__version__ = '${LIBRARY_VERSION}'" - -tag: - git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}" +dev-deps: + python3 -m pip install -r requirements-dev.txt + sudo apt install dos2unix shellcheck -python-readme: library/README.md +check: + @bash check.sh -python-license: library/LICENSE.txt +shellcheck: + shellcheck *.sh -library/README.md: README.md library/CHANGELOG.txt - cp README.md library/README.md - printf "\n# Changelog\n" >> library/README.md - cat library/CHANGELOG.txt >> library/README.md +qa: + tox -e qa -library/LICENSE.txt: LICENSE - cp LICENSE library/LICENSE.txt +pytest: + tox -e py -python-wheels: python-readme python-license - cd library; python3 setup.py bdist_wheel - cd library; python setup.py bdist_wheel +nopost: + @bash check.sh --nopost -python-sdist: python-readme python-license - cd library; python setup.py sdist +tag: version + git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}" -python-clean: - -rm -r library/dist - -rm -r library/build - -rm -r library/*.egg-info +build: check + @hatch build -python-dist: python-clean python-wheels python-sdist - ls library/dist +clean: + -rm -r dist -python-testdeploy: python-dist - twine upload --repository-url https://test.pypi.org/legacy/ library/dist/* +testdeploy: build + twine upload --repository testpypi dist/* -python-deploy: check python-dist - twine upload library/dist/* +deploy: nopost build + twine upload dist/* diff --git a/README.md b/README.md index 36c18ae..98593eb 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,29 @@ Latest/development library from GitHub: * `cd lsm303d-python` * `sudo ./install.sh --unstable` + +# Changelog +0.0.5 +----- + +* Require i2cdevice>=0.0.6 + +0.0.4 +----- + +* Port to i2cdevice>=0.0.6 set/get API + +0.0.3 +----- + +* BugFix: Fix for compatibility with i2cdevice>=0.0.6 + +0.0.2 +----- + +* Bumped i2cdevice dependency to >=0.0.4 + +0.0.1 +----- + +* Initial Release diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..38dfc3a --- /dev/null +++ b/check.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# This script handles some basic QA checks on the source + +NOPOST=$1 +LIBRARY_NAME=$(hatch project metadata name) +LIBRARY_VERSION=$(hatch version | awk -F "." '{print $1"."$2"."$3}') +POST_VERSION=$(hatch version | awk -F "." '{print substr($4,0,length($4))}') +TERM=${TERM:="xterm-256color"} + +success() { + echo -e "$(tput setaf 2)$1$(tput sgr0)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)$1$(tput sgr0)" +} + +while [[ $# -gt 0 ]]; do + K="$1" + case $K in + -p|--nopost) + NOPOST=true + shift + ;; + *) + if [[ $1 == -* ]]; then + printf "Unrecognised option: %s\n" "$1"; + exit 1 + fi + POSITIONAL_ARGS+=("$1") + shift + esac +done + +inform "Checking $LIBRARY_NAME $LIBRARY_VERSION\n" + +inform "Checking for trailing whitespace..." +if grep -IUrn --color "[[:blank:]]$" --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO; then + warning "Trailing whitespace found!" + exit 1 +else + success "No trailing whitespace found." +fi +printf "\n" + +inform "Checking for DOS line-endings..." +if grep -lIUrn --color $'\r' --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile; then + warning "DOS line-endings found!" + exit 1 +else + success "No DOS line-endings found." +fi +printf "\n" + +inform "Checking CHANGELOG.md..." +if ! grep "^${LIBRARY_VERSION}" CHANGELOG.md > /dev/null 2>&1; then + warning "Changes missing for version ${LIBRARY_VERSION}! Please update CHANGELOG.md." + exit 1 +else + success "Changes found for version ${LIBRARY_VERSION}." +fi +printf "\n" + +inform "Checking for git tag ${LIBRARY_VERSION}..." +if ! git tag -l | grep -E "${LIBRARY_VERSION}$"; then + warning "Missing git tag for version ${LIBRARY_VERSION}" +fi +printf "\n" + +if [[ $NOPOST ]]; then + inform "Checking for .postN on library version..." + if [[ "$POST_VERSION" != "" ]]; then + warning "Found .$POST_VERSION on library version." + inform "Please only use these for testpypi releases." + exit 1 + else + success "OK" + fi +fi diff --git a/install-legacy.sh b/install-legacy.sh deleted file mode 100755 index 3572e50..0000000 --- a/install-legacy.sh +++ /dev/null @@ -1,256 +0,0 @@ -#!/bin/bash - -CONFIG=/boot/config.txt -DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"` -CONFIG_BACKUP=false -APT_HAS_UPDATED=false -USER_HOME=/home/$SUDO_USER -RESOURCES_TOP_DIR=$USER_HOME/Pimoroni -WD=`pwd` -USAGE="sudo ./install.sh (--unstable)" -POSITIONAL_ARGS=() -FORCE=false -UNSTABLE=false -CODENAME=`lsb_release -sc` - -if [[ $CODENAME == "bullseye" ]]; then - bash ./install.sh $@ - exit $? -fi - -user_check() { - if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./install.sh'\n" - exit 1 - fi -} - -confirm() { - if $FORCE; then - true - else - read -r -p "$1 [y/N] " response < /dev/tty - if [[ $response =~ ^(yes|y|Y)$ ]]; then - true - else - false - fi - fi -} - -prompt() { - read -r -p "$1 [y/N] " response < /dev/tty - if [[ $response =~ ^(yes|y|Y)$ ]]; then - true - else - false - fi -} - -success() { - echo -e "$(tput setaf 2)$1$(tput sgr0)" -} - -inform() { - echo -e "$(tput setaf 6)$1$(tput sgr0)" -} - -warning() { - echo -e "$(tput setaf 1)$1$(tput sgr0)" -} - -function do_config_backup { - if [ ! $CONFIG_BACKUP == true ]; then - CONFIG_BACKUP=true - FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt" - inform "Backing up $CONFIG to /boot/$FILENAME\n" - cp $CONFIG /boot/$FILENAME - mkdir -p $RESOURCES_TOP_DIR/config-backups/ - cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME - if [ -f "$UNINSTALLER" ]; then - echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER - fi - fi -} - -function apt_pkg_install { - PACKAGES=() - PACKAGES_IN=("$@") - for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do - PACKAGE="${PACKAGES_IN[$i]}" - if [ "$PACKAGE" == "" ]; then continue; fi - printf "Checking for $PACKAGE\n" - dpkg -L $PACKAGE > /dev/null 2>&1 - if [ "$?" == "1" ]; then - PACKAGES+=("$PACKAGE") - fi - done - PACKAGES="${PACKAGES[@]}" - if ! [ "$PACKAGES" == "" ]; then - echo "Installing missing packages: $PACKAGES" - if [ ! $APT_HAS_UPDATED ]; then - apt update - APT_HAS_UPDATED=true - fi - apt install -y $PACKAGES - if [ -f "$UNINSTALLER" ]; then - echo "apt uninstall -y $PACKAGES" - fi - fi -} - -while [[ $# -gt 0 ]]; do - K="$1" - case $K in - -u|--unstable) - UNSTABLE=true - shift - ;; - -f|--force) - FORCE=true - shift - ;; - *) - if [[ $1 == -* ]]; then - printf "Unrecognised option: $1\n"; - printf "Usage: $USAGE\n"; - exit 1 - fi - POSITIONAL_ARGS+=("$1") - shift - esac -done - -user_check - -apt_pkg_install python-configparser - -CONFIG_VARS=`python - < $UNINSTALLER -printf "It's recommended you run these steps manually.\n" -printf "If you want to run the full script, open it in\n" -printf "an editor and remove 'exit 1' from below.\n" -exit 1 -EOF - -printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n" - -if $UNSTABLE; then - warning "Installing unstable library from source.\n\n" -else - printf "Installing stable library from pypi.\n\n" -fi - -cd library - -printf "Installing for Python 2..\n" -apt_pkg_install "${PY2_DEPS[@]}" -if $UNSTABLE; then - python setup.py install > /dev/null -else - pip install --upgrade $LIBRARY_NAME -fi -if [ $? -eq 0 ]; then - success "Done!\n" - echo "pip uninstall $LIBRARY_NAME" >> $UNINSTALLER -fi - -if [ -f "/usr/bin/python3" ]; then - printf "Installing for Python 3..\n" - apt_pkg_install "${PY3_DEPS[@]}" - if $UNSTABLE; then - python3 setup.py install > /dev/null - else - pip3 install --upgrade $LIBRARY_NAME - fi - if [ $? -eq 0 ]; then - success "Done!\n" - echo "pip3 uninstall $LIBRARY_NAME" >> $UNINSTALLER - fi -fi - -cd $WD - -for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do - CMD="${SETUP_CMDS[$i]}" - # Attempt to catch anything that touches /boot/config.txt and trigger a backup - if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG"* ]] || [[ "$CMD" == *"\$CONFIG"* ]]; then - do_config_backup - fi - eval $CMD -done - -for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do - CONFIG_LINE="${CONFIG_TXT[$i]}" - if ! [ "$CONFIG_LINE" == "" ]; then - do_config_backup - inform "Adding $CONFIG_LINE to $CONFIG\n" - sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG - if ! grep -q "^$CONFIG_LINE" $CONFIG; then - printf "$CONFIG_LINE\n" >> $CONFIG - fi - fi -done - -if [ -d "examples" ]; then - if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then - inform "Copying examples to $RESOURCES_DIR" - cp -r examples/ $RESOURCES_DIR - echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER - success "Done!" - fi -fi - -printf "\n" - -if [ -f "/usr/bin/pydoc" ]; then - printf "Generating documentation.\n" - pydoc -w $LIBRARY_NAME > /dev/null - if [ -f "$LIBRARY_NAME.html" ]; then - cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html - rm -f $LIBRARY_NAME.html - inform "Documentation saved to $RESOURCES_DIR/docs.html" - success "Done!" - else - warning "Error: Failed to generate documentation." - fi -fi - -success "\nAll done!" -inform "If this is your first time installing you should reboot for hardware changes to take effect.\n" -inform "Find uninstall steps in $UNINSTALLER\n" diff --git a/install.sh b/install.sh index 28d3d46..3db90bc 100755 --- a/install.sh +++ b/install.sh @@ -1,22 +1,24 @@ #!/bin/bash -CONFIG=/boot/config.txt -DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"` +LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}') +CONFIG_FILE=config.txt +CONFIG_DIR="/boot/firmware" +DATESTAMP=$(date "+%Y-%m-%d-%H-%M-%S") CONFIG_BACKUP=false APT_HAS_UPDATED=false -USER_HOME=/home/$SUDO_USER -RESOURCES_TOP_DIR=$USER_HOME/Pimoroni -WD=`pwd` -USAGE="sudo ./install.sh (--unstable)" +RESOURCES_TOP_DIR="$HOME/Pimoroni" +VENV_BASH_SNIPPET="$RESOURCES_TOP_DIR/auto_venv.sh" +VENV_DIR="$HOME/.virtualenvs/pimoroni" +USAGE="./install.sh (--unstable)" POSITIONAL_ARGS=() FORCE=false UNSTABLE=false -PYTHON="/usr/bin/python3" +PYTHON="python" +CMD_ERRORS=false user_check() { - if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./install.sh'\n" - exit 1 + if [ "$(id -u)" -eq 0 ]; then + fatal "Script should not be run as root. Try './install.sh'\n" fi } @@ -33,15 +35,6 @@ confirm() { fi } -prompt() { - read -r -p "$1 [y/N] " response < /dev/tty - if [[ $response =~ ^(yes|y|Y)$ ]]; then - true - else - false - fi -} - success() { echo -e "$(tput setaf 2)$1$(tput sgr0)" } @@ -51,49 +44,128 @@ inform() { } warning() { - echo -e "$(tput setaf 1)$1$(tput sgr0)" + echo -e "$(tput setaf 1)⚠ WARNING:$(tput sgr0) $1" +} + +fatal() { + echo -e "$(tput setaf 1)⚠ FATAL:$(tput sgr0) $1" + exit 1 +} + +find_config() { + if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then + CONFIG_DIR="/boot" + if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then + fatal "Could not find $CONFIG_FILE!" + fi + fi + inform "Using $CONFIG_FILE in $CONFIG_DIR" +} + +venv_bash_snippet() { + inform "Checking for $VENV_BASH_SNIPPET\n" + if [ ! -f "$VENV_BASH_SNIPPET" ]; then + inform "Creating $VENV_BASH_SNIPPET\n" + mkdir -p "$RESOURCES_TOP_DIR" + cat << EOF > "$VENV_BASH_SNIPPET" +# Add "source $VENV_BASH_SNIPPET" to your ~/.bashrc to activate +# the Pimoroni virtual environment automagically! +VENV_DIR="$VENV_DIR" +if [ ! -f \$VENV_DIR/bin/activate ]; then + printf "Creating user Python environment in \$VENV_DIR, please wait...\n" + mkdir -p \$VENV_DIR + python3 -m venv --system-site-packages \$VENV_DIR +fi +printf " ↓ ↓ ↓ ↓ Hello, we've activated a Python venv for you. To exit, type \"deactivate\".\n" +source \$VENV_DIR/bin/activate +EOF + fi +} + +venv_check() { + PYTHON_BIN=$(which "$PYTHON") + if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then + printf "This script should be run in a virtual Python environment.\n" + if confirm "Would you like us to create and/or use a default one?"; then + printf "\n" + if [ ! -f "$VENV_DIR/bin/activate" ]; then + inform "Creating a new virtual Python environment in $VENV_DIR, please wait...\n" + mkdir -p "$VENV_DIR" + /usr/bin/python3 -m venv "$VENV_DIR" --system-site-packages + venv_bash_snippet + # shellcheck disable=SC1091 + source "$VENV_DIR/bin/activate" + else + inform "Activating existing virtual Python environment in $VENV_DIR\n" + printf "source \"%s/bin/activate\"\n" "$VENV_DIR" + # shellcheck disable=SC1091 + source "$VENV_DIR/bin/activate" + fi + else + printf "\n" + fatal "Please create and/or activate a virtual Python environment and try again!\n" + fi + fi + printf "\n" +} + +check_for_error() { + if [ $? -ne 0 ]; then + CMD_ERRORS=true + warning "^^^ 😬 previous command did not exit cleanly!" + fi } function do_config_backup { if [ ! $CONFIG_BACKUP == true ]; then CONFIG_BACKUP=true FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt" - inform "Backing up $CONFIG to /boot/$FILENAME\n" - cp $CONFIG /boot/$FILENAME - mkdir -p $RESOURCES_TOP_DIR/config-backups/ - cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME + inform "Backing up $CONFIG_DIR/$CONFIG_FILE to $CONFIG_DIR/$FILENAME\n" + sudo cp "$CONFIG_DIR/$CONFIG_FILE" "$CONFIG_DIR/$FILENAME" + mkdir -p "$RESOURCES_TOP_DIR/config-backups/" + cp $CONFIG_DIR/$CONFIG_FILE "$RESOURCES_TOP_DIR/config-backups/$FILENAME" if [ -f "$UNINSTALLER" ]; then - echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER + echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> "$UNINSTALLER" fi fi } function apt_pkg_install { - PACKAGES=() + PACKAGES_NEEDED=() PACKAGES_IN=("$@") + # Check the list of packages and only run update/install if we need to for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do PACKAGE="${PACKAGES_IN[$i]}" if [ "$PACKAGE" == "" ]; then continue; fi - printf "Checking for $PACKAGE\n" - dpkg -L $PACKAGE > /dev/null 2>&1 + printf "Checking for %s\n" "$PACKAGE" + dpkg -L "$PACKAGE" > /dev/null 2>&1 if [ "$?" == "1" ]; then - PACKAGES+=("$PACKAGE") + PACKAGES_NEEDED+=("$PACKAGE") fi done - PACKAGES="${PACKAGES[@]}" + PACKAGES="${PACKAGES_NEEDED[*]}" if ! [ "$PACKAGES" == "" ]; then - echo "Installing missing packages: $PACKAGES" + printf "\n" + inform "Installing missing packages: $PACKAGES" if [ ! $APT_HAS_UPDATED ]; then - apt update + sudo apt update APT_HAS_UPDATED=true fi - apt install -y $PACKAGES + # shellcheck disable=SC2086 + sudo apt install -y $PACKAGES + check_for_error if [ -f "$UNINSTALLER" ]; then - echo "apt uninstall -y $PACKAGES" + echo "apt uninstall -y $PACKAGES" >> "$UNINSTALLER" fi fi } +function pip_pkg_install { + # A null Keyring prevents pip stalling in the background + PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring $PYTHON -m pip install --upgrade "$@" + check_for_error +} + while [[ $# -gt 0 ]]; do K="$1" case $K in @@ -112,8 +184,8 @@ while [[ $# -gt 0 ]]; do ;; *) if [[ $1 == -* ]]; then - printf "Unrecognised option: $1\n"; - printf "Usage: $USAGE\n"; + printf "Unrecognised option: %s\n" "$1"; + printf "Usage: %s\n" "$USAGE"; exit 1 fi POSITIONAL_ARGS+=("$1") @@ -121,131 +193,178 @@ while [[ $# -gt 0 ]]; do esac done +printf "Installing %s...\n\n" "$LIBRARY_NAME" + user_check +venv_check -if [ ! -f "$PYTHON" ]; then - printf "Python path $PYTHON not found!\n" - exit 1 +if [ ! -f "$(which "$PYTHON")" ]; then + fatal "Python path %s not found!\n" "$PYTHON" fi -PYTHON_VER=`$PYTHON --version` +PYTHON_VER=$($PYTHON --version) -inform "Installing. Please wait..." +inform "Checking Dependencies. Please wait..." -$PYTHON -m pip install --upgrade configparser +# Install toml and try to read pyproject.toml into bash variables -CONFIG_VARS=`$PYTHON - < $UNINSTALLER +# Create a stub uninstaller file, we'll try to add the inverse of every +# install command run to here, though it's not complete. +cat << EOF > "$UNINSTALLER" printf "It's recommended you run these steps manually.\n" printf "If you want to run the full script, open it in\n" printf "an editor and remove 'exit 1' from below.\n" exit 1 +source $VIRTUAL_ENV/bin/activate EOF -printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n" +printf "\n" -if $UNSTABLE; then - warning "Installing unstable library from source.\n\n" -else - printf "Installing stable library from pypi.\n\n" -fi +inform "Installing for $PYTHON_VER...\n" -cd library +# Install apt packages from pyproject.toml / tool.pimoroni.apt_packages +apt_pkg_install "${APT_PACKAGES[@]}" + +printf "\n" -printf "Installing for $PYTHON_VER...\n" -apt_pkg_install "${PY3_DEPS[@]}" if $UNSTABLE; then - $PYTHON setup.py install > /dev/null + warning "Installing unstable library from source.\n" + pip_pkg_install . else - $PYTHON -m pip install --upgrade $LIBRARY_NAME + inform "Installing stable library from pypi.\n" + pip_pkg_install "$LIBRARY_NAME" fi + +# shellcheck disable=SC2181 # One of two commands run, depending on --unstable flag if [ $? -eq 0 ]; then success "Done!\n" - echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> $UNINSTALLER + echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> "$UNINSTALLER" fi -cd $WD +find_config + +printf "\n" + +# Run the setup commands from pyproject.toml / tool.pimoroni.commands +inform "Running setup commands...\n" for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do CMD="${SETUP_CMDS[$i]}" - # Attempt to catch anything that touches /boot/config.txt and trigger a backup - if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG"* ]] || [[ "$CMD" == *"\$CONFIG"* ]]; then + # Attempt to catch anything that touches config.txt and trigger a backup + if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG_DIR/$CONFIG_FILE"* ]] || [[ "$CMD" == *"\$CONFIG_DIR/\$CONFIG_FILE"* ]]; then do_config_backup fi - eval $CMD + if [[ ! "$CMD" == printf* ]]; then + printf "Running: \"%s\"\n" "$CMD" + fi + eval "$CMD" + check_for_error done +printf "\n" + +# Add the config.txt entries from pyproject.toml / tool.pimoroni.configtxt + for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do CONFIG_LINE="${CONFIG_TXT[$i]}" if ! [ "$CONFIG_LINE" == "" ]; then do_config_backup - inform "Adding $CONFIG_LINE to $CONFIG\n" - sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG - if ! grep -q "^$CONFIG_LINE" $CONFIG; then - printf "$CONFIG_LINE\n" >> $CONFIG + inform "Adding $CONFIG_LINE to $CONFIG_DIR/$CONFIG_FILE" + sudo sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG_DIR/$CONFIG_FILE + if ! grep -q "^$CONFIG_LINE" $CONFIG_DIR/$CONFIG_FILE; then + printf "%s \n" "$CONFIG_LINE" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE fi fi done +printf "\n" + +# Just a straight copy of the examples/ dir into ~/Pimoroni/board/examples + if [ -d "examples" ]; then if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then inform "Copying examples to $RESOURCES_DIR" - cp -r examples/ $RESOURCES_DIR - echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER + cp -r examples/ "$RESOURCES_DIR" + echo "rm -r $RESOURCES_DIR" >> "$UNINSTALLER" success "Done!" fi fi printf "\n" -if [ -f "/usr/bin/pydoc" ]; then - printf "Generating documentation.\n" - pydoc -w $LIBRARY_NAME > /dev/null - if [ -f "$LIBRARY_NAME.html" ]; then - cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html - rm -f $LIBRARY_NAME.html - inform "Documentation saved to $RESOURCES_DIR/docs.html" +# Use pdoc to generate basic documentation from the installed module + +if confirm "Would you like to generate documentation?"; then + inform "Installing pdoc. Please wait..." + pip_pkg_install pdoc + inform "Generating documentation.\n" + if $PYTHON -m pdoc "$LIBRARY_NAME" -o "$RESOURCES_DIR/docs" > /dev/null; then + inform "Documentation saved to $RESOURCES_DIR/docs" success "Done!" else warning "Error: Failed to generate documentation." fi fi -success "\nAll done!" -inform "If this is your first time installing you should reboot for hardware changes to take effect.\n" -inform "Find uninstall steps in $UNINSTALLER\n" +printf "\n" + +if [ "$CMD_ERRORS" = true ]; then + warning "One or more setup commands appear to have failed." + printf "This might prevent things from working properly.\n" + printf "Make sure your OS is up to date and try re-running this installer.\n" + printf "If things still don't work, report this or find help at %s.\n\n" "$GITHUB_URL" +else + success "\nAll done!" +fi + +printf "If this is your first time installing you should reboot for hardware changes to take effect.\n" +printf "Find uninstall steps in %s\n\n" "$UNINSTALLER" + +if [ "$CMD_ERRORS" = true ]; then + exit 1 +else + exit 0 +fi diff --git a/library/LICENSE.txt b/library/LICENSE.txt deleted file mode 100644 index aed751a..0000000 --- a/library/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Pimoroni Ltd. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/library/MANIFEST.in b/library/MANIFEST.in deleted file mode 100644 index d766f08..0000000 --- a/library/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include CHANGELOG.txt -include LICENSE.txt -include README.md -include setup.py -recursive-include lsm303d *.py diff --git a/library/README.md b/library/README.md deleted file mode 100644 index 98593eb..0000000 --- a/library/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# lsm303d Accelerometer And Compass - -[![Build Status](https://shields.io/github/workflow/status/pimoroni/lsm303d-python/Python%20Tests.svg)](https://github.com/pimoroni/sgp30-python/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/pimoroni/lsm303d-python/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/lsm303d-python?branch=master) -[![PyPi Package](https://img.shields.io/pypi/v/lsm303d.svg)](https://pypi.python.org/pypi/lsm303d) -[![Python Versions](https://img.shields.io/pypi/pyversions/lsm303d.svg)](https://pypi.python.org/pypi/lsm303d) - -Suitable for measuring orientation and motion, the lsm303d has a 3-axis accelerometer and compass. - -# Installing - -Stable library from PyPi: - -* Just run `python3 -m pip install lsm303d` - -Latest/development library from GitHub: - -* `git clone https://github.com/pimoroni/lsm303d-python` -* `cd lsm303d-python` -* `sudo ./install.sh --unstable` - - -# Changelog -0.0.5 ------ - -* Require i2cdevice>=0.0.6 - -0.0.4 ------ - -* Port to i2cdevice>=0.0.6 set/get API - -0.0.3 ------ - -* BugFix: Fix for compatibility with i2cdevice>=0.0.6 - -0.0.2 ------ - -* Bumped i2cdevice dependency to >=0.0.4 - -0.0.1 ------ - -* Initial Release diff --git a/library/README.rst b/library/README.rst deleted file mode 100644 index d6198ab..0000000 --- a/library/README.rst +++ /dev/null @@ -1,18 +0,0 @@ -lsm303d Accelerometer And Compass -================================= - -Suitable for measuring orientation and motion, the lsm303d has a 3-axis -accelerometer and compass. - -Installing -========== - -Stable library from PyPi: - -- Just run ``sudo pip install lsm303d`` - -Latest/development library from GitHub: - -- ``git clone https://github.com/pimoroni/lsm303d-python`` -- ``cd lsm303d-python`` -- ``sudo ./install.sh`` diff --git a/library/setup.cfg b/library/setup.cfg deleted file mode 100644 index 4598184..0000000 --- a/library/setup.cfg +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -[metadata] -name = lsm303d -version = 0.0.5 -author = Philip Howard -author_email = phil@pimoroni.com -description = Python library for the LSM303d accelerometer and magnetometer -long_description = file: README.md -long_description_content_type = text/markdown -keywords = Raspberry Pi -url = https://www.pimoroni.com -project_urls = - GitHub=https://www.github.com/pimoroni/lsm303d-python -license = MIT -# This includes the license file(s) in the wheel. -# https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file -license_files = LICENSE.txt -classifiers = - Development Status :: 4 - Beta - Operating System :: POSIX :: Linux - License :: OSI Approved :: MIT License - Intended Audience :: Developers - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Topic :: Software Development - Topic :: Software Development :: Libraries - Topic :: System :: Hardware - -[options] -python_requires = >= 2.7 -packages = lsm303d -install_requires = - i2cdevice >= 0.0.6 - -[flake8] -exclude = - .tox, - .eggs, - .git, - __pycache__, - build, - dist -ignore = - E501 - -[pimoroni] -py2deps = -py3deps = -configtxt = -commands = - printf "Setting up i2c..\n" - raspi-config nonint do_i2c 0 diff --git a/library/setup.py b/library/setup.py deleted file mode 100755 index afb1ee1..0000000 --- a/library/setup.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Copyright (c) 2016 Pimoroni - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -from setuptools import setup, __version__ -from pkg_resources import parse_version - -minimum_version = parse_version('30.4.0') - -if parse_version(__version__) < minimum_version: - raise RuntimeError("Package setuptools must be at least version {}".format(minimum_version)) - -setup() diff --git a/library/tox.ini b/library/tox.ini deleted file mode 100644 index 8aede38..0000000 --- a/library/tox.ini +++ /dev/null @@ -1,21 +0,0 @@ -[tox] -envlist = py{27,35,37,39},qa -skip_missing_interpreters = True - -[testenv] -commands = - python setup.py install - coverage run -m py.test -v -r wsx - coverage report -deps = - mock - pytest>=3.1 - pytest-cov - -[testenv:qa] -commands = - flake8 --ignore E501 - rstcheck README.rst -deps = - flake8 - rstcheck diff --git a/library/lsm303d/__init__.py b/lsm303d/__init__.py similarity index 100% rename from library/lsm303d/__init__.py rename to lsm303d/__init__.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..abad5f3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,118 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "lsm303d" +dynamic = ["version", "readme"] +description = "Python library for the LSM303d accelerometer and magnetometer" +license = {file = "LICENSE"} +requires-python = ">= 3.7" +authors = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +maintainers = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +keywords = [ + "Pi", + "Raspberry", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Hardware", +] +dependencies = [ + "i2cdevice>=1.0.0" +] + +[project.urls] +GitHub = "https://www.github.com/pimoroni/lsm303d-python" +Homepage = "https://www.pimoroni.com" + +[tool.hatch.version] +path = "lsm303d/__init__.py" + +[tool.hatch.build] +include = [ + "lsm303d", + "README.md", + "CHANGELOG.md", + "LICENSE" +] + +[tool.hatch.build.targets.sdist] +include = [ + "*" +] +exclude = [ + ".*", + "dist" +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" +fragments = [ + { path = "README.md" }, + { text = "\n" }, + { path = "CHANGELOG.md" } +] + +[tool.ruff] +exclude = [ + '.tox', + '.egg', + '.git', + '__pycache__', + 'build', + 'dist' +] +line-length = 200 + +[tool.codespell] +skip = """ +./.tox,\ +./.egg,\ +./.git,\ +./__pycache__,\ +./build,\ +./dist.\ +""" + +[tool.isort] +line_length = 200 + +[tool.check-manifest] +ignore = [ + '.stickler.yml', + 'boilerplate.md', + 'check.sh', + 'install.sh', + 'uninstall.sh', + 'Makefile', + 'tox.ini', + 'tests/*', + 'examples/*', + '.coveragerc', + 'requirements-dev.txt' +] + +[tool.pimoroni] +apt_packages = [""] +configtxt = [] +commands = [ + "printf \"Setting up i2c..\n\"", + "raspi-config nonint do_i2c 0" +] diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..525b042 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,9 @@ +check-manifest +ruff +codespell +isort +twine +hatch +hatch-fancy-pypi-readme +tox +pdoc diff --git a/library/test.py b/test.py similarity index 100% rename from library/test.py rename to test.py diff --git a/library/tests/test_setup.py b/tests/test_setup.py similarity index 100% rename from library/tests/test_setup.py rename to tests/test_setup.py diff --git a/library/tests/tools.py b/tests/tools.py similarity index 100% rename from library/tests/tools.py rename to tests/tools.py diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..4726cef --- /dev/null +++ b/tox.ini @@ -0,0 +1,34 @@ +[tox] +envlist = py,qa +skip_missing_interpreters = True +isolated_build = true +minversion = 4.0.0 + +[testenv] +commands = + coverage run -m pytest -v -r wsx + coverage report +deps = + mock + pytest>=3.1 + pytest-cov + build + +[testenv:qa] +commands = + check-manifest + python -m build --no-isolation + python -m twine check dist/* + isort --check . + ruff check . + codespell . +deps = + check-manifest + ruff + codespell + isort + twine + build + hatch + hatch-fancy-pypi-readme + diff --git a/uninstall.sh b/uninstall.sh index 4848039..3314b7f 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,25 +1,72 @@ #!/bin/bash -LIBRARY_VERSION=`cat library/setup.cfg | grep version | awk -F" = " '{print $2}'` -LIBRARY_NAME=`cat library/setup.cfg | grep name | awk -F" = " '{print $2}'` +FORCE=false +LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}') +RESOURCES_DIR=$HOME/Pimoroni/$LIBRARY_NAME +PYTHON="python" -printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Uninstaller\n\n" -if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./uninstall.sh'\n" - exit 1 -fi +venv_check() { + PYTHON_BIN=$(which $PYTHON) + if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then + printf "This script should be run in a virtual Python environment.\n" + exit 1 + fi +} -cd library +user_check() { + if [ "$(id -u)" -eq 0 ]; then + printf "Script should not be run as root. Try './uninstall.sh'\n" + exit 1 + fi +} -printf "Unnstalling for Python 2..\n" -pip uninstall $LIBRARY_NAME +confirm() { + if $FORCE; then + true + else + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi + fi +} -if [ -f "/usr/bin/pip3" ]; then - printf "Uninstalling for Python 3..\n" - pip3 uninstall $LIBRARY_NAME -fi +prompt() { + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi +} + +success() { + echo -e "$(tput setaf 2)$1$(tput sgr0)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)$1$(tput sgr0)" +} -cd .. +printf "%s Python Library: Uninstaller\n\n" "$LIBRARY_NAME" + +user_check +venv_check + +printf "Uninstalling for Python 3...\n" +$PYTHON -m pip uninstall "$LIBRARY_NAME" + +if [ -d "$RESOURCES_DIR" ]; then + if confirm "Would you like to delete $RESOURCES_DIR?"; then + rm -r "$RESOURCES_DIR" + fi +fi printf "Done!\n" From 28cd1f42e055880a8c2d73134253a64918daefc6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 20 May 2024 16:52:30 +0100 Subject: [PATCH 2/7] QA: Apply isort suggestions. --- examples/accelerometer.py | 1 + examples/magnetometer.py | 1 + lsm303d/__init__.py | 4 ++-- test.py | 1 + tests/test_setup.py | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/accelerometer.py b/examples/accelerometer.py index 071ed98..21bf533 100755 --- a/examples/accelerometer.py +++ b/examples/accelerometer.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import time + from lsm303d import LSM303D lsm = LSM303D(0x1d) # Change to 0x1e if you have soldered the address jumper diff --git a/examples/magnetometer.py b/examples/magnetometer.py index 1bf0a33..49049c9 100755 --- a/examples/magnetometer.py +++ b/examples/magnetometer.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import time + from lsm303d import LSM303D lsm = LSM303D(0x1d) # Change to 0x1e if you have soldered the address jumper diff --git a/lsm303d/__init__.py b/lsm303d/__init__.py index f580e36..71ce2ad 100644 --- a/lsm303d/__init__.py +++ b/lsm303d/__init__.py @@ -1,7 +1,7 @@ -from i2cdevice import Device, Register, BitField, _int_to_bytes -from i2cdevice.adapter import Adapter, LookupAdapter, U16ByteSwapAdapter import struct +from i2cdevice import BitField, Device, Register, _int_to_bytes +from i2cdevice.adapter import Adapter, LookupAdapter, U16ByteSwapAdapter __version__ = '0.0.5' diff --git a/test.py b/test.py index c100b92..8915c2a 100644 --- a/test.py +++ b/test.py @@ -1,4 +1,5 @@ import time + from lsm303d import LSM303D lsm = LSM303D(0x1e) diff --git a/tests/test_setup.py b/tests/test_setup.py index 9eb13d8..9ea98b8 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -1,4 +1,5 @@ import sys + import mock import pytest From 162bc9eac981f6913a5b3673bb52fd715f7fc13d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 20 May 2024 16:55:36 +0100 Subject: [PATCH 3/7] QA: Apply codespell suggestions. Rename set_mag_full_scale_guass and provide a back-compat shim. --- lsm303d/__init__.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lsm303d/__init__.py b/lsm303d/__init__.py index 71ce2ad..9b9fa33 100644 --- a/lsm303d/__init__.py +++ b/lsm303d/__init__.py @@ -340,7 +340,7 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): self._is_setup = False self._accel_full_scale_g = 2 - self._mag_full_scale_guass = 2 + self._mag_full_scale_gauss = 2 def set_accel_full_scale_g(self, scale): """Set the full scale range for the accelerometer in g @@ -351,15 +351,17 @@ def set_accel_full_scale_g(self, scale): self._accel_full_scale_g = scale self._lsm303d.set('CONTROL2', accel_full_scale_g=self._accel_full_scale_g) - def set_mag_full_scale_guass(self, scale): - """Set the full scale range for the magnetometer in guass + def set_mag_full_scale_gauss(self, scale): + """Set the full scale range for the magnetometer in gauss - :param scale: One of 2, 4, 8 or 12 guass + :param scale: One of 2, 4, 8 or 12 gauss """ - self._mag_full_scale_guass = scale + self._mag_full_scale_gauss = scale self._lsm303d.set('CONTROL6', mag_full_scale_gauss=scale) # +-2 + set_mag_full_scale_guass = set_mag_full_scale_gauss + def setup(self): if self._is_setup: return @@ -406,26 +408,26 @@ def setup(self): mag_data_rate_hz=50, enable_temperature=1) - self.set_mag_full_scale_guass(2) + self.set_mag_full_scale_gauss(2) self._lsm303d.set('CONTROL7', mag_mode='continuous') def magnetometer(self): """Return magnetometer x, y and z readings. - These readings are given in guass and should be +/- the given mag_full_scale_guass value. + These readings are given in gauss and should be +/- the given mag_full_scale_gauss value. """ self.setup() mag = self._lsm303d.get('MAGNETOMETER') x, y, z = mag.x, mag.y, mag.z - x, y, z = [(p / 32767.0) * self._mag_full_scale_guass for p in (x, y, z)] + x, y, z = [(p / 32767.0) * self._mag_full_scale_gauss for p in (x, y, z)] return x, y, z def accelerometer(self): - """Return acelerometer x, y and z readings. + """Return accelerometer x, y and z readings. - These readings are given in g annd should be +/- the given accel_full_scale_g value. + These readings are given in g and should be +/- the given accel_full_scale_g value. """ self.setup() From 015a27f101253cbf6564d9799ed1cf5401c8ff88 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 20 May 2024 17:42:34 +0100 Subject: [PATCH 4/7] QA: Update tests. --- tests/conftest.py | 27 +++++++++++++++++++++++++++ tests/test_setup.py | 14 ++------------ 2 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..4fc0e6a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,27 @@ +import sys + +import mock +import pytest +from tools import SMBusFakeDevice + + +@pytest.fixture() +def smbus_not_present(): + sys.modules['smbus2'] = mock.MagicMock() + yield sys.modules['smbus2'] + del sys.modules['smbus2'] + + +@pytest.fixture() +def smbus(): + sys.modules['smbus2'] = mock.MagicMock() + sys.modules['smbus2'].SMBus = SMBusFakeDevice + yield sys.modules['smbus2'] + del sys.modules['smbus2'] + + +@pytest.fixture() +def LSM303D(): + from lsm303d import LSM303D + yield LSM303D + del sys.modules['lsm303d'] \ No newline at end of file diff --git a/tests/test_setup.py b/tests/test_setup.py index 9ea98b8..d96da2a 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -1,22 +1,12 @@ -import sys - -import mock import pytest -def test_setup_not_present(): - sys.modules['smbus'] = mock.MagicMock() - from lsm303d import LSM303D +def test_setup_not_present(smbus_not_present, LSM303D): lsm303d = LSM303D() with pytest.raises(RuntimeError): lsm303d.setup() -def test_setup_mock_present(): - from tools import SMBusFakeDevice - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus - from lsm303d import LSM303D +def test_setup_mock_present(smbus, LSM303D): lsm303d = LSM303D() lsm303d.setup() From 5393504d67ee931f44f106dd86a9b4c92911f6c4 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 21 May 2024 10:17:41 +0100 Subject: [PATCH 5/7] QA: Basic code formatting tweaks. --- lsm303d/__init__.py | 374 ++++++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/lsm303d/__init__.py b/lsm303d/__init__.py index 9b9fa33..f29d863 100644 --- a/lsm303d/__init__.py +++ b/lsm303d/__init__.py @@ -3,7 +3,7 @@ from i2cdevice import BitField, Device, Register, _int_to_bytes from i2cdevice.adapter import Adapter, LookupAdapter, U16ByteSwapAdapter -__version__ = '0.0.5' +__version__ = "1.0.0" class TemperatureAdapter(Adapter): @@ -37,85 +37,85 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): self._i2c_dev = i2c_dev self._lsm303d = Device([0x1D, 0x1E], i2c_dev=self._i2c_dev, bit_width=8, registers=( - Register('TEMPERATURE', 0x05 | 0x80, fields=( - BitField('temperature', 0xFFFF, adapter=TemperatureAdapter()), + Register("TEMPERATURE", 0x05 | 0x80, fields=( + BitField("temperature", 0xFFFF, adapter=TemperatureAdapter()), ), bit_width=16), # Magnetometer interrupt status - Register('MAGNETOMETER_STATUS', 0x07, fields=( - BitField('xdata', 0b00000001), - BitField('ydata', 0b00000010), - BitField('zdata', 0b00000100), - BitField('data', 0b00001000), - BitField('xoverrun', 0b00010000), - BitField('yoverrun', 0b00100000), - BitField('zoverrun', 0b01000000), - BitField('overrun', 0b10000000), + Register("MAGNETOMETER_STATUS", 0x07, fields=( + BitField("xdata", 0b00000001), + BitField("ydata", 0b00000010), + BitField("zdata", 0b00000100), + BitField("data", 0b00001000), + BitField("xoverrun", 0b00010000), + BitField("yoverrun", 0b00100000), + BitField("zoverrun", 0b01000000), + BitField("overrun", 0b10000000), )), - Register('MAGNETOMETER', 0x08 | 0x80, fields=( - BitField('x', 0xFFFF00000000, adapter=S16ByteSwapAdapter()), - BitField('y', 0x0000FFFF0000, adapter=S16ByteSwapAdapter()), - BitField('z', 0x00000000FFFF, adapter=S16ByteSwapAdapter()), + Register("MAGNETOMETER", 0x08 | 0x80, fields=( + BitField("x", 0xFFFF00000000, adapter=S16ByteSwapAdapter()), + BitField("y", 0x0000FFFF0000, adapter=S16ByteSwapAdapter()), + BitField("z", 0x00000000FFFF, adapter=S16ByteSwapAdapter()), ), bit_width=8 * 6), - Register('WHOAMI', 0x0F, fields=( - BitField('id', 0xFF), + Register("WHOAMI", 0x0F, fields=( + BitField("id", 0xFF), )), - Register('MAGNETOMETER_INTERRUPT', 0x12, fields=( - BitField('enable', 0b00000001), - BitField('enable_4d', 0b00000010), - BitField('latch', 0b00000100), - BitField('polarity', 0b00001000), # 0 = active-low, 1 = active-high - BitField('pin_config', 0b00010000), # 0 = push-pull, 1 = open-drain - BitField('z_enable', 0b00100000), - BitField('y_enable', 0b01000000), - BitField('x_enable', 0b10000000), + Register("MAGNETOMETER_INTERRUPT", 0x12, fields=( + BitField("enable", 0b00000001), + BitField("enable_4d", 0b00000010), + BitField("latch", 0b00000100), + BitField("polarity", 0b00001000), # 0 = active-low, 1 = active-high + BitField("pin_config", 0b00010000), # 0 = push-pull, 1 = open-drain + BitField("z_enable", 0b00100000), + BitField("y_enable", 0b01000000), + BitField("x_enable", 0b10000000), )), - Register('MAGNETOMETER_INTERRUPT_SOURCE', 0x13, fields=( - BitField('event', 0b00000001), - BitField('overflow', 0b00000010), - BitField('z_negative', 0b00000100), - BitField('y_negative', 0b00001000), - BitField('x_negative', 0b00010000), - BitField('z_positive', 0b00100000), - BitField('y_positive', 0b01000000), - BitField('x_positive', 0b10000000), + Register("MAGNETOMETER_INTERRUPT_SOURCE", 0x13, fields=( + BitField("event", 0b00000001), + BitField("overflow", 0b00000010), + BitField("z_negative", 0b00000100), + BitField("y_negative", 0b00001000), + BitField("x_negative", 0b00010000), + BitField("z_positive", 0b00100000), + BitField("y_positive", 0b01000000), + BitField("x_positive", 0b10000000), )), - Register('MAGNETOMETER_INTERRUPT_THRESHOLD', 0x14 | 0x80, fields=( - BitField('threshold', 0xFFFF, adapter=U16ByteSwapAdapter()), + Register("MAGNETOMETER_INTERRUPT_THRESHOLD", 0x14 | 0x80, fields=( + BitField("threshold", 0xFFFF, adapter=U16ByteSwapAdapter()), ), bit_width=16), - Register('MAGNETOMETER_OFFSET', 0x16 | 0x80, fields=( - BitField('x', 0xFFFF00000000, adapter=S16ByteSwapAdapter()), - BitField('y', 0x0000FFFF0000, adapter=S16ByteSwapAdapter()), - BitField('z', 0x00000000FFFF, adapter=S16ByteSwapAdapter()), + Register("MAGNETOMETER_OFFSET", 0x16 | 0x80, fields=( + BitField("x", 0xFFFF00000000, adapter=S16ByteSwapAdapter()), + BitField("y", 0x0000FFFF0000, adapter=S16ByteSwapAdapter()), + BitField("z", 0x00000000FFFF, adapter=S16ByteSwapAdapter()), ), bit_width=8 * 6), - Register('HP_ACCELEROMETER_REFERENCE', 0x1c | 0x80, fields=( - BitField('x', 0xFF0000), - BitField('y', 0x00FF00), - BitField('z', 0x0000FF), + Register("HP_ACCELEROMETER_REFERENCE", 0x1c | 0x80, fields=( + BitField("x", 0xFF0000), + BitField("y", 0x00FF00), + BitField("z", 0x0000FF), ), bit_width=8 * 3), - Register('CONTROL0', 0x1f, fields=( - BitField('int2_high_pass', 0b00000001), - BitField('int1_high_pass', 0b00000010), - BitField('click_high_pass', 0b00000100), - BitField('fifo_threshold', 0b00100000), - BitField('fifo_enable', 0b01000000), - BitField('reboot_memory', 0b10000000), + Register("CONTROL0", 0x1f, fields=( + BitField("int2_high_pass", 0b00000001), + BitField("int1_high_pass", 0b00000010), + BitField("click_high_pass", 0b00000100), + BitField("fifo_threshold", 0b00100000), + BitField("fifo_enable", 0b01000000), + BitField("reboot_memory", 0b10000000), )), - Register('CONTROL1', 0x20, fields=( - BitField('accel_x_enable', 0b00000001), - BitField('accel_y_enable', 0b00000010), - BitField('accel_z_enable', 0b00000100), - BitField('block_data_update', 0b00001000), - BitField('accel_data_rate_hz', 0b11110000, adapter=LookupAdapter({ + Register("CONTROL1", 0x20, fields=( + BitField("accel_x_enable", 0b00000001), + BitField("accel_y_enable", 0b00000010), + BitField("accel_z_enable", 0b00000100), + BitField("block_data_update", 0b00001000), + BitField("accel_data_rate_hz", 0b11110000, adapter=LookupAdapter({ 0: 0, 3.125: 0b0001, 6.25: 0b0010, @@ -130,17 +130,17 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): })), )), - Register('CONTROL2', 0x21, fields=( - BitField('serial_interface_mode', 0b00000001), - BitField('accel_self_test', 0b00000010), - BitField('accel_full_scale_g', 0b00111000, adapter=LookupAdapter({ + Register("CONTROL2", 0x21, fields=( + BitField("serial_interface_mode", 0b00000001), + BitField("accel_self_test", 0b00000010), + BitField("accel_full_scale_g", 0b00111000, adapter=LookupAdapter({ 2: 0b000, 4: 0b001, 6: 0b010, 8: 0b011, 16: 0b100 })), - BitField('accel_antialias_bw_hz', 0b11000000, adapter=LookupAdapter({ + BitField("accel_antialias_bw_hz", 0b11000000, adapter=LookupAdapter({ 50: 0b11, 362: 0b10, 194: 0b01, @@ -149,33 +149,33 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): )), # Known in the datasheet as CTRL3 - Register('INTERRUPT1', 0x22, fields=( - BitField('enable_fifo_empty', 0b00000001), - BitField('enable_accel_dataready', 0b00000010), - BitField('enable_accelerometer', 0b00000100), - BitField('enable_magnetometer', 0b00001000), - BitField('enable_ig2', 0b00010000), - BitField('enable_ig1', 0b00100000), - BitField('enable_click', 0b01000000), - BitField('enable_boot', 0b10000000), + Register("INTERRUPT1", 0x22, fields=( + BitField("enable_fifo_empty", 0b00000001), + BitField("enable_accel_dataready", 0b00000010), + BitField("enable_accelerometer", 0b00000100), + BitField("enable_magnetometer", 0b00001000), + BitField("enable_ig2", 0b00010000), + BitField("enable_ig1", 0b00100000), + BitField("enable_click", 0b01000000), + BitField("enable_boot", 0b10000000), )), # Known in the datasheet as CTRL4 - Register('INTERRUPT2', 0x23, fields=( - BitField('enable_fifo', 0b00000001), - BitField('enable_fifo_overrun', 0b00000010), - BitField('enable_mag_dataready', 0b00000100), - BitField('enable_accel_dataready', 0b00001000), - BitField('enable_magnetometer', 0b00010000), - BitField('enable_ig2', 0b00100000), - BitField('enable_ig1', 0b01000000), - BitField('enable_click', 0b10000000), + Register("INTERRUPT2", 0x23, fields=( + BitField("enable_fifo", 0b00000001), + BitField("enable_fifo_overrun", 0b00000010), + BitField("enable_mag_dataready", 0b00000100), + BitField("enable_accel_dataready", 0b00001000), + BitField("enable_magnetometer", 0b00010000), + BitField("enable_ig2", 0b00100000), + BitField("enable_ig1", 0b01000000), + BitField("enable_click", 0b10000000), )), - Register('CONTROL5', 0x24, fields=( - BitField('latch_int1', 0b00000001), - BitField('latch_int2', 0b00000010), - BitField('mag_data_rate_hz', 0b00011100, adapter=LookupAdapter({ + Register("CONTROL5", 0x24, fields=( + BitField("latch_int1", 0b00000001), + BitField("latch_int2", 0b00000010), + BitField("mag_data_rate_hz", 0b00011100, adapter=LookupAdapter({ 3.125: 0b000, 6.25: 0b001, 12.5: 0b010, @@ -183,12 +183,12 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): 50: 0b100, 100: 0b101, })), - BitField('mag_resolution', 0b01100000), - BitField('enable_temperature', 0b10000000), + BitField("mag_resolution", 0b01100000), + BitField("enable_temperature", 0b10000000), )), - Register('CONTROL6', 0x25, fields=( - BitField('mag_full_scale_gauss', 0b01100000, adapter=LookupAdapter({ + Register("CONTROL6", 0x25, fields=( + BitField("mag_full_scale_gauss", 0b01100000, adapter=LookupAdapter({ 2: 0b00, 4: 0b01, 8: 0b10, @@ -196,103 +196,103 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): })), )), - Register('CONTROL7', 0x26, fields=( - BitField('mag_mode', 0b00000011, adapter=LookupAdapter({'continuous': 0b00, 'single': 0b01, 'off': 0b10})), - BitField('mag_lowpowermode', 0b00000100), - BitField('temperature_only', 0b00010000), - BitField('filter_accel', 0b00100000), - BitField('high_pass_mode_accel', 0b11000000), # See page 39 of lsm303d.pdf + Register("CONTROL7", 0x26, fields=( + BitField("mag_mode", 0b00000011, adapter=LookupAdapter({"continuous": 0b00, "single": 0b01, "off": 0b10})), + BitField("mag_lowpowermode", 0b00000100), + BitField("temperature_only", 0b00010000), + BitField("filter_accel", 0b00100000), + BitField("high_pass_mode_accel", 0b11000000), # See page 39 of lsm303d.pdf )), # Accelerometer interrupt status register - Register('ACCELEROMETER_STATUS', 0x27, fields=( - BitField('xdata', 0b00000001), - BitField('ydata', 0b00000010), - BitField('zdata', 0b00000100), - BitField('data', 0b00001000), - BitField('xoverrun', 0b00010000), - BitField('yoverrun', 0b00100000), - BitField('zoverrun', 0b01000000), - BitField('overrun', 0b10000000) + Register("ACCELEROMETER_STATUS", 0x27, fields=( + BitField("xdata", 0b00000001), + BitField("ydata", 0b00000010), + BitField("zdata", 0b00000100), + BitField("data", 0b00001000), + BitField("xoverrun", 0b00010000), + BitField("yoverrun", 0b00100000), + BitField("zoverrun", 0b01000000), + BitField("overrun", 0b10000000) )), # X/Y/Z values from accelerometer - Register('ACCELEROMETER', 0x28 | 0x80, fields=( - BitField('x', 0xFFFF00000000, adapter=S16ByteSwapAdapter()), - BitField('y', 0x0000FFFF0000, adapter=S16ByteSwapAdapter()), - BitField('z', 0x00000000FFFF, adapter=S16ByteSwapAdapter()), + Register("ACCELEROMETER", 0x28 | 0x80, fields=( + BitField("x", 0xFFFF00000000, adapter=S16ByteSwapAdapter()), + BitField("y", 0x0000FFFF0000, adapter=S16ByteSwapAdapter()), + BitField("z", 0x00000000FFFF, adapter=S16ByteSwapAdapter()), ), bit_width=8 * 6), # FIFO control register - Register('FIFO_CONTROL', 0x2e, fields=( - BitField('mode', 0b11100000), - BitField('threshold', 0b00011111), + Register("FIFO_CONTROL", 0x2e, fields=( + BitField("mode", 0b11100000), + BitField("threshold", 0b00011111), )), # FIFO status register - Register('FIFO_STATUS', 0x2f, fields=( - BitField('threshold_exceeded', 1 << 7), - BitField('overrun', 1 << 6), - BitField('empty', 1 << 5), - BitField('unread_levels', 0b00011111), # Current number of unread FIFO levels + Register("FIFO_STATUS", 0x2f, fields=( + BitField("threshold_exceeded", 1 << 7), + BitField("overrun", 1 << 6), + BitField("empty", 1 << 5), + BitField("unread_levels", 0b00011111), # Current number of unread FIFO levels )), # 0x30: Internal interrupt generator 1: configuration register # 0x31: Internal interrupt generator 1: status register # 0x32: Internal interrupt generator 1: threshold register # 0x33: Internal interrupt generator 1: duration register - Register('IG_CONFIG1', 0x30 | 0x80, fields=( + Register("IG_CONFIG1", 0x30 | 0x80, fields=( # 0x30 - BitField('and_or_combination', 1 << 31), - BitField('enable_6d', 1 << 30), - BitField('z_high_enable', 1 << 29), - BitField('z_low_enable', 1 << 28), - BitField('y_high_enable', 1 << 27), - BitField('y_low_enable', 1 << 26), - BitField('x_high_enable', 1 << 25), - BitField('x_low_enble', 1 << 24), + BitField("and_or_combination", 1 << 31), + BitField("enable_6d", 1 << 30), + BitField("z_high_enable", 1 << 29), + BitField("z_low_enable", 1 << 28), + BitField("y_high_enable", 1 << 27), + BitField("y_low_enable", 1 << 26), + BitField("x_high_enable", 1 << 25), + BitField("x_low_enble", 1 << 24), # 0x31 - BitField('interrupt_status', 1 << 23), - BitField('z_high', 1 << 22), - BitField('z_low', 1 << 21), - BitField('y_high', 1 << 20), - BitField('y_low', 1 << 19), - BitField('x_high', 1 << 18), - BitField('x_low', 1 << 17), - BitField('status', 1 << 16), + BitField("interrupt_status", 1 << 23), + BitField("z_high", 1 << 22), + BitField("z_low", 1 << 21), + BitField("y_high", 1 << 20), + BitField("y_low", 1 << 19), + BitField("x_high", 1 << 18), + BitField("x_low", 1 << 17), + BitField("status", 1 << 16), # 0x32 - BitField('threshold', 0xff << 8), + BitField("threshold", 0xff << 8), # 0x33 - BitField('duration', 0xff), + BitField("duration", 0xff), ), bit_width=32), # 0x34: Internal interrupt generator 2: configuration register # 0x35: Internal interrupt generator 2: status register # 0x36: Internal interrupt generator 2: threshold register # 0x37: Internal interrupt generator 2: duration register - Register('IG_CONFIG1', 0x30 | 0x80, fields=( + Register("IG_CONFIG1", 0x30 | 0x80, fields=( # 0x34 - BitField('and_or_combination', 1 << 31), - BitField('enable_6d', 1 << 30), - BitField('z_high_enable', 1 << 29), - BitField('z_low_enable', 1 << 28), - BitField('y_high_enable', 1 << 27), - BitField('y_low_enable', 1 << 26), - BitField('x_high_enable', 1 << 25), - BitField('x_low_enble', 1 << 24), + BitField("and_or_combination", 1 << 31), + BitField("enable_6d", 1 << 30), + BitField("z_high_enable", 1 << 29), + BitField("z_low_enable", 1 << 28), + BitField("y_high_enable", 1 << 27), + BitField("y_low_enable", 1 << 26), + BitField("x_high_enable", 1 << 25), + BitField("x_low_enble", 1 << 24), # 0x35 - BitField('interrupt_status', 1 << 23), - BitField('z_high', 1 << 22), - BitField('z_low', 1 << 21), - BitField('y_high', 1 << 20), - BitField('y_low', 1 << 19), - BitField('x_high', 1 << 18), - BitField('x_low', 1 << 17), - BitField('status', 1 << 16), + BitField("interrupt_status", 1 << 23), + BitField("z_high", 1 << 22), + BitField("z_low", 1 << 21), + BitField("y_high", 1 << 20), + BitField("y_low", 1 << 19), + BitField("x_high", 1 << 18), + BitField("x_low", 1 << 17), + BitField("status", 1 << 16), # 0x36 - BitField('threshold', 0xff << 8), + BitField("threshold", 0xff << 8), # 0x37 - BitField('duration', 0xff), + BitField("duration", 0xff), ), bit_width=32), # 0x38: Click: configuration register @@ -301,38 +301,38 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): # 0x3B: Click: time limit register # 0x3C: Click: time latency register # 0x3D: Click: time window register - Register('CLICK', 0x38 | 0x80, fields=( + Register("CLICK", 0x38 | 0x80, fields=( # 0x38 # bits 1 << 47 and 1 << 46 are unimplemented - BitField('z_doubleclick_enable', 1 << 45), - BitField('z_click_enable', 1 << 44), - BitField('y_doubleclick_enable', 1 << 43), - BitField('y_click_enable', 1 << 42), - BitField('x_doubleclick_enable', 1 << 41), - BitField('x_click_enable', 1 << 40), + BitField("z_doubleclick_enable", 1 << 45), + BitField("z_click_enable", 1 << 44), + BitField("y_doubleclick_enable", 1 << 43), + BitField("y_click_enable", 1 << 42), + BitField("x_doubleclick_enable", 1 << 41), + BitField("x_click_enable", 1 << 40), # 0x39 # bit 1 << 39 is unimplemented - BitField('interrupt_enable', 1 << 38), - BitField('doubleclick_enable', 1 << 37), - BitField('click_enable', 1 << 36), - BitField('sign', 1 << 35), # 0 positive detection, 1 negative detection - BitField('z', 1 << 34), - BitField('y', 1 << 33), - BitField('x', 1 << 32), + BitField("interrupt_enable", 1 << 38), + BitField("doubleclick_enable", 1 << 37), + BitField("click_enable", 1 << 36), + BitField("sign", 1 << 35), # 0 positive detection, 1 negative detection + BitField("z", 1 << 34), + BitField("y", 1 << 33), + BitField("x", 1 << 32), # 0x3A - BitField('threshod', 0xFF << 24), + BitField("threshod", 0xFF << 24), # 0x3B - BitField('time_limit', 0xFF << 16), + BitField("time_limit", 0xFF << 16), # 0x3C - BitField('time_latency', 0xFF << 8), + BitField("time_latency", 0xFF << 8), # 0x3D - BitField('time_window', 0xFF), + BitField("time_window", 0xFF), ), bit_width=8 * 6), # Controls the threshold and duration of returning to sleep mode - Register('ACT', 0x3e | 0x80, fields=( - BitField('threshold', 0xFF00), # 1 LSb = 16mg - BitField('duration', 0x00FF) # (duration + 1) * 8/output_data_rate + Register("ACT", 0x3e | 0x80, fields=( + BitField("threshold", 0xFF00), # 1 LSb = 16mg + BitField("duration", 0x00FF) # (duration + 1) * 8/output_data_rate ), bit_width=16) )) @@ -349,7 +349,7 @@ def set_accel_full_scale_g(self, scale): """ self._accel_full_scale_g = scale - self._lsm303d.set('CONTROL2', accel_full_scale_g=self._accel_full_scale_g) + self._lsm303d.set("CONTROL2", accel_full_scale_g=self._accel_full_scale_g) def set_mag_full_scale_gauss(self, scale): """Set the full scale range for the magnetometer in gauss @@ -358,7 +358,7 @@ def set_mag_full_scale_gauss(self, scale): """ self._mag_full_scale_gauss = scale - self._lsm303d.set('CONTROL6', mag_full_scale_gauss=scale) # +-2 + self._lsm303d.set("CONTROL6", mag_full_scale_gauss=scale) # +-2 set_mag_full_scale_guass = set_mag_full_scale_gauss @@ -370,13 +370,13 @@ def setup(self): self._lsm303d.select_address(self._i2c_addr) try: - chip = self._lsm303d.get('WHOAMI') + chip = self._lsm303d.get("WHOAMI") if chip.id != 0x49: raise RuntimeError("Unable to find lsm303d on 0x{:02x}, WHOAMI returned {:02x}".format(self._i2c_addr, chip.id)) except IOError: raise RuntimeError("Unable to find lsm303d on 0x{:02x}, IOError".format(self._i2c_addr)) - self._lsm303d.set('CONTROL1', + self._lsm303d.set("CONTROL1", accel_x_enable=1, accel_y_enable=1, accel_z_enable=1, @@ -384,7 +384,7 @@ def setup(self): self.set_accel_full_scale_g(2) - self._lsm303d.set('INTERRUPT1', + self._lsm303d.set("INTERRUPT1", enable_fifo_empty=0, enable_accel_dataready=0, enable_accelerometer=0, @@ -394,7 +394,7 @@ def setup(self): enable_click=0, enable_boot=0) - self._lsm303d.set('INTERRUPT2', + self._lsm303d.set("INTERRUPT2", enable_fifo=0, enable_fifo_overrun=0, enable_mag_dataready=0, @@ -404,13 +404,13 @@ def setup(self): enable_ig1=0, enable_click=0) - self._lsm303d.set('CONTROL5', + self._lsm303d.set("CONTROL5", mag_data_rate_hz=50, enable_temperature=1) self.set_mag_full_scale_gauss(2) - self._lsm303d.set('CONTROL7', mag_mode='continuous') + self._lsm303d.set("CONTROL7", mag_mode="continuous") def magnetometer(self): """Return magnetometer x, y and z readings. @@ -419,7 +419,7 @@ def magnetometer(self): """ self.setup() - mag = self._lsm303d.get('MAGNETOMETER') + mag = self._lsm303d.get("MAGNETOMETER") x, y, z = mag.x, mag.y, mag.z x, y, z = [(p / 32767.0) * self._mag_full_scale_gauss for p in (x, y, z)] return x, y, z @@ -431,7 +431,7 @@ def accelerometer(self): """ self.setup() - accel = self._lsm303d.get('ACCELEROMETER') + accel = self._lsm303d.get("ACCELEROMETER") x, y, z = accel.x, accel.y, accel.z x, y, z = [(p / 32767.0) * self._accel_full_scale_g for p in (x, y, z)] return x, y, z @@ -439,4 +439,4 @@ def accelerometer(self): def temperature(self): """Return the temperature""" self.setup() - return self._lsm303d.get('TEMPERATURE').temperature + return self._lsm303d.get("TEMPERATURE").temperature From 2db71357464c209228bc60444823a4ecceebec65 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 21 May 2024 10:24:05 +0100 Subject: [PATCH 6/7] Fix threshold on CLICK register. --- lsm303d/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsm303d/__init__.py b/lsm303d/__init__.py index f29d863..b695544 100644 --- a/lsm303d/__init__.py +++ b/lsm303d/__init__.py @@ -320,7 +320,7 @@ def __init__(self, i2c_addr=0x1D, i2c_dev=None): BitField("y", 1 << 33), BitField("x", 1 << 32), # 0x3A - BitField("threshod", 0xFF << 24), + BitField("threshold", 0xFF << 24), # 0x3B BitField("time_limit", 0xFF << 16), # 0x3C From 2bcbf3a613014c4796598b47f1f0cdaa2e50709d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 21 May 2024 10:45:40 +0100 Subject: [PATCH 7/7] Prep for v0.0.0. --- CHANGELOG.md | 7 +++++++ README.md | 52 ++++++++++++++++++++-------------------------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b5543..8b7f248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +1.0.0 +----- + +* Port to hatch/pyproject.toml +* Require i2cdevice>=1.0.0 +* Fix typo in CLICK register "threshold" option + 0.0.5 ----- diff --git a/README.md b/README.md index 98593eb..62ff5a6 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,35 @@ # lsm303d Accelerometer And Compass -[![Build Status](https://shields.io/github/workflow/status/pimoroni/lsm303d-python/Python%20Tests.svg)](https://github.com/pimoroni/sgp30-python/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/pimoroni/lsm303d-python/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/lsm303d-python?branch=master) +[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/lsm303d-python/test.yml?branch=main)](https://github.com/pimoroni/lsm303d-python/actions/workflows/test.yml) +[![Coverage Status](https://coveralls.io/repos/github/pimoroni/lsm303d-python/badge.svg?branch=main)](https://coveralls.io/github/pimoroni/lsm303d-python?branch=main) [![PyPi Package](https://img.shields.io/pypi/v/lsm303d.svg)](https://pypi.python.org/pypi/lsm303d) [![Python Versions](https://img.shields.io/pypi/pyversions/lsm303d.svg)](https://pypi.python.org/pypi/lsm303d) -Suitable for measuring orientation and motion, the lsm303d has a 3-axis accelerometer and compass. # Installing -Stable library from PyPi: +If you've already set up a Python virtual environment, you can also install the stable library manually from PyPi: -* Just run `python3 -m pip install lsm303d` +``` +pip install lsm303d +``` -Latest/development library from GitHub: - -* `git clone https://github.com/pimoroni/lsm303d-python` -* `cd lsm303d-python` -* `sudo ./install.sh --unstable` - - -# Changelog -0.0.5 ------ - -* Require i2cdevice>=0.0.6 +Otherwise our install script will set one up for you. -0.0.4 ------ +Stable library from GitHub: -* Port to i2cdevice>=0.0.6 set/get API +``` +git clone https://github.com/pimoroni/lsm303d-python +cd lsm303d-python +./install.sh +``` -0.0.3 ------ - -* BugFix: Fix for compatibility with i2cdevice>=0.0.6 - -0.0.2 ------ - -* Bumped i2cdevice dependency to >=0.0.4 +Latest/development library from GitHub: -0.0.1 ------ +``` +git clone https://github.com/pimoroni/lsm303d-python +cd lsm303d-python +./install.sh --unstable +``` -* Initial Release +**Note** Libraries will be installed in the "pimoroni" virtual environment, you will need to activate it to run examples: