Compare commits

...

61 commits
1.3.2 ... main

Author SHA1 Message Date
Melissa LeBlanc-Williams
7def5108ea
Merge pull request #27 from makermelissa/main
New features required for installer script changes
2025-08-06 15:07:47 -07:00
Melissa LeBlanc-Williams
01be1e437d Allow finding multiple items 2025-08-06 14:58:27 -07:00
Melissa LeBlanc-Williams
0a414a2342 Add read_text_file function 2025-08-06 13:50:52 -07:00
Melissa LeBlanc-Williams
7fc4630f1d Fix wayland dm detection 2025-08-06 11:39:58 -07:00
Melissa LeBlanc-Williams
1522b71317 Use class write funtion for appending 2025-08-06 10:13:38 -07:00
Melissa LeBlanc-Williams
45b0cafa2b Fix error from pre-commit suggestion 2025-08-04 12:52:49 -07:00
Melissa LeBlanc-Williams
897d6ddbe8 Add window manager detection 2025-08-04 12:41:42 -07:00
Melissa LeBlanc-Williams
7bc3e74309 Fixed bug introduced by pylint suggestion 2025-07-18 15:16:27 -07:00
Melissa LeBlanc-Williams
4da4afa879 Add template functions and chmod string support 2025-07-18 13:21:02 -07:00
Melissa LeBlanc-Williams
9358afeb47
Merge pull request #26 from makermelissa/main
Use new platform detect feature to see if board is Pi5
2025-03-04 12:17:25 -08:00
Melissa LeBlanc-Williams
97bb660d5a Merge branch 'main' of https://github.com/adafruit/Adafruit_Python_Shell 2025-03-04 12:15:42 -08:00
Melissa LeBlanc-Williams
03c93a553d Use platform detect to see if board is Pi5 2025-03-04 12:15:29 -08:00
Limor "Ladyada" Fried
96716ae034
Merge pull request #25 from makermelissa/main
Add some useful functions for wayland detection
2025-03-02 18:48:45 -05:00
Melissa LeBlanc-Williams
41516d60bf Update docs config and pre-commit version 2025-03-01 13:50:40 -08:00
Melissa LeBlanc-Williams
1047b67c97 Update doc config 2025-03-01 13:42:48 -08:00
Melissa LeBlanc-Williams
dddedd92ba Run pre-commit 2025-03-01 13:37:12 -08:00
Melissa LeBlanc-Williams
7d12b16fba Add some useful functions for wayland detection 2025-03-01 13:21:40 -08:00
Melissa LeBlanc-Williams
9bd7f6a47e
Merge pull request #23 from LostNulide/patch-1
Added new raspbian release(bookworm) to raspbian_releases
2024-04-15 14:09:51 -07:00
Melissa LeBlanc-Williams
85686eede4 Run pre-commit 2024-04-15 14:06:44 -07:00
Melissa LeBlanc-Williams
3f624ecf57 Merge branch 'main' of https://github.com/adafruit/Adafruit_Python_Shell into patch-1 2024-04-15 14:04:11 -07:00
Melissa LeBlanc-Williams
0dbb994e53 Merge branch 'patch-1' of https://github.com/LostNulide/Adafruit_Python_Shell into patch-1 2024-04-15 14:04:04 -07:00
Melissa LeBlanc-Williams
9f669806d7
Merge pull request #24 from makermelissa/main
Pin python version to 3.11
2024-04-15 14:03:07 -07:00
Melissa LeBlanc-Williams
163cccd4bf Add release drafter 2024-04-15 14:01:13 -07:00
Melissa LeBlanc-Williams
e75ff020e4 Pin python version to 3.11 2024-04-15 13:54:10 -07:00
LostNulide
91e6e1cd67
Added new raspbian release(bookworm) to raspbian_releases 2024-04-15 19:16:42 +02:00
Melissa LeBlanc-Williams
8503f01a23
Merge pull request #22 from makermelissa/main
Allow run_command to run as user
2023-09-14 12:35:25 -07:00
Melissa LeBlanc-Williams
3ede731b6c Improve error message 2023-09-12 14:46:15 -07:00
Melissa LeBlanc-Williams
c6bf44ccd9 Merge branch 'main' of https://github.com/adafruit/Adafruit_Python_Shell 2023-09-12 11:48:59 -07:00
Melissa LeBlanc-Williams
4e2a55651d Allow run_command to run as user 2023-09-12 11:48:45 -07:00
Melissa LeBlanc-Williams
5bee5778fc
Merge pull request #21 from tekktrik/dev/build-wheels
Move to pyproject.toml, build wheels
2023-05-26 10:47:06 -07:00
Alec Delaney
c818ade2ae Update version string 2023-05-26 12:52:42 -04:00
Alec Delaney
5f10a029a2 Move to pyproject.toml, build wheels in CI 2023-05-26 12:51:35 -04:00
Alec Delaney
7051c1e78c Remove pylint action step 2023-05-26 12:48:16 -04:00
Alec Delaney
9af4579fdd Remove dumping GitHub context 2023-05-26 12:45:31 -04:00
Alec Delaney
a403315421 Update action versions 2023-05-26 12:44:58 -04:00
Melissa LeBlanc-Williams
478b164bba
Merge pull request #20 from makermelissa/main
Added kernel userspace mismatch check and updated pre-commit config
2023-05-26 09:22:35 -07:00
Melissa LeBlanc-Williams
205d2faf9d Added kernel userspace mismatch check and updated precommit config 2023-05-26 09:12:15 -07:00
Melissa LeBlanc-Williams
3b6984a1fe
Merge pull request #17 from makermelissa/main
Added more features
2022-11-17 13:21:46 -08:00
Melissa LeBlanc-Williams
54b292238f Check chmod modes and more functions I forgot to commit 2022-11-17 13:18:11 -08:00
Melissa LeBlanc-Williams
66b78dc9e4 Don't use dir a variable name 2022-11-15 10:38:41 -08:00
Melissa LeBlanc-Williams
8a9018a95f Add chown function 2022-11-15 10:05:47 -08:00
Melissa LeBlanc-Williams
3108ca4848 Allow pattern search to return match instead of just boolean 2022-11-14 18:04:01 -08:00
Melissa LeBlanc-Williams
2003329e0e Add chmod() that uses Python lib functions 2022-11-14 14:03:39 -08:00
Melissa LeBlanc-Williams
0a235e344e
Merge pull request #16 from makermelissa/main
Fixed an issue with run_command() sometimes locking up
2022-11-11 08:19:06 -08:00
Melissa LeBlanc-Williams
7c8bedcb4a Fix pylint and lint 2022-11-10 14:18:59 -08:00
Melissa LeBlanc-Williams
90aad5375b Remove unused import and delete test files 2022-11-10 12:46:52 -08:00
Melissa LeBlanc-Williams
8a15f4f5d7 Run pre-commit 2022-11-10 12:45:10 -08:00
Melissa LeBlanc-Williams
69d333ff9b Rename read func 2022-11-10 12:44:43 -08:00
Melissa LeBlanc-Williams
0bc10b7dc0 Fix run_command sometimes stops 2022-11-10 12:43:27 -08:00
Melissa LeBlanc-Williams
c6581122fa
Merge pull request #15 from makermelissa/main
Update with buster and allow arguments for prompt_reboot
2022-11-09 08:18:10 -08:00
Melissa LeBlanc-Williams
5cdff5eae6 Allow prompt to return a specific value for force_arg 2022-11-07 11:08:02 -08:00
Melissa LeBlanc-Williams
f5af4f9d11 Reformatted 2022-11-07 10:47:33 -08:00
Melissa LeBlanc-Williams
023db8fc6a Update with buster and allow arguments for prompt_reboot 2022-11-07 10:41:50 -08:00
Alec Delaney
758dbc0e8c
Merge pull request #14 from makermelissa/main
Fix language setting in docs
2022-05-31 15:55:48 -04:00
Melissa LeBlanc-Williams
11c3f2c77e Update to Python 3.7 for futures 2022-05-31 12:12:56 -07:00
Melissa LeBlanc-Williams
4cd19619c9 Update black and run it 2022-05-31 12:09:52 -07:00
Melissa LeBlanc-Williams
8314d1ca16 Update to fstrings 2022-05-31 12:03:56 -07:00
Melissa LeBlanc-Williams
3df36b8185 Fix language setting in docs 2022-05-31 11:50:45 -07:00
Melissa LeBlanc-Williams
3c023111a8
Merge pull request #13 from makermelissa/main
Add Raspberry Pi OS 64-bit Detection
2021-09-09 17:37:32 -07:00
Melissa LeBlanc-Williams
6cfaeaf892 Updated linting 2021-09-09 17:33:28 -07:00
Melissa LeBlanc-Williams
608144d76f Add Raspberry Pi OS 64-bit Detection 2021-09-09 17:22:11 -07:00
11 changed files with 521 additions and 243 deletions

13
.github/release-drafter.yml vendored Normal file
View file

@ -0,0 +1,13 @@
# 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.

View file

@ -10,31 +10,27 @@ 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 ::set-output name=repo-name::$( echo 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.6 - name: Set up Python 3.11
uses: actions/setup-python@v1 uses: actions/setup-python@v4
with: with:
python-version: 3.6 python-version: '3.11'
- name: Versions - name: Versions
run: | run: |
python3 --version python3 --version
- name: Checkout Current Repo - name: Checkout Current Repo
uses: actions/checkout@v1 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Checkout tools repo - name: Checkout tools repo
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
repository: adafruit/actions-ci-circuitpython-libs repository: adafruit/actions-ci-circuitpython-libs
path: actions-ci path: actions-ci
@ -44,26 +40,23 @@ 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 pylint Sphinx sphinx-rtd-theme pre-commit pip install --force-reinstall 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 setup.py - name: Check For pyproject.toml
id: need-pypi id: need-pypi
run: | run: |
echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) echo pyproject-toml=$( find . -wholename './pyproject.toml' ) >> $GITHUB_OUTPUT
- name: Build Python package - name: Build Python package
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
run: | run: |
pip install --upgrade setuptools wheel twine readme_renderer testresources pip install --upgrade build twine
python setup.py sdist 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 bdist_wheel --universal python -m build
twine check dist/* twine check dist/*

25
.github/workflows/release-drafter.yml vendored Normal file
View file

@ -0,0 +1,25 @@
# 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 }}

View file

@ -12,26 +12,27 @@ jobs:
upload-pypi: upload-pypi:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
- name: Check For setup.py - name: Check For pyproject.toml
id: need-pypi id: need-pypi
run: | run: |
echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) echo pyproject-toml=$( find . -wholename './pyproject.toml' ) >> $GITHUB_OUTPUT
- name: Set up Python - name: Set up Python
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
uses: actions/setup-python@v1 uses: actions/setup-python@v4
with: with:
python-version: '3.x' python-version: '3.11'
- name: Install dependencies - name: Install dependencies
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install setuptools wheel twine pip install --upgrade build twine
- name: Build and publish - name: Build and publish
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml')
env: env:
TWINE_USERNAME: ${{ secrets.pypi_username }} TWINE_USERNAME: ${{ secrets.pypi_username }}
TWINE_PASSWORD: ${{ secrets.pypi_password }} TWINE_PASSWORD: ${{ secrets.pypi_password }}
run: | run: |
python setup.py sdist 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 -m build
twine upload dist/* twine upload dist/*

View file

@ -3,17 +3,40 @@
# SPDX-License-Identifier: Unlicense # SPDX-License-Identifier: Unlicense
repos: repos:
- repo: https://github.com/python/black - repo: https://github.com/python/black
rev: 21.6b0 rev: 23.3.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/fsfe/reuse-tool - repo: https://github.com/fsfe/reuse-tool
rev: v0.12.1 rev: v1.1.2
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: v4.0.1 rev: v5.0.0
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

View file

@ -9,11 +9,11 @@
# run arbitrary code # run arbitrary code
extension-pkg-whitelist= extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not # Add files or directories to the ignore-list. They should be base names, not
# paths. # paths.
ignore=CVS ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The # Add files or directories matching the regex patterns to the ignore-list. The
# regex matches against base names, not paths. # regex matches against base names, not paths.
ignore-patterns= ignore-patterns=
@ -22,12 +22,11 @@ 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= load-plugins=pylint.extensions.no_self_use
# Pickle collected data for later comparisons. # Pickle collected data for later comparisons.
persistent=yes persistent=yes
@ -55,8 +54,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,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=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call
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 disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding
# 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
@ -226,12 +225,6 @@ 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
@ -250,46 +243,30 @@ ignore-comments=yes
ignore-docstrings=yes ignore-docstrings=yes
# Ignore imports when computing similarities. # Ignore imports when computing similarities.
ignore-imports=no ignore-imports=yes
# Minimum lines number of a similarity. # Minimum lines number of a similarity.
min-similarity-lines=4 min-similarity-lines=12
[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_]*)|(__.*__))$
@ -297,9 +274,6 @@ 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_]*))$
@ -310,21 +284,12 @@ 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]+))$
@ -340,9 +305,6 @@ 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_]*))$
@ -434,4 +396,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=Exception overgeneral-exceptions=builtins.Exception

View file

@ -23,18 +23,57 @@ 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:
""" """
@ -63,79 +102,145 @@ class Shell:
) )
return prompt.options(message, options) return prompt.options(message, options)
def run_command(self, cmd, suppress_message=False, return_output=False): def run_command(
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
original_stderr = sys.stderr
try:
# pylint: disable=consider-using-with
proc = subprocess.Popen(
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
# 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 as err: # 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
def info(self, message): def read_stream(output):
file_descriptor = output.fileno()
file_flags = fcntl.fcntl(file_descriptor, fcntl.F_GETFL)
fcntl.fcntl(file_descriptor, fcntl.F_SETFL, file_flags | os.O_NONBLOCK)
try:
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
append = kwargs.get("append", False)
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) print(colored.green(self._group) + " " + message, **kwargs)
else: else:
print(message) print(message, **kwargs)
def warn(self, message): def warn(self, message, **kwargs):
""" """
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) print(colored.yellow(self._group) + " " + message, **kwargs)
else: else:
print(message) print(message, **kwargs)
def bail(self, message=None): def bail(self, message=None, **kwargs):
""" """
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") self.error("Exiting due to error", **kwargs)
else: else:
self.error("Exiting due to error: {}".format(message)) self.error(f"Exiting due to error: {message}", **kwargs)
sys.exit(1) sys.exit(1)
def error(self, message): def error(self, message, **kwargs):
""" """
Display some inforrmation Display some information
""" """
if self._group is not None: if self._group is not None:
print(colored.red(self._group) + " " + message) print(colored.red(self._group) + " " + message, **kwargs)
else: else:
print(message) print(message, **kwargs)
@staticmethod @staticmethod
def print_colored(message, color): def print_colored(message, color):
@ -145,13 +250,13 @@ class Shell:
colorize = getattr(colored, color) colorize = getattr(colored, color)
print(colorize(message)) print(colorize(message))
def prompt(self, message, *, default=None, force_arg=None): def prompt(self, message, *, default=None, force_arg=None, force_arg_value=True):
""" """
A Yes/No prompt that accepts optional defaults A Yes/No prompt that accepts optional defaults
Returns True for Yes and False for No Returns True for Yes and False for No
""" """
if force_arg is not None and self.argument_exists(force_arg): if force_arg is not None and self.argument_exists(force_arg):
return True return force_arg_value
if default is None: if default is None:
choicebox = "[y/n]" choicebox = "[y/n]"
else: else:
@ -199,9 +304,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("Directory does not exist") raise ValueError(f"Directory '{directory}' does not exist")
if not self.isdir(directory): if not self.isdir(directory):
raise ValueError("Given location is not a directory") raise ValueError(f"The given location '{directory}' is not a directory")
os.chdir(directory) os.chdir(directory)
def pushd(self, directory): def pushd(self, directory):
@ -257,9 +362,7 @@ class Shell:
Run the grep command and return the result Run the grep command and return the result
""" """
location = self.path(location) location = self.path(location)
return self.run_command( return self.run_command(f"grep {search_term} {location}", suppress_message=True)
"grep {} {}".format(search_term, location), suppress_message=True
)
@staticmethod @staticmethod
def date(): def date():
@ -281,7 +384,10 @@ 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)
def pattern_search(self, location, pattern, multi_line=False): # pylint: disable=too-many-arguments
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,
@ -291,17 +397,22 @@ 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+") as file: with open(location, "r+", encoding="utf-8") as file:
if re.search(pattern, file.read(), flags=re.DOTALL): match = search_function(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):
if re.search(pattern, line): match = search_function(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):
@ -315,7 +426,7 @@ class Shell:
if self.pattern_search(location, pattern, multi_line): if self.pattern_search(location, pattern, multi_line):
if multi_line: if multi_line:
regex = re.compile(pattern, flags=re.DOTALL) regex = re.compile(pattern, flags=re.DOTALL)
with open(location, "r+") as file: with open(location, "r+", encoding="utf-8") as file:
data = file.read() data = file.read()
file.seek(0) file.seek(0)
file.write(regex.sub(replace, data)) file.write(regex.sub(replace, data))
@ -368,6 +479,42 @@ 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
@ -385,7 +532,7 @@ class Shell:
""" """
if not self.is_root(): if not self.is_root():
print("Installer must be run as root.") print("Installer must be run as root.")
print("Try 'sudo python3 {}'".format(self.script())) print(f"Try 'sudo python3 {self.script()}'")
sys.exit(1) sys.exit(1)
def write_text_file(self, path, content, append=True): def write_text_file(self, path, content, append=True):
@ -397,9 +544,19 @@ class Shell:
content = "\n" + content content = "\n" + content
else: else:
mode = "w" mode = "w"
with open(self.path(path), mode) 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"
@ -441,6 +598,13 @@ class Shell:
""" """
return platform.machine() == "armv8l" return platform.machine() == "armv8l"
@staticmethod
def is_arm64():
"""
Check if Platform.machine() returns ARM 64
"""
return platform.machine() == "aarch64"
@staticmethod @staticmethod
def get_arch(): def get_arch():
"""Return a string containing the architecture""" """Return a string containing the architecture"""
@ -461,15 +625,21 @@ class Shell:
) )
release = None release = None
if os.path.exists("/etc/os-release"): if os.path.exists("/etc/os-release"):
with open("/etc/os-release") 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") as f: with open("/etc/os-release", encoding="utf-8") as f:
release_file = f.read() release_file = f.read()
for opsys in os_releases: for opsys in os_releases:
if opsys in release_file: if opsys in release_file:
release = opsys release = opsys
if release == "Debian" and os.path.exists("/etc/rpi-issue"):
release = "Raspbian"
if os.path.isdir(os.path.expanduser("~/.kano-settings")) or os.path.isdir( if os.path.isdir(os.path.expanduser("~/.kano-settings")) or os.path.isdir(
os.path.expanduser("~/.kanoprofile") os.path.expanduser("~/.kanoprofile")
): ):
@ -484,20 +654,32 @@ 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 = ("buster", "stretch", "jessie", "wheezy")
if os.path.exists("/etc/os-release"): if os.path.exists("/etc/os-release"):
with open("/etc/os-release") 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 raspbian_releases: for raspbian in RASPI_VERSIONS:
if raspbian in release_file: if raspbian in release_file:
return raspbian return raspbian
return None return None
def prompt_reboot(self): 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):
"""Prompt the user for a reboot""" """Prompt the user for a reboot"""
if not self.prompt("REBOOT NOW?", default="y"): if not self.prompt("REBOOT NOW?", default=default, **kwargs):
print("Exiting without reboot.") print("Exiting without reboot.")
else: else:
print("Reboot started...") print("Reboot started...")
@ -507,15 +689,103 @@ class Shell:
def check_kernel_update_reboot_required(self): def check_kernel_update_reboot_required(self):
"""Checks if the pi needs to be rebooted since the last kernel update""" """Checks if the pi needs to be rebooted since the last kernel update"""
if not self.exists("/lib/modules/{}".format(self.release())): if not self.exists(f"/lib/modules/{self.release()}"):
self.error( self.error(
"OS has not been rebooted since last kernel update. " "OS has not been rebooted since last kernel update. "
"Please reboot and re-run the script." "Please reboot and re-run the script."
) )
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():
""" """
@ -532,6 +802,14 @@ 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():
""" """

View file

@ -6,6 +6,7 @@
import os import os
import sys import sys
import datetime
sys.path.insert(0, os.path.abspath("..")) sys.path.insert(0, os.path.abspath(".."))
@ -16,20 +17,14 @@ 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",
] ]
# TODO: Please Read!
# Uncomment the below if you use native CircuitPython modules such as
# digitalio, micropython and busio. List the modules you use. Without it, the
# autodoc module docs will fail to generate with a warning.
# autodoc_mock_imports = ["digitalio", "busio"]
intersphinx_mapping = { intersphinx_mapping = {
"python": ("https://docs.python.org/3.4", None), "python": ("https://docs.python.org/3", None),
"CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
} }
@ -43,7 +38,14 @@ master_doc = "index"
# General information about the project. # General information about the project.
project = "Adafruit Shell Library" project = "Adafruit Shell Library"
copyright = "2020 Melissa LeBlanc-Williams" creation_year = "2020"
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
@ -60,7 +62,7 @@ release = "1.0"
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language = None language = "en"
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
@ -98,19 +100,9 @@ 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.
# #
on_rtd = os.environ.get("READTHEDOCS", None) == "True" import sphinx_rtd_theme
if not on_rtd: # only import and set the theme if we're building docs locally html_theme = "sphinx_rtd_theme"
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,

View file

@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 Alec Delaney, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT

View file

@ -1,6 +1,54 @@
# 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
[tool.black] [build-system]
target-version = ['py35'] requires = [
"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"]}}

View file

@ -1,60 +0,0 @@
# 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"],
)