diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8ac6b8c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b75050d..75b3a9c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,11 +35,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,7 +50,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -64,4 +64,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/python-build-test.yml b/.github/workflows/python-build-test.yml new file mode 100644 index 0000000..8f36aa8 --- /dev/null +++ b/.github/workflows/python-build-test.yml @@ -0,0 +1,76 @@ +name: Build and test + +on: + push: + branches: + - 'master' + pull_request: + +jobs: + build-py3: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + python setup.py install + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --show-source --statistics + - name: Test with pytest + run: | + pytest --capture=no + + build-py_legacy: + runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: ["3.6"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest mock + python setup.py install + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --show-source --statistics + - name: Test with pytest + run: | + pytest --capture=no + + build-docs: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install sphinx + - name: Build docs with Sphinx + run: | + cd doc + sphinx-build -W -b html -d ./doctrees . ./html diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 80adb8b..07885aa 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -5,7 +5,7 @@ name: Upload Python Package on: release: - types: [created] + types: [published] jobs: deploy: @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: '3.x' - name: Install dependencies diff --git a/.gitignore b/.gitignore index 89552e2..c11b868 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .tox/ doc/_build +doc/doctrees +doc/html build/ dist/ .vscode/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3aac176..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: python -cache: pip - -matrix: - include: - - python: 2.7 - env: TOXENV=py27 - - python: 3.4 - env: TOXENV=py34 - - python: 3.5 - env: TOXENV=py35 - - python: 3.6 - env: TOXENV=py36 - - python: 3.7 - env: TOXENV=py37 - - python: 3.8 - env: TOXENV=py38 - - python: 3.9 - env: TOXENV=py39 - - python: 3.5 - env: TOXENV=qa,doc - -install: pip install --upgrade setuptools pip tox -script: tox -v diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b175dc..18799a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,21 @@ Notable changes to the smbus2 project are recorded here. ## [Unreleased] -No unreleased updates. +No changes since last release. + +## [0.4.3] - 2023-08-25 +- Build pipeline and test updates only: + - Upgrade build pipelines + - Added Python 3.11 + - Python 2.7, 3.4, and 3.5 no longer tested. +- Update deprecated Sphinx config format. +- Corrected deprecated `assertEquals` in the unit tests. + +## [0.4.2] - 2022-06-05 +### General +- Explicitly export from top level of package [#69](https://github.com/kplindegaard/smbus2/pull/69). +- Transitioned pipelines from Travis-CI to GitHub Actions. + - Python 3.10 added. ## [0.4.1] - 2021-01-17 ### General @@ -86,7 +100,9 @@ with SMBus(1) as bus: First published version. -[Unreleased]: https://github.com/kplindegaard/smbus2/compare/0.4.1...HEAD +[Unreleased]: https://github.com/kplindegaard/smbus2/compare/0.4.3...HEAD +[0.4.3]: https://github.com/kplindegaard/smbus2/compare/0.4.2...0.4.3 +[0.4.2]: https://github.com/kplindegaard/smbus2/compare/0.4.1...0.4.2 [0.4.1]: https://github.com/kplindegaard/smbus2/compare/0.4.0...0.4.1 [0.4.0]: https://github.com/kplindegaard/smbus2/compare/0.3.0...0.4.0 [0.3.0]: https://github.com/kplindegaard/smbus2/compare/0.2.3...0.3.0 diff --git a/README.md b/README.md index 0c8e921..b14ff8a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # smbus2 A drop-in replacement for smbus-cffi/smbus-python in pure Python -[![Build Status](https://travis-ci.org/kplindegaard/smbus2.svg?branch=master)](https://travis-ci.org/kplindegaard/smbus2) +[![Build Status](https://github.com/kplindegaard/smbus2/actions/workflows/python-build-test.yml/badge.svg?branch=master)](https://github.com/kplindegaard/smbus2/actions/workflows/python-build-test.yml) [![Documentation Status](https://readthedocs.org/projects/smbus2/badge/?version=latest)](http://smbus2.readthedocs.io/en/latest/?badge=latest) +![CodeQL](https://github.com/kplindegaard/smbus2/actions/workflows/codeql-analysis.yml/badge.svg?branch=master) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=kplindegaard_smbus2&metric=alert_status)](https://sonarcloud.io/dashboard?id=kplindegaard_smbus2) ![Python Verions](https://img.shields.io/pypi/pyversions/smbus2.svg) @@ -11,7 +12,7 @@ A drop-in replacement for smbus-cffi/smbus-python in pure Python # Introduction -smbus2 is (yet another) pure Python implementation of of the [python-smbus](http://www.lm-sensors.org/browser/i2c-tools/trunk/py-smbus/) package. +smbus2 is (yet another) pure Python implementation of the [python-smbus](http://www.lm-sensors.org/browser/i2c-tools/trunk/py-smbus/) package. It was designed from the ground up with two goals in mind: @@ -47,66 +48,78 @@ smbus2 installs next to smbus as the package, so it's not really a 100% replacem ## Example 1a: Read a byte - from smbus2 import SMBus +```python +from smbus2 import SMBus + +# Open i2c bus 1 and read one byte from address 80, offset 0 +bus = SMBus(1) +b = bus.read_byte_data(80, 0) +print(b) +bus.close() +``` - # Open i2c bus 1 and read one byte from address 80, offset 0 - bus = SMBus(1) - b = bus.read_byte_data(80, 0) - print(b) - bus.close() - ## Example 1b: Read a byte using 'with' This is the very same example but safer to use since the smbus will be closed automatically when exiting the with block. - from smbus2 import SMBus - - with SMBus(1) as bus: - b = bus.read_byte_data(80, 0) - print(b) +```python +from smbus2 import SMBus + +with SMBus(1) as bus: + b = bus.read_byte_data(80, 0) + print(b) +``` ## Example 1c: Read a byte with PEC enabled Same example with Packet Error Checking enabled. - from smbus2 import SMBus +```python +from smbus2 import SMBus - with SMBus(1) as bus: - bus.pec = 1 # Enable PEC - b = bus.read_byte_data(80, 0) - print(b) +with SMBus(1) as bus: + bus.pec = 1 # Enable PEC + b = bus.read_byte_data(80, 0) + print(b) +``` ## Example 2: Read a block of data You can read up to 32 bytes at once. - from smbus2 import SMBus - - with SMBus(1) as bus: - # Read a block of 16 bytes from address 80, offset 0 - block = bus.read_i2c_block_data(80, 0, 16) - # Returned value is a list of 16 bytes - print(block) +```python +from smbus2 import SMBus + +with SMBus(1) as bus: + # Read a block of 16 bytes from address 80, offset 0 + block = bus.read_i2c_block_data(80, 0, 16) + # Returned value is a list of 16 bytes + print(block) +``` ## Example 3: Write a byte - from smbus2 import SMBus - - with SMBus(1) as bus: - # Write a byte to address 80, offset 0 - data = 45 - bus.write_byte_data(80, 0, data) +```python +from smbus2 import SMBus + +with SMBus(1) as bus: + # Write a byte to address 80, offset 0 + data = 45 + bus.write_byte_data(80, 0, data) +``` ## Example 4: Write a block of data It is possible to write 32 bytes at the time, but I have found that error-prone. Write less and add a delay in between if you run into trouble. - from smbus2 import SMBus - - with SMBus(1) as bus: - # Write a block of 8 bytes to address 80 from offset 0 - data = [1, 2, 3, 4, 5, 6, 7, 8] - bus.write_i2c_block_data(80, 0, data) +```python +from smbus2 import SMBus + +with SMBus(1) as bus: + # Write a block of 8 bytes to address 80 from offset 0 + data = [1, 2, 3, 4, 5, 6, 7, 8] + bus.write_i2c_block_data(80, 0, data) +``` # I2C @@ -120,61 +133,72 @@ Each operation is represented by a *i2c_msg* message object. ## Example 5: Single i2c_rdwr - from smbus2 import SMBus, i2c_msg +```python +from smbus2 import SMBus, i2c_msg + +with SMBus(1) as bus: + # Read 64 bytes from address 80 + msg = i2c_msg.read(80, 64) + bus.i2c_rdwr(msg) + + # Write a single byte to address 80 + msg = i2c_msg.write(80, [65]) + bus.i2c_rdwr(msg) - with SMBus(1) as bus: - # Read 64 bytes from address 80 - msg = i2c_msg.read(80, 64) - bus.i2c_rdwr(msg) - - # Write a single byte to address 80 - msg = i2c_msg.write(80, [65]) - bus.i2c_rdwr(msg) - - # Write some bytes to address 80 - msg = i2c_msg.write(80, [65, 66, 67, 68]) - bus.i2c_rdwr(msg) + # Write some bytes to address 80 + msg = i2c_msg.write(80, [65, 66, 67, 68]) + bus.i2c_rdwr(msg) +``` ## Example 6: Dual i2c_rdwr To perform dual operations just add more i2c_msg instances to the bus call: - from smbus2 import SMBus, i2c_msg - - # Single transaction writing two bytes then read two at address 80 - write = i2c_msg.write(80, [40, 50]) - read = i2c_msg.read(80, 2) - with SMBus(1) as bus: - bus.i2c_rdwr(write, read) +```python +from smbus2 import SMBus, i2c_msg + +# Single transaction writing two bytes then read two at address 80 +write = i2c_msg.write(80, [40, 50]) +read = i2c_msg.read(80, 2) +with SMBus(1) as bus: + bus.i2c_rdwr(write, read) +``` ## Example 7: Access i2c_msg data All data is contained in the i2c_msg instances. Here are some data access alternatives. - # 1: Convert message content to list - msg = i2c_msg.write(60, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - data = list(msg) # data = [1, 2, 3, ...] - print(len(data)) # => 10 - - # 2: i2c_msg is iterable - for value in msg: - print(value) - - # 3: Through i2c_msg properties - for k in range(msg.len): - print(msg.buf[k]) +```python +# 1: Convert message content to list +msg = i2c_msg.write(60, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) +data = list(msg) # data = [1, 2, 3, ...] +print(len(data)) # => 10 + +# 2: i2c_msg is iterable +for value in msg: + print(value) +# 3: Through i2c_msg properties +for k in range(msg.len): + print(msg.buf[k]) +``` # Installation instructions -From PyPi with `pip`: +From [PyPi](https://pypi.org/) with `pip`: - pip install smbus2 +``` +pip install smbus2 +``` -From conda-forge using `conda`: +From [conda-forge](https://anaconda.org/conda-forge) using `conda`: - conda install -c conda-forge smbus2 +``` +conda install -c conda-forge smbus2 +``` Installation from source code is straight forward: - python setup.py install +``` +python setup.py install +``` diff --git a/doc/conf.py b/doc/conf.py index 3194589..036b3e4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -22,7 +22,7 @@ import sys sys.path.insert(0, os.path.abspath('..')) -from smbus2 import __version__ +from smbus2 import __version__ # noqa: E402 # -- General configuration ------------------------------------------------ @@ -34,8 +34,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.intersphinx'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx' +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -68,7 +70,7 @@ # # This is also used if you do content translation via gettext catalogs. # 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 # directories to ignore when looking for source files. @@ -171,4 +173,4 @@ # Configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/2/': None} +intersphinx_mapping = {'python': ('https://docs.python.org/3/', None)} diff --git a/doc/index.rst b/doc/index.rst index c9314ae..801a63c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,7 +1,7 @@ smbus2 ====== -.. image:: https://travis-ci.org/kplindegaard/smbus2.svg?branch=master - :target: https://travis-ci.org/kplindegaard/smbus2 +.. image:: https://github.com/kplindegaard/smbus2/actions/workflows/python-build-test.yml/badge.svg?branch=master + :target: https://github.com/kplindegaard/smbus2/actions/workflows/python-build-test.yml .. image:: https://img.shields.io/pypi/pyversions/smbus2.svg :target: https://pypi.python.org/pypi/smbus2 @@ -12,4 +12,3 @@ smbus2 .. automodule:: smbus2 :members: SMBus, i2c_msg :undoc-members: - diff --git a/setup.cfg b/setup.cfg index f1d8a6f..9b3beb5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,3 +14,6 @@ exclude = doc, build, dist + +[pycodestyle] +max-line-length = 100 diff --git a/setup.py b/setup.py index 962ec4b..faa3117 100644 --- a/setup.py +++ b/setup.py @@ -65,6 +65,8 @@ def find_version(*file_paths): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9" + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11" ], ) diff --git a/smbus2/__init__.py b/smbus2/__init__.py index 38a5f03..f52948f 100644 --- a/smbus2/__init__.py +++ b/smbus2/__init__.py @@ -22,5 +22,5 @@ from .smbus2 import SMBus, i2c_msg, I2cFunc # noqa: F401 -__version__ = "0.4.1" +__version__ = "0.4.3" __all__ = ["SMBus", "i2c_msg", "I2cFunc"] diff --git a/tests/test_smbus2.py b/tests/test_smbus2.py index 286fbcf..378664b 100644 --- a/tests/test_smbus2.py +++ b/tests/test_smbus2.py @@ -106,17 +106,26 @@ def mock_ioctl(fd, command, msg): open_mock = mock.patch('smbus2.smbus2.os.open', mock_open) close_mock = mock.patch('smbus2.smbus2.os.close', mock_close) ioctl_mock = mock.patch('smbus2.smbus2.ioctl', mock_ioctl) -open_mock.start() -close_mock.start() -ioctl_mock.start() ########################################################################## # Common error messages INCORRECT_LENGTH_MSG = "Result array of incorrect length." +class SMBusTestCase(unittest.TestCase): + def setUp(self): + open_mock.start() + close_mock.start() + ioctl_mock.start() + + def tearDown(self): + open_mock.stop() + close_mock.stop() + ioctl_mock.stop() + + # Test cases -class TestSMBus(unittest.TestCase): +class TestSMBus(SMBusTestCase): def test_func(self): bus = SMBus(1) print("\nSupported I2C functionality: %x" % bus.funcs) @@ -184,16 +193,16 @@ def set_pec(bus, enable=True): bus = SMBus(1) self.assertRaises(IOError, set_pec, bus, True) self.assertRaises(IOError, set_pec, bus, 1) - self.assertEquals(bus.pec, 0) + self.assertEqual(bus.pec, 0) # Ensure PEC status is reset by close() bus._pec = 1 - self.assertEquals(bus.pec, 1) + self.assertEqual(bus.pec, 1) bus.close() - self.assertEquals(bus.pec, 0) + self.assertEqual(bus.pec, 0) -class TestSMBusWrapper(unittest.TestCase): +class TestSMBusWrapper(SMBusTestCase): """Similar test as TestSMBus except it encapsulates it all access within "with" blocks.""" def test_func(self): @@ -231,7 +240,7 @@ def test_read(self): self.assertListEqual(res, res3, msg="Byte and block reads differ") -class TestI2CMsg(unittest.TestCase): +class TestI2CMsg(SMBusTestCase): def test_i2c_msg(self): # 1: Convert message content to list msg = i2c_msg.write(60, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 5154409..0000000 --- a/tox.ini +++ /dev/null @@ -1,20 +0,0 @@ -[tox] -envlist = py{27,34,35,36,37,38,39},qa,doc -skip_missing_interpreters = True - -[testenv] -commands = - python setup.py install - nosetests -v -deps = .[test] - -[testenv:qa] -commands = - flake8 -deps = .[qa] - -[testenv:doc] -commands = - sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html -changedir = doc -deps = .[docs]