diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..634594e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# 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 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..0a90630 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,67 @@ +name: Build + +on: [push, pull_request] + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + python_version: ['3.8'] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python_version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install hatch + hatch env create + - name: Lint and typecheck + run: | + hatch run lint-check + - name: Test + run: | + hatch run test-cov-xml + - uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + verbose: true + + release: + runs-on: ubuntu-latest + needs: test + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + - name: Install dependencies + shell: bash + run: | + python -m pip install --upgrade pip + pip install hatch + - name: Build and publish on PyPI + env: + HATCH_INDEX_USER: ${{ secrets.HATCH_INDEX_USER }} + HATCH_INDEX_AUTH: ${{ secrets.HATCH_INDEX_AUTH }} + run: | + hatch build + hatch publish + - name: Create release + uses: ncipollo/release-action@v1 + with: + draft: true + body: ${{ github.event.head_commit.message }} + artifacts: dist/*.whl,dist/*.tar.gz + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..11ca5bb --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,56 @@ +name: Build documentation + +on: + push: + branches: + - main + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +# Default to bash +defaults: + run: + shell: bash + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install hatch + hatch env create + - name: Build + run: hatch run docs-build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./site + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c52bbeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,108 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +junit/ +junit.xml +test.db + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# OS files +.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bbef73d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "python.analysis.typeCheckingMode": "basic", + "python.analysis.autoImportCompletions": true, + "python.terminal.activateEnvironment": true, + "python.terminal.activateEnvInCurrentTerminal": true, + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "editor.rulers": [88], + "python.defaultInterpreterPath": "${workspaceFolder}/.hatch/pwdlib/bin/python", + "python.testing.pytestPath": "${workspaceFolder}/.hatch/pwdlib/bin/pytest", + "python.testing.cwd": "${workspaceFolder}", + "python.testing.pytestArgs": ["--no-cov"], + "[python]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff" + } + } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..80b08db --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2024, François Voron + +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..52b1d43 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# pwdlib + +

+ Modern password hashing for Python +

+ +[![build](https://github.com/frankie567/pwdlib/workflows/Build/badge.svg)](https://github.com/frankie567/pwdlib/actions) +[![codecov](https://codecov.io/gh/frankie567/pwdlib/branch/master/graph/badge.svg)](https://codecov.io/gh/frankie567/pwdlib) +[![PyPI version](https://badge.fury.io/py/pwdlib.svg)](https://badge.fury.io/py/pwdlib) + +--- + +**Documentation**: https://frankie567.github.io/pwdlib/ + +**Source Code**: https://github.com/frankie567/pwdlib + +--- + +## Development + +### Setup environment + +We use [Hatch](https://hatch.pypa.io/latest/install/) to manage the development environment and production build. Ensure it's installed on your system. + +### Run unit tests + +You can run all the tests with: + +```bash +hatch run test +``` + +### Format the code + +Execute the following command to apply linting and check typing: + +```bash +hatch run lint +``` + +### Publish a new version + +You can bump the version, create a commit and associated tag with one command: + +```bash +hatch version patch +``` + +```bash +hatch version minor +``` + +```bash +hatch version major +``` + +Your default Git text editor will open so you can add information about the release. + +When you push the tag on GitHub, the workflow will automatically publish it on PyPi and a GitHub release will be created as draft. + +## Serve the documentation + +You can serve the Mkdocs documentation with: + +```bash +hatch run docs-serve +``` + +It'll automatically watch for changes in your code. + +## License + +This project is licensed under the terms of the MIT license. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..612c7a5 --- /dev/null +++ b/docs/index.md @@ -0,0 +1 @@ +--8<-- "README.md" diff --git a/docs/reference/pwdlib.md b/docs/reference/pwdlib.md new file mode 100644 index 0000000..7d4dada --- /dev/null +++ b/docs/reference/pwdlib.md @@ -0,0 +1,6 @@ +# Reference + +::: pwdlib + options: + show_root_heading: false + show_source: false diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..02e9222 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,64 @@ +site_name: pwdlib +site_description: Modern password hashing for Python + +repo_url: https://github.com/frankie567/pwdlib +repo_name: frankie567/pwdlib + +theme: + name: material + icon: + logo: octicons/key-16 + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: amber + accent: amber + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: amber + accent: amber + toggle: + icon: material/brightness-4 + name: Switch to light mode + +markdown_extensions: + - toc: + permalink: true + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + +plugins: + - search + - mkdocstrings: + handlers: + python: + import: + - https://docs.python.org/3.8/objects.inv + options: + docstring_style: google + +watch: + - docs + - pwdlib + +nav: + - About: index.md + - Reference: + - pwdlib: reference/pwdlib.md diff --git a/pwdlib/__init__.py b/pwdlib/__init__.py new file mode 100644 index 0000000..e5787d5 --- /dev/null +++ b/pwdlib/__init__.py @@ -0,0 +1,22 @@ +"""Modern password hashing for Python""" + +__version__ = "0.0.0" + + +def add(a: int, b: int) -> int: + """ + Add two integers. + + Args: + a: + The first operand. + b: + The second operand. + + Examples: + Add two integers + + r = add(2, 3) + print(r) # 5 + """ + return a + b diff --git a/pwdlib/py.typed b/pwdlib/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d04de90 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,70 @@ +[tool.ruff] +target-version = "py38" + +[tool.ruff.lint] +extend-select = ["I", "TRY", "UP"] + +[tool.pytest.ini_options] +addopts = "--cov=pwdlib/ --cov-report=term-missing" + + +[tool.hatch] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.version] +source = "regex_commit" +commit_extra_args = ["-e"] +path = "pwdlib/__init__.py" + +[tool.hatch.envs.default] +python = "3.8" +dependencies = [ + "mypy", + "ruff", + "pytest", + "pytest-cov", + "mkdocs-material", + "mkdocstrings[python]", + +] + +[tool.hatch.envs.default.scripts] +test = "pytest" +test-cov-xml = "pytest --cov-report=xml" +lint = [ + "ruff format .", + "ruff --fix .", + "mypy pwdlib/", +] +lint-check = [ + "ruff format --check .", + "ruff .", + "mypy pwdlib/", +] +docs-serve = "mkdocs serve" +docs-build = "mkdocs build" + +[build-system] +requires = ["hatchling", "hatch-regex-commit"] +build-backend = "hatchling.build" + +[project] +name = "pwdlib" +authors = [ + { name = "pwdlib", email = "fvoron@gmail.com" } +] +description = "Modern password hashing for Python" +readme = "README.md" +dynamic = ["version"] +classifiers = [ + "Programming Language :: Python :: 3 :: Only", +] +requires-python = ">=3.8" +dependencies = [ +] + +[project.urls] +Documentation = "https://frankie567.github.io/pwdlib/" +Source = "https://github.com/frankie567/pwdlib" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_add.py b/tests/test_add.py new file mode 100644 index 0000000..a498c40 --- /dev/null +++ b/tests/test_add.py @@ -0,0 +1,15 @@ +import pytest + +from pwdlib import add + + +@pytest.mark.parametrize( + "a,b,result", + [ + (0, 0, 0), + (1, 1, 2), + (3, 2, 5), + ], +) +def test_add(a: int, b: int, result: int): + assert add(a, b) == result