Compare commits
No commits in common. "main" and "1.4.0" have entirely different histories.
11 changed files with 219 additions and 497 deletions
13
.github/release-drafter.yml
vendored
13
.github/release-drafter.yml
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams, written for Adafruit Industries
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
change-template: "- $TITLE #$NUMBER by @$AUTHOR"
|
|
||||||
template: |
|
|
||||||
## What's Changed
|
|
||||||
|
|
||||||
$CHANGES
|
|
||||||
|
|
||||||
To use in CPython, `pip3 install Adafruit-Python-Shell`.
|
|
||||||
|
|
||||||
Read the [docs](https://circuitpython.readthedocs.io/projects/shell/en/latest/) for info on how to use it.
|
|
||||||
35
.github/workflows/build.yml
vendored
35
.github/workflows/build.yml
vendored
|
|
@ -10,27 +10,31 @@ jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Dump GitHub context
|
||||||
|
env:
|
||||||
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||||
|
run: echo "$GITHUB_CONTEXT"
|
||||||
- name: Translate Repo Name For Build Tools filename_prefix
|
- name: Translate Repo Name For Build Tools filename_prefix
|
||||||
id: repo-name
|
id: repo-name
|
||||||
run: |
|
run: |
|
||||||
echo repo-name=$(
|
echo ::set-output name=repo-name::$(
|
||||||
echo ${{ github.repository }} |
|
echo ${{ github.repository }} |
|
||||||
awk -F '\/' '{ print tolower($2) }' |
|
awk -F '\/' '{ print tolower($2) }' |
|
||||||
tr '_' '-'
|
tr '_' '-'
|
||||||
) >> $GITHUB_OUTPUT
|
)
|
||||||
- name: Set up Python 3.11
|
- name: Set up Python 3.7
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: 3.7
|
||||||
- name: Versions
|
- name: Versions
|
||||||
run: |
|
run: |
|
||||||
python3 --version
|
python3 --version
|
||||||
- name: Checkout Current Repo
|
- name: Checkout Current Repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v1
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: adafruit/actions-ci-circuitpython-libs
|
repository: adafruit/actions-ci-circuitpython-libs
|
||||||
path: actions-ci
|
path: actions-ci
|
||||||
|
|
@ -40,23 +44,26 @@ jobs:
|
||||||
source actions-ci/install.sh
|
source actions-ci/install.sh
|
||||||
- name: Pip install pylint, Sphinx, pre-commit
|
- name: Pip install pylint, Sphinx, pre-commit
|
||||||
run: |
|
run: |
|
||||||
pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit
|
pip install --force-reinstall pylint Sphinx sphinx-rtd-theme pre-commit
|
||||||
- name: Library version
|
- name: Library version
|
||||||
run: git describe --dirty --always --tags
|
run: git describe --dirty --always --tags
|
||||||
- name: Pre-commit hooks
|
- name: Pre-commit hooks
|
||||||
run: |
|
run: |
|
||||||
pre-commit run --all-files
|
pre-commit run --all-files
|
||||||
|
- name: PyLint
|
||||||
|
run: |
|
||||||
|
pylint $( find . -path './adafruit*.py' )
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
working-directory: docs
|
working-directory: docs
|
||||||
run: sphinx-build -E -W -b html . _build/html
|
run: sphinx-build -E -W -b html . _build/html
|
||||||
- name: Check For pyproject.toml
|
- name: Check For setup.py
|
||||||
id: need-pypi
|
id: need-pypi
|
||||||
run: |
|
run: |
|
||||||
echo pyproject-toml=$( find . -wholename './pyproject.toml' ) >> $GITHUB_OUTPUT
|
echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
|
||||||
- name: Build Python package
|
- name: Build Python package
|
||||||
if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
|
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade build twine
|
pip install --upgrade setuptools wheel twine readme_renderer testresources
|
||||||
find -type f -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) -exec sed -i -e "s/0.0.0+auto.0/1.2.3/" {} +
|
python setup.py sdist
|
||||||
python -m build
|
python setup.py bdist_wheel --universal
|
||||||
twine check dist/*
|
twine check dist/*
|
||||||
|
|
|
||||||
25
.github/workflows/release-drafter.yml
vendored
25
.github/workflows/release-drafter.yml
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams, written for Adafruit Industries
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
name: Release Drafter
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update_release_draft:
|
|
||||||
permissions:
|
|
||||||
# write permission is required to create a github release
|
|
||||||
contents: write
|
|
||||||
pull-requests: read
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: release-drafter/release-drafter@v5
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
|
|
@ -12,27 +12,26 @@ jobs:
|
||||||
upload-pypi:
|
upload-pypi:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v1
|
||||||
- name: Check For pyproject.toml
|
- name: Check For setup.py
|
||||||
id: need-pypi
|
id: need-pypi
|
||||||
run: |
|
run: |
|
||||||
echo pyproject-toml=$( find . -wholename './pyproject.toml' ) >> $GITHUB_OUTPUT
|
echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
|
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.x'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
|
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install --upgrade build twine
|
pip install setuptools wheel twine
|
||||||
- name: Build and publish
|
- name: Build and publish
|
||||||
if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
|
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
|
||||||
env:
|
env:
|
||||||
TWINE_USERNAME: ${{ secrets.pypi_username }}
|
TWINE_USERNAME: ${{ secrets.pypi_username }}
|
||||||
TWINE_PASSWORD: ${{ secrets.pypi_password }}
|
TWINE_PASSWORD: ${{ secrets.pypi_password }}
|
||||||
run: |
|
run: |
|
||||||
find -type f -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) -exec sed -i -e "s/0.0.0+auto.0/${{github.event.release.tag_name}}/" {} +
|
python setup.py sdist
|
||||||
python -m build
|
|
||||||
twine upload dist/*
|
twine upload dist/*
|
||||||
|
|
|
||||||
|
|
@ -3,40 +3,17 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/python/black
|
- repo: https://github.com/python/black
|
||||||
rev: 23.3.0
|
rev: 22.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
- repo: https://github.com/fsfe/reuse-tool
|
- repo: https://github.com/fsfe/reuse-tool
|
||||||
rev: v1.1.2
|
rev: v0.12.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: reuse
|
- id: reuse
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v5.0.0
|
rev: v4.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/pycqa/pylint
|
|
||||||
rev: v2.17.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
|
|
||||||
|
|
|
||||||
56
.pylintrc
56
.pylintrc
|
|
@ -9,11 +9,11 @@
|
||||||
# run arbitrary code
|
# run arbitrary code
|
||||||
extension-pkg-whitelist=
|
extension-pkg-whitelist=
|
||||||
|
|
||||||
# Add files or directories to the ignore-list. They should be base names, not
|
# Add files or directories to the blacklist. They should be base names, not
|
||||||
# paths.
|
# paths.
|
||||||
ignore=CVS
|
ignore=CVS
|
||||||
|
|
||||||
# Add files or directories matching the regex patterns to the ignore-list. The
|
# Add files or directories matching the regex patterns to the blacklist. The
|
||||||
# regex matches against base names, not paths.
|
# regex matches against base names, not paths.
|
||||||
ignore-patterns=
|
ignore-patterns=
|
||||||
|
|
||||||
|
|
@ -22,11 +22,12 @@ ignore-patterns=
|
||||||
#init-hook=
|
#init-hook=
|
||||||
|
|
||||||
# Use multiple processes to speed up Pylint.
|
# Use multiple processes to speed up Pylint.
|
||||||
jobs=1
|
# jobs=1
|
||||||
|
jobs=2
|
||||||
|
|
||||||
# List of plugins (as comma separated values of python modules names) to load,
|
# List of plugins (as comma separated values of python modules names) to load,
|
||||||
# usually to register additional checkers.
|
# usually to register additional checkers.
|
||||||
load-plugins=pylint.extensions.no_self_use
|
load-plugins=
|
||||||
|
|
||||||
# Pickle collected data for later comparisons.
|
# Pickle collected data for later comparisons.
|
||||||
persistent=yes
|
persistent=yes
|
||||||
|
|
@ -54,8 +55,8 @@ confidence=
|
||||||
# --enable=similarities". If you want to run only the classes checker, but have
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||||
# --disable=W"
|
# --disable=W"
|
||||||
# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call
|
# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
|
||||||
disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding
|
disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation
|
||||||
|
|
||||||
# Enable the message, report, category or checker with the given id(s). You can
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
# either give multiple identifier separated by comma (,) or put this option
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
|
|
@ -225,6 +226,12 @@ max-line-length=100
|
||||||
# Maximum number of lines in a module
|
# Maximum number of lines in a module
|
||||||
max-module-lines=1000
|
max-module-lines=1000
|
||||||
|
|
||||||
|
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||||
|
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||||
|
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||||
|
# `empty-line` allows space-only lines.
|
||||||
|
no-space-check=trailing-comma,dict-separator
|
||||||
|
|
||||||
# Allow the body of a class to be on the same line as the declaration if body
|
# Allow the body of a class to be on the same line as the declaration if body
|
||||||
# contains single statement.
|
# contains single statement.
|
||||||
single-line-class-stmt=no
|
single-line-class-stmt=no
|
||||||
|
|
@ -243,30 +250,46 @@ ignore-comments=yes
|
||||||
ignore-docstrings=yes
|
ignore-docstrings=yes
|
||||||
|
|
||||||
# Ignore imports when computing similarities.
|
# Ignore imports when computing similarities.
|
||||||
ignore-imports=yes
|
ignore-imports=no
|
||||||
|
|
||||||
# Minimum lines number of a similarity.
|
# Minimum lines number of a similarity.
|
||||||
min-similarity-lines=12
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
|
||||||
[BASIC]
|
[BASIC]
|
||||||
|
|
||||||
|
# Naming hint for argument names
|
||||||
|
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
# Regular expression matching correct argument names
|
# Regular expression matching correct argument names
|
||||||
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
|
# Naming hint for attribute names
|
||||||
|
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
# Regular expression matching correct attribute names
|
# Regular expression matching correct attribute names
|
||||||
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
# Bad variable names which should always be refused, separated by a comma
|
# Bad variable names which should always be refused, separated by a comma
|
||||||
bad-names=foo,bar,baz,toto,tutu,tata
|
bad-names=foo,bar,baz,toto,tutu,tata
|
||||||
|
|
||||||
|
# Naming hint for class attribute names
|
||||||
|
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||||
|
|
||||||
# Regular expression matching correct class attribute names
|
# Regular expression matching correct class attribute names
|
||||||
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||||
|
|
||||||
|
# Naming hint for class names
|
||||||
|
# class-name-hint=[A-Z_][a-zA-Z0-9]+$
|
||||||
|
class-name-hint=[A-Z_][a-zA-Z0-9_]+$
|
||||||
|
|
||||||
# Regular expression matching correct class names
|
# Regular expression matching correct class names
|
||||||
# class-rgx=[A-Z_][a-zA-Z0-9]+$
|
# class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||||
class-rgx=[A-Z_][a-zA-Z0-9_]+$
|
class-rgx=[A-Z_][a-zA-Z0-9_]+$
|
||||||
|
|
||||||
|
# Naming hint for constant names
|
||||||
|
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||||
|
|
||||||
# Regular expression matching correct constant names
|
# Regular expression matching correct constant names
|
||||||
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||||
|
|
||||||
|
|
@ -274,6 +297,9 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||||
# ones are exempt.
|
# ones are exempt.
|
||||||
docstring-min-length=-1
|
docstring-min-length=-1
|
||||||
|
|
||||||
|
# Naming hint for function names
|
||||||
|
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
# Regular expression matching correct function names
|
# Regular expression matching correct function names
|
||||||
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
|
|
@ -284,12 +310,21 @@ good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_
|
||||||
# Include a hint for the correct naming format with invalid-name
|
# Include a hint for the correct naming format with invalid-name
|
||||||
include-naming-hint=no
|
include-naming-hint=no
|
||||||
|
|
||||||
|
# Naming hint for inline iteration names
|
||||||
|
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
|
||||||
|
|
||||||
# Regular expression matching correct inline iteration names
|
# Regular expression matching correct inline iteration names
|
||||||
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||||
|
|
||||||
|
# Naming hint for method names
|
||||||
|
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
# Regular expression matching correct method names
|
# Regular expression matching correct method names
|
||||||
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
|
# Naming hint for module names
|
||||||
|
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||||
|
|
||||||
# Regular expression matching correct module names
|
# Regular expression matching correct module names
|
||||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||||
|
|
||||||
|
|
@ -305,6 +340,9 @@ no-docstring-rgx=^_
|
||||||
# to this list to register other decorators that produce valid properties.
|
# to this list to register other decorators that produce valid properties.
|
||||||
property-classes=abc.abstractproperty
|
property-classes=abc.abstractproperty
|
||||||
|
|
||||||
|
# Naming hint for variable names
|
||||||
|
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
# Regular expression matching correct variable names
|
# Regular expression matching correct variable names
|
||||||
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
|
||||||
|
|
||||||
|
|
@ -396,4 +434,4 @@ min-public-methods=1
|
||||||
|
|
||||||
# Exceptions that will emit a warning when being caught. Defaults to
|
# Exceptions that will emit a warning when being caught. Defaults to
|
||||||
# "Exception"
|
# "Exception"
|
||||||
overgeneral-exceptions=builtins.Exception
|
overgeneral-exceptions=Exception
|
||||||
|
|
|
||||||
|
|
@ -23,57 +23,18 @@ Implementation Notes
|
||||||
# imports
|
# imports
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import stat
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import fcntl
|
|
||||||
import platform
|
import platform
|
||||||
import fileinput
|
import fileinput
|
||||||
import re
|
import re
|
||||||
import pwd
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from clint.textui import colored, prompt
|
from clint.textui import colored, prompt
|
||||||
import adafruit_platformdetect
|
import adafruit_platformdetect
|
||||||
|
|
||||||
__version__ = "0.0.0+auto.0"
|
__version__ = "0.0.0-auto.0"
|
||||||
__repo__ = "https://github.com/adafruit/Adafruit_Python_Shell.git"
|
__repo__ = "https://github.com/adafruit/Adafruit_Python_Shell.git"
|
||||||
|
|
||||||
# This must be by order of release
|
|
||||||
RASPI_VERSIONS = (
|
|
||||||
"wheezy",
|
|
||||||
"jessie",
|
|
||||||
"stretch",
|
|
||||||
"buster",
|
|
||||||
"bullseye",
|
|
||||||
"bookworm",
|
|
||||||
"trixie",
|
|
||||||
)
|
|
||||||
|
|
||||||
WINDOW_MANAGERS = {
|
|
||||||
"x11": "W1",
|
|
||||||
"wayfire": "W2",
|
|
||||||
"labwc": "W3",
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE_MODES = {
|
|
||||||
"+x": stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,
|
|
||||||
"+r": stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH,
|
|
||||||
"+w": stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH,
|
|
||||||
"a+x": stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,
|
|
||||||
"a+r": stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH,
|
|
||||||
"a+w": stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH,
|
|
||||||
"u+x": stat.S_IXUSR,
|
|
||||||
"u+r": stat.S_IRUSR,
|
|
||||||
"u+w": stat.S_IWUSR,
|
|
||||||
"g+x": stat.S_IXGRP,
|
|
||||||
"g+r": stat.S_IRGRP,
|
|
||||||
"g+w": stat.S_IWGRP,
|
|
||||||
"o+x": stat.S_IXOTH,
|
|
||||||
"o+r": stat.S_IROTH,
|
|
||||||
"o+w": stat.S_IWOTH,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
class Shell:
|
class Shell:
|
||||||
"""
|
"""
|
||||||
|
|
@ -102,145 +63,79 @@ class Shell:
|
||||||
)
|
)
|
||||||
return prompt.options(message, options)
|
return prompt.options(message, options)
|
||||||
|
|
||||||
def run_command(
|
def run_command(self, cmd, suppress_message=False, return_output=False):
|
||||||
self, cmd, suppress_message=False, return_output=False, run_as_user=None
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Run a shell command and show the output as it runs
|
Run a shell command and show the output as it runs
|
||||||
"""
|
"""
|
||||||
|
original_stdout = sys.stdout
|
||||||
def read_stream(output):
|
original_stderr = sys.stderr
|
||||||
file_descriptor = output.fileno()
|
try:
|
||||||
file_flags = fcntl.fcntl(file_descriptor, fcntl.F_GETFL)
|
# pylint: disable=consider-using-with
|
||||||
fcntl.fcntl(file_descriptor, fcntl.F_SETFL, file_flags | os.O_NONBLOCK)
|
proc = subprocess.Popen(
|
||||||
try:
|
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
return output.read()
|
|
||||||
except TypeError:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
# Allow running as a different user if we are root
|
|
||||||
if self.is_root() and run_as_user is not None:
|
|
||||||
pw_record = pwd.getpwnam(run_as_user)
|
|
||||||
env = os.environ.copy()
|
|
||||||
env["HOME"] = pw_record.pw_dir
|
|
||||||
env["LOGNAME"] = run_as_user
|
|
||||||
env["USER"] = pw_record.pw_name
|
|
||||||
|
|
||||||
def preexec():
|
|
||||||
os.setgid(pw_record.pw_gid)
|
|
||||||
os.setuid(pw_record.pw_uid)
|
|
||||||
|
|
||||||
else:
|
|
||||||
env = None
|
|
||||||
preexec = None
|
|
||||||
|
|
||||||
full_output = ""
|
|
||||||
with subprocess.Popen( # pylint: disable=subprocess-popen-preexec-fn
|
|
||||||
cmd,
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
universal_newlines=True,
|
|
||||||
env=env,
|
|
||||||
preexec_fn=preexec,
|
|
||||||
) as proc:
|
|
||||||
while proc.poll() is None:
|
|
||||||
err = read_stream(proc.stderr)
|
|
||||||
if err != "" and not suppress_message:
|
|
||||||
self.error(err.strip(), end="\n\r")
|
|
||||||
output = read_stream(proc.stdout)
|
|
||||||
if output != "" and not suppress_message:
|
|
||||||
self.info(output.strip(), end="\n\r")
|
|
||||||
full_output += output
|
|
||||||
return_code = proc.poll()
|
|
||||||
proc.stdout.close()
|
|
||||||
proc.stderr.close()
|
|
||||||
if return_output:
|
|
||||||
return full_output
|
|
||||||
if return_code:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def write_templated_file(self, output_path, template, **kwargs):
|
|
||||||
"""
|
|
||||||
Use a template file and render it with the given context and write it to the specified path.
|
|
||||||
The template file should contain placeholders in the format {key} which will be replaced
|
|
||||||
with the corresponding values from the kwargs dictionary.
|
|
||||||
"""
|
|
||||||
# if path is an existing directory, the template filename will be used
|
|
||||||
output_path = self.path(output_path)
|
|
||||||
if os.path.isdir(output_path):
|
|
||||||
output_path = os.path.join(output_path, os.path.basename(template))
|
|
||||||
|
|
||||||
# Render the template with the provided context
|
|
||||||
rendered_content = self.load_template(template, **kwargs)
|
|
||||||
|
|
||||||
if rendered_content is None:
|
|
||||||
self.error(
|
|
||||||
f"Failed to load template '{template}'. Unable to write file '{output_path}'."
|
|
||||||
)
|
)
|
||||||
return False
|
# pylint: enable=consider-using-with
|
||||||
|
full_output = ""
|
||||||
|
while True:
|
||||||
|
output = proc.stdout.readline()
|
||||||
|
err = proc.stderr.read()
|
||||||
|
if err and not suppress_message:
|
||||||
|
self.error(err.decode("utf-8", errors="ignore"))
|
||||||
|
if len(output) == 0 and proc.poll() is not None:
|
||||||
|
break
|
||||||
|
if output:
|
||||||
|
decoded_output = output.decode("utf-8", errors="ignore").strip()
|
||||||
|
if not suppress_message:
|
||||||
|
self.info(decoded_output)
|
||||||
|
full_output += decoded_output
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
sys.stdout = original_stdout
|
||||||
|
sys.stderr = original_stderr
|
||||||
|
if return_output:
|
||||||
|
return full_output
|
||||||
|
r = proc.poll()
|
||||||
|
if r == 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
append = kwargs.get("append", False)
|
def info(self, message):
|
||||||
self.write_text_file(output_path, rendered_content, append=append)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def load_template(self, template, **kwargs):
|
|
||||||
"""
|
|
||||||
Load a template file and return its content with the placeholders replaced by the provided
|
|
||||||
context. The template file should contain placeholders in the format {key} which will be
|
|
||||||
replaced with the corresponding values from the kwargs dictionary.
|
|
||||||
"""
|
|
||||||
if not os.path.exists(template):
|
|
||||||
self.error(f"Template file '{template}' does not exist")
|
|
||||||
return None
|
|
||||||
|
|
||||||
with open(template, "r") as template_file:
|
|
||||||
template_content = template_file.read()
|
|
||||||
|
|
||||||
# Render the template with the provided context
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
template_content = template_content.replace(f"{{{key}}}", str(value))
|
|
||||||
|
|
||||||
return template_content
|
|
||||||
|
|
||||||
def info(self, message, **kwargs):
|
|
||||||
"""
|
"""
|
||||||
Display a message with the group in green
|
Display a message with the group in green
|
||||||
"""
|
"""
|
||||||
if self._group is not None:
|
if self._group is not None:
|
||||||
print(colored.green(self._group) + " " + message, **kwargs)
|
print(colored.green(self._group) + " " + message)
|
||||||
else:
|
else:
|
||||||
print(message, **kwargs)
|
print(message)
|
||||||
|
|
||||||
def warn(self, message, **kwargs):
|
def warn(self, message):
|
||||||
"""
|
"""
|
||||||
Display a message with the group in yellow
|
Display a message with the group in yellow
|
||||||
"""
|
"""
|
||||||
if self._group is not None:
|
if self._group is not None:
|
||||||
print(colored.yellow(self._group) + " " + message, **kwargs)
|
print(colored.yellow(self._group) + " " + message)
|
||||||
else:
|
else:
|
||||||
print(message, **kwargs)
|
print(message)
|
||||||
|
|
||||||
def bail(self, message=None, **kwargs):
|
def bail(self, message=None):
|
||||||
"""
|
"""
|
||||||
Exit and display an error message if given
|
Exit and display an error message if given
|
||||||
"""
|
"""
|
||||||
if message is None:
|
if message is None:
|
||||||
self.error("Exiting due to error", **kwargs)
|
self.error("Exiting due to error")
|
||||||
else:
|
else:
|
||||||
self.error(f"Exiting due to error: {message}", **kwargs)
|
self.error(f"Exiting due to error: {message}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def error(self, message, **kwargs):
|
def error(self, message):
|
||||||
"""
|
"""
|
||||||
Display some information
|
Display some inforrmation
|
||||||
"""
|
"""
|
||||||
if self._group is not None:
|
if self._group is not None:
|
||||||
print(colored.red(self._group) + " " + message, **kwargs)
|
print(colored.red(self._group) + " " + message)
|
||||||
else:
|
else:
|
||||||
print(message, **kwargs)
|
print(message)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def print_colored(message, color):
|
def print_colored(message, color):
|
||||||
|
|
@ -304,9 +199,9 @@ class Shell:
|
||||||
# directory = self.getcwd() + "/" + directory
|
# directory = self.getcwd() + "/" + directory
|
||||||
directory = self.path(directory)
|
directory = self.path(directory)
|
||||||
if not self.exists(directory):
|
if not self.exists(directory):
|
||||||
raise ValueError(f"Directory '{directory}' does not exist")
|
raise ValueError("Directory does not exist")
|
||||||
if not self.isdir(directory):
|
if not self.isdir(directory):
|
||||||
raise ValueError(f"The given location '{directory}' is not a directory")
|
raise ValueError("Given location is not a directory")
|
||||||
os.chdir(directory)
|
os.chdir(directory)
|
||||||
|
|
||||||
def pushd(self, directory):
|
def pushd(self, directory):
|
||||||
|
|
@ -384,10 +279,7 @@ class Shell:
|
||||||
# Not found; append (silently)
|
# Not found; append (silently)
|
||||||
self.write_text_file(file, replacement, append=True)
|
self.write_text_file(file, replacement, append=True)
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
def pattern_search(self, location, pattern, multi_line=False):
|
||||||
def pattern_search(
|
|
||||||
self, location, pattern, multi_line=False, return_match=False, find_all=False
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Similar to grep, but uses pure python
|
Similar to grep, but uses pure python
|
||||||
multi_line will search the entire file as a large text glob,
|
multi_line will search the entire file as a large text glob,
|
||||||
|
|
@ -397,22 +289,17 @@ class Shell:
|
||||||
"""
|
"""
|
||||||
location = self.path(location)
|
location = self.path(location)
|
||||||
found = False
|
found = False
|
||||||
search_function = re.findall if find_all else re.search
|
|
||||||
|
|
||||||
if self.exists(location) and not self.isdir(location):
|
if self.exists(location) and not self.isdir(location):
|
||||||
if multi_line:
|
if multi_line:
|
||||||
with open(location, "r+", encoding="utf-8") as file:
|
with open(location, "r+", encoding="utf-8") as file:
|
||||||
match = search_function(pattern, file.read(), flags=re.DOTALL)
|
if re.search(pattern, file.read(), flags=re.DOTALL):
|
||||||
if match:
|
|
||||||
found = True
|
found = True
|
||||||
else:
|
else:
|
||||||
for line in fileinput.FileInput(location):
|
for line in fileinput.FileInput(location):
|
||||||
match = search_function(pattern, line)
|
if re.search(pattern, line):
|
||||||
if match:
|
|
||||||
found = True
|
found = True
|
||||||
break
|
|
||||||
if return_match:
|
|
||||||
return match
|
|
||||||
return found
|
return found
|
||||||
|
|
||||||
def pattern_replace(self, location, pattern, replace="", multi_line=False):
|
def pattern_replace(self, location, pattern, replace="", multi_line=False):
|
||||||
|
|
@ -479,42 +366,6 @@ class Shell:
|
||||||
destination += os.sep + os.path.basename(source)
|
destination += os.sep + os.path.basename(source)
|
||||||
shutil.copy(source, destination)
|
shutil.copy(source, destination)
|
||||||
|
|
||||||
def chmod(self, location, mode):
|
|
||||||
"""
|
|
||||||
Change the permissions of a file or directory
|
|
||||||
"""
|
|
||||||
location = self.path(location)
|
|
||||||
# Convert a text mode to an integer mode
|
|
||||||
if isinstance(mode, str):
|
|
||||||
if mode not in FILE_MODES:
|
|
||||||
raise ValueError(f"Invalid mode string '{mode}'")
|
|
||||||
mode = FILE_MODES[mode]
|
|
||||||
if not 0 <= mode <= 0o777:
|
|
||||||
raise ValueError(f"Invalid mode value '{mode}'")
|
|
||||||
if os.path.exists(location):
|
|
||||||
os.chmod(location, mode)
|
|
||||||
|
|
||||||
def chown(self, location, user, group=None, recursive=False):
|
|
||||||
"""
|
|
||||||
Change the owner of a file or directory
|
|
||||||
"""
|
|
||||||
if group is None:
|
|
||||||
group = user
|
|
||||||
|
|
||||||
location = self.path(location)
|
|
||||||
if recursive and os.path.isdir(location):
|
|
||||||
for root, dirs, files in os.walk(location):
|
|
||||||
for directory in dirs:
|
|
||||||
shutil.chown(
|
|
||||||
os.path.join(root, directory),
|
|
||||||
user,
|
|
||||||
group,
|
|
||||||
)
|
|
||||||
for file in files:
|
|
||||||
shutil.chown(os.path.join(root, file), user, group)
|
|
||||||
else:
|
|
||||||
shutil.chown(location, user, group)
|
|
||||||
|
|
||||||
def remove(self, location):
|
def remove(self, location):
|
||||||
"""
|
"""
|
||||||
Remove a file or directory if it exists
|
Remove a file or directory if it exists
|
||||||
|
|
@ -547,16 +398,6 @@ class Shell:
|
||||||
with open(self.path(path), mode, encoding="utf-8") as service_file:
|
with open(self.path(path), mode, encoding="utf-8") as service_file:
|
||||||
service_file.write(content)
|
service_file.write(content)
|
||||||
|
|
||||||
def read_text_file(self, path):
|
|
||||||
"""
|
|
||||||
Read the contents of a file at the specified path
|
|
||||||
"""
|
|
||||||
path = self.path(path)
|
|
||||||
if not os.path.exists(path):
|
|
||||||
raise FileNotFoundError(f"File '{path}' does not exist")
|
|
||||||
with open(path, "r", encoding="utf-8") as file:
|
|
||||||
return file.read()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_python3():
|
def is_python3():
|
||||||
"Check if we are running Python 3 or later"
|
"Check if we are running Python 3 or later"
|
||||||
|
|
@ -628,10 +469,6 @@ class Shell:
|
||||||
with open("/etc/os-release", encoding="utf-8") as f:
|
with open("/etc/os-release", encoding="utf-8") as f:
|
||||||
if "Raspbian" in f.read():
|
if "Raspbian" in f.read():
|
||||||
release = "Raspbian"
|
release = "Raspbian"
|
||||||
if self.exists("/etc/rpi-issue"):
|
|
||||||
with open("/etc/rpi-issue", encoding="utf-8") as f:
|
|
||||||
if "Raspberry Pi" in f.read():
|
|
||||||
release = "Raspbian"
|
|
||||||
if self.run_command("command -v apt-get", suppress_message=True):
|
if self.run_command("command -v apt-get", suppress_message=True):
|
||||||
with open("/etc/os-release", encoding="utf-8") as f:
|
with open("/etc/os-release", encoding="utf-8") as f:
|
||||||
release_file = f.read()
|
release_file = f.read()
|
||||||
|
|
@ -654,29 +491,17 @@ class Shell:
|
||||||
"""Return a string containing the raspbian version"""
|
"""Return a string containing the raspbian version"""
|
||||||
if self.get_os() != "Raspbian":
|
if self.get_os() != "Raspbian":
|
||||||
return None
|
return None
|
||||||
|
raspbian_releases = ("bullseye", "buster", "stretch", "jessie", "wheezy")
|
||||||
if os.path.exists("/etc/os-release"):
|
if os.path.exists("/etc/os-release"):
|
||||||
with open("/etc/os-release", encoding="utf-8") as f:
|
with open("/etc/os-release", encoding="utf-8") as f:
|
||||||
release_file = f.read()
|
release_file = f.read()
|
||||||
if "/sid" in release_file:
|
if "/sid" in release_file:
|
||||||
return "unstable"
|
return "unstable"
|
||||||
for raspbian in RASPI_VERSIONS:
|
for raspbian in raspbian_releases:
|
||||||
if raspbian in release_file:
|
if raspbian in release_file:
|
||||||
return raspbian
|
return raspbian
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def is_minumum_version(self, version):
|
|
||||||
"""Check if the version is at least the specified version"""
|
|
||||||
# Check that version is a string
|
|
||||||
if not isinstance(version, str):
|
|
||||||
raise ValueError("Version must be a string")
|
|
||||||
# Check that version is in the list of valid versions
|
|
||||||
if version.lower() not in RASPI_VERSIONS:
|
|
||||||
raise ValueError("Invalid version")
|
|
||||||
# Check that the current version is at least the specified version
|
|
||||||
return RASPI_VERSIONS.index(
|
|
||||||
self.get_raspbian_version()
|
|
||||||
) >= RASPI_VERSIONS.index(version.lower())
|
|
||||||
|
|
||||||
def prompt_reboot(self, default="y", **kwargs):
|
def prompt_reboot(self, default="y", **kwargs):
|
||||||
"""Prompt the user for a reboot"""
|
"""Prompt the user for a reboot"""
|
||||||
if not self.prompt("REBOOT NOW?", default=default, **kwargs):
|
if not self.prompt("REBOOT NOW?", default=default, **kwargs):
|
||||||
|
|
@ -696,96 +521,8 @@ class Shell:
|
||||||
)
|
)
|
||||||
self.prompt_reboot()
|
self.prompt_reboot()
|
||||||
|
|
||||||
def check_kernel_userspace_mismatch(self, attempt_fix=True, fix_with_x11=False):
|
|
||||||
"""
|
|
||||||
Check if the userspace is 64-bit and kernel is 32-bit
|
|
||||||
"""
|
|
||||||
if self.is_kernel_userspace_mismatched():
|
|
||||||
print(
|
|
||||||
"Unable to compile driver because kernel space is 64-bit, but user space is 32-bit."
|
|
||||||
)
|
|
||||||
config = self.get_boot_config()
|
|
||||||
if (
|
|
||||||
self.is_raspberry_pi_os()
|
|
||||||
and attempt_fix
|
|
||||||
and config
|
|
||||||
and self.prompt(f"Add parameter to {config} to use 32-bit kernel?")
|
|
||||||
):
|
|
||||||
# Set to use 32-bit kernel
|
|
||||||
self.reconfig(config, "^.*arm_64bit.*$", "arm_64bit=0")
|
|
||||||
if fix_with_x11:
|
|
||||||
self.set_window_manager("x11")
|
|
||||||
self.prompt_reboot()
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Unable to continue while mismatch is present.")
|
|
||||||
|
|
||||||
def set_window_manager(self, manager):
|
|
||||||
"""
|
|
||||||
Call raspi-config to set a new window manager
|
|
||||||
"""
|
|
||||||
if not self.is_minumum_version("bullseye"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if manager.lower() not in WINDOW_MANAGERS:
|
|
||||||
raise ValueError("Invalid window manager")
|
|
||||||
|
|
||||||
if manager.lower() == "labwc" and not self.exists("/usr/bin/labwc"):
|
|
||||||
raise RuntimeError("labwc is not installed")
|
|
||||||
|
|
||||||
print(f"Using {manager} as the window manager")
|
|
||||||
if not self.run_command(
|
|
||||||
"sudo raspi-config nonint do_wayland " + WINDOW_MANAGERS[manager.lower()]
|
|
||||||
):
|
|
||||||
raise RuntimeError("Unable to change window manager")
|
|
||||||
|
|
||||||
def get_window_manager(self):
|
|
||||||
"""
|
|
||||||
Get the current window manager
|
|
||||||
"""
|
|
||||||
sessions = {"wayfire": "LXDE-pi-wayfire"}
|
|
||||||
# Check for Raspbian Desktop sessions
|
|
||||||
if self.exists("/usr/share/xsessions/rpd-x.desktop") or self.exists(
|
|
||||||
"/usr/share/wayland-sessions/rpd-labwc.desktop"
|
|
||||||
):
|
|
||||||
sessions.update({"x11": "rpd-x", "labwc": "rpd-labwc"})
|
|
||||||
else:
|
|
||||||
sessions.update({"x11": "LXDE-pi-x", "labwc": "LXDE-pi-labwc"})
|
|
||||||
|
|
||||||
matches = self.pattern_search(
|
|
||||||
"/etc/lightdm/lightdm.conf", "^(?!#.*?)user-session=(.+)", False, True
|
|
||||||
)
|
|
||||||
if matches:
|
|
||||||
session_match = matches.group(1)
|
|
||||||
for key, session in sessions.items():
|
|
||||||
if session_match == session:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_boot_config(self):
|
|
||||||
"""
|
|
||||||
Get the location of the boot config file
|
|
||||||
"""
|
|
||||||
# check if /boot/firmware/config.txt exists
|
|
||||||
if self.exists("/boot/firmware/config.txt"):
|
|
||||||
return "/boot/firmware/config.txt"
|
|
||||||
if self.exists("/boot/config.txt"):
|
|
||||||
return "/boot/config.txt"
|
|
||||||
return None
|
|
||||||
|
|
||||||
def is_kernel_userspace_mismatched(self):
|
|
||||||
"""
|
|
||||||
If the userspace 64-bit and kernel is 32-bit?
|
|
||||||
"""
|
|
||||||
return self.is_arm64() and platform.architecture()[0] == "32bit"
|
|
||||||
|
|
||||||
# pylint: enable=invalid-name
|
# pylint: enable=invalid-name
|
||||||
|
|
||||||
def is_raspberry_pi_os(self):
|
|
||||||
"""
|
|
||||||
Check if we are running Raspberry Pi OS or Raspbian
|
|
||||||
"""
|
|
||||||
return self.get_os() == "Raspbian"
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_raspberry_pi():
|
def is_raspberry_pi():
|
||||||
"""
|
"""
|
||||||
|
|
@ -802,14 +539,6 @@ class Shell:
|
||||||
detector = adafruit_platformdetect.Detector()
|
detector = adafruit_platformdetect.Detector()
|
||||||
return detector.board.id
|
return detector.board.id
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_pi5_or_newer():
|
|
||||||
"""
|
|
||||||
Use PlatformDetect to check if this is a Raspberry Pi 5 or newer
|
|
||||||
"""
|
|
||||||
detector = adafruit_platformdetect.Detector()
|
|
||||||
return detector.board.any_raspberry_pi_5_board
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_architecture():
|
def get_architecture():
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
27
docs/conf.py
27
docs/conf.py
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import datetime
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath(".."))
|
sys.path.insert(0, os.path.abspath(".."))
|
||||||
|
|
||||||
|
|
@ -17,14 +16,13 @@ sys.path.insert(0, os.path.abspath(".."))
|
||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinxcontrib.jquery",
|
|
||||||
"sphinx.ext.intersphinx",
|
"sphinx.ext.intersphinx",
|
||||||
"sphinx.ext.napoleon",
|
"sphinx.ext.napoleon",
|
||||||
"sphinx.ext.todo",
|
"sphinx.ext.todo",
|
||||||
]
|
]
|
||||||
|
|
||||||
intersphinx_mapping = {
|
intersphinx_mapping = {
|
||||||
"python": ("https://docs.python.org/3", None),
|
"python": ("https://docs.python.org/3.4", None),
|
||||||
"CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
|
"CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,14 +36,7 @@ master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = "Adafruit Shell Library"
|
project = "Adafruit Shell Library"
|
||||||
creation_year = "2020"
|
copyright = "2020 Melissa LeBlanc-Williams"
|
||||||
current_year = str(datetime.datetime.now().year)
|
|
||||||
year_duration = (
|
|
||||||
current_year
|
|
||||||
if current_year == creation_year
|
|
||||||
else creation_year + " - " + current_year
|
|
||||||
)
|
|
||||||
copyright = year_duration + " Melissa LeBlanc-Williams"
|
|
||||||
author = "Melissa LeBlanc-Williams"
|
author = "Melissa LeBlanc-Williams"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
|
@ -100,9 +91,19 @@ napoleon_numpy_docstring = False
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
#
|
#
|
||||||
import sphinx_rtd_theme
|
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
|
||||||
|
|
||||||
html_theme = "sphinx_rtd_theme"
|
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||||
|
try:
|
||||||
|
import sphinx_rtd_theme
|
||||||
|
|
||||||
|
html_theme = "sphinx_rtd_theme"
|
||||||
|
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
|
||||||
|
except:
|
||||||
|
html_theme = "default"
|
||||||
|
html_theme_path = ["."]
|
||||||
|
else:
|
||||||
|
html_theme_path = ["."]
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2023 Alec Delaney, written for Adafruit Industries
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
@ -1,54 +1,6 @@
|
||||||
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
|
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
|
||||||
# SPDX-FileCopyrightText: 2023 Alec Delaney for Adafruit Industries
|
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
[build-system]
|
[tool.black]
|
||||||
requires = [
|
target-version = ['py35']
|
||||||
"setuptools",
|
|
||||||
"wheel",
|
|
||||||
"setuptools-scm",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "adafruit-python-shell"
|
|
||||||
description = "Python helper for running Shell scripts in Python"
|
|
||||||
version = "0.0.0+auto.0"
|
|
||||||
readme = "README.rst"
|
|
||||||
authors = [
|
|
||||||
{name = "Adafruit Industries", email = "circuitpython@adafruit.com"}
|
|
||||||
]
|
|
||||||
urls = {Homepage = "https://github.com/adafruit/Adafruit_Python_Shell"}
|
|
||||||
keywords = [
|
|
||||||
"adafruit",
|
|
||||||
"blinka",
|
|
||||||
"circuitpython",
|
|
||||||
"micropython",
|
|
||||||
"python",
|
|
||||||
"shell",
|
|
||||||
"installation",
|
|
||||||
"raspberry",
|
|
||||||
"pi",
|
|
||||||
"console",
|
|
||||||
"terminal",
|
|
||||||
"installer",
|
|
||||||
]
|
|
||||||
license = {text = "MIT"}
|
|
||||||
classifiers = [
|
|
||||||
"Development Status :: 3 - Alpha",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"Topic :: Software Development :: Libraries",
|
|
||||||
"Topic :: System :: Hardware",
|
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Programming Language :: Python :: 3.4",
|
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
]
|
|
||||||
dynamic = ["dependencies", "optional-dependencies"]
|
|
||||||
|
|
||||||
[tool.setuptools]
|
|
||||||
py-modules = ["adafruit_shell"]
|
|
||||||
|
|
||||||
[tool.setuptools.dynamic]
|
|
||||||
dependencies = {file = ["requirements.txt"]}
|
|
||||||
optional-dependencies = {optional = {file = ["optional_requirements.txt"]}}
|
|
||||||
|
|
|
||||||
60
setup.py
Normal file
60
setup.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2020 Melissa LeBlanc-Williams for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
"""A setuptools based setup module.
|
||||||
|
|
||||||
|
See:
|
||||||
|
https://packaging.python.org/en/latest/distributing.html
|
||||||
|
https://github.com/pypa/sampleproject
|
||||||
|
"""
|
||||||
|
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
# To use a consistent encoding
|
||||||
|
from codecs import open
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
here = path.abspath(path.dirname(__file__))
|
||||||
|
|
||||||
|
# Get the long description from the README file
|
||||||
|
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
|
||||||
|
long_description = f.read()
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="adafruit-python-shell",
|
||||||
|
use_scm_version=True,
|
||||||
|
setup_requires=["setuptools_scm"],
|
||||||
|
description="Python helper for running Shell scripts in Python",
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type="text/x-rst",
|
||||||
|
# The project's main homepage.
|
||||||
|
url="https://github.com/adafruit/Adafruit_Python_Shell",
|
||||||
|
# Author details
|
||||||
|
author="Adafruit Industries",
|
||||||
|
author_email="circuitpython@adafruit.com",
|
||||||
|
install_requires=[
|
||||||
|
"clint",
|
||||||
|
"Adafruit-PlatformDetect",
|
||||||
|
],
|
||||||
|
# Choose your license
|
||||||
|
license="MIT",
|
||||||
|
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Topic :: Software Development :: Libraries",
|
||||||
|
"Topic :: System :: Hardware",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.4",
|
||||||
|
"Programming Language :: Python :: 3.5",
|
||||||
|
],
|
||||||
|
# What does your project relate to?
|
||||||
|
keywords="adafruit blinka circuitpython micropython python shell installation raspberry pi "
|
||||||
|
"console terminal installer",
|
||||||
|
# You can just specify the packages manually here if your project is
|
||||||
|
# simple. Or you can use find_packages().
|
||||||
|
py_modules=["adafruit_shell"],
|
||||||
|
)
|
||||||
Loading…
Reference in a new issue