From 1f343606ec1f2a99acf8a37b9900d78a8fb37282 Mon Sep 17 00:00:00 2001
From: Matthias Mair <code@mjmair.com>
Date: Sun, 7 Jan 2024 20:42:14 +0100
Subject: [PATCH] Squashed commit of the following:

commit f5cf7b2e7872fc19633321713965763d1890b495
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:36:57 2024 +0100

    fixed reqs

commit 9d845bee98befa4e53c2ac3c783bd704369e3ad2
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:32:35 2024 +0100

    disable autofix/format

commit aff5f271484c3500df7ddde043767c008ce4af21
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:28:50 2024 +0100

    adjust checks

commit 47271cf1efa848ec8374a0d83b5646d06fffa6e7
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:28:22 2024 +0100

    reorder order of operations

commit e1bf178b40b3f0d2d59ba92209156c43095959d2
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:01:09 2024 +0100

    adapted ruff settings to better fit code base

commit ad7d88a6f4f15c9552522131c4e207256fc2bbf6
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:59:45 2024 +0100

    auto fixed docstring

commit a2e54a760e17932dbbc2de0dec23906107f2cda9
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:46:35 2024 +0100

    fix getattr useage

commit cb80c73bc6c0be7f5d2ed3cc9b2ac03fdefd5c41
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:25:09 2024 +0100

    fix requirements file

commit b7780bbd21a32007f3b0ce495b519bf59bb19bf5
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:42:28 2024 +0100

    fix removed sections

commit 71f1681f55c15f62c16c1d7f30a745adc496db97
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:41:21 2024 +0100

    fix djlint syntax

commit a0bcf1bccef8a8ffd482f38e2063bc9066e1d759
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:35:28 2024 +0100

    remove flake8 from code base

commit 22475b31cc06919785be046e007915e43f356793
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:34:56 2024 +0100

    remove flake8 from code base

commit 0413350f14773ac6161473e0cfb069713c13c691
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:24:39 2024 +0100

    moved ruff section

commit d90c48a0bf98befdfacbbb093ee56cdb28afb40d
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:24:24 2024 +0100

    move djlint config to pyproject

commit c5ce55d5119bf2e35e429986f62f875c86178ae1
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:20:39 2024 +0100

    added isort again

commit 42a41d23afc280d4ee6f0e640148abc6f460f05a
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:19:02 2024 +0100

    move config section

commit 85692331816348cb1145570340d1f6488a8265cc
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:17:52 2024 +0100

    fix codespell error

commit 2897c6704d1311a800ce5aa47878d96d6980b377
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 17:29:21 2024 +0100

    replaced flake8 with ruff
    mostly for speed improvements
---
 .devcontainer/devcontainer.json               |  2 -
 .djlintrc                                     |  3 -
 .github/workflows/qc_checks.yaml              | 40 +--------
 .pre-commit-config.yaml                       | 26 +++---
 InvenTree/common/models.py                    |  2 +-
 InvenTree/common/notifications.py             |  4 +-
 InvenTree/part/stocktake.py                   |  2 +-
 InvenTree/part/test_api.py                    |  2 +-
 .../plugin/base/integration/APICallMixin.py   |  2 +-
 InvenTree/plugin/base/label/mixins.py         |  2 +-
 InvenTree/plugin/models.py                    |  2 +-
 InvenTree/plugins/__init__.py                 |  3 +-
 InvenTree/stock/test_api.py                   |  2 +-
 pyproject.toml                                | 81 +++++++++++++++++++
 requirements-dev.in                           |  3 -
 requirements-dev.txt                          | 19 -----
 requirements.txt                              |  9 ++-
 setup.cfg                                     | 20 -----
 18 files changed, 114 insertions(+), 110 deletions(-)
 delete mode 100644 .djlintrc
 create mode 100644 pyproject.toml

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index e6fc394897..5862479a5f 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -21,12 +21,10 @@
         "python.defaultInterpreterPath": "${containerWorkspaceFolder}/dev/venv/bin/python",
         "python.linting.enabled": true,
         "python.linting.pylintEnabled": false,
-        "python.linting.flake8Enabled": true,
         "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
         "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
         "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
         "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
-        "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
         "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
         "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
         "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
diff --git a/.djlintrc b/.djlintrc
deleted file mode 100644
index 58e7fe1c0d..0000000000
--- a/.djlintrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "ignore": "D018,H006,H008,H020,H021,H023,H025,H030,H031,T002"
-}
diff --git a/.github/workflows/qc_checks.yaml b/.github/workflows/qc_checks.yaml
index 80ad555380..8a3e1d609b 100644
--- a/.github/workflows/qc_checks.yaml
+++ b/.github/workflows/qc_checks.yaml
@@ -47,27 +47,11 @@ jobs:
             frontend:
               - 'src/frontend/**'
 
-  pep_style:
-    name: Style [Python]
-    runs-on: ubuntu-20.04
-
-    needs: paths-filter
-    if: needs.paths-filter.outputs.server == 'true'
-
-    steps:
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
-      - name: Environment Setup
-        uses: ./.github/actions/setup
-        with:
-          dev-install: true
-      - name: Run flake8
-        run: flake8 InvenTree --extend-ignore=D
-
   javascript:
     name: Style - Classic UI [JS]
     runs-on: ubuntu-20.04
 
-    needs: [ 'pep_style', 'pre-commit' ]
+    needs: [ 'pre-commit' ]
 
     steps:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
@@ -167,27 +151,11 @@ jobs:
           invoke check-server
           coverage run -m unittest discover -s test/
 
-  docstyle:
-    name: Style [Python Docstrings]
-    runs-on: ubuntu-20.04
-
-    needs: pre-commit
-    continue-on-error: true
-
-    steps:
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
-      - name: Environment Setup
-        uses: ./.github/actions/setup
-        with:
-          install: true
-      - name: Run flake8
-        run: flake8 InvenTree --statistics
-
   coverage:
     name: Tests - DB [SQLite] + Coverage
     runs-on: ubuntu-20.04
 
-    needs: [ 'pep_style', 'pre-commit' ]
+    needs: [ 'pre-commit' ]
     continue-on-error: true # continue if a step fails so that coverage gets pushed
 
     env:
@@ -220,7 +188,7 @@ jobs:
   postgres:
     name: Tests - DB [PostgreSQL]
     runs-on: ubuntu-20.04
-    needs: [ 'pep_style', 'pre-commit' ]
+    needs: [ 'pre-commit' ]
 
     env:
       INVENTREE_DB_ENGINE: django.db.backends.postgresql
@@ -264,7 +232,7 @@ jobs:
     name: Tests - DB [MySQL]
     runs-on: ubuntu-20.04
 
-    needs: [ 'pep_style', 'pre-commit' ]
+    needs: [ 'pre-commit' ]
 
     env:
       # Database backend configuration
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index bd43f93b65..2458c3f7f7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,8 @@ exclude: |
     (?x)^(
         InvenTree/InvenTree/static/.*|
         InvenTree/locale/.*|
-        src/frontend/src/locales/.*
+        src/frontend/src/locales/.*|
+        .*/migrations/.*
     )$
 repos:
 -   repo: https://github.com/pre-commit/pre-commit-hooks
@@ -14,23 +15,20 @@ repos:
     -   id: end-of-file-fixer
     -   id: check-yaml
     -   id: mixed-line-ending
--   repo: https://github.com/pycqa/flake8
-    rev: '6.1.0'
-    hooks:
-    -   id: flake8
-        additional_dependencies: [
-                'flake8-bugbear',
-                'flake8-comprehensions',
-                'flake8-docstrings',
-                'flake8-string-format',
-                'flake8-tidy-imports',
-                'pep8-naming',
-                'flake8-logging'
-        ]
 -   repo: https://github.com/pycqa/isort
     rev: '5.12.0'
     hooks:
     -   id: isort
+-   repo: https://github.com/astral-sh/ruff-pre-commit
+    rev: v0.1.11
+    hooks:
+#    - id: ruff-format
+#      args: [--preview]
+    - id: ruff
+      args: [
+        #--fix,
+        --preview
+      ]
 -   repo: https://github.com/jazzband/pip-tools
     rev: 7.3.0
     hooks:
diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py
index e177f17d57..649ea6adfa 100644
--- a/InvenTree/common/models.py
+++ b/InvenTree/common/models.py
@@ -2472,7 +2472,7 @@ class ColorTheme(models.Model):
     def get_color_themes_choices(cls):
         """Get all color themes from static folder."""
         if not settings.STATIC_COLOR_THEMES_DIR.exists():
-            logger.error('Theme directory does not exsist')
+            logger.error('Theme directory does not exists')
             return []
 
         # Get files list from css/color-themes/ folder
diff --git a/InvenTree/common/notifications.py b/InvenTree/common/notifications.py
index e52e2d86ff..c32ee3ec82 100644
--- a/InvenTree/common/notifications.py
+++ b/InvenTree/common/notifications.py
@@ -336,9 +336,9 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
 
     # Try with some defaults
     if not obj_ref_value:
-        obj_ref_value = getattr(obj, 'pk')
+        obj_ref_value = getattr(obj, 'pk', None)
     if not obj_ref_value:
-        obj_ref_value = getattr(obj, 'id')
+        obj_ref_value = getattr(obj, 'id', None)
     if not obj_ref_value:
         raise KeyError(f"Could not resolve an object reference for '{str(obj)}' with {obj_ref}, pk, id")
 
diff --git a/InvenTree/part/stocktake.py b/InvenTree/part/stocktake.py
index b889b11474..7ccd6cd185 100644
--- a/InvenTree/part/stocktake.py
+++ b/InvenTree/part/stocktake.py
@@ -24,7 +24,7 @@ logger = logging.getLogger('inventree')
 def perform_stocktake(target: part.models.Part, user: User, note: str = '', commit=True, **kwargs):
     """Perform stocktake action on a single part.
 
-    arguments:
+    Arguments:
         target: A single Part model instance
         commit: If True (default) save the result to the database
         user: User who requested this stocktake
diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py
index fe334f6b9a..78ab67a9ef 100644
--- a/InvenTree/part/test_api.py
+++ b/InvenTree/part/test_api.py
@@ -80,7 +80,7 @@ class PartCategoryAPITest(InvenTreeAPITestCase):
             ({'parent': 1, 'cascade': False, 'depth': 1}, 3, 'Dont cascade even with depth=1 specified with parent'),
             ({'parent': 1, 'cascade': True, 'depth': 1}, 5, 'Cascade with depth=1 with parent'),
             ({'parent': 1, 'cascade': True, 'depth': 'abcdefg'}, 5, 'Cascade with invalid depth and parent'),
-            ({'parent': 42}, 8, 'Should return everything if parent_pk is not vaild'),
+            ({'parent': 42}, 8, 'Should return everything if parent_pk is not valid'),
             ({'parent': 'null', 'exclude_tree': 1, 'cascade': True}, 2, 'Should return everything from except tree with pk=1'),
             ({'parent': 'null', 'exclude_tree': 42, 'cascade': True}, 8, 'Should return everything because exclude_tree=42 is no valid pk'),
             ({'parent': 1, 'starred': True, 'cascade': True}, 2, 'Should return the starred categories for the current user within the pk=1 tree'),
diff --git a/InvenTree/plugin/base/integration/APICallMixin.py b/InvenTree/plugin/base/integration/APICallMixin.py
index 7fa6a43d25..1dcef4dc61 100644
--- a/InvenTree/plugin/base/integration/APICallMixin.py
+++ b/InvenTree/plugin/base/integration/APICallMixin.py
@@ -93,7 +93,7 @@ class APICallMixin:
         Check the mixin class docstring for a full example.
         """
         headers = {'Content-Type': 'application/json'}
-        if getattr(self, 'API_TOKEN_SETTING'):
+        if getattr(self, 'API_TOKEN_SETTING', None):
             token = self.get_setting(self.API_TOKEN_SETTING)
 
             if token:
diff --git a/InvenTree/plugin/base/label/mixins.py b/InvenTree/plugin/base/label/mixins.py
index fe4aa1607f..56194c29a4 100644
--- a/InvenTree/plugin/base/label/mixins.py
+++ b/InvenTree/plugin/base/label/mixins.py
@@ -82,7 +82,7 @@ class LabelPrintingMixin:
             items: The list of database items to print (e.g. StockItem instances)
             request: The HTTP request object which triggered this print job
 
-        Keyword arguments:
+        Keyword Arguments:
             printing_options: The printing options set for this print job defined in the PrintingOptionsSerializer
 
         Returns:
diff --git a/InvenTree/plugin/models.py b/InvenTree/plugin/models.py
index 44f281cf02..fef716b86a 100644
--- a/InvenTree/plugin/models.py
+++ b/InvenTree/plugin/models.py
@@ -189,7 +189,7 @@ class PluginSetting(common.models.BaseInvenTreeSetting):
             plugin = kwargs.pop('plugin', None)
 
             if plugin:
-                mixin_settings = getattr(registry, 'mixins_settings')
+                mixin_settings = getattr(registry, 'mixins_settings', None)
                 if mixin_settings:
                     kwargs['settings'] = mixin_settings.get(plugin.key, {})
 
diff --git a/InvenTree/plugins/__init__.py b/InvenTree/plugins/__init__.py
index 997ae5addf..47a9b3c42c 100644
--- a/InvenTree/plugins/__init__.py
+++ b/InvenTree/plugins/__init__.py
@@ -1,5 +1,4 @@
-"""
-Directory for custom plugin development.
+"""Directory for custom plugin development.
 
 Please read the docs for more information https://docs.inventree.org/en/latest/extend/plugins/#local-directory
 """
diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py
index 3c9dd6931d..0243aaa405 100644
--- a/InvenTree/stock/test_api.py
+++ b/InvenTree/stock/test_api.py
@@ -77,7 +77,7 @@ class StockLocationTest(StockAPITestCase):
             ({'parent': 1, 'cascade': False, 'depth': 1}, 2, 'Dont cascade even with depth=1 specified with parent'),
             ({'parent': 1, 'cascade': True, 'depth': 1}, 2, 'Cascade with depth=1 with parent'),
             ({'parent': 1, 'cascade': True, 'depth': 'abcdefg'}, 2, 'Cascade with invalid depth and parent'),
-            ({'parent': 42}, 8, 'Should return everything if parent_pk is not vaild'),
+            ({'parent': 42}, 8, 'Should return everything if parent_pk is not valid'),
             ({'parent': 'null', 'exclude_tree': 1, 'cascade': True}, 5, 'Should return everything except tree with pk=1'),
             ({'parent': 'null', 'exclude_tree': 42, 'cascade': True}, 8, 'Should return everything because exclude_tree=42 is no valid pk'),
         ]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000..f3807470d3
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,81 @@
+
+[tool.ruff]
+exclude = [
+    ".git",
+    "__pycache__",
+    "dist",
+    "build",
+    "test.py",
+    "tests",
+    "venv",
+    "env",
+    ".venv",
+    ".env",
+]
+src = ["../InvenTree"]
+# line-length = 120
+
+[tool.ruff.extend-per-file-ignores]
+"__init__.py" = ["D104"]
+
+[tool.ruff.lint]
+select = ["A", "B", "C4", "D", "N"]
+# Things that should be enabled in the future:
+# - LOG
+# - I
+# - DJ # for Django stuff
+# - S # for security stuff (bandit)
+
+ignore = [
+    "N999",
+    # - N802 - function name should be lowercase
+	"N802",
+	# - N806 - variable should be lowercase
+	"N806",
+	# - N812 - lowercase imported as non-lowercase
+	"N812",
+    # - D202 - No blank lines allowed after function docstring
+    "D202",
+    # - D415 - First line should end with a period, question mark, or exclamation point
+    "D415",
+    # - D417 Missing argument descriptions in the docstring
+    "D417",
+
+    # TODO These should be followed up and fixed
+    # - B904 Within an `except` clause, raise exceptions
+    "B904",
+
+    # Remove fast
+    "A001", "A002","A003","B018"
+]
+
+[tool.ruff.lint.pydocstyle]
+convention = "google"
+
+[tool.ruff.lint.isort]
+split-on-trailing-comma = false
+combine-as-imports = false
+section-order = [
+    "future",
+    "standard-library",
+    "django",
+    "third-party",
+    "first-party",
+    "local-folder",
+]
+known-first-party = ["src", "plugin", "InvenTree", "common"]
+
+[tool.ruff.lint.isort.sections]
+"django" = ["django"]
+
+[tool.ruff.format]
+quote-style = "preserve"
+indent-style = "space"
+skip-magic-trailing-comma = true
+line-ending = "auto"
+
+[tool.coverage.run]
+source = "InvenTree"
+
+[tool.djlint]
+ignore = "D018,H006,H008,H020,H021,H023,H025,H030,H031,T002"
diff --git a/requirements-dev.in b/requirements-dev.in
index 50f25f136c..1392a8a05c 100644
--- a/requirements-dev.in
+++ b/requirements-dev.in
@@ -5,10 +5,7 @@ coveralls==2.1.2                        # Coveralls linking (for tracking covera
 django-debug-toolbar                    # Debug / profiling toolbar
 django-slowtests                        # Show which unit tests are running slowly
 django-test-migrations                  # Unit testing for database migrations
-flake8                                  # PEP checking
-flake8-docstrings                       # docstring format testing
 isort                                   # python import sorting
-pep8-naming                             # PEP naming convention extension
 pip-tools                               # Compile pip requirements
 pre-commit                              # Git pre-commit
 setuptools                              # Standard dependency
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 8409bb3757..4b9e21ce45 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -54,13 +54,6 @@ docopt==0.6.2
     # via coveralls
 filelock==3.13.1
     # via virtualenv
-flake8==6.1.0
-    # via
-    #   -r requirements-dev.in
-    #   flake8-docstrings
-    #   pep8-naming
-flake8-docstrings==1.7.0
-    # via -r requirements-dev.in
 identify==2.5.31
     # via pre-commit
 idna==3.4
@@ -73,8 +66,6 @@ importlib-metadata==6.8.0
     #   build
 isort==5.12.0
     # via -r requirements-dev.in
-mccabe==0.7.0
-    # via flake8
 nodeenv==1.8.0
     # via pre-commit
 packaging==23.2
@@ -83,24 +74,16 @@ packaging==23.2
     #   build
 pdfminer-six==20221105
     # via -r requirements-dev.in
-pep8-naming==0.13.3
-    # via -r requirements-dev.in
 pip-tools==7.3.0
     # via -r requirements-dev.in
 platformdirs==3.11.0
     # via virtualenv
 pre-commit==3.5.0
     # via -r requirements-dev.in
-pycodestyle==2.11.1
-    # via flake8
 pycparser==2.21
     # via
     #   -c requirements.txt
     #   cffi
-pydocstyle==6.3.0
-    # via flake8-docstrings
-pyflakes==3.1.0
-    # via flake8
 pyproject-hooks==1.0.0
     # via build
 pytz==2023.3.post1
@@ -115,8 +98,6 @@ requests==2.31.0
     # via
     #   -c requirements.txt
     #   coveralls
-snowballstemmer==2.2.0
-    # via pydocstyle
 sqlparse==0.4.4
     # via
     #   -c requirements.txt
diff --git a/requirements.txt b/requirements.txt
index 1921cc1d57..4f0e76d36c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,7 +15,9 @@ attrs==23.1.0
 babel==2.13.1
     # via py-moneyed
 bleach[css]==6.1.0
-    # via django-markdownify
+    # via
+    #   bleach
+    #   django-markdownify
 brotli==1.1.0
     # via fonttools
 certifi==2023.7.22
@@ -162,7 +164,9 @@ et-xmlfile==1.1.0
 feedparser==6.0.10
     # via -r requirements.in
 fonttools[woff]==4.44.0
-    # via weasyprint
+    # via
+    #   fonttools
+    #   weasyprint
 gunicorn==21.2.0
     # via -r requirements.in
 html5lib==1.1
@@ -221,6 +225,7 @@ pyjwt[crypto]==2.8.0
     # via
     #   django-allauth
     #   djangorestframework-simplejwt
+    #   pyjwt
 pyphen==0.14.0
     # via weasyprint
 pypng==0.20220715.0
diff --git a/setup.cfg b/setup.cfg
index 46d86e7832..95ca4635f6 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -8,29 +8,9 @@ ignore =
 	E722,
 	# - C901 - function is too complex
 	C901,
-	# - N802 - function name should be lowercase
-	# TODO (In the future, we should conform to this!)
-	N802,
-	# - N806 - variable should be lowercase
-	N806,
-	# - N812 - lowercase imported as non-lowercase
-	N812,
-	# - D202 - No blank lines allowed after function docstring
-	D202,
-	# - D415 - First line should end with a period, question mark, or exclamation point
-	D415,
 	# - B009 - Do not call getattr with a constant attribute value
 	B009
 exclude = .git,__pycache__,*/migrations/*,*/lib/*,*/bin/*,*/media/*,*/static/*,InvenTree/plugins/*
-per-file-ignores =
-    # Do not enforce docstring on __init__
-    __init__.py: D104
-max-complexity = 20
-docstring-convention=google
-ban-relative-imports = parents
-
-[coverage:run]
-source = ./InvenTree
 
 [isort]
 src_paths=InvenTree