[tool.ruff.format]
quote-style = "double"
indent-style = "tab"
skip-magic-trailing-comma = false
line-ending = "lf"
docstring-code-format = false
exclude = ["pyglossary/plugin_lib/ripemd128.py"]

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

# Exclude a variety of commonly ignored directories.
exclude = [
	"pyglossary/repro_zipfile/",
	"whitelist.py", # for vulture
	"pyglossary/plugins/babylon_bgl/bgl_gzip.py",
	"pyglossary/plugins/testformat",
	"pyglossary/ui/gtk*_utils/__init__.py",
	"pyglossary/ui/ui_qt.py",
	"pyglossary/ui/progressbar/",
	"pyglossary/reverse.py",
	"wcwidth*",
	".direnv",
	".eggs",
	".git",
	".mypy_cache",
	".nox",
	".pants.d",
	".ruff_cache",
	".tox",
	".venv",
	"__pypackages__",
	"_build",
	"buck-out",
	"build",
	"dist",
	"venv",
]

[tool.ruff.lint]
select = [
	"ANN", # flake8-annotationsq
	"F",   # Pyflakes
	"E",   # pycodestyle Error
	"W",   # pycodestyle Warning
	"C90", # mccabe: C901: {name} is too complex ({complexity})
	"I",   # isort: unsorted-imports, missing-required-import
	"D",   # pydocstyle
	"B",   # flake8-bugbear
	"UP",  # pyupgrade
	"YTT", # flake8-2020
	"ASYNC1", # flake8-trio
	"BLE", # flake8-blind-except
	"B", # flake8-bugbear
	"A", # flake8-builtins
	"COM", # flake8-commas
	# "CPY", # flake8-copyright   --preview
	"C4",   # flake8-comprehensions
	"DTZ",  # flake8-datetimez
	"T10",  # flake8-debugger
	"DJ",   # flake8-django
	"EXE",  # flake8-executable
	"FA",   # flake8-future-annotations
	"ISC",  # flake8-implicit-str-concat
	"ICN",  # flake8-import-conventions
	"G",    # flake8-logging-format
	"INP",  # flake8-no-pep420
	"PIE",  # flake8-pie
	"T20",  # flake8-print
	"PYI",  # flake8-pyi
	"PT",   # flake8-pytest-style
	"Q",    # flake8-quotes
	"RSE",  # flake8-raise
	"RET",  # flake8-return
	"SLF",  # flake8-self
	"SLOT", # flake8-slots
	"SIM",  # flake8-simplify
	"TID",  # flake8-tidy-imports
	"TCH",  # flake8-type-checking
	"INT",  # flake8-gettext
	"ARG",  # flake8-unused-arguments
	# "PTH",   # flake8-use-pathlib
	"PD",  # pandas-vet
	"PGH", # pygrep-hooks
	"PL",  # Pylint
	# "TRY",   # tryceratops, they all sound BS
	# "FLY",   # flynt
	"NPY",  # NumPy-specific rules
	"AIR",  # Airflow
	"PERF", # Perflint
	"FURB", # refurb   --preview
	"LOG",  # flake8-logging
	"RUF",  # Ruff-specific rules
]
ignore = [
	"ANN003",  # Missing type annotation for `**kwargs`, 15 remaining
	"PLR0917", # Too many positional arguments (x/5)
	"PLR0914", # Too many local variables (x/15)
	"ANN401",  # Dynamically typed expressions (typing.Any) are disallowed in ...

	"PYI042",  # Type alias `...` should be CamelCase FIXME
	"RUF039",  # First argument to `re.compile()` is not raw string
	"FURB189",
	# FURB189 Subclassing `dict` can be error prone, use `collections.UserDict` instead
	# FURB189 Subclassing `str` can be error prone, use `collections.UserStr` instead
	"COM812", # Trailing comma missing
	"SLF",    # Private member accessed
	"PYI034", # py3.11: `__iadd__` methods in classes like `SqEntryList` usually return `self` at runtime
	"DTZ001", # The use of `datetime.datetime()` without `tzinfo` argument is not allowed
	"DTZ005", # The use of `datetime.datetime.now()` without `tz` argument is not allowed
	"PGH003", # Use specific rule codes when ignoring type issues

	"PLR0915", # Too many statements
	"PLR0911", # Too many return statements (x > 6)
	"PLR2004", # Magic value used in comparison, consider replacing `...` with a constant variable

	"FURB166", # Use of `int` with explicit `base=16` after removing prefix
	"FURB103", # `open` and `write` should be replaced by `Path(...
	"PLC0415", # `import` should be at the top-level of a file
	"PLW0603", # Using the global statement to update `mockLog` is discouraged
	"PT027",   # Use `pytest.raises` instead of unittest-style `assertRaises`, why?
	"PD011",   # Use `.to_numpy()` instead of `.values`, WTF?
	"ICN001",  # `tkinter` should be imported as `tk`, WTF?
	"RUF005",  # Consider `[*_list, x]` instead of concatenation
	"PT009",   # Use a regular `assert` instead of unittest-style `assertEqual`, why?
	"RUF012",  # Mutable class attributes should be annotated with `typing.ClassVar`
	"BLE001",  # Do not catch blind exception: `Exception`
	"G004",    # Logging statement uses f-string, WTF?
	"TRY400",  # Use `logging.exception` instead of `logging.error`
	"TRY003",  # Avoid specifying long messages outside the exception class, ???
	"RUF100",  # Unused `noqa` directive (non-enabled: ...)
	"FURB101", # `open` and `read` should be replaced by `Path(rootConfJsonFile).read_text()`
	"B019",    # Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks
	"D100",    # Missing docstring in public module
	"D101",    # Missing docstring in public class
	"D102",    # Missing docstring in public method
	"D103",    # Missing docstring in public function
	"D104",    # Missing docstring in public package
	"D105",    # Missing docstring in magic method
	"D107",    # Missing docstring in `__init__`
	"D205",    # 1 blank line required between summary line and description
	"D206",    # Docstring should be indented with spaces, not tabs
	"D211",    # (Do not enable) no-blank-line-before-class
	"D212",    # multi-line-summary-first-line, conflicts with D213:multi-line-summary-second-line
	"D401",    # First line of docstring should be in imperative mood
	"D417",    # Missing argument descriptions in the docstring
	"E402",    # Module level import not at top of file
	"E721",    # Do not compare types, use `isinstance()`
	"SIM105",  # Use contextlib.suppress({exception}) instead of try-except-pass
	"SIM117",  # Use a single with statement with multiple contexts...
	"UP009",   # UTF-8 encoding declaration is unnecessary
	"UP037",   # Remove quotes from type annotation
	"SIM115",  # Use context handler for opening files
	"W191",    # Indentation contains tabs
]

# Allow autofix for all enabled rules (when `--fix`) is provided.
unfixable = []


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

mccabe.max-complexity = 13 # Unlike Flake8, default to a complexity level of 10.


[tool.ruff.lint.per-file-ignores]

"pyglossary/sort_modules/random.py" = [
	"A005", # Module `random` shadows a Python standard-library module
]

"pyglossary/glossary.py" = ["ANN"]

"*_types.py" = [
	"TC003", # Move standard library import `...` into a type-checking block
]
"pyglossary/plugins/**/*.py" = [
	"PLR0904", # Too many public methods
	"C90",     # mccabe: C901: {name} is too complex ({complexity})
]
"slob.py" = [
	"C901", # `...` is too complex (14 > 13)
	"ANN",
]
"html_dir.py" = [
	"C901", # `...` is too complex
]
"zimfile.py" = [
	"C901", # `...` is too complex
]
"pyglossary/plugins/babylon_bgl/*.py" = [
	"C901", # `...` is too complex
]

"pyglossary/html_utils.py" = ["RUF003"]
"persian_utils.py" = ["RUF001"]
"ru.py" = ["RUF001", "RUF003"]
"pyglossary/plugins/dikt_json/*.py" = ["RUF003"]
"pyglossary/plugin_lib/*.py" = [
	"ANN",
	"PT018",   # Assertion should be broken down into multiple parts
	"D",
	"RUF015",  # Prefer `next(zip(*_list, strict=False))` over single element slice
	"PLR2004", # Magic value used in comparison, consider replacing `...` with a constant variable
]
"scripts/wiki-formats.py" = ["E501"]
"pyglossary/io_utils.py" = ["ANN"]
"pyglossary/plugins/babylon_bgl/reader_debug.py" = ["ANN", "FURB"]
"pyglossary/ui/**/*.py" = [
	"ANN",
	"T201",
	"PERF203",
	"PLR0904", # Too many public methods
	"PLR0912", # Too many branches
	"PLR0915", # Too many statements
	"PLR6301", # Method `...` could be a function, class method, or static method
	"C90",     # mccabe: C901: {name} is too complex ({complexity})
]
"tests/*.py" = [
	"ANN",
	"T201",
	"PLR0915", # Too many statements
	"PLR6301", # Method `...` could be a function, class method, or static method
	"E501",    # Line too long
]
"*_test.py" = [
	"ANN",
	"T201",
	"RUF001",  # String contains ambiguous ... (ARABIC LETTER ...). Did you mean `l` ...
	"PLR0904", # Too many public methods
]
"test.py" = ["ANN", "T201"]
"scripts/*.py" = ["ANN", "T201", "INP001"]
"scripts/*/*.py" = ["ANN", "T201", "INP001"]
"doc/lib-examples/*.py" = ["ANN", "INP"]


[tool.mypy]
exclude = [
	# '.*/plugin_lib/.*',
]

[tool.refurb]
ignore = [
	117, # Replace `open(filename, "rb")` with `filename.open("rb")`
	184, # Assignment statement should be chained
	101, # Replace `with open(x, ...) as f: y = f.read()` with `y = Path(x).read_bytes()`
	103, # Replace `with open(x, ...) as f: f.write(y)` with `Path(x).write_bytes(y)`
	104, # Replace `os.getcwd()` with `Path.cwd()`
	107, # Replace `try: ... except OSError: pass` with `with suppress(OSError): ...`
	141, # Replace `os.path.exists(x)` with `Path(x).exists()`
	144, # Replace `os.remove(x)` with `Path(x).unlink()`
	146, # Replace `os.path.isfile(x)` with `Path(x).is_file()`
	150, # Replace `os.makedirs(x)` with `Path(x).mkdir(parents=True)`
	155, # Replace `os.path.getsize(x)` with `Path(x).stat().st_size`
]
# refurb has no exclude param!
#load = ["some_module"]
#quiet = true

[tool.pylint.messages_control]
max-line-length = 88
disable = [
	"no-member",
	"no-name-in-module",
	"missing-module-docstring",
	"bad-indentation",
	"invalid-name",
	"logging-fstring-interpolation",
	"too-many-arguments",
	"broad-exception-caught",
	"missing-function-docstring",
	"unused-argument",
	"import-outside-toplevel",
	"missing-class-docstring",
	"too-many-instance-attributes",
	"fixme",
	"redefined-builtin",
	"pointless-statement",
	"abstract-method",
	"unidiomatic-typecheck",
	"attribute-defined-outside-init",
	"unspecified-encoding",
	"super-init-not-called",
	"redefined-slots-in-subclass",
	"redefined-outer-name",
	"wrong-import-position",
	"too-few-public-methods",
	"too-many-lines",
	"too-many-public-methods",
	"too-many-statements",
	"too-many-locals",
	"too-many-branches",
	"too-many-return-statements",
	"unused-import",
	"import-error",
	"protected-access",
	"consider-using-with",
	"disallowed-name",
	"useless-return",
	"method-cache-max-size-none",
	"global-statement",
	"R0801",                          # Similar lines in 2 files
	"ungrouped-imports",              # C0412: Imports from package pyglossary are not grouped
	"inconsistent-return-statements", # R1710: Either all return statements in a function should return an expression, or none of them should
	"too-many-ancestors",             # R0901: Too many ancestors
]

[tool.pylint.master]
ignore-paths = [
	"^pyglossary/reverse.py$",
	"^pyglossary/ui/progressbar/.*",
	"^pyglossary/ui/ui_qt.py$",
	"^pyglossary/ui/wcwidth/",
]

[tool.vulture]
exclude = [
	"build/",
	"tests/",
	"*_test.py",
	"test.py",
	"pyglossary/ui/",
	"*_types.py",
	"pyglossary/ui_type.py",
	"pyglossary/reverse.py",
	"doc/lib-examples/",
	"pyglossary/plugin_lib/",
]
# ignore_decorators = ["@require_*"]
ignore_names = [
	"_*",
	"Generator",
	"GLOSSARY_API_VERSION",
	# "Iterable", "AnyStr",
	# "RawEntryType",
	# "EntryListType",
]
make_whitelist = true
min_confidence = 60
# paths = []
sort_by_size = false
verbose = false

[tool.import-analyzer]
exclude = ["pyglossary/ui/wcwidth/", "build/"]

[tool.pyright]
pythonVersion = "3.10"
pythonPlatform = "Linux"
reportMissingImports = "error"
reportMissingTypeStubs = false
exclude = [
	"pyglossary/slob.py",
	"setup.py",
	"whitelist.py",       # for vulture
	# "pyglossary/ui/gtk4_utils/*",
	# "pyglossary/ui/gtk3_utils/*",
	"pyglossary/plugins/babylon_bgl/bgl_gzip.py",
	"pyglossary/plugins/testformat.py",
	# "pyglossary/plugin_lib/*",
	"pyglossary/ui/gtk*_utils/__init__.py",
	"pyglossary/ui/ui_qt.py",
	"pyglossary/ui/progressbar/",
	"pyglossary/reverse.py",
	"wcwidth*",
	".direnv",
	".eggs",
	".git",
	".mypy_cache",
	".nox",
	".pants.d",
	".ruff_cache",
	".tox",
	".venv",
	"__pypackages__",
	"_build",
	"buck-out",
	"build",
	"dist",
	"venv",
]

[tool.fixit]
disable = ["fixit.rules.no_namedtuple"]

[[tool.fixit.overrides]]
path = "pyglossary/reverse.py"
disable = ["fixit.rules"]

[[tool.fixit.overrides]]
path = "pyglossary/ui/wcwidth/"
disable = ["fixit.rules"]

[[tool.fixit.overrides]]
path = "pyglossary/ui/progressbar/"
disable = ["fixit.rules"]

[build-system]
requires = [
	"setuptools", # min version?
	# "setuptools_scm[toml] >= 4, <6",
	# "setuptools_scm_git_archive",
	# "wheel >= 0.29.0",
]
build-backend = "setuptools.build_meta"

[project]
name = "pyglossary"
version = "5.0.6"
description = "A tool for converting dictionary files aka glossaries."
readme = "README.md"
authors = [{ name = "Saeed Rasooli", email = "saeed.gnu@gmail.com" }]
license = { text = "GPLv3+" }
keywords = ["dictionary", "glossary"]
classifiers = [
	"Development Status :: 5 - Production/Stable",
	"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
	"Operating System :: Unix",
	"Operating System :: MacOS :: MacOS X",
	"Operating System :: Microsoft :: Windows",
	"Typing :: Typed",
	"Programming Language :: Python :: 3.10",
	"Programming Language :: Python :: 3.11",
	"Programming Language :: Python :: 3.12",
]
requires-python = ">= 3.10"
dependencies = []

[project.optional-dependencies]
all = ["PyICU", "lxml", "beautifulsoup4"]

[project.urls]
Homepage = "https://github.com/ilius/pyglossary"
Issues = "https://github.com/ilius/pyglossary/issues"
Changelog = "https://github.com/ilius/pyglossary/tree/master/doc/releases"
