diff --git a/.all-contributorsrc b/.all-contributorsrc
new file mode 100644
index 0000000..2c541ea
--- /dev/null
+++ b/.all-contributorsrc
@@ -0,0 +1,15 @@
+{
+ "projectName": "aiooui",
+ "projectOwner": "bluetooth-devices",
+ "repoType": "github",
+ "repoHost": "https://github.com",
+ "files": [
+ "README.md"
+ ],
+ "imageSize": 80,
+ "commit": true,
+ "commitConvention": "angular",
+ "contributors": [],
+ "contributorsPerLine": 7,
+ "skipCi": true
+}
diff --git a/.copier-answers.yml b/.copier-answers.yml
new file mode 100644
index 0000000..c391d9a
--- /dev/null
+++ b/.copier-answers.yml
@@ -0,0 +1,20 @@
+# Changes here will be overwritten by Copier
+_commit: 2f1e40f
+_src_path: gh:browniebroke/pypackage-template
+add_me_as_contributor: false
+copyright_year: '2024'
+documentation: false
+email: nick@koston.org
+full_name: J. Nick Koston
+github_username: bluetooth-devices
+has_cli: false
+initial_commit: true
+open_source_license: MIT
+package_name: aiooui
+project_name: aiooui
+project_short_description: Async OUI lookups
+project_slug: aiooui
+run_poetry_install: true
+setup_github: true
+setup_pre_commit: true
+
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d4a2c44
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+# http://editorconfig.org
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+insert_final_newline = true
+charset = utf-8
+end_of_line = lf
+
+[*.bat]
+indent_style = tab
+end_of_line = crlf
+
+[LICENSE]
+insert_final_newline = false
+
+[Makefile]
+indent_style = tab
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..b889b2f
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: ["bluetooth-devices"]
diff --git a/.github/ISSUE_TEMPLATE/1-bug_report.md b/.github/ISSUE_TEMPLATE/1-bug_report.md
new file mode 100644
index 0000000..4644945
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1-bug_report.md
@@ -0,0 +1,14 @@
+---
+name: Bug report
+about: Create a report to help us improve
+labels: bug
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/2-feature-request.md b/.github/ISSUE_TEMPLATE/2-feature-request.md
new file mode 100644
index 0000000..a4c01cc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/2-feature-request.md
@@ -0,0 +1,14 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+labels: enhancement
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/labels.toml b/.github/labels.toml
new file mode 100644
index 0000000..56865be
--- /dev/null
+++ b/.github/labels.toml
@@ -0,0 +1,94 @@
+[breaking]
+color = "ffcc00"
+name = "breaking"
+description = "Breaking change."
+
+[bug]
+color = "d73a4a"
+name = "bug"
+description = "Something isn't working"
+
+[dependencies]
+color = "0366d6"
+name = "dependencies"
+description = "Pull requests that update a dependency file"
+
+[github_actions]
+color = "000000"
+name = "github_actions"
+description = "Update of github actions"
+
+[documentation]
+color = "1bc4a5"
+name = "documentation"
+description = "Improvements or additions to documentation"
+
+[duplicate]
+color = "cfd3d7"
+name = "duplicate"
+description = "This issue or pull request already exists"
+
+[enhancement]
+color = "a2eeef"
+name = "enhancement"
+description = "New feature or request"
+
+["good first issue"]
+color = "7057ff"
+name = "good first issue"
+description = "Good for newcomers"
+
+["help wanted"]
+color = "008672"
+name = "help wanted"
+description = "Extra attention is needed"
+
+[invalid]
+color = "e4e669"
+name = "invalid"
+description = "This doesn't seem right"
+
+[nochangelog]
+color = "555555"
+name = "nochangelog"
+description = "Exclude pull requests from changelog"
+
+[question]
+color = "d876e3"
+name = "question"
+description = "Further information is requested"
+
+[removed]
+color = "e99695"
+name = "removed"
+description = "Removed piece of functionalities."
+
+[tests]
+color = "bfd4f2"
+name = "tests"
+description = "CI, CD and testing related changes"
+
+[wontfix]
+color = "ffffff"
+name = "wontfix"
+description = "This will not be worked on"
+
+[discussion]
+color = "c2e0c6"
+name = "discussion"
+description = "Some discussion around the project"
+
+[hacktoberfest]
+color = "ffa663"
+name = "hacktoberfest"
+description = "Good issues for Hacktoberfest"
+
+[answered]
+color = "0ee2b6"
+name = "answered"
+description = "Automatically closes as answered after a delay"
+
+[waiting]
+color = "5f7972"
+name = "waiting"
+description = "Automatically closes if no answer after a delay"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..ea6fe18
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,109 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+concurrency:
+ group: ${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: 3.x
+ - uses: pre-commit/action@v3.0.1
+
+ # Make sure commit messages follow the conventional commits convention:
+ # https://www.conventionalcommits.org
+ commitlint:
+ name: Lint Commit Messages
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - uses: wagoid/commitlint-github-action@v5.4.5
+
+ test:
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version:
+ - "3.8"
+ - "3.9"
+ - "3.10"
+ - "3.11"
+ - "3.12"
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macOS-latest
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ - uses: snok/install-poetry@v1.3.4
+ - name: Install Dependencies
+ run: poetry install
+ shell: bash
+ - name: Test with Pytest
+ run: poetry run pytest --cov-report=xml
+ shell: bash
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+
+ release:
+ needs:
+ - test
+ - lint
+ - commitlint
+
+ runs-on: ubuntu-latest
+ environment: release
+ concurrency: release
+ permissions:
+ id-token: write
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: ${{ github.head_ref || github.ref_name }}
+
+ # Do a dry run of PSR
+ - name: Test release
+ uses: python-semantic-release/python-semantic-release@v9.1.0
+ if: github.ref_name != 'main'
+ with:
+ root_options: --noop
+
+ # On main branch: actual PSR + upload to PyPI & GitHub
+ - name: Release
+ uses: python-semantic-release/python-semantic-release@v9.1.0
+ id: release
+ if: github.ref_name == 'main'
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Publish package distributions to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ if: steps.release.outputs.released == 'true'
+
+ - name: Publish package distributions to GitHub Releases
+ uses: python-semantic-release/upload-to-gh-release@main
+ if: steps.release.outputs.released == 'true'
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml
new file mode 100644
index 0000000..3a3fbab
--- /dev/null
+++ b/.github/workflows/issue-manager.yml
@@ -0,0 +1,32 @@
+name: Issue Manager
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+ issue_comment:
+ types:
+ - created
+ issues:
+ types:
+ - labeled
+ pull_request_target:
+ types:
+ - labeled
+ workflow_dispatch:
+
+jobs:
+ issue-manager:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: tiangolo/issue-manager@0.5.0
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ config: >
+ {
+ "answered": {
+ "message": "Assuming the original issue was solved, it will be automatically closed now."
+ },
+ "waiting": {
+ "message": "Automatically closing. To re-open, please provide the additional information requested."
+ }
+ }
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
new file mode 100644
index 0000000..230db33
--- /dev/null
+++ b/.github/workflows/labels.yml
@@ -0,0 +1,22 @@
+name: Sync Github labels
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - ".github/**"
+
+jobs:
+ labels:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.x
+ - name: Install labels
+ run: pip install labels
+ - name: Sync config with Github
+ run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GH_PAT }} sync -f .github/labels.toml
diff --git a/.github/workflows/poetry-upgrade.yml b/.github/workflows/poetry-upgrade.yml
new file mode 100644
index 0000000..49bda02
--- /dev/null
+++ b/.github/workflows/poetry-upgrade.yml
@@ -0,0 +1,12 @@
+name: Upgrader
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "15 16 24 * *"
+
+jobs:
+ upgrade:
+ uses: browniebroke/github-actions/.github/workflows/poetry-upgrade.yml@v1
+ secrets:
+ gh_pat: ${{ secrets.GH_PAT }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..08cfdfd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,140 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder {{package_name}} settings
+.spyderproject
+.spyproject
+
+# Rope {{package_name}} settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 0000000..450add9
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,8 @@
+tasks:
+ - command: |
+ pip install poetry
+ PIP_USER=false poetry install
+ - command: |
+ pip install pre-commit
+ pre-commit install
+ PIP_USER=false pre-commit install-hooks
diff --git a/.idea/aiooui.iml b/.idea/aiooui.iml
new file mode 100644
index 0000000..a46d9bb
--- /dev/null
+++ b/.idea/aiooui.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
new file mode 100644
index 0000000..22b6eba
--- /dev/null
+++ b/.idea/watcherTasks.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..f2e6d22
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..6d20d07
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,56 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+exclude: "CHANGELOG.md|.copier-answers.yml|.all-contributorsrc"
+default_stages: [commit]
+
+ci:
+ autofix_commit_msg: "chore(pre-commit.ci): auto fixes"
+ autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate"
+
+repos:
+ - repo: https://github.com/commitizen-tools/commitizen
+ rev: v3.15.0
+ hooks:
+ - id: commitizen
+ stages: [commit-msg]
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.5.0
+ hooks:
+ - id: debug-statements
+ - id: check-builtin-literals
+ - id: check-case-conflict
+ - id: check-docstring-first
+ - id: check-json
+ - id: check-toml
+ - id: check-xml
+ - id: check-yaml
+ - id: detect-private-key
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - repo: https://github.com/python-poetry/poetry
+ rev: 1.7.1
+ hooks:
+ - id: poetry-check
+ - repo: https://github.com/pre-commit/mirrors-prettier
+ rev: v3.1.0
+ hooks:
+ - id: prettier
+ args: ["--tab-width", "2"]
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.2.2
+ hooks:
+ - id: ruff
+ args: [--fix, --exit-non-zero-on-fix]
+ - repo: https://github.com/psf/black
+ rev: 24.2.0
+ hooks:
+ - id: black
+ - repo: https://github.com/codespell-project/codespell
+ rev: v2.2.6
+ hooks:
+ - id: codespell
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v1.8.0
+ hooks:
+ - id: mypy
+ additional_dependencies: []
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..825c32f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1 @@
+# Changelog
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ae5f910
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,117 @@
+# Contributing
+
+Contributions are welcome, and they are greatly appreciated! Every little helps, and credit will always be given.
+
+You can contribute in many ways:
+
+## Types of Contributions
+
+### Report Bugs
+
+Report bugs to [our issue page][gh-issues]. If you are reporting a bug, please include:
+
+- Your operating system name and version.
+- Any details about your local setup that might be helpful in troubleshooting.
+- Detailed steps to reproduce the bug.
+
+### Fix Bugs
+
+Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it.
+
+### Implement Features
+
+Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it.
+
+### Write Documentation
+
+aiooui could always use more documentation, whether as part of the official aiooui docs, in docstrings, or even on the web in blog posts, articles, and such.
+
+### Submit Feedback
+
+The best way to send feedback [our issue page][gh-issues] on GitHub. If you are proposing a feature:
+
+- Explain in detail how it would work.
+- Keep the scope as narrow as possible, to make it easier to implement.
+- Remember that this is a volunteer-driven project, and that contributions are welcome 😊
+
+## Get Started!
+
+Ready to contribute? Here's how to set yourself up for local development.
+
+1. Fork the repo on GitHub.
+
+2. Clone your fork locally:
+
+ ```shell
+ $ git clone git@github.com:your_name_here/aiooui.git
+ ```
+
+3. Install the project dependencies with [Poetry](https://python-poetry.org):
+
+ ```shell
+ $ poetry install
+ ```
+
+4. Create a branch for local development:
+
+ ```shell
+ $ git checkout -b name-of-your-bugfix-or-feature
+ ```
+
+ Now you can make your changes locally.
+
+5. When you're done making changes, check that your changes pass our tests:
+
+ ```shell
+ $ poetry run pytest
+ ```
+
+6. Linting is done through [pre-commit](https://pre-commit.com). Provided you have the tool installed globally, you can run them all as one-off:
+
+ ```shell
+ $ pre-commit run -a
+ ```
+
+ Or better, install the hooks once and have them run automatically each time you commit:
+
+ ```shell
+ $ pre-commit install
+ ```
+
+7. Commit your changes and push your branch to GitHub:
+
+ ```shell
+ $ git add .
+ $ git commit -m "feat(something): your detailed description of your changes"
+ $ git push origin name-of-your-bugfix-or-feature
+ ```
+
+ Note: the commit message should follow [the conventional commits](https://www.conventionalcommits.org). We run [`commitlint` on CI](https://github.com/marketplace/actions/commit-linter) to validate it, and if you've installed pre-commit hooks at the previous step, the message will be checked at commit time.
+
+8. Submit a pull request through the GitHub website or using the GitHub CLI (if you have it installed):
+
+ ```shell
+ $ gh pr create --fill
+ ```
+
+## Pull Request Guidelines
+
+We like to have the pull request open as soon as possible, that's a great place to discuss any piece of work, even unfinished. You can use draft pull request if it's still a work in progress. Here are a few guidelines to follow:
+
+1. Include tests for feature or bug fixes.
+2. Update the documentation for significant features.
+3. Ensure tests are passing on CI.
+
+## Tips
+
+To run a subset of tests:
+
+```shell
+$ pytest tests
+```
+
+## Making a new release
+
+The deployment should be automated and can be triggered from the Semantic Release workflow in GitHub. The next version will be based on [the commit logs](https://python-semantic-release.readthedocs.io/en/latest/commit-log-parsing.html#commit-log-parsing). This is done by [python-semantic-release](https://python-semantic-release.readthedocs.io/en/latest/index.html) via a GitHub action.
+
+[gh-issues]: https://github.com/bluetooth-devices/aiooui/issues
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..44ba6f4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+
+MIT License
+
+Copyright (c) 2024 J. Nick Koston
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c633e07
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# aiooui
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+**Source Code**: https://github.com/bluetooth-devices/aiooui
+
+---
+
+Async OUI lookups
+
+## Installation
+
+Install this via pip (or your favourite package manager):
+
+`pip install aiooui`
+
+## Usage
+
+Start by importing it:
+
+```python
+import aiooui
+```
+
+## Contributors ✨
+
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+
+
+
+
+
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+
+## Credits
+
+This package was created with
+[Copier](https://copier.readthedocs.io/) and the
+[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
+project template.
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..8b82768
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+ extends: ["@commitlint/config-conventional"],
+ rules: {
+ "header-max-length": [0, "always", Infinity],
+ "body-max-line-length": [0, "always", Infinity],
+ "footer-max-line-length": [0, "always", Infinity],
+ },
+};
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..c3d02b2
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,186 @@
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "coverage"
+version = "7.4.3"
+description = "Code coverage measurement for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
+ {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
+ {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
+ {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
+ {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
+ {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
+ {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
+ {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
+ {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
+ {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
+ {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
+ {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
+ {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
+ {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
+ {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
+ {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
+ {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
+ {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
+ {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
+ {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
+ {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
+ {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
+]
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.0"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
+ {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "packaging"
+version = "23.2"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
+ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+]
+
+[[package]]
+name = "pluggy"
+version = "1.4.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
+ {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pytest"
+version = "7.4.4"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
+ {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=0.12,<2.0"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+
+[package.extras]
+testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-cov"
+version = "3.0.0"
+description = "Pytest plugin for measuring coverage."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
+ {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
+]
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.8"
+content-hash = "ce39f1419f42f4b94a5d1936239dda15495ae7ade16df3d0c053c978cae42fb3"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..37dbd7a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,129 @@
+[tool.poetry]
+name = "aiooui"
+version = "0.0.0"
+description = "Async OUI lookups"
+authors = ["J. Nick Koston "]
+license = "MIT"
+readme = "README.md"
+repository = "https://github.com/bluetooth-devices/aiooui"
+classifiers = [
+ "Development Status :: 2 - Pre-Alpha",
+ "Intended Audience :: Developers",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Topic :: Software Development :: Libraries",
+]
+packages = [
+ { include = "aiooui", from = "src" },
+]
+
+[tool.poetry.urls]
+"Bug Tracker" = "https://github.com/bluetooth-devices/aiooui/issues"
+"Changelog" = "https://github.com/bluetooth-devices/aiooui/blob/main/CHANGELOG.md"
+
+[tool.poetry.dependencies]
+python = "^3.8"
+
+[tool.poetry.group.dev.dependencies]
+pytest = "^7.0"
+pytest-cov = "^3.0"
+
+[tool.semantic_release]
+version_toml = ["pyproject.toml:tool.poetry.version"]
+version_variables = [
+ "src/aiooui/__init__.py:__version__",
+]
+build_command = "pip install poetry && poetry build"
+
+[tool.semantic_release.changelog]
+exclude_commit_patterns = [
+ "chore*",
+ "ci*",
+]
+
+[tool.semantic_release.changelog.environment]
+keep_trailing_newline = true
+
+[tool.semantic_release.branches.main]
+match = "main"
+
+[tool.semantic_release.branches.noop]
+match = "(?!main$)"
+prerelease = true
+
+[tool.pytest.ini_options]
+addopts = "-v -Wdefault --cov=aiooui --cov-report=term-missing:skip-covered"
+pythonpath = ["src"]
+
+[tool.coverage.run]
+branch = true
+
+[tool.coverage.report]
+exclude_lines = [
+ "pragma: no cover",
+ "@overload",
+ "if TYPE_CHECKING",
+ "raise NotImplementedError",
+ 'if __name__ == "__main__":',
+]
+
+[tool.ruff]
+target-version = "py38"
+line-length = 88
+ignore = [
+ "D203", # 1 blank line required before class docstring
+ "D212", # Multi-line docstring summary should start at the first line
+ "D100", # Missing docstring in public module
+ "D104", # Missing docstring in public package
+ "D107", # Missing docstring in `__init__`
+ "D401", # First line of docstring should be in imperative mood
+]
+select = [
+ "B", # flake8-bugbear
+ "D", # flake8-docstrings
+ "C4", # flake8-comprehensions
+ "S", # flake8-bandit
+ "F", # pyflake
+ "E", # pycodestyle
+ "W", # pycodestyle
+ "UP", # pyupgrade
+ "I", # isort
+ "RUF", # ruff specific
+]
+
+[tool.ruff.per-file-ignores]
+"tests/**/*" = [
+ "D100",
+ "D101",
+ "D102",
+ "D103",
+ "D104",
+ "S101",
+]
+"setup.py" = ["D100"]
+"conftest.py" = ["D100"]
+
+[tool.ruff.isort]
+known-first-party = ["aiooui", "tests"]
+
+[tool.mypy]
+check_untyped_defs = true
+disallow_any_generics = true
+disallow_incomplete_defs = true
+disallow_untyped_defs = true
+mypy_path = "src/"
+no_implicit_optional = true
+show_error_codes = true
+warn_unreachable = true
+warn_unused_ignores = true
+exclude = [
+ 'setup.py',
+]
+
+[[tool.mypy.overrides]]
+module = "tests.*"
+allow_untyped_defs = true
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..0200e15
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,3 @@
+{
+ "extends": ["github>browniebroke/renovate-configs:python"]
+}
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..3fca3d5
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+# This is a shim to allow GitHub to detect the package, build is done with poetry
+# Taken from https://github.com/Textualize/rich
+
+import setuptools
+
+if __name__ == "__main__":
+ setuptools.setup(name="aiooui")
diff --git a/src/aiooui/__init__.py b/src/aiooui/__init__.py
new file mode 100644
index 0000000..6c8e6b9
--- /dev/null
+++ b/src/aiooui/__init__.py
@@ -0,0 +1 @@
+__version__ = "0.0.0"
diff --git a/src/aiooui/main.py b/src/aiooui/main.py
new file mode 100644
index 0000000..8400ef8
--- /dev/null
+++ b/src/aiooui/main.py
@@ -0,0 +1,3 @@
+def add(n1: int, n2: int) -> int:
+ """Add the arguments."""
+ return n1 + n2
diff --git a/src/aiooui/py.typed b/src/aiooui/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/templates/CHANGELOG.md.j2 b/templates/CHANGELOG.md.j2
new file mode 100644
index 0000000..c1c91be
--- /dev/null
+++ b/templates/CHANGELOG.md.j2
@@ -0,0 +1,17 @@
+# Changelog
+
+{%- for version, release in context.history.released.items() %}
+
+## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
+
+{%- for category, commits in release["elements"].items() %}
+{# Category title: Breaking, Fix, Documentation #}
+### {{ category | capitalize }}
+{# List actual changes in the category #}
+{%- for commit in commits %}
+- {{ commit.descriptions[0] | capitalize }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
+{%- endfor %}{# for commit #}
+
+{%- endfor %}{# for category, commits #}
+
+{%- endfor %}{# for version, release #}
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_main.py b/tests/test_main.py
new file mode 100644
index 0000000..a63e787
--- /dev/null
+++ b/tests/test_main.py
@@ -0,0 +1,6 @@
+from aiooui.main import add
+
+
+def test_add():
+ """Adding two number works as expected."""
+ assert add(1, 1) == 2