This document describes the modules that make up btx_lib_mail and the public
and notable internal components of each. It reflects the code as it currently
stands; for narrative usage and configuration guidance see the
README.
btx_lib_mail is a small SMTP delivery library with a rich-click CLI. The
package is a CLI-first utility whose modules live in the adapter/transport layer,
with behaviors.py acting as a thin placeholder domain. import-linter
enforces that the CLI depends on the behaviour helpers only.
Delivery flows in one direction, from intent to SMTP side effects:
cli.cli_send_mail (resolve CLI flags / env / .env)
-> lib_mail.send (validate, prepare, orchestrate)
-> _prepare_recipients / _prepare_attachments / _prepare_hosts
-> _resolve_delivery_options / _resolve_attachment_security_options
-> _deliver_to_any_host (compose once to a spool, failover across hosts)
-> Transport.deliver (SmtplibTransport: connect, STARTTLS, login,
stream via BDAT or DATA)
Configuration is a Pydantic model (ConfMail) with a global conf instance;
per-call overrides passed to send win over conf. Resolved runtime knobs are
frozen dataclasses (DeliveryOptions, AttachmentSecurityOptions) so the
low-level helpers receive one immutable object each.
The components below back the CLI surface and the delivery engine.
The SMTP delivery boundary: configuration, input normalisation, message rendering, attachment security, and the delivery orchestration.
- Purpose: Enumerate the closed set of attachment security violation categories so callers match on a typed member instead of a bare string.
- Type:
class AttachmentViolation(str, Enum)(astrmixin rather than the 3.11+StrEnum, to keep the Python 3.10 baseline). Members:PATH_TRAVERSAL,SYMLINK,SENSITIVE_PATTERN,DIRECTORY,EXTENSION,SIZE. - Notes: Members subclass
str, soviolation == "symlink", JSON serialisation, andAttachmentViolation("symlink")round-tripping all keep the original wire value. - Location: src/btx_lib_mail/lib_mail.py
- Purpose: Structured exception raised when an attachment violates a security policy, so callers can handle or report it.
- Fields:
path(pathlib.Path),reason(str),violation_type(AttachmentViolation). - Notes:
__str__rendersviolation_type.valueto keep the message stable across Python versions. - Location: src/btx_lib_mail/lib_mail.py
- Purpose: Name a validated attachment and point at its source file, so the bytes are read only while the message is streamed to the transport, never held in memory from preparation onward.
- Fields:
filename(str),source(pathlib.Path). Immutable (frozen=True). - Location: src/btx_lib_mail/lib_mail.py
- Purpose: Authoritative SMTP configuration (Pydantic
BaseModel) merging CLI options, environment variables, and defaults with type and range checks. - Fields:
smtphosts(list[str]),raise_on_missing_attachments(bool),raise_on_invalid_recipient(bool),smtp_username/smtp_password(str | None),smtp_use_starttls(bool, defaultTrue),smtp_starttls_verify(bool, defaultTrue),smtp_timeout(float, default30.0), and the attachment security fields (attachment_allowed_extensions,attachment_blocked_extensions,attachment_allowed_directories,attachment_blocked_directories,attachment_max_size_bytes,attachment_allow_symlinks,attachment_raise_on_security_violation). - Validation: coerces
smtphostsfrom string/iterable, rejects a non-positivesmtp_timeoutandattachment_max_size_bytes, and normalises extension/directory sets. - Global:
confis the shared instance used when per-call overrides are absent. - Location: src/btx_lib_mail/lib_mail.py
- Purpose: Return
(username, password)when both are populated, elseNone, so callers do not juggle two separate optionals. - Location: src/btx_lib_mail/lib_mail.py
- Purpose: Freeze the resolved delivery knobs for one attempt.
- Fields:
credentials(tuple[str, str] | None),use_starttls(bool),starttls_verify(bool),timeout(float). - Notes: Resolved by
_resolve_delivery_optionsfrom per-call overrides falling back toconf.starttls_verify=Falsekeeps STARTTLS encryption but skips certificate/hostname validation (for internal self-signed relays); it has no effect whenuse_starttlsisFalse. - Location: src/btx_lib_mail/lib_mail.py
- Purpose: Freeze the resolved attachment security options for one send.
- Fields:
allowed_extensions(frozenset[str] | None),blocked_extensions(frozenset[str]),allowed_directories(frozenset[Path] | None),blocked_directories(frozenset[Path]),max_size_bytes(int | None),allow_symlinks(bool),raise_on_violation(bool). - Notes: Resolved by
_resolve_attachment_security_options;Nonemeans "use theconfdefault", an empty frozenset means "no restriction". - Location: src/btx_lib_mail/lib_mail.py
- Purpose: The library/CLI facade that turns validated intent (sender, recipients, bodies, attachments) into SMTP activity while honouring the delivery and security policies.
- Input:
mail_from,mail_recipients,mail_subject, optionalmail_body/mail_body_html,smtphosts,attachment_file_paths, and keyword overridescredentials,use_starttls,starttls_verify,timeout, the attachment security parameters, andraise_on_missing_attachments/raise_on_invalid_recipient. Omitted overrides fall back toconf. - Output:
Truewhen every recipient is delivered. Failure raises rather than returningFalse. - Raises:
ValueError(no valid recipients / invalid sender),FileNotFoundError(missing required attachment),AttachmentSecurityError(policy violation in strict mode),RuntimeError(every host failed for a recipient). - Location: src/btx_lib_mail/lib_mail.py
_deliver_to_any_hostcomposes the message once into aSpooledTemporaryFileand iterates the host tuple, delegating to the injectedTransportuntil one accepts the message, logging a warning per failed host. The spool is reused across host attempts.Transportis a protocol (delivery seam);SmtplibTransportis the default adapter. It opens thesmtplib.SMTPsession, runs STARTTLS via_build_starttls_context(verify=...)when enabled, logs in when credentials are present, then streams the message to the socket in_STREAM_CHUNK_SIZEchunks: RFC 3030BDATwhen the server advertisesCHUNKING, otherwise theDATAphase with_DotStufferincremental dot-stuffing.sendaccepts atransport=override for testing or alternative transports._build_starttls_context(*, verify)returnsssl.create_default_context(); whenverifyisFalseit clearscheck_hostnameand setsverify_modetoCERT_NONE(encrypted but unverified)._compose_to_spoolserialises the message (EmailMessage+email.policy.SMTPCRLF) into a spooled temp file, streaming each attachment's base64 from disk in chunks so a large payload is never buffered whole.- Location: src/btx_lib_mail/lib_mail.py
validate_email_address(address)raisesValueErrorwhen the address does not matchEMAIL_PATTERN.validate_smtp_host(host)raisesValueErrorfor a malformed host, acceptinghostname,hostname:port,[IPv6], and[IPv6]:port.- Both are public;
_parse_smtp_hostreusesvalidate_smtp_hostbefore splitting hostname and port. - Location: src/btx_lib_mail/lib_mail.py
_validate_attachment_security orchestrates, in order: _check_path_traversal,
_check_symlink, _check_sensitive_patterns, _check_directory_restrictions,
_check_extension, and _check_file_size. Each raises AttachmentSecurityError
with the matching AttachmentViolation category. _prepare_attachments applies
them before reading file bytes, honouring raise_on_violation and
raise_on_missing.
DANGEROUS_EXTENSIONS_POSIX, DANGEROUS_EXTENSIONS_WINDOWS,
DANGEROUS_DIRECTORIES_POSIX, DANGEROUS_DIRECTORIES_WINDOWS, and
SENSITIVE_PATH_PATTERNS provide the OS-appropriate blacklists. EMAIL_PATTERN
is the compiled address regex.
The rich-click adapter that exposes the commands and keeps traceback handling
consistent across the console script and python -m.
- Commands:
info,hello,send,validate-email,validate-smtp-host,fail, plus the root groupcliand the placeholdercli_main. - Root group cli {#cli-root}: registers the global
--traceback/--no-tracebackflag, mirrors it intolib_cli_exit_tools.config, and prints help when invoked without a subcommand (unless--tracebackwas explicitly set). - cli_send_mail {#cli-send-mail}: the
sendcommand. Resolves--host,--recipient,--sender,--subject,--body,--html-body,--attachment,--starttls/--no-starttls,--starttls-verify/--no-starttls-verify,--username,--password,--timeout, and the--attachment-*security options, falling back to theBTX_MAIL_*environment variables (or a local.env). Precedence: CLI options, then environment variables, then.enventries, thenbtx_lib_mail.lib_mail.conf. Delegates tosendand echoes a summary line. - Resolution helpers:
_configured_value,_dotenv_value,_resolve_list,_resolve_bool,_resolve_optional_bool,_resolve_float,_resolve_int,_resolve_extensions,_resolve_directories,_resolve_credentialsparse boundary input (CLI string / env /.env) into typed values. - Traceback helpers:
apply_traceback_preferences{#cli-apply-traceback-preferences},snapshot_traceback_state{#cli-snapshot-traceback-state},restore_traceback_state{#cli-restore-traceback-state} keeplib_cli_exit_toolsin sync and restorable. - Entry point main {#cli-main-entry}: runs the command through
lib_cli_exit_tools, choosing the traceback character budget, and restores the prior traceback state unless asked not to. - Location: src/btx_lib_mail/cli.py
Strictly-typed wrappers (option, version_option, argument) over the
rich-click decorators whose re-exported click ParamType is untyped. This module
is the single boundary that carries the # pyright: ignore[reportUnknownMemberType]
for that third-party gap, keeping the rest of the CLI layer strict-clean.
- Location: src/btx_lib_mail/typed_click.py
The placeholder domain helpers backing the CLI scaffold.
- emit_greeting(stream=None) {#behaviors-emit-greeting}: writes
CANONICAL_GREETINGplus a newline to the stream (defaultsys.stdout) and flushes when possible. - raise_intentional_failure() {#behaviors-raise-intentional-failure}: always
raises
RuntimeError('I should fail'), the vehicle for error-path and traceback tests. - noop_main() {#behaviors-noop-main}: returns
None; honours tooling that expects amaincallable. - CANONICAL_GREETING: the shared greeting line (
"Hello World"). - Location: src/btx_lib_mail/behaviors.py
Implements python -m btx_lib_mail, delegating to cli.main so exit semantics
match the console script.
- _open_cli_session() {#module-main-open-cli-session}: returns a
lib_cli_exit_tools.cli_sessioncontext manager wired with the shared traceback limits. - _command_to_run() {#module-main-command-to-run}: returns the root
cli.clicommand. - _command_name() {#module-main-command-name}: returns
__init__conf__.shell_command. - _module_main() {#module-main-module-main}: opens the session and runs the command, returning the exit code.
- Location: src/btx_lib_mail/main.py
Static project metadata as plain constants, kept in sync with pyproject.toml by
development automation so runtime code never queries packaging APIs.
- Constants:
name,title,version,homepage,author,author_email,shell_command, and the layered-config identifiersLAYEREDCONF_VENDOR,LAYEREDCONF_APP,LAYEREDCONF_SLUG. - print_info(): renders the constants for the CLI
infocommand. - Location: src/btx_lib_mail/init__conf.py
Re-exports the public API. __all__ covers: AttachmentSecurityError,
AttachmentViolation, CANONICAL_GREETING, ConfMail,
DANGEROUS_DIRECTORIES_POSIX, DANGEROUS_DIRECTORIES_WINDOWS,
DANGEROUS_EXTENSIONS_POSIX, DANGEROUS_EXTENSIONS_WINDOWS,
SENSITIVE_PATH_PATTERNS, conf, emit_greeting, logger, noop_main,
print_info, raise_intentional_failure, send, validate_email_address,
validate_smtp_host.
- Location: src/btx_lib_mail/init.py