[project]
name = "calibre"
description = "The one stop solution to all your e-book needs"
readme = "README.md"
requires-python = ">=3.14"
license = "GPL-3.0-only"
dynamic = ["version"]
dependencies = [
	"six == 1.17.0",
	"unrardll == 0.1.7",
	"lxml == 6.1.1",
	"pychm == 0.8.6",
	"html5-parser == 0.4.12",
	"css-parser == 1.1.1",
	"python-dateutil == 2.9.0",
	"jeepney == 0.9.0",
	"dnspython == 2.8.0",
	"mechanize == 0.4.10",
	"feedparser-sgmllib == 2.0.1",
	"feedparser == 6.0.14",
	"Markdown == 3.10.0",
	"html2text == 2025.4.15",
	"soupsieve == 2.8.4",
    "typing-extensions == 4.15.0",  # needed by beautifulsoup4
	"beautifulsoup4 == 4.14.3",
	"regex == 2025.11.3",
	"chardet == 5.2.0",
	"msgpack == 1.2.1",
	"Pygments == 2.20.0",
	"pycryptodome == 3.23.0",
	"apsw == 3.53.2.0",
	"webencodings == 0.5.1",
	"html5lib == 1.1",
	"pillow == 12.3.0",
	"netifaces == 0.11.0",
	"psutil == 7.1.3",
	"ifaddr == 0.2.0",
	"texttable == 1.7.0",
	"multivolumefile == 0.2.3",
	"Brotli == 1.2.0",
	"pyppmd == 1.3.1",
	"pybcj == 1.0.7",
	"inflate64 == 1.0.4",
	"py7zr == 1.1.3",
	"jaconv == 0.4.1",
	"wrapt == 2.0.1",
	"Deprecated == 1.3.1",
	"pykakasi == 2.3.0",
	"zeroconf == 0.149.16",
	"fonttools == 4.61.0",
	"lxml_html_clean == 0.4.5",
	"PyQt6_sip == 13.10.2",
	"PyQt6 == 6.10.0",
	"PyQt6_WebEngine == 6.10.0",
	"MacFSEvents == 0.8.4; sys_platform == 'darwin'",
	"xxhash == 3.6.0",
    "tzdata == 2025.2",
    "tzlocal == 5.3.1",
	"pystache == 0.6.8",
]

[project.urls]
Homepage = "https://calibre-ebook.com"
FAQ = "https://manual.calibre-ebook.com/faq.html"
Documentation = "https://manual.calibre-ebook.com"
Repository = "https://github.com/kovidgoyal/calibre.git"
Issues = "https://bugs.launchpad.net/calibre"
Changelog = "https://github.com/kovidgoyal/calibre/blob/master/Changelog.txt"
Community-Forum = "https://www.mobileread.com/forums/forumdisplay.php?f=166"

[build-system]
requires = [
    "installer == 0.7.0",  # Needed for installing pure python packages at build time
    "packaging == 25.0",   # Needed for build which is needed for setuptools
	"pyproject_hooks == 1.2.0", # Needed for build which is needed for setuptools
	"wheel == 0.46.2", # Needed for build which is needed for setuptools
	"build == 1.3.0", # Needed for build which is needed for setuptools
    "pybind11 == 3.0.1", # Needed for pillow
	"setuptools == 83.0.0",
    "pkgconfig == 1.5.5",  # Needed by Brotli
	# Some packages (xxhash/py7zr/etc) erroneously declare this as a dependency even
	# though it is needed only for sdist not building wheels
	"setuptools_scm == 9.2.2",
	"poetry_core == 2.2.1",  # needed for building zeroconf
	"ply == 3.11",  # needed for sip
	"sip == 6.14.0",
	"PyQt_builder == 1.19.1",
]
[dependency-groups]
dev = [
    "ruff >= 0.15.21",
    "types-lxml >= 2026.2.16",
    "ty >= 0.0.59",
    "rapydscript-ng >= 0.8.5",
    "clang-format >= 22.1.8",
]

[tool.ruff]
line-length = 160
builtins = ['_', 'I', 'P']
include = ['*.py', '*.pyi', '*.recipe']
extension = { recipe = "python" }
extend-exclude = [
    "*_ui.py",
    "bypy/*",
    "setup/polib.py",
    "setup/linux-installer.py",
    "src/css_selectors/*",
    "src/polyglot/*",
    "src/tinycss/*",
]
preview = true

[tool.ruff.format]
quote-style = 'preserve'

[tool.ruff.lint]
explicit-preview-rules = true
ignore = [
    'E402', 'E741',
    'UP012', 'UP030', 'C413', 'C420', 'PIE790', 'ISC003', 'FURB122', 'FURB166', 'FURB167',
    'RUF001', 'RUF002', 'RUF003', 'RUF005', 'RUF012', 'RUF013', 'RUF015', 'RUF031', 'RUF100',
    'PLE1205', 'PLW0602', 'PLW0603', 'PLW1510', 'PLW1641', 'PLW2901', 'PLC0415',
    'PLR0911', 'PLR0912', 'PLR0913', 'PLR0914', 'PLR0915', 'PLR0916', 'PLR0917', 'PLR1702', 'PLR1704', 'PLR2004', 'PLR6301',
    'RUF059',  # unused unpacked variable
]
select = [
    'E', 'F', 'I', 'W', 'INT',
    'Q', 'UP', 'YTT', 'TID', 'C4', 'COM818', 'PIE', 'RET501', 'ISC',
    'PLE', 'PLW', 'PLC', 'PLR', 'ASYNC', 'FURB',
    'RUF',  # note: RUF can flag many unsolicited errors
    # preview rules
    'RUF051', 'RUF056',  # useless dict operation
    'RUF055',  # unnecessary regex
    'RUF039',  # always use raw-string for regex
    'RUF047',  # needless else
    'E302', 'E303', 'E304', 'E305', 'W391',  # blank-line standard
    'E111', 'E112', 'E113', 'E117',  # code indentation
    'E114', 'E115', 'E116', 'E261', 'E262', 'E265',  # comment formatting
    'E201', 'E202', 'E211', 'E251', 'E275',  # various whitespace
]
unfixable = ['PIE794', 'ISC001']

[tool.ruff.lint.per-file-ignores]
"recipes/*" = ['UP', 'FURB188']
"setup/changelog.py" = ['ISC001']
"src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501']
"src/calibre/ebooks/metadata/sources/*" = ['UP', 'FURB188']
"src/calibre/gui2/store/stores/*" = ['UP', 'FURB188']
"src/qt/*.py" = ['I', 'E302']
"src/qt/*.pyi" = ['I']

[tool.ruff.lint.isort]
detect-same-package = true
extra-standard-library = ["aes", "elementmaker", "encodings"]
known-first-party = ["calibre_extensions", "calibre_plugins", "polyglot"]
known-third-party = ["odf", "qt", "tinycss", "css_selectors"]
relative-imports-order = "closest-to-furthest"
split-on-trailing-comma = false
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]

[tool.ruff.lint.flake8-comprehensions]
allow-dict-calls-with-keyword-arguments = true

[tool.ruff.lint.flake8-quotes]
avoid-escape = true
docstring-quotes = 'double'  # PEP 257
inline-quotes = 'single'
multiline-quotes = 'single'

[tool.codespell]
count = false
summary = false
quiet-level = 3
regex = '''\b[\w\-'’]+\b'''
ignore-regex = '''\b[\w\-'’]*&(amp;)?[\w\-'’]*\b'''
builtin = [
    'clear',
    'rare',
    'informal',
    'code',
]
exclude-file = "codespell.exclude"
ignore-words-list = [
    "alo",
    "ans",
    "clen",
    "eto",
    "fo",
    "nam",
    "nd",
    "som",
    "te",
    "atLeast",
    "Implementor",
    "implementor",
    "Implementors",
    "implementors",
    "missings",
    "re-use",
    "re-used",
    "re-using",
    "splited",
    "succeded",
    # code
    "ro",
    "ws",
    "deque",
    "assertIn",
    "atEnd",
    "errorString",
    "FocusIn",
    "iff",
    "lets",
    "lite",
    "UINT",
    "uInt",
    "uint",
    "KeyPair",
    "Keypair",
    "keypair",
    "seeked",
    "stdio",
    "thead",
]
uri-ignore-words-list = '*'
skip = [
    "*.svg",
    "*.rcc",
    "*_ui.py",
    "./src/calibre/ebooks/rtf2xml/char_set.py",
    "./src/calibre/ebooks/unihandecode/*",
    "./src/calibre/ebooks/html_entities.h",
    "./src/calibre/ebooks/html_entities.py",
    "./src/calibre/utils/icu_test.py",
    "./src/calibre/utils/search_query_parser_test.py",
    "./Changelog.old.txt",
    "./COPYRIGHT",
    "./LICENSE",
    "./LICENSE.rtf",
    "./session.vim",
    "./build/*",
    "./docs/*",
    "./nbproject/*",
    "./recipes/*",
    "./translations/*",
    "./tags/*",
    "./manual/generated/*",
    "./manual/locale/*",
    "./resources/dictionaries/*",
    "./resources/localization/*",
    "./resources/hyphenation/*",
    "./resources/mathjax/*",
    "./resources/builtin_recipes.xml",
    "./resources/changelog.json",
    "./resources/editor.js",
    "./resources/editor-functions.json",
    "./resources/mime.types",
    "./resources/piper-voices.json",
    "./resources/stylelint-bundle.min.js",
    "./resources/user-manual-translation-stats.json",
    "./resources/template-functions.json",
    "./resources/viewer.js",
    "./resources/viewer.html",
    "./resources/content-server/index-generated.html",
    "./setup/installer/*",
    "./setup/pyqt_enums/*",
    "./setup/lc_data.py",
    "./setup/linux-installer.py",
    "./src/css_selectors/*",
    "./src/polyglot/*",
    "./src/tinycss/*",
    "./src/unicode_names/*",
]

[tool.flynt]
line-length = 400  # over value to catch every case
transform-format = false  # don't transform already existing format call
exclude = [
    "bypy/",
    "setup/polib.py",
    "setup/linux-installer.py",
    "src/calibre/ebooks/metadata/sources/",
    "src/calibre/gui2/store/stores/",
    "src/css_selectors/",
    "src/polyglot/",
    "src/tinycss/",
]

[tool.ty.environment]
root = ['./src']
extra-paths = ['./stubs']
python-platform = "all"

[tool.ty.src]
exclude = [
	'**/*_ui.py', '**/*_test.py', '**/test_*.py',
	'setup/*.py', 'setup.py', 'manual/**/*.py', 'bypy/**/*.py',
	'**/tests/**',
    '.github/workflows/*.py',
	'src/calibre/library/add_to_library.py',
	'src/calibre/library/database.py',
	'src/calibre/library/database2.py',
	'src/calibre/library/sqlite.py',
	'src/calibre/library/prefs.py',
	'src/calibre/library/restore.py',
	'src/calibre/library/custom_columns.py',
	'src/calibre/library/schema_upgrades.py',
    'src/calibre/gui2/pyqt6_compat.py',
]
