Files
InvenTree/pyproject.toml
T
Matthias Mair 7e57196d76 Add import linter (#13248)
* add import-linter

* move machine imports to plugin space

* clean up samples

* extend scope

* extend checks

* move plugin internals into a seperat folder

* remove unneeded re-export

* adjust import

* update import checker

* update src ref

* fix mocks
2026-11-12 18:41:59 +11:00

250 lines
6.1 KiB
TOML

[tool.ruff]
exclude = [
".git",
"__pycache__",
"test.py",
"tests",
"venv",
"env",
".venv",
".env",
"src/backend/plugins/*",
]
src = ["src/backend/InvenTree"]
# line-length = 120
[tool.ruff.lint.extend-per-file-ignores]
"__init__.py" = ["D104"]
[tool.ruff.lint]
select = ["A", "B", "C", "C4", "D", "F", "I", "N", "SIM", "PIE", "PLE", "PLW", "RUF", "UP", "W",
#"DOC201", "DOC202", # enforce return docs
"DOC402","DOC403", # enforce yield docs
#"DOC501","DOC502", # enforce raise
]
# Things that should be enabled in the future:
# - LOG
# - DJ # for Django stuff
# - S # for security stuff (bandit)
# - D401 - Imperative docstrings
ignore = [
"PLE1205",
# - PLE1205 - Too many arguments for logging format string
"PLW2901",
# - PLW2901 - Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target
"PLW0602","PLW0603","PLW0604", # global variable things
"RUF015",
# - RUF015 - Prefer next({iterable}) over single element slice
"RUF012",
# - RUF012 - Mutable class attributes should be annotated with typing.ClassVar
"RUF052",
# - RUF052 - Local dummy variable is accessed
"SIM117",
# - SIM117 - Use a single with statement with multiple contexts instead of nested with statements
"SIM102",
# - SIM102 - Use a single if statement instead of nested if statements
"SIM105",
# - SIM105 - Use contextlib.suppress({exception}) instead of try-except-pass
"C901",
# - C901 - function is too complex
"N999",
# - N802 - function name should be lowercase
"N802",
# - N806 - variable should be lowercase
"N806",
# - N812 - lowercase imported as non-lowercase
"N812",
# - RUF032 - decimal-from-float-literal
"RUF032",
# - RUF045 - implicit-class-var-in-dataclass
"RUF045",
# - UP045 - Use `X | None` instead of `Optional[X]`
"UP045",
# TODO These should be followed up and fixed
# - B904 Within an `except` clause, raise exceptions
"B904",
# Remove fast
"A002", "B018"
]
[tool.ruff.lint.pydocstyle]
convention = "google"
ignore-var-parameters = true
[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",
]
[tool.ruff.lint.isort.sections]
"django" = ["django"]
[tool.ruff.format]
quote-style = "single"
indent-style = "space"
skip-magic-trailing-comma = true
line-ending = "auto"
[tool.uv.pip]
python-version = "3.12"
no-strip-extras=true
generate-hashes=true
[tool.ty.environment]
extra-paths = ["src/backend/InvenTree"]
[tool.ty.rules]
unresolved-attribute="ignore" # 837 # need Plugin Mixin typing
call-non-callable="ignore" # 8 ##
invalid-assignment="ignore" # 40 # need to wait for better django field stubs
invalid-method-override="ignore" # 103
invalid-return-type="ignore" # 44 ##
invalid-argument-type="ignore" # 49
no-matching-overload="ignore" # 10 # need to wait for betterdjango field stubs
# warn about unused ignore comments
unused-ignore-comment = "error"
[tool.coverage.run]
source = ["src/backend/InvenTree", "InvenTree"]
dynamic_context = "test_function"
omit = ["*/test_migrations.py", "*/migrations/*"]
[tool.coverage.html]
show_contexts = true
[tool.djlint]
ignore = "D018,H006,H008,H020,H021,H023,H025,H030,H031,T002"
[tool.isort]
src_paths=["src/backend/InvenTree", ]
skip_glob ="*/migrations/*.py"
known_django="django"
sections=["FUTURE","STDLIB","DJANGO","THIRDPARTY","FIRSTPARTY","LOCALFOLDER"]
[tool.codespell]
ignore-words-list = ["assertIn","SME","intoto","fitH"]
[tool.pytest]
django_find_project = false
pythonpath = ["src/backend/InvenTree"]
DJANGO_SETTINGS_MODULE = "InvenTree.settings"
python_files = ["test*.py",]
timeout = "120"
[tool.importlinter]
# Architectural import boundaries for the backend, enforced by import-linter (prek).
root_packages = [
"InvenTree",
"audit",
"build",
"common",
"company",
"data_exporter",
"generic",
"importer",
"machine",
"order",
"part",
"plugin",
"plugins",
"report",
"stock",
"users",
"web",
]
include_external_packages = false
[[tool.importlinter.contracts]]
# Plugins are only allowed to use the documented, stable import targets:
# `plugin` , `plugin.mixins`, `plugin.machine`
name = "Plugins only import the public plugin API"
type = "forbidden"
source_modules = ["plugin.builtin", "plugin.samples", "plugins"]
forbidden_modules = [
"machine",
"plugin",
"plugin._internal",
"plugin.admin",
"plugin.api",
"plugin.apps",
"plugin.base",
"plugin.broken",
"plugin.builtin",
"plugin.events",
# "plugin.machine", - allowed
"plugin.migrations",
# "plugin.mixins", - allowed
"plugin.mock",
"plugin.models",
"plugin.samples",
"plugin.serializers",
"plugin.staticfiles",
"plugin.template",
"plugin.test_api",
"plugin.test_plugin",
"plugin.test_staticfiles",
"plugin.testing",
"plugin.urls",
]
allow_indirect_imports = true
# [[tool.importlinter.contracts]]
# name = "Plugin mixins must be layered"
# type = "independence"
# modules=[
# "plugin.base.*"
# ]
[[tool.importlinter.contracts]]
# `generic` holds shared enums, events and state machines
name = "Generic app does not depend on feature apps"
type = "forbidden"
source_modules = ["generic"]
forbidden_modules = [
"build",
"company",
"importer",
"machine",
"order",
"part",
"report",
"stock",
"users",
"web",
]
allow_indirect_imports = true
ignore_imports = [
"generic.states.tests -> build.status_codes",
"generic.states.test_transition -> order.models",
"generic.states.test_transition -> order.status_codes",
"generic.states.api_helpers -> users.permissions",
"generic.states.test_transition -> users.models",
]
[[tool.importlinter.contracts]]
# Ban dependency cycles between sibling modules/subpackages
name = "No dependency cycles inside an app"
type = "acyclic_siblings"
ancestors = [
"company",
"data_exporter",
"generic",
"report",
"stock",
"web",
]
ignore_imports = ["generic.templatetags.generic -> generic.states.tags"]