Files
parsedmarc/pyproject.toml
T

252 lines
9.4 KiB
TOML

[build-system]
requires = [
"hatchling>=1.27.0",
]
requires_python = ">=3.10,<3.15"
build-backend = "hatchling.build"
[project]
name = "parsedmarc"
dynamic = [
"version",
]
description = "A Python package and CLI for parsing aggregate, failure, and SMTP TLS DMARC reports"
readme = "README.md"
license = "Apache-2.0"
authors = [
{ name = "Sean Whalen", email = "whalenster@gmail.com" },
]
keywords = [
"DMARC",
"parser",
"reporting",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
requires-python = ">=3.10"
# The base install is the parsing core plus a working core CLI: file,
# IMAP, Maildir, and mbox input; CSV/JSON, Splunk HEC, webhook, and
# syslog output. Every other output and mailbox integration lives in an extra
# below, so a library or small-mail-host install does not have to carry
# the Elasticsearch, OpenSearch, Kafka, AWS, Azure, Gmail, and Microsoft
# Graph SDKs (see issue #883).
dependencies = [
# The [doh] extra supplies the httpx/h2 floors DNS over HTTPS needs;
# 2.7.0 is the floor verified against the dns.nameserver and
# dns.query.https(session=...) APIs utils.py builds on.
"dnspython[doh]>=2.7.0",
"expiringdict>=1.1.4",
# The runtime HTTP library (utils.py fetches, webhook and Splunk HEC
# clients, Graph error handling in cli.py). The floor matches
# microsoft-kiota-http's own requirement.
"httpx>=0.25",
"lxml>=4.4.0",
# No extras: the base mailsuite supplies IMAP and Maildir connections
# plus mail-parser. Gmail and Microsoft Graph come from the gmail and
# msgraph extras below.
"mailsuite>=2.3.2",
"maxminddb>=2.0.0",
"publicsuffixlist>=0.10.0",
# Imported directly at utils.py:39 (dateutil.parser). It used to
# arrive transitively via dateparser, which nothing ever imported.
"python-dateutil>=2.8.0",
"tqdm>=4.31.1",
"xmltodict>=0.12.0",
"PyYAML>=6.0.3"
]
# Splunk HEC, webhook, and syslog outputs deliberately have no extra:
# they need only httpx (a base dependency) and the standard library, so
# do not add empty marker extras for them.
[project.optional-dependencies]
elastic = [
"elasticsearch>=8.18,<9",
]
opensearch = [
"opensearch-py>=2.4.2,<=4.0.0",
# boto3 supplies the SigV4 signer opensearch.py imports for AWS auth.
"boto3>=1.16.63",
]
kafka = [
"kafka-python>=2.3.2",
]
s3 = [
"boto3>=1.16.63",
]
gelf = [
"pygelf>=0.4.2",
]
loganalytics = [
"azure-identity>=1.8.0",
"azure-monitor-ingestion>=1.0.0",
]
msgraph = [
"mailsuite[msgraph]>=2.3.1",
# Imported directly in cli.py for Graph error handling; otherwise
# only a transitive dep of mailsuite[msgraph] -> msgraph-sdk.
"microsoft-kiota-abstractions>=1.8.0",
]
gmail = [
"mailsuite[gmail]>=2.3.1",
]
# `all` must stay the union of every extra above — and must keep
# excluding `postgresql`: psycopg's prebuilt binary wheels do not exist
# for every platform/arch, so folding it in here would make
# `pip install parsedmarc[all]` fail on platforms it does not cover.
# Spelled out as an explicit package list rather than a self-referential
# `parsedmarc[elastic,...]` spec, which would make the project a
# dependency of itself; keep this list in sync when an extra above
# gains, drops, or re-floors a package.
all = [
"azure-identity>=1.8.0",
"azure-monitor-ingestion>=1.0.0",
"boto3>=1.16.63",
"elasticsearch>=8.18,<9",
"kafka-python>=2.3.2",
"mailsuite[gmail,msgraph]>=2.3.1",
"microsoft-kiota-abstractions>=1.8.0",
"opensearch-py>=2.4.2,<=4.0.0",
"pygelf>=0.4.2",
]
postgresql = [
# Optional output backend. psycopg ships prebuilt binary wheels via the
# [binary] extra, but those wheels don't exist for every platform/arch,
# so PostgreSQL support is opt-in rather than a mandatory dependency.
"psycopg[binary]>=3.1.0",
]
build = [
# Used only by maintainer tooling under parsedmarc/resources/maps/ —
# `collect_domain_info.py --use-search-fallback` falls back to a
# DuckDuckGo search when the homepage fetch returns a bot-block / parked
# / empty page. Optional import; the script runs without it as long as
# the fallback flag isn't passed.
"ddgs>=9.0.0",
"hatch>=1.14.0",
"myst-parser[linkify]",
"nose",
# Pinned exactly: pyright's checks evolve between releases, so an
# unpinned version could break CI without any code change. Bump
# deliberately (and fix any new findings) rather than implicitly.
"pyright==1.1.411",
"pytest",
"pytest-cov",
# Test-only: tests/test_init.py imports pytz for a fixed-offset
# timezone. It used to arrive transitively via dateparser, which the
# base dependencies no longer declare.
"pytz",
# Used only by the out-of-wheel maintainer script
# parsedmarc/resources/maps/collect_domain_info.py, which deliberately
# stays on requests because its permissive-TLS fallback is built on
# urllib3's HTTPAdapter machinery.
"requests>=2.22.0",
# Pinned exactly for the same reason as pyright: ruff's default rule
# set evolves between releases (e.g. 0.16.0 began flagging the
# str.format() style this codebase used until then), so an unpinned
# version breaks CI without any code change. Bump deliberately and fix
# any new findings in the same PR as the bump.
"ruff==0.16.0",
"sphinx",
"sphinx_rtd_theme",
]
[project.scripts]
parsedmarc = "parsedmarc.cli:_main"
[project.urls]
Homepage = "https://domainaware.github.io/parsedmarc"
[tool.hatch.version]
path = "parsedmarc/constants.py"
[tool.hatch.build.targets.sdist]
include = [
"/parsedmarc",
]
[tool.hatch.build]
exclude = [
"base_reverse_dns.csv",
"unknown_base_reverse_dns.csv",
"README.md",
"AGENTS.md",
"CLAUDE.md",
"*.bak",
# Maintenance tooling: any Python file under parsedmarc/resources/maps/
# whose name doesn't start with `_` (i.e. everything except __init__.py,
# which must keep shipping for `importlib.resources.files()` lookups).
"parsedmarc/resources/maps/[!_]*.py",
]
[tool.ruff.lint]
# The rule set is selected explicitly rather than floating on ruff's
# defaults: ruff 0.16.0 expanded the default selection from the
# long-standing E4/E7/E9/F to many more rule families (BLE, SIM, C4, DTZ,
# I, PL, S, ...), some of which conflict with deliberate house style —
# e.g. BLE001 flags the parser's intentional broad catches that keep one
# malformed report from crashing a batch. E4/E7/E9/F is the pre-0.16
# default set; adopting any of the new families is a deliberate
# per-family decision, made here with a comment, not an upgrade side
# effect.
#
# UP006/UP007/UP035/UP045 enforce modern type-hint syntax: with
# requires-python >=3.10, PEP 585 builtins (list[int]) and PEP 604 unions
# (X | Y, X | None) are available, so keep the deprecated typing.List /
# Union / Optional spellings out of the codebase.
select = [
"E4", # import rules from pycodestyle
"E7", # statement rules from pycodestyle
"E9", # runtime/syntax error rules from pycodestyle
"F", # pyflakes
"UP006", # non-pep585-annotation: List -> list, Dict -> dict
"UP007", # non-pep604-annotation-union: Union[X, Y] -> X | Y
"UP030", # format-literals: "{0}".format(x) -> "{}".format(x)
"UP032", # f-string: "{}".format(x) -> f"{x}"
"UP035", # deprecated-import: typing.List etc. / typing -> collections.abc
"UP045", # non-pep604-annotation-optional: Optional[X] -> X | None
]
[tool.pyright]
# The whole codebase passes pyright with zero errors and warnings; CI
# enforces this (see .github/workflows/python-tests.yml). Run locally with
# `pyright` from the repo root. Requires the [all] and [postgresql] extras
# to be installed so the optional integration imports (parsedmarc/cli.py's
# guarded output modules, the psycopg import in parsedmarc/postgres.py)
# resolve.
include = ["parsedmarc", "tests", "docs"]
typeCheckingMode = "standard"
[tool.pytest.ini_options]
# Default to the per-module test layout under tests/. New tests should go
# into tests/test_<module>.py to match the file they exercise; do not
# reintroduce a monolithic tests.py.
testpaths = ["tests"]
[tool.coverage.run]
# Coverage measures shipped code only. Master's reported ≈66.9% on
# Codecov was an artefact of the old monolithic tests.py having no
# [tool.coverage.run] block, which let coverage's default behaviour
# measure every file imported during the run — including the test file
# itself at ~99% "covered". That inflated the headline by ~8 percentage
# points without any actual testing signal. Restricting to the parsedmarc
# package gives a meaningful number that tracks how much of the shipped
# library the test suite actually exercises.
source = ["parsedmarc"]
# Maintainer-only batch scripts under parsedmarc/resources/maps/ ship
# out of the wheel (see the [tool.hatch.build] exclude block above) —
# omit them so the headline number reflects only installed library code.
omit = [
"*/parsedmarc/resources/maps/*.py",
]