mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-03-12 02:31:45 +01:00
385 lines
14 KiB
TOML
385 lines
14 KiB
TOML
# 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
|
|
"pydantic>=2.0.0",
|
|
"ruamel.yaml",
|
|
"psutil",
|
|
"wcmatch",
|
|
"sanitize-filename>=1.2.0",
|
|
]
|
|
|
|
[dependency-groups] # https://peps.python.org/pep-0735/
|
|
dev = [
|
|
"pip-audit",
|
|
"pytest>=8.3.4",
|
|
"pytest-asyncio>=0.25.3",
|
|
"pytest-rerunfailures",
|
|
"pytest-cov>=6.0.0",
|
|
"ruff",
|
|
"mypy",
|
|
"basedpyright",
|
|
"autopep8",
|
|
"yamlfix",
|
|
"pyinstaller",
|
|
"platformdirs",
|
|
"types-requests>=2.32.0.20250515",
|
|
"pytest-mock>=3.14.0",
|
|
]
|
|
|
|
[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"
|
|
CI = "https://github.com/Second-Hand-Friends/kleinanzeigen-bot/actions"
|
|
|
|
|
|
#####################
|
|
# 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 <project_root>/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"
|
|
debug = "python -m pdb -m kleinanzeigen_bot"
|
|
|
|
# build & packaging
|
|
generate-schemas = "python scripts/generate_schemas.py"
|
|
compile.cmd = "python -O -m PyInstaller pyinstaller.spec --clean --workpath .temp"
|
|
compile.env = {PYTHONHASHSEED = "1", SOURCE_DATE_EPOCH = "0"} # https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
|
|
|
|
deps = "pdm list --fields name,version,groups"
|
|
"deps:tree" = "pdm list --tree"
|
|
"deps:runtime" = "pdm list --fields name,version,groups --include default"
|
|
"deps:runtime:tree" = "pdm list --tree --include default"
|
|
|
|
# format & lint
|
|
format = { composite = ["format:py", "format:yaml"] }
|
|
"format:py" = { shell = "autopep8 --recursive --in-place scripts src tests --verbose && python scripts/post_autopep8.py scripts src tests" }
|
|
"format:yaml" = "yamlfix scripts/ src/ tests/"
|
|
|
|
lint = { composite = ["lint:ruff", "lint:mypy", "lint:pyright"] }
|
|
"lint:ruff" = "ruff check --preview"
|
|
"lint:mypy" = "mypy"
|
|
"lint:pyright" = "basedpyright"
|
|
"lint:fix" = {shell = "ruff check --preview --fix" }
|
|
|
|
# tests
|
|
# Run unit tests only (exclude smoke and itest)
|
|
utest = "python -m pytest --capture=tee-sys -m \"not itest and not smoke\""
|
|
# Run integration tests only (exclude smoke)
|
|
itest = "python -m pytest --capture=tee-sys -m \"itest and not smoke\""
|
|
# Run smoke tests only
|
|
smoke = "python -m pytest --capture=tee-sys -m smoke"
|
|
# Run all tests in order: unit, integration, smoke
|
|
# (for CI: run these three scripts in sequence)
|
|
test = { composite = ["utest", "itest", "smoke"] }
|
|
# Run all tests in a single invocation for unified summary (unit tests run first)
|
|
"test:unified" = "python -m pytest --capture=tee-sys"
|
|
#
|
|
# Coverage scripts:
|
|
# - Each group writes its own data file to .temp/.coverage.<group>.xml
|
|
#
|
|
"test:cov" = { composite = ["utest:cov", "itest:cov", "smoke:cov", "coverage:combine"] }
|
|
"utest:cov" = { shell = "python -m pytest --capture=tee-sys -m \"not itest and not smoke\" --cov=src/kleinanzeigen_bot --cov-report=xml:.temp/coverage-unit.xml --cov-append" }
|
|
"itest:cov" = { shell = "python -m pytest --capture=tee-sys -m \"itest and not smoke\" --cov=src/kleinanzeigen_bot --cov-report=xml:.temp/coverage-integration.xml --cov-append" }
|
|
"smoke:cov" = { shell = "python -m pytest --capture=tee-sys -m smoke --cov=src/kleinanzeigen_bot --cov-report=xml:.temp/coverage-smoke.xml --cov-append" }
|
|
"coverage:combine" = { shell = "coverage report -m" }
|
|
# Run all tests with coverage in a single invocation
|
|
"test:cov:unified" = "python -m pytest --capture=tee-sys --cov=src/kleinanzeigen_bot --cov-report=term-missing"
|
|
|
|
# Test script structure:
|
|
# - Composite test groups (unit, integration, smoke) are run in order to fail fast and surface critical errors early.
|
|
# - This prevents running all tests if a foundational component is broken, saving time.
|
|
# - Each group is covered and reported separately.
|
|
#
|
|
# See docs/TESTING.md for more details.
|
|
|
|
|
|
#####################
|
|
# 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]
|
|
cache-dir = ".temp/cache_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
|
|
"FIX002", # Line contains TODO, consider resolving the issue
|
|
"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()`
|
|
"TD002", # Missing author in TODO
|
|
"TD003", # Missing issue link for this TODO
|
|
]
|
|
|
|
[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.isort]
|
|
# combine-straight-imports = true # not (yet) supported by ruff
|
|
|
|
[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 = 6 # max. number of args for function / method (R0913)
|
|
# max-attributes = 15 # TODO max. number of instance attrs for a class (R0902)
|
|
max-branches = 45 # 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 = 25 # 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"
|
|
cache_dir = ".temp/cache_mypy"
|
|
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]
|
|
cache_dir = ".temp/cache_pytest"
|
|
testpaths = [
|
|
"src",
|
|
"tests"
|
|
]
|
|
# https://docs.pytest.org/en/stable/reference.html#confval-addopts
|
|
addopts = """
|
|
--strict-markers
|
|
--doctest-modules
|
|
--cov-report=term-missing
|
|
"""
|
|
markers = [
|
|
"smoke: marks a test as a high-level smoke test (critical path, no mocks)",
|
|
"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"
|
|
]
|
|
|
|
[tool.coverage.run]
|
|
# https://coverage.readthedocs.io/en/latest/config.html#run
|
|
data_file = ".temp/coverage.sqlite"
|
|
branch = true # track branch coverage
|
|
relative_files = true
|
|
|
|
|
|
#####################
|
|
# yamlfix
|
|
# https://lyz-code.github.io/yamlfix/
|
|
#####################
|
|
[tool.yamlfix]
|
|
allow_duplicate_keys = true
|
|
comments_min_spaces_from_content = 2
|
|
comments_require_starting_space = false # FIXME should be true but rule is buggy
|
|
comments_whitelines = 1
|
|
section_whitelines = 1
|
|
explicit_start = false
|
|
indentation = 2
|
|
line_length = 1024
|
|
preserve_quotes = true
|
|
quote_basic_values = false
|
|
quote_keys_and_basic_values = false
|
|
quote_representation = '"'
|
|
whitelines = 1
|