# https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/ # # SPDX-FileCopyrightText: © Sebastian Thomschke and contributors # SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/ # [build-system] # https://backend.pdm-project.org/ requires = ["pdm-backend"] build-backend = "pdm.backend" [project] name = "kleinanzeigen-bot" dynamic = ["version"] description = "Command line tool to publish ads on kleinanzeigen.de" readme = "README.md" authors = [ {name = "sebthom", email = "sebthom@users.noreply.github.com"}, ] license = {text = "AGPL-3.0-or-later"} classifiers = [ # https://pypi.org/classifiers/ "Private :: Do Not Upload", "Development Status :: 5 - Production/Stable", "Environment :: Console", "Operating System :: OS Independent", "Intended Audience :: End Users/Desktop", "Topic :: Office/Business", "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10" ] requires-python = ">=3.10,<3.14" dependencies = [ "certifi", "colorama", "jaraco.text", # required by pkg_resources during runtime "nodriver==0.39.0", # 0.40-0.44 have issues starting browsers and evaluating self.web_execute("window.BelenConf") fails "ruamel.yaml", "psutil", "wcmatch", ] [dependency-groups] # https://peps.python.org/pep-0735/ dev = [ # testing: "pytest>=8.3.4", "pytest-asyncio>=0.25.3", "pytest-rerunfailures", "pytest-cov>=6.0.0", # linting: "autopep8", "ruff", "mypy", "basedpyright", # packaging: "pyinstaller", "platformdirs", # required by pyinstaller ] [project.urls] Homepage = "https://github.com/Second-Hand-Friends/kleinanzeigen-bot" Repository = "https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git" Documentation = "https://github.com/Second-Hand-Friends/kleinanzeigen-bot/README.md" Issues = "https://github.com/Second-Hand-Friends/kleinanzeigen-bot/issues" ##################### # pdm https://github.com/pdm-project/pdm/ ##################### [tool.pdm.version] # https://backend.pdm-project.org/metadata/#dynamic-project-version source = "call" getter = "version:get_version" # uses get_version() of /version.py write_to = "kleinanzeigen_bot/_version.py" write_template = "__version__ = '{}'\n" [tool.pdm.scripts] # https://pdm-project.org/latest/usage/scripts/ app = "python -m kleinanzeigen_bot" compile.cmd = "python -O -m PyInstaller pyinstaller.spec --clean" compile.env = {PYTHONHASHSEED = "1", SOURCE_DATE_EPOCH = "0"} # https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build debug = "python -m pdb -m kleinanzeigen_bot" format = {shell = "autopep8 --recursive --in-place scripts src tests --verbose && python scripts/post_autopep8.py scripts src tests" } lint = {shell = "ruff check --preview && mypy && basedpyright" } fix = {shell = "ruff check --preview --fix" } test = "python -m pytest --capture=tee-sys -v" utest = "python -m pytest --capture=tee-sys -v -m 'not itest'" itest = "python -m pytest --capture=tee-sys -v -m 'itest'" ##################### # autopep8 # https://pypi.org/project/autopep8/ # https://github.com/hhatto/autopep8 ##################### [tool.autopep8] max_line_length = 160 ignore = [ # https://github.com/hhatto/autopep8#features "E124", # Don't change indention of multi-line statements "E128", # Don't change indention of multi-line statements "E231", # Don't add whitespace after colon (:) on type declaration "E251", # Don't remove whitespace around parameter '=' sign. "E401" # Don't put imports on separate lines ] aggressive = 3 ##################### # ruff # https://pypi.org/project/ruff/ # https://docs.astral.sh/ruff/configuration/ ##################### [tool.ruff] include = ["pyproject.toml", "scripts/**/*.py", "src/**/*.py", "tests/**/*.py"] line-length = 160 indent-width = 4 target-version = "py310" [tool.ruff.lint] # https://docs.astral.sh/ruff/rules/ select = [ "A", # flake8-builtins "ARG", # flake8-unused-arguments "ANN", # flake8-annotations "ASYNC", # flake8-async #"BLE", # flake8-blind-except "B", # flake8-bugbear "C4", # flake8-comprehensions "COM", # flake8-commas "CPY", # flake8-copyright "DTZ", # flake8-datetimez #"EM", # flake8-errmsg #"ERA", # eradicate commented-out code "EXE", # flake8-executable "FA", # flake8-future-annotations "FBT", # flake8-boolean-trap "FIX", # flake8-fixme "G", # flake8-logging-format "ICN", # flake8-import-conventions "ISC", # flake8-implicit-str-concat "INP", # flake8-no-pep420 "INT", # flake8-gettext "LOG", # flake8-logging "PIE", # flake8-pie "PT", # flake8-pytest-style #"PTH", # flake8-use-pathlib "PYI", # flake8-pyi "Q", # flake8-quotes "RET", # flake8-return "RSE", # flake8-raise "S", # flake8-bandit "SIM", # flake8-simplify "SLF", # flake8-self "SLOT", # flake8-slots "T10", # flake8-debugger #"T20", # flake8-print "TC", # flake8-type-checking "TD", # flake8-todo "TID", # flake8-flake8-tidy-import "YTT", # flake8-2020 "E", # pycodestyle-errors "W", # pycodestyle-warnings #"C90", # mccabe "D", # pydocstyle "F", # pyflakes "FLY", # flynt "I", # isort "PERF", # perflint "PGH", # pygrep-hooks "PL", # pylint ] ignore = [ "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ASYNC210", # TODO Async functions should not call blocking HTTP methods "ASYNC230", # TODO Async functions should not open files with blocking methods like `open` "COM812", # Trailing comma missing "D1", # Missing docstring in ... "D200", # One-line docstring should fit on one line "D202", # No blank lines allowed after function docstring (found 1) "D203", # 1 blank line required before class docstring "D204", # 1 blank line required after class docstring "D205", # 1 blank line required between summary line and description "D209", # Multi-line docstring closing quotes should be on a separate line" "D212", # Multi-line docstring summary should start at the first line "D213", # Multi-line docstring summary should start at the second line "D400", # First line should end with a period "D401", # First line of docstring should be in imperative mood "D402", # First line should not be the function's signature "D404", # First word of the docstring should not be "This" "D413", # Missing blank line after last section ("Returns")" "D415", # First line should end with a period, question mark, or exclamation point "D417", # Missing argument description in the docstring for #"E124", # Don't change indention of multi-line statements #"E128", # Don't change indention of multi-line statements "E231", # Don't add whitespace after colon (:) on type declaration "E251", # Don't remove whitespace around parameter '=' sign. "E401", # Don't put imports on separate lines "PERF203", # `try`-`except` within a loop incurs performance overhead "RET504", # Unnecessary assignment to `...` before `return` statement "PLR6301", # Method `...` could be a function, class method, or static method "PYI041", # Use `float` instead of `int | float` "SIM102", # Use a single `if` statement instead of nested `if` statements "SIM105", # Use `contextlib.suppress(TimeoutError)` instead of `try`-`except`-`pass` "SIM114", # Combine `if` branches using logical `or` operator "TC006", # Add quotes to type expression in `typing.cast()` ] [tool.ruff.lint.per-file-ignores] "scripts/**/*.py" = [ "INP001", # File `...` is part of an implicit namespace package. Add an `__init__.py`. ] "tests/**/*.py" = [ "ARG", "B", "FBT", "INP", "SLF", "S101", # Use of `assert` detected "PLR0904", # Too many public methods (12 > 10) "PLR2004", # Magic value used in comparison ] [tool.ruff.lint.flake8-copyright] notice-rgx = "SPDX-FileCopyrightText: .*" min-file-size = 256 [tool.ruff.lint.pylint] # https://pylint.pycqa.org/en/latest/user_guide/configuration/all-options.html#design-checker # https://pylint.pycqa.org/en/latest/user_guide/checkers/features.html#design-checker-messages max-args = 5 # max. number of args for function / method (R0913) # max-attributes = 15 # max. number of instance attrs for a class (R0902) max-branches = 40 # max. number of branch for function / method body (R0912) max-locals = 30 # max. number of local vars for function / method body (R0914) max-returns = 15 # max. number of return / yield for function / method body (R0911) max-statements = 150 # max. number of statements in function / method body (R0915) max-public-methods = 20 # max. number of public methods for a class (R0904) # max-positional-arguments = 5 # max. number of positional args for function / method (R0917) ##################### # mypy # https://github.com/python/mypy ##################### [tool.mypy] # https://mypy.readthedocs.io/en/stable/config_file.html #mypy_path = "$MYPY_CONFIG_FILE_DIR/tests/stubs" python_version = "3.10" files = "scripts,src,tests" strict = true disallow_untyped_calls = false disallow_untyped_defs = true disallow_incomplete_defs = true ignore_missing_imports = true show_error_codes = true warn_unused_ignores = true verbosity = 0 ##################### # basedpyright # https://github.com/detachhead/basedpyright ##################### [tool.basedpyright] # https://docs.basedpyright.com/latest/configuration/config-files/ include = ["scripts", "src", "tests"] defineConstant = { DEBUG = false } pythonVersion = "3.10" typeCheckingMode = "standard" ##################### # pytest # https://pypi.org/project/pytest/ ##################### [tool.pytest.ini_options] testpaths = [ "src", "tests" ] # https://docs.pytest.org/en/stable/reference.html#confval-addopts addopts = """ --strict-markers -p no:cacheprovider --doctest-modules --cov=src/kleinanzeigen_bot --cov-report=term-missing """ markers = [ "itest: marks a test as an integration test (i.e. a test with external dependencies)", "asyncio: mark test as async" ] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [ "ignore:Exception ignored in:pytest.PytestUnraisableExceptionWarning", "ignore::DeprecationWarning" ]