diff --git a/.copier/package.yml b/.copier/package.yml new file mode 100644 index 0000000..70a3b59 --- /dev/null +++ b/.copier/package.yml @@ -0,0 +1,24 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: v2024.3 +_src_path: gh:westerveltco/django-twc-package +author_email: josh@joshthomas.dev +author_name: Josh Thomas +current_version: 0.1.0 +django_versions: +- '3.2' +- '4.2' +- '5.0' +docs_domain: westervelt.dev +github_owner: westerveltco +github_repo: django-simple-nav +module_name: django_simple_nav +package_description: A simple, flexible, and extensible navigation menu for Django. +package_name: django-simple-nav +python_versions: +- '3.8' +- '3.9' +- '3.10' +- '3.11' +- '3.12' +test_django_main: true +versioning_scheme: SemVer diff --git a/.editorconfig b/.editorconfig index e8f7f94..d3229ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,20 +4,22 @@ root = true [*] charset = utf-8 end_of_line = lf +indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[{{J,j}ustfile,.{J,j}ustfile,{*.{py,rst,ini,md}}}] +[{,.}{j,J}ustfile] +indent_size = 4 + +[*.{py,rst,ini,md}] indent_size = 4 -indent_style = space [*.py] line_length = 120 multi_line_output = 3 -[*.{css,html,js,json,sass,scss,yml,yaml}] +[*.{css,html,js,json,jsx,sass,scss,svelte,ts,tsx,yml,yaml}] indent_size = 2 -indent_style = space [*.md] trim_trailing_whitespace = false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1e1d012..27c6fb3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,4 +6,8 @@ updates: interval: weekly timezone: America/Chicago labels: - - "dependabot" + - dependabot + groups: + gha: + patterns: + - "*" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a878f83..9562e3a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,13 +3,6 @@ name: release on: release: types: [created] - workflow_dispatch: - inputs: - pypi: - description: "Publish to PyPI" - required: false - default: true - type: boolean jobs: check: @@ -21,6 +14,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 + - name: Check most recent test run on `main` id: latest-test-result run: | @@ -30,16 +24,17 @@ jobs: --json headBranch,workflowName,conclusion \ --jq '.[] | select(.headBranch=="main" and .conclusion=="success") | .conclusion' \ | head -n 1)" >> $GITHUB_OUTPUT + - name: OK if: ${{ (contains(steps.latest-test-result.outputs.result, 'success')) }} run: exit 0 + - name: Fail if: ${{ !contains(steps.latest-test-result.outputs.result, 'success') }} run: exit 1 - pypi: - if: ${{ github.event_name == 'release' || inputs.pypi }} + if: ${{ github.event_name == 'release' }} runs-on: ubuntu-latest needs: check environment: release @@ -48,21 +43,19 @@ jobs: id-token: write steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 + - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install hatch + - name: Build package run: | hatch build - - if: ${{ github.event_name == 'workflow_dispatch' }} - name: Publish to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ - - if: ${{ github.event_name != 'workflow_dispatch' }} - name: Publish to PyPI + + - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 4b069eb..9999ab1 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,35 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +!.vscode/*.example +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +staticfiles/ +mediafiles/ + +# pyright config for nvim-lspconfig +pyrightconfig.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c55b63..85626e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: check-yaml - repo: https://github.com/adamchainz/django-upgrade - rev: "1.15.0" + rev: 1.15.0 hooks: - id: django-upgrade args: [--target-version, "3.2"] @@ -20,7 +20,7 @@ repos: rev: v0.1.3 hooks: - id: ruff - args: [ --fix ] + args: [--fix] - id: ruff-format - repo: https://github.com/adamchainz/blacken-docs @@ -31,18 +31,30 @@ repos: additional_dependencies: - black==22.12.0 - - repo: https://github.com/rtts/djhtml - rev: "3.0.6" + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.0.3 hooks: - - id: djhtml - entry: djhtml --tabwidth 2 - alias: autoformat + - id: prettier + # lint the following with prettier: + # - javascript + # - typescript + # - JSX/TSX + # - CSS + # - yaml + # ignore any minified code + files: '^(?!.*\.min\..*)(?P[\w-]+(\.[\w-]+)*\.(js|jsx|ts|tsx|yml|yaml|css))$' + + - repo: https://github.com/djlint/djLint + rev: v1.34.1 + hooks: + - id: djlint-reformat-django + - id: djlint-django - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks rev: v2.11.0 hooks: - - id: pretty-format-toml - args: [--autofix] + - id: pretty-format-toml + args: [--autofix] - repo: https://github.com/abravalheri/validate-pyproject rev: v0.15 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c5531da..40baf96 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,7 +6,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.12" sphinx: configuration: docs/conf.py diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a27d973 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + "recommendations": [ + "charliermarsh.ruff", + "EditorConfig.EditorConfig", + "esbenp.prettier-vscode", + "monosans.djlint", + "ms-python.black-formatter", + "ms-python.pylint", + "ms-python.python", + "ms-python.vscode-pylance", + "skellock.just", + "tamasfe.even-better-toml" + ] +} diff --git a/.vscode/settings.json.example b/.vscode/settings.json.example new file mode 100644 index 0000000..f16d649 --- /dev/null +++ b/.vscode/settings.json.example @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "files.associations": { + "Justfile": "just" + }, + "ruff.organizeImports": true, + "[django-html][handlebars][hbs][mustache][jinja][jinja-html][nj][njk][nunjucks][twig]": { + "editor.defaultFormatter": "monosans.djlint" + } +} diff --git a/AUTHORS.md b/AUTHORS.md index 807a8ea..898e4db 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,4 +1,4 @@ # Authors - Josh Thomas -- Jeff Triplett <@jefftriplett> +- Jeff Triplett [@jefftriplett](https://github.com/jefftriplett) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09520cb..aa3b4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/ ## [Unreleased] +### Changed + +- Now using [`django-twc-package`](https://github.com/westerveltco/django-twc-package) template for repository and package structure. + ## [0.1.0] Initial release! 🎉 @@ -25,9 +29,9 @@ Initial release! 🎉 ### Added - A group of navigation classes -- `Nav`, `NavGroup`, and `NavItem` -- that can be used together to build a simple navigation structure. - - `Nav` is the main container for a navigation structure. - - `NavGroup` is a container for a group of `NavItem` objects. - - `NavItem` is a single navigation item. + - `Nav` is the main container for a navigation structure. + - `NavGroup` is a container for a group of `NavItem` objects. + - `NavItem` is a single navigation item. - A `django_simple_nav` template tag that renders a `Nav` object to a template. The template tag takes a string represented the dotted path to a `Nav` object and renders it to the template. - Navigation item urls can be either a URL string (e.g. `https://example.com/about/` or `/about/`) or a Django URL name (e.g. `about-view`). When rendering out to the template, the template tag will resolve the URL name to the actual URL. - Navigation items also can take a list of permissions to control the visibility of the item. The permissions can be user attributes (e.g. `is_staff`, `is_superuser`, etc.) or a specific permission (e.g. `auth.add_user`). @@ -37,7 +41,7 @@ Initial release! 🎉 - Initial tests. - Initial CI/CD (GitHub Actions). -### New Contributors! +### New Contributors - Josh Thomas (maintainer) - Jeff Triplett [@jefftriplett](https://github.com/jefftriplett) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 855f0ea..4c54ff8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ We adhere to Django's Code of Conduct in all interactions and expect all contrib ## Setup -The following setup steps assume you are using a Unix-like operating system, such as Linux or macOS, and that you have a [supported](#requirements) version of Python installed. If you are using Windows, you will need to adjust the commands accordingly. If you do not have Python installed, you can visit [python.org](https://www.python.org/) for instructions on how to install it for your operating system. +The following setup steps assume you are using a Unix-like operating system, such as Linux or macOS, and that you have a [supported](README.md#requirements) version of Python installed. If you are using Windows, you will need to adjust the commands accordingly. If you do not have Python installed, you can visit [python.org](https://www.python.org/) for instructions on how to install it for your operating system. 1. Fork the repository and clone it locally. 2. Create a virtual environment and activate it. You can use whatever tool you prefer for this. Below is an example using the Python standard library's `venv` module: @@ -23,23 +23,29 @@ source venv/bin/activate 3. Install `django-simple-nav` and the `dev` dependencies in editable mode: ```shell -python -m pip install -e '.[dev]' +python -m pip install --editable '.[dev]' +# or using [just](#just) +just bootstrap ``` ## Testing We use [`pytest`](https://docs.pytest.org/) for testing and [`nox`](https://nox.thea.codes/) to run the tests in multiple environments. -To run the test suite against the current environment, run: +To run the test suite against the default versions of Python (lower bound of supported versions) and Django (lower bound of LTS versions), run: ```shell -python -m pytest +python -m nox --session "test" +# or using [just](#just) +just test ``` To run the test suite against all supported versions of Python and Django, run: ```shell -python -m nox +python -m nox --session "tests" +# or using [just](#just) +just testall ``` ## `just` diff --git a/Justfile b/Justfile index 2a29229..e8c1884 100644 --- a/Justfile +++ b/Justfile @@ -3,13 +3,13 @@ set dotenv-load := true @_default: just --list -################## -# DEPENDENCIES # -################## - bootstrap: python -m pip install --editable '.[dev]' +# ---------------------------------------------------------------------- +# DEPENDENCIES +# ---------------------------------------------------------------------- + pup: python -m pip install --upgrade pip @@ -17,38 +17,9 @@ update: @just pup @just bootstrap -venv PY_VERSION="3.11": - #!/usr/bin/env python - from __future__ import annotations - - import subprocess - from pathlib import Path - - PY_VERSION = ( - subprocess.run( - ["pyenv", "latest", "{{ PY_VERSION }}"], capture_output=True, check=True - ) - .stdout.decode() - .strip() - ) - name = f"nav-{PY_VERSION}" - - home = Path.home() - - pyenv_version_dir = home / ".pyenv" / "versions" / PY_VERSION - pyenv_virtualenv_dir = home / ".pyenv" / "versions" / name - - if not pyenv_version_dir.exists(): - subprocess.run(["pyenv", "install", PY_VERSION], check=True) - - if not pyenv_virtualenv_dir.exists(): - subprocess.run(["pyenv", "virtualenv", PY_VERSION, name], check=True) - - (python_version_file := Path(".python-version")).write_text(name) - -################## -# TESTING/TYPES # -################## +# ---------------------------------------------------------------------- +# TESTING/TYPES +# ---------------------------------------------------------------------- test: python -m nox --reuse-existing-virtualenvs --session "test" @@ -62,9 +33,9 @@ coverage: types: python -m nox --reuse-existing-virtualenvs --session "mypy" -################## -# DJANGO # -################## +# ---------------------------------------------------------------------- +# DJANGO +# ---------------------------------------------------------------------- manage *COMMAND: #!/usr/bin/env python @@ -91,12 +62,9 @@ makemigrations *APPS: migrate *ARGS: @just manage migrate {{ ARGS }} -shell: - @just manage shell - -################## -# DOCS # -################## +# ---------------------------------------------------------------------- +# DOCS +# ---------------------------------------------------------------------- @docs-install: python -m pip install '.[docs]' @@ -112,17 +80,18 @@ shell: @docs-build LOCATION="docs/_build/html": sphinx-build docs {{ LOCATION }} -################## -# UTILS # -################## - -lint: - python -m nox --reuse-existing-virtualenvs --session "lint" +# ---------------------------------------------------------------------- +# UTILS +# ---------------------------------------------------------------------- -# format Justfile +# format justfile fmt: just --fmt --unstable +# run pre-commit on all files +lint: + pre-commit run --all-files + # ---------------------------------------------------------------------- # COPIER # ---------------------------------------------------------------------- diff --git a/LICENSE b/LICENSE index 2d44d9b..c159dca 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2023 Josh Thomas +Copyright (c) 2024 Josh Thomas -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: +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 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. +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 index e7e7ef7..af67646 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,17 @@ `django-simple-nav` is a Python/Django application designed to simplify the integration of navigation and menu bars in your Django projects. With a straightforward API and customizable options, you can easily add and manage navigational elements in your web applications. -## Installation +## Requirements -1. **Install the package**: +- Python 3.8, 3.9, 3.10, 3.11, 3.12 +- Django 3.2, 4.2, 5.0 - You can install `django-simple-nav` using pip: +## Getting Started - ```shell - pip install django-simple-nav +1. Install the package from PyPI: + + ```bash + python -m pip install django-simple-nav ``` 2. **Add to Installed Apps**: @@ -25,9 +28,9 @@ ```python INSTALLED_APPS = [ - # ... + ..., "django_simple_nav", - # ... + ..., ] ``` @@ -35,57 +38,62 @@ 1. **Create a navigation definition**: - Define your navigation structure in a Python file. Here's an example configuration: - - ```python - from django_simple_nav.nav import Nav - from django_simple_nav.nav import NavGroup - from django_simple_nav.nav import NavItem - - - class MainNav(Nav): - template_name = "main_nav.html" - items = [ - NavItem(title="Relative URL", url="/relative-url"), - NavItem(title="Absolute URL", url="https://example.com/absolute-url"), - NavItem(title="Internal Django URL by Name", url="fake-view"), - NavGroup( - title="Group", - url="/group", - items=[ - NavItem(title="Relative URL", url="/relative-url"), - NavItem(title="Absolute URL", url="https://example.com/absolute-url"), - NavItem(title="Internal Django URL by Name", url="fake-view"), - ], - ), - NavItem( - title="is_authenticated Item", url="#", permissions=["is_authenticated"] - ), - NavItem(title="is_staff Item", url="#", permissions=["is_staff"]), - NavItem(title="is_superuser Item", url="#", permissions=["is_superuser"]), - NavItem( - title="myapp.django_perm Item", url="#", permissions=["myapp.django_perm"] - ), - ] - ``` + Define your navigation structure in a Python file. Here's an example configuration: + + ```python + from django_simple_nav.nav import Nav + from django_simple_nav.nav import NavGroup + from django_simple_nav.nav import NavItem + + + class MainNav(Nav): + template_name = "main_nav.html" + items = [ + NavItem(title="Relative URL", url="/relative-url"), + NavItem(title="Absolute URL", url="https://example.com/absolute-url"), + NavItem(title="Internal Django URL by Name", url="fake-view"), + NavGroup( + title="Group", + url="/group", + items=[ + NavItem(title="Relative URL", url="/relative-url"), + NavItem(title="Absolute URL", url="https://example.com/absolute-url"), + NavItem(title="Internal Django URL by Name", url="fake-view"), + ], + ), + NavItem( + title="is_authenticated Item", url="#", permissions=["is_authenticated"] + ), + NavItem(title="is_staff Item", url="#", permissions=["is_staff"]), + NavItem(title="is_superuser Item", url="#", permissions=["is_superuser"]), + NavItem( + title="myapp.django_perm Item", url="#", permissions=["myapp.django_perm"] + ), + ] + ``` 2. **Integrate Navigation in Templates**: - Use the `django_simple_nav` template tag in your Django templates where you want to display the navigation. - For example: + Use the `django_simple_nav` template tag in your Django templates where you want to display the navigation. - ```html - {% load django_simple_nav %} - ... - - ... - ``` + For example: + + ```html + {% load django_simple_nav %} + ... + + ... + ``` After configuring your navigation, you can use it across your Django project by calling the `django_simple_nav` template tag in your templates. This tag dynamically renders navigation based on your defined structure, ensuring a consistent and flexible navigation experience throughout your application. +## Documentation + +Please refer to the [documentation](https://django-simple-nav.westervelt.dev/) for more information. + ## License `django-simple-nav` is licensed under the MIT license. See the [`LICENSE`](LICENSE) file for more information. diff --git a/RELEASING.md b/RELEASING.md index 141bd9a..2372460 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,7 +4,7 @@ When it comes time to cut a new release, follow these steps: 1. Create a new git branch off of `main` for the release. - Prefer the convention `release-`, where `` is the next incremental version number (e.g. `release-v1.0.0` for version 1.0.0). + Prefer the convention `release-`, where `` is the next incremental version number (e.g. `release-v0.1.0` for version 0.1.0). ```shell git checkout -b release-v @@ -20,7 +20,7 @@ When it comes time to cut a new release, follow these steps: ```shell python -m pip install --editable '.[dev]' - # or using the included `Justfile` + # or using [just](CONTRIBUTING.md#just) just bootstrap ``` @@ -52,7 +52,7 @@ When it comes time to cut a new release, follow these steps: bumpver update --tag=final ``` -3. Ensure the [CHANGELOG](https://github.com/westerveltco/django-simple-nav/blob/main/CHANGELOG.md) is up to date. If updates are needed, add them now in the release branch. +3. Ensure the [CHANGELOG](CHANGELOG.md) is up to date. If updates are needed, add them now in the release branch. 4. Create a pull request from the release branch to `main`. @@ -60,7 +60,7 @@ When it comes time to cut a new release, follow these steps: 6. Draft a [new release](https://github.com/westerveltco/django-simple-nav/releases/new) on GitHub. - Use the version number with a leading `v` as the tag name (e.g. `v1.0.0`). + Use the version number with a leading `v` as the tag name (e.g. `v0.1.0`). Allow GitHub to generate the release title and release notes, using the 'Generate release notes' button above the text box. If this is a final release coming from a tagged release (or multiple tagged releases), make sure to copy the release notes from the previous tagged release(s) to the new release notes (after the release notes already generated for this final release). diff --git a/docs/conf.py b/docs/conf.py index e7b9f26..8da4e9e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -75,10 +75,6 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] -html_css_files = [ - "css/custom.css", -] - html_title = project html_theme_options = { @@ -99,7 +95,6 @@ html_sidebars = { "**": [ "sidebar/brand.html", - # "sidebar/repo.html", "sidebar/search.html", "sidebar/scroll-start.html", "sidebar/navigation.html", diff --git a/docs/development/just.md b/docs/development/just.md new file mode 100644 index 0000000..320b3c4 --- /dev/null +++ b/docs/development/just.md @@ -0,0 +1,61 @@ +# Justfile + +This project uses [Just](https://github.com/casey/just) as a command runner. + +The following commands are available: + + + + +## Commands + +```{code-block} shell +:class: copy + +$ just --list +``` + + + + + diff --git a/docs/index.md b/docs/index.md index 691383a..9385dba 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,11 @@ ```{include} ../README.md ``` + +```{toctree} +:hidden: +:maxdepth: 3 +:caption: Development + +development/just.md +``` diff --git a/docs/requirements.in b/docs/requirements.in new file mode 100644 index 0000000..fe9acfc --- /dev/null +++ b/docs/requirements.in @@ -0,0 +1,8 @@ +-r ../requirements.in +cogapp +furo +myst-parser +sphinx +sphinx-autobuild +sphinx-copybutton +sphinx-inline-tabs diff --git a/noxfile.py b/noxfile.py index 94d9c16..caa9fd1 100644 --- a/noxfile.py +++ b/noxfile.py @@ -21,7 +21,7 @@ DJMAIN_MIN_PY = PY310 DJ_VERSIONS = [DJ32, DJ42, DJ50, DJMAIN] DJ_LTS = [DJ32, DJ42] -DJ_DEFAULT = DJ_LTS[-1] +DJ_DEFAULT = DJ_LTS[0] DJ_LATEST = DJ_VERSIONS[-2] @@ -32,6 +32,7 @@ def version(ver: str) -> tuple[int, ...]: def should_skip(python: str, django: str) -> bool: """Return True if the test should be skipped""" + if django == DJMAIN and version(python) < version(DJMAIN_MIN_PY): # Django main requires Python 3.10+ return True diff --git a/pyproject.toml b/pyproject.toml index 074f2bf..5b48c0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,9 +9,7 @@ authors = [ classifiers = [ "Development Status :: 3 - Alpha", "Framework :: Django", - "Framework :: Django :: 3", "Framework :: Django :: 3.2", - "Framework :: Django :: 4", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "License :: OSI Approved :: MIT License", @@ -26,7 +24,9 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython" ] -dependencies = ["django>=3.2"] +dependencies = [ + "django>=3.2" +] description = "A simple, flexible, and extensible navigation menu for Django." dynamic = ["version"] keywords = [] @@ -41,9 +41,10 @@ dev = [ "coverage[toml]", "django-stubs", "django-stubs-ext", + "faker", "hatch", "mypy", - "model_bakery", + "model-bakery", "nox", "pytest", "pytest-cov", @@ -64,7 +65,7 @@ docs = [ lint = ["pre-commit"] [project.urls] -Documentation = "https://django-simple-nav.westervelt.dev" +Documentation = "https://django-simple-nav.westervelt.dev/" Issues = "https://github.com/westerveltco/django-simple-nav/issues" Source = "https://github.com/westerveltco/django-simple-nav" @@ -88,7 +89,15 @@ version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]" source = ["src"] [tool.coverage.report] -fail_under = 85 +exclude_lines = [ + "pragma: no cover", + "if DEBUG:", + "if not DEBUG:", + "if settings.DEBUG:", + "if TYPE_CHECKING:", + 'def __str__\(self\)\s?\-?\>?\s?\w*\:' +] +fail_under = 75 [tool.coverage.run] omit = [ @@ -106,13 +115,8 @@ indent = 2 [tool.hatch.build] exclude = [ - ".github/*", - ".dockerfiles/*", - ".dockerignore", - ".editorconfig", - ".pre-commit-config.yaml", - "Justfile", - "src/service/*" + ".*", + "Justfile" ] [tool.hatch.build.targets.wheel] @@ -125,7 +129,6 @@ path = "src/django_simple_nav/__init__.py" check_untyped_defs = true exclude = "docs/.*\\.py$" mypy_path = "src/" -namespace_packages = false no_implicit_optional = true plugins = [ "mypy_django_plugin.main" @@ -137,7 +140,10 @@ warn_unused_ignores = true [[tool.mypy.overrides]] ignore_errors = true ignore_missing_imports = true -module = "tests.*" +module = [ + "django_simple_nav.*.migrations.*", + "tests.*" +] [tool.mypy_django_plugin] ignore_missing_model_attributes = true @@ -168,12 +174,10 @@ exclude = [ ".venv", "__pypackages__", "_build", - "buck-out", "build", "dist", "migrations", "node_modules", - "static", "venv" ] extend-include = ["*.pyi?"] @@ -183,7 +187,6 @@ ignore = ["E501", "E741"] # temporary indent-width = 4 # Same as Black. line-length = 88 -per-file-ignores = {} select = [ "B", # flake8-bugbear "E", # Pycodestyle @@ -208,6 +211,10 @@ force-single-line = true known-first-party = ["django_simple_nav"] required-imports = ["from __future__ import annotations"] +[tool.ruff.per-file-ignores] +# Tests can use magic values, assertions, and relative imports +"tests/**/*" = ["PLR2004", "S101", "TID252"] + [tool.ruff.pyupgrade] # Preserve types, even if a file imports `from __future__ import annotations`. keep-runtime-typing = true diff --git a/src/django_simple_nav/__init__.py b/src/django_simple_nav/__init__.py index f11ec6a..3538900 100644 --- a/src/django_simple_nav/__init__.py +++ b/src/django_simple_nav/__init__.py @@ -1,3 +1,4 @@ from __future__ import annotations __version__ = "0.1.0" +__template_version__ = "2024.3" diff --git a/tests/conftest.py b/tests/conftest.py index 331b396..810fe75 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,42 +10,45 @@ pytest_plugins = [] # type: ignore -# Settings fixtures to bootstrap our tests def pytest_configure(config): logging.disable(logging.CRITICAL) - settings.configure( - ALLOWED_HOSTS=["*"], - CACHES={ - "default": { - "BACKEND": "django.core.cache.backends.dummy.DummyCache", - } - }, - DATABASES={ - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": ":memory:", - }, + settings.configure(**TEST_SETTINGS) + + +TEST_SETTINGS = { + "ALLOWED_HOSTS": ["*"], + "DEBUG": False, + "CACHES": { + "default": { + "BACKEND": "django.core.cache.backends.dummy.DummyCache", + } + }, + "DATABASES": { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": ":memory:", }, - EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend", - INSTALLED_APPS=[ - "django.contrib.auth", - "django.contrib.contenttypes", - "django_simple_nav", - "tests", - ], - LOGGING_CONFIG=None, - PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"], - ROOT_URLCONF="tests.urls", - SECRET_KEY="NOTASECRET", - TEMPLATES=[ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "APP_DIRS": True, - } - ], - USE_TZ=True, - ) + }, + "EMAIL_BACKEND": "django.core.mail.backends.locmem.EmailBackend", + "INSTALLED_APPS": [ + "django.contrib.auth", + "django.contrib.contenttypes", + "django_simple_nav", + "tests", + ], + "LOGGING_CONFIG": None, + "PASSWORD_HASHERS": [ + "django.contrib.auth.hashers.MD5PasswordHasher", + ], + "ROOT_URLCONF": "tests.urls", + "TEMPLATES": [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + } + ], +} @pytest.fixture diff --git a/tests/settings.py b/tests/settings.py index e69de29..2f47211 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -0,0 +1 @@ +# here to make `mypy` and `django-stubs` happy diff --git a/tests/templates/tests/alternate.html b/tests/templates/tests/alternate.html index ef602f0..04dbb06 100644 --- a/tests/templates/tests/alternate.html +++ b/tests/templates/tests/alternate.html @@ -1,4 +1,2 @@ -
- This is an alternate template. -
+
This is an alternate template.
{% include "tests/dummy_nav.html"} diff --git a/tests/templates/tests/dummy_nav.html b/tests/templates/tests/dummy_nav.html index 1bce6fe..e00e4db 100644 --- a/tests/templates/tests/dummy_nav.html +++ b/tests/templates/tests/dummy_nav.html @@ -1,6 +1,6 @@ {% for item in items %} {{ item.title }} {% if item.items %} - {% include 'tests/dummy_nav.html' with items=item.items %} + {% include "tests/dummy_nav.html" with items=item.items %} {% endif %} {% endfor %}