[build-system]
requires = [
  "setuptools >= 65.0.0",
]
build-backend = "setuptools.build_meta"

[project]
name = "gajim"
description = "A GTK XMPP Client"
readme = "README.md"
requires-python = ">=3.12"
license = {text = "GPL-3.0-or-later"}
authors = [
  {name = "Philipp Hörist", email = "philipp@hoerist.com"},
  {name = "Yann Leboulanger", email = "yann@leboulanger.org"},
]
keywords = ["chat", "messaging", "im", "xmpp", "voip"]
classifiers = [
  "Development Status :: 5 - Production/Stable",
  "Environment :: X11 Applications :: GTK",
  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
  "Operating System :: OS Independent",
  "Programming Language :: Python :: 3.12",
  "Topic :: Communications :: Chat",
]
dependencies = [
  "cryptography>=43.0.0",
  "css-parser",
  "emoji>=2.6.0",
  "httpx[http2,socks]",
  "keyring",
  "nbxmpp>=7.2.0,<8.0.0",
  "omemo-dr>=1.2.0,<2.0.0",
  "packaging",
  "pillow>=9.1.0",
  "precis-i18n>=1.0.0",
  "pycairo>=1.16.0",
  "PyGObject>=3.53.0",
  "pysequoia>=0.1.33",
  "pystray; platform_system == 'Windows'",
  "qrcode>=7.3.1",
  "SQLAlchemy>=2.0.0",
  "truststore",
  "winrt-Windows.ApplicationModel; platform_system == 'Windows'",
  "winrt-Windows.Foundation; platform_system == 'Windows'",
  "winrt-Windows.UI; platform_system == 'Windows'",
  "winrt-Windows.UI.ViewManagement; platform_system == 'Windows'",
  "windows-toasts; platform_system == 'Windows'",
]
dynamic = ["version"]

[project.urls]
homepage = "https://gajim.org"
repository = "https://dev.gajim.org/gajim/gajim"

[project.scripts]
gajim-remote = "gajim.remote:run"

[project.gui-scripts]
gajim = "gajim.main:run"

[dependency-groups]
dev = [
  "codespell==2.4.2",
  "cryptography@git+https://github.com/pyca/cryptography.git",
  "distro",
  "nbxmpp@git+https://dev.gajim.org/gajim/python-nbxmpp.git",
  "omemo-dr@git+https://dev.gajim.org/gajim/omemo-dr.git",
  "pre-commit",
  "pygobject-stubs==2.17.0",
  "pygobject>=3.56.1",
  "pytest>=9.0.2",
  "python-gitlab",
  "ruamel.yaml",
  "ruff==0.15.7",
  "sentry-sdk",
]

# [tool.uv.sources]
# pygobject-stubs = { path = "../pygobject-stubs" }
# nbxmpp = { path = "../python-nbxmpp" }

[tool.setuptools.packages.find]
include = ["gajim*"]

[tool.setuptools.package-data]
gajim = ["py.typed"]
"gajim.data" = ["**/*"]

[tool.setuptools.dynamic]
version = {attr = "gajim.__version__"}

[tool.codespell]
skip = "*__pycache__*,_build_root,build,debian_build,dist,test,*.egg-info,.git,*.po,*.po~,*.pot,*.nsi,*.spec,./gajim/common/iana"
ignore-words-list = "claus,pres,ser,trough,THIRDPARTY,addin,selectin,FLE"

[tool.pyright]
pythonVersion = "3.12"
pythonPlatform = "All"
typeCheckingMode = "strict"

reportUnnecessaryTypeIgnoreComment = "error"
reportPropertyTypeMismatch = "error"
reportMissingModuleSource = "none"

exclude = [
  "**/__pycache__",
  ".flatpak-builder",
  ".git",
  ".venv",
  "build",
  "debian_build",
  "dist",
  "gajim/common/app.py",
  "gajim/common/client.py",
  "gajim/common/file_props.py",
  "gajim/common/helpers.py",
  "gajim/common/jingle_content.py",
  "gajim/common/jingle_ft.py",
  "gajim/common/jingle_ftstates.py",
  "gajim/common/jingle_rtp.py",
  "gajim/common/jingle_session.py",
  "gajim/common/jingle_transport.py",
  "gajim/common/modules/bits_of_binary.py",
  "gajim/common/modules/bytestream.py",
  "gajim/common/modules/contacts.py",
  "gajim/common/modules/discovery.py",
  "gajim/common/modules/gateway.py",
  "gajim/common/modules/ibb.py",
  "gajim/common/modules/jingle.py",
  "gajim/common/modules/roster_item_exchange.py",
  "gajim/common/modules/search.py",
  "gajim/common/settings.py",
  "gajim/common/socks5.py",
  "gajim/common/structs.py",
  "gajim/common/util/av.py",
  "gajim/common/winapi/system_style.py",
  "gajim/gtk/dataform.py",
  "gajim/gtk/filetransfer.py",
  "gajim/gtk/settings.py",
  "gajim/gtk/start_chat.py",
  "gajim/gtk/types.py",
  "gajim/gtk/shortcut_manager.py",
  "scripts/update_flatpak_manifest.py",
  "test/common/test_uri_util.py",
  "test/gui/test_menus.py",
  "typings/pystray/*",
  "typings/windows_toasts/*",
  "win/misc/depcheck.py",
]

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]

select = [
  "A",     # flake8-builtins
  # "AIR",   # Airflow
  # "ANN",   # flake8-annotations
  # "ARG",   # flake8-unused-arguments
  # "ASYNC", # flake8-async
  "B",     # flake8-bugbear
  "BLE",   # flake8-blind-except
  "C4",    # flake8-comprehensions
  "C90",   # mccabe
  "COM",   # flake8-commas
  # "CPY",   # flake8-copyright
  # "D",     # pydocstyle
  # "DJ",    # flake8-django
  # "DOC",   # pydoclint 
  # "DTZ",   # flake8-datetimez
  "E",     # Error
  # "EM",    # flake8-errmsg
  # "ERA",   # eradicate
  "EXE",   # flake8-executable
  "F",     # Pyflakes
  "FA",    # flake8-future-annotations
  # "FAST",  # FastAPI 
  # "FBT",   # flake8-boolean-trap
  # "FIX",   # flake8-fixme
  # "FLY",   # flynt
  "FURB",  # refurb 
  "G",     # flake8-logging-format
  "I",     # isort
  "ICN",   # flake8-import-conventions
  # "INP",   # flake8-no-pep420
  "INT",   # flake8-gettext
  # "ISC",   # flake8-implicit-str-concat
  "LOG",   # flake8-logging 
  "N",     # pep8-naming
  # "NPY",   # NumPy-specific rules
  # "PD",    # pandas-vet
  # "PERF",  # Perflint
  "PGH",   # pygrep-hooks
  "PIE",   # flake8-pie
  "PL",    # Pylint
  # "PT",    # flake8-pytest-style
  # "PTH",   # flake8-use-pathlib
  # "PYI",   # flake8-pyi
  # "Q",     # flake8-quotes
  # "RET",   # flake8-return
  "RSE",   # flake8-raise
  "RUF",   # Ruff-specific rules
  "S",     # flake8-bandit
  "SIM",   # flake8-simplify
  # "SLF",   # flake8-self
  "SLOT",  # flake8-slots
  "T10",   # flake8-debugger
  # "T20",   # flake8-print
  # "TC",    # flake8-type-checking
  # "TD",    # flake8-todos
  # "TID",   # flake8-tidy-imports
  # "TRY",   # tryceratops
  "UP",    # pyupgrade
  "W",     # Warning
  "YTT",   # flake8-2020
]

ignore = [
  "A003",   # Class attribute is shadowing python builtin
  "A005",   # Module shadows a Python standard-library module
  "B904",   # Within an except clause ..
  "BLE001", # Blind except Exception: statement
  "C901",   # too complex
  "COM812", # Trailing comma missing
  "FBT003", # Boolean positional value in function call
  "ICN001", # Unconventional-import-alias
  "N802",   # Function name should be lower case
  "N806",   # Variable should be lower case
  "N817",   # CamelCase imported as acronym
  "N818",   # Exception name should be named with an Error suffix
  "PGH003", # Use specific rule codes when ignoring type issues
  "PLC0415",# import-outside-top-level
  "PLC1901",# Expression can be simplified to x as an empty string is falsey
  "PLR0911",# Too many return statements
  "PLR0912",# Too many branches
  "PLR0913",# Too many arguments to function call
  "PLR0915",# Too many statements
  "PLR2004",# Magic value used in comparison, consider replacing x with a constant variable
  "PLR5501",# Consider using `elif` instead of `else` then `if` to remove one indentation level
  "PLW0603",# Using the global statement to update `x` is discouraged
  "PLW2901",# `for` loop variable `x` overwritten by assignment target
  "RUF001", # AmbiguousUnicodeCharacterString
  "RUF002", # AmbiguousUnicodeCharacterDocstring
  "RUF003", # AmbiguousUnicodeCharacterComment
  "RUF005", # Consider x instead of concatenation
  "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
  "RUF100", # Unused `noqa` directive
  "S101",   # Use of `assert` detected
  "S110",   # `try`-`except`-`pass` detected, consider logging the exception
  "S301",   # `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
  "S303",   # Use of insecure MD2, MD4, MD5, or SHA1 hash function
  "S310",   # Audit URL open for permitted schemes
  "S311",   # Standard pseudo-random generators are not suitable for cryptographic purposes
  "S314",   # Using `xml` to parse untrusted data is known to be vulnerable to XML attacks; use `defusedxml` equivalents
  "S324",   # Probable use of insecure hash functions
  "S608",   # Possible SQL injection vector through string-based query construction
  "SIM102", # Use a single `if` statement instead of nested `if` statements
  "SIM105", # Use `contextlib.suppress(Exception)` instead of try-except-pass
  "SIM108", # Use ternary operator
  "SIM114", # Combine `if` branches using logical `or` operator"
  "SIM115", # Use context handler for opening files
  "SIM201", # Use x instead of not x
  "SIM212", # Use `value if value else ''` instead of `'' if not value else value`
  "SIM300", # Yoda conditions are discouraged use x instead
  "UP031",  # Use format specifiers instead of percent format
  "UP037",  # Remove quotes from type annotation
  "UP046",  # Generic class uses `Generic` subclass instead of type parameters
  "UP047",  # Generic should use type parameters
]

exclude = [
  ".eggs",
  ".git",
  ".ruff_cache",
  ".venv",
  "build",
  "dist",
  "venv",
  "*.pyi",
  # Code Folders
  "typings",
  # legacy modules, will be removed
  "gajim/common/jingle_ftstates.py",
  "gajim/common/modules/bytestream.py",
  "gajim/common/socks5.py",
]

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[tool.ruff.lint.per-file-ignores]
"gajim/common/iana.py" = ["E501"]
"gajim/common/storage/omemo.py" = ["N803"]
"gajim/common/storage/*" = ["E501"]
"gajim/common/modules/chatstates.py" = ["E501"]
"gajim/gtk/types.py" = ["F401"]
"gajim/gtk/*" = ["E501"]
"test/*" = ["E402"]
"test/common/test_styling.py" = ["RUF001", "E501"]
"test/common/test_regex.py" = ["RUF001"]
"test/database/*" = ["E501"]
"make.py" = ["S603", "S607"]
"mac/*" = ["S603", "S607"]
"win/*" = ["S603", "S605", "S607"]
"scripts/*" = ["S603", "S607"]
"launch.py" = ["S603", "S607"]

[tool.ruff.lint.mccabe]
max-complexity = 15

[tool.ruff.lint.isort]
case-sensitive = false
order-by-type = false
force-single-line = true
section-order = [
  "future",
  "typing",
  "standard-library",
  "third-party",
  "first-party",
  "gtk",
  "local-folder"
]

[tool.ruff.lint.isort.sections]
gtk = ["gajim.gtk"]
typing = ["_typeshed", "typing", "typing_extensions"]
