Skip to content

Commit

Permalink
MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
matteosox committed Aug 13, 2023
0 parents commit 80a22e6
Show file tree
Hide file tree
Showing 52 changed files with 4,785 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Exclude everything
*

# Except files we explicitly need, grouped by copy command
!.git
!src/pysparkplug
src/**/__pycache__/
src/**/*.egg-info/
!pyproject.toml
!README.md
!LICENSE
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Require reviews by this Github Team, for any change
* @matteosox
159 changes: 159 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Contributor Guide

## Getting started

We use Docker as a clean, reproducible development environment within which to build, test, generate docs, and so on. As long as you have a modern version of Docker, you should be able to run all developer workflows. That's it! Of course, running things natively isn't a supported/maintained thing.

## Tests

_TL;DR: Run `test/test.sh` to run the full suite of tests._

### Black Code Formatting

_TL;DR: Run `test/test.sh -s black` to test your code's formatting._

We use [Black](https://black.readthedocs.io/en/stable/index.html) for code formatting. To format your code, run `test/test.sh -s fix` to get all your spaces in a row. Black configuration can be found in the `pyproject.toml` file at the root of the repo.

### isort Import Ordering

_TL;DR: Run `test/test.sh -s isort` to test your code's imports._

For import ordering, we use [isort](https://pycqa.github.io/isort/). To get imports ordered correctly, run `test/test.sh -s fix`. isort configuration can be found in the `pyproject.toml` file at the root of the repo.

### Pylint Code Linting

_TL;DR: Run `test/test.sh -s pylint` to lint your code._

We use [Pyint](https://pylint.pycqa.org/en/latest/) for Python linting (h/t Itamar Turner-Trauring from his site [pythonspeed](https://pythonspeed.com/articles/pylint/) for inspiration). To lint your code, run `test/test.sh -s pylint`. In addition to showing any linting errors, it will also print out a report. Pylint configuration can be found in the `pylintrc` file at the root of the repo.

Pylint is setup to lint the `src`, `test/unit_tests` and `docs` directories, along with `noxfile.py`. To add more modules or packages for linting, edit the `pylint` test found in `noxfile.py`.

### Mypy Static Type Checking

_TL;DR: Run `test/test.sh -s mypy` to type check your code._

We use [Mypy](https://mypy.readthedocs.io/en/stable/) for static type checking. To type check your code, run `test/test.sh -s mypy`. Mypy configuration can be found in the `pyproject.toml` file at the root of the repo.

Mypy is setup to run on the `src` and`test/unit_tests`, along with `noxfile.py` and `docs/linkcode.py`. To add more modules or packages for type checking, edit the `mypy` test found in `noxfile.py`.

### Unit Tests

_TL;DR: Run `test/test.sh -s unit_tests-3.10 -- fast` to unit test your code quickly._

While we use [`unittest`](https://docs.python.org/3/library/unittest.html) to write unit tests, we use [`pytest`](https://docs.pytest.org/) for running them. To unit test your code, run `test/test.sh -s unit_tests-3.10 -- fast`. This will run unit tests in Python 3.10 only, without any coverage reporting overhead. To run the tests across all supported versions of Python, run `test/test.sh -s unit_tests`, which will also generate coverage reports which can be aggregated using `test/test.sh -s coverage`.

`pytest` is setup to discover tests in the `test/unit_tests` directory. All test files must match the pattern `test*.py`. `pytest` configuration can be found in the `pyproject.toml` file at the root of the repo. To add more directories for unit test discovery, edit the `testpaths` configuration option.

### Test Coverage

_TL;DR: Run `test/test.sh -s coverage` after running the unit tests with coverage to test the coverage of the unit test suite._

We use [Coverage.py](https://coverage.readthedocs.io/en/coverage-5.5/) to test the coverage of the unit test suite. This will print any coverage gaps from the full test suite. Coverage.py configuration can be found in the `pyproject.toml` file at the root of the repo.

### Documentation Tests

_TL;DR: Run `test/test.sh -s docs` to build and test the documentation._

See [below](#documentation) for more info on the documentation build process. In addition to building the documentation, the `test/docs.sh` shell script uses Sphinx's [`doctest`](https://www.sphinx-doc.org/en/master/usage/extensions/doctest.html) builder to ensure the documented output of usage examples is accurate. Note that the `README.md` file's ` ```python` code sections are transformed into `{doctest}` directives by `docs/conf.py` during the documentation build process. This allows the `README.md` to render code with syntax highlighting on Github & [PyPI](https://pypi.org) while still ensuring accuracy using `doctest`.

### Packaging Tests

_TL;DR: Run `test/test.sh -s packaging` to build and test the package._

We use [`build`](https://pypa-build.readthedocs.io/en/latest/) to build source distributions and wheels. We then use [`check-wheel-contents`](https://github.com/jwodder/check-wheel-contents) to test for common errors and mistakes found when building Python wheels. Finally, we use [`twine check`](https://twine.readthedocs.io/en/latest/#twine-check) to check whether or not `pysparkplug`'s long description will render correctly on [PyPI](https://pypi.org). To test the package build, run `test/test.sh -s packaging`. While there is no configuration for `build` or `twine`, the configuration for `check-wheel-contents` can be found in the `pyproject.toml` file at the root of the repo.

## Documentation

_TL;DR: To build and test the documentation, run `test/test.sh -s docs`._

We use [Sphinx](https://www.sphinx-doc.org/en/master/index.html) for documentation site generation. To build the documentation, run `test/test.sh -s docs`. To view it, open `docs/build/html/index.html` in your browser.

Sphinx configuration can be found in `docs/conf.py`. It is setup to generate pages based on what it finds in the `toctree` directive in `docs/index.md`. To add new pages, add them to the table of contents with that directive.

### API Reference

The "API Reference" page is mostly auto-generated using the [`autodoc`](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html), [`autosummary`](https://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html), [`intersphinx`](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html), and [`linkcode`](https://www.sphinx-doc.org/en/master/usage/extensions/viewcode.html) Sphinx extensions. Classes, functions, decorators, and so on need to be added manually to the `docs/api.rst` file, but once included, the entries are auto-generated using type annotations and docstrings.

### Docstring Formatting

We use the [`napoleon`](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) Sphinx extension to enable docstring formats other than Sphinx's default, rather unreadable format. Instead, we use [Google's docstring standard](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings). Types and defaults should not be referenced in the docstring, instead included in annotations.

### Auto-generated Github Links

We use the [`linkcode`](https://www.sphinx-doc.org/en/master/usage/extensions/linkcode.html) Sphinx extension to add links to Github on the API Reference page. The code for mapping Python objects to links can be found in the `docs/linkcode.py` Python module.

### Changelog

We document changes in the `CHANGELOG.md` file. This project adheres to the [keep a changelog](https://keepachangelog.com/en/1.0.0/) standard. Before committing changes that impact users, make sure to document features added, changed, deprecated, removed, fixed, or security-related changes to the "## Unreleased" section.

### Publishing Documentation

We use [Read the Docs](https://docs.readthedocs.io/en/stable/index.html) for building and publishing `pysparkplug`'s documentation. Its Github integration makes this process seamless. Read the Docs configuration can be found in the `.readthedocs.yaml` file at the root of the repo.

While documentation for the `pysparkplug` package is generated and hosted by Read the Docs, the documentation can be found at a custom domain: [pysparkplug.mattefay.com](https://pysparkplug.mattefay.com). You can read more about this [here](https://docs.readthedocs.io/en/stable/custom_domains.html).

## Releasing

### Release Process

Every push to the `main` branch on Github generates a draft release on Github. To publish a release, one should:

1.) If creating a final release (i.e. not a pre-release), create and merge a pull request that updates the `CHANGELOG.md` such that the released changes section is renamed from "## Unreleased" to "## {MAJOR.MINOR.MICRO} (YYYY-MM-DD)"

2.) Review the draft release. Update the tag for the draft release to the version you want to release with a prefixed v, i.e. "v{MAJOR.MINOR.MICRO}", and add any additional notes as you see fit. Publish it. This will trigger the `release` Github Action, which will publish to [PyPI](https://pypi.org).

3.) After confirming that the release on Github look good, as does the package on [PyPI](https://pypi.org), if this was a final release (i.e. you updated the `CHANGELOG.md`) create and merge a new pull request that creates a new "## Unreleased" section at the top of the `CHANGELOG.md`. This should have new, empty sections for Added, Changed, Deprecated, Removed, Fixed, and Security.

### Determining the Version

`pysparkplug` is versioned according to [PEP 440](https://www.python.org/dev/peps/pep-0440/). The type of final release (major, minor, or micro) should be determined by the types of unreleased changes in the changelog. Any "Removed" changes call for a major release (increment the major digit, minor and micro reset to 0). "Added" changes call for a minor release (increment the minor digit, micro set to 0). Otherwise, a "micro" release is called for (increment the micro digit only).

Intermediate versions between releases are incremented with `dev` and taken care of by [`hatch-vcs`](https://github.com/ofek/hatch-vcs).

## Continuous Integration & Continuous Deployment

We use Github actions to run our CI/CD pipeline on every pull request. The configuration can be found in `.github/workflows/cicd.yaml`. That said, every step of every job can also be run locally.

### Main

This is the "main" job, which consists of running the test suite, creating a draft release, and publishing the package to [TestPyPI](https://test.pypi.org).

### OS Compatibility

Using Github Actions' [build matrix feature](https://docs.github.com/en/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix), we're able to run unit tests on MacOS, Windows, & Linux, for each supported version of Python.

### Publish

A separate `publish` workflow is configured in `.github/workflows/publish.yaml`. This workflow publishes the package to [PyPI](https://pypi.org), and is triggered by a Github release being published.

## Pull Requests

The `main` branch has [branch protections](https://help.github.com/en/github/administering-a-repository/about-protected-branches) turned on in Github, requiring one reviewer to approve a PR before merging. We also use the code owners feature to specify who can approve certain PRs. As well, merging a PR requires status checks (Read the Docs along with both CI/CD jobs) to complete successfully.

When naming a branch, please use the syntax `username/branch-name-here`. If you plan to collaborate with others on that branch, use `team/branch-name-here`.

## Future Work

- Better unit testing
- 100% test coverage
- Doctest
- Improve README.md
- Integration testing
- Primary host usecase
- Edge Node
- Aliases
- Rebirth
- Multiple MQTT server support
- Drop datatypes
- Report by exception logic
- Device metric polling
- data types
- Template types
- Metadata
- Properties
- DataSet types
- Array types
- MQTT v5
- Historian/analytics (just listens)
- Refactor all of `_payload.py`.
- Refactor `_datatype.py` for better type annotation.
21 changes: 21 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: Bug report
about: Create a report to help us fix the issue
title: '[BUG]'
labels: ''
assignees: matteosox

---

## Describe the bug 🐛
A clear and concise description of what the bug is, i.e. what you did, what you expected to happen, and what happened instead.

## To Reproduce 🔁
Preferably a code snippet.

## Environment 🏔
Copy and paste what you get when you run the code below in the Python interpreter you're using:
`import sys, platform; print(f"{sys.version}\n{platform.platform()}")`

## Additional context ✨
Add any other context about the problem here.
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest a new idea
title: '[FEATURE]'
labels: ''
assignees: matteosox

---

## What seems to be the problem here? 🐛
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

## Proposed solution 👩‍💻
A clear and concise description of what you want to happen.

## Alternatives 🚧
A clear and concise description of any alternative solutions or features you've considered.

## Additional context ✨
Add any other context or screenshots about the feature request here.
27 changes: 27 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Summary

What's the [new hotness](https://youtu.be/ha-uagjJQ9k?t=17)?

### Why?

What is my [purpose](https://youtu.be/X7HmltUWXgs?t=52)?

### How?

But how?!

## Checklist

Most checks are automated, but a few aren't, so make sure to go through and tick them off, even if they don't apply. This checklist is here to help, not deter you. Remember, "Slow is smooth, and smooth is fast".

- [ ] **Unit tests**
- Every input should have a test for it.
- Every potential raised exception should have a test ensuring it is raised.
- [ ] **Documentation**
- New functions/classes/etc. must be added to `docs/api.rst`.
- Changed/added classes/methods/functions have appropriate `versionadded`, `versionchanged`, or `deprecated` [directives](http://www.sphinx-doc.org/en/stable/markup/para.html#directive-versionadded).
- The appropriate entry in `CHANGELOG.md` has been included in the "Unreleased" section, i.e. "Added", "Changed", "Deprecated", "Removed", "Fixed", or "Security".
- [ ] **Future work**
- Future work should be documented in the contributor guide, i.e. `.github/CONTRIBUTING.md`.

If you have any questions not answered by a quick readthrough of the [contributor guide](https://pysparkplug.mattefay.com/en/latest/contributor_guide.html), add them to this PR and submit it.
75 changes: 75 additions & 0 deletions .github/workflows/cicd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI/CD

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Test
run: test/test.sh

- name: Create draft Github release
if: ${{ github.ref == 'refs/heads/main' }}
run: test/test.sh -s draft_release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish package to testpypi
if: ${{ github.ref == 'refs/heads/main' }}
run: test/test.sh -s publish -- testpypi
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TESTPYPI_TOKEN }}

os_compatibility:
runs-on: ${{ matrix.os }}
name: "OS: ${{ matrix.os }} Python: ${{ matrix.python-version }}"
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
python-version: ["3.8", "3.9", "3.10", "3.11"]
env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
pip install --upgrade pip wheel setuptools
pip install coverage[toml] nox==2023.04.22
- name: Run unit tests
run: nox --session unit_tests-{{ matrix.python-version }}

- name: Combine coverage reports
run: |
coverage combine
coverage xml --fail-under 0
- name: Upload to Codecov
uses: codecov/codecov-action@v2
with:
env_vars: OS,PYTHON
fail_ci_if_error: true
verbose: true
20 changes: 20 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Release

on:
release:
types:
- published
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Publish package to pypi
run: test/test.sh -s publish -- pypi
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# MacOS
.DS_Store

# Python
__pycache__/
*.pyc
*.egg-info
.ipynb_checkpoints
src/pysparkplug/_version.py

# VS Code
/.vscode/
/*.code-workspace

# Cache from Docker container
/.cache

# Docs
/docs/build
29 changes: 29 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.10"

# Declare the Python requirements required to build your docs
python:
install:
- method: pip
path: .
- requirements:
- furo
- myst-parser
- packaging
- sphinx
- sphinx-copybutton
- sphinx-notfound-page
- sphinxext-opengraph

# Build documentation in the docs/source directory with Sphinx
sphinx:
fail_on_warning: true
configuration: docs/conf.py
Loading

0 comments on commit 80a22e6

Please sign in to comment.