Downloads and network boundaries#
AnyDownloader is the central URL transfer router. It delegates to
specialists for web/direct downloads, Google Drive, GitHub and YouTube while
preserving the common DownloadResult contract.
DownloadPolicy: one transport contract#
DownloadPolicy centralizes the settings that describe transport
security and resource budgets. It contains no credentials, headers or output
paths, so it can be serialized in configuration/evidence without accidentally
serializing authentication material.
Built-in presets keep security controls enabled:
secureGeneral-purpose defaults: TLS verification, private-IP blocking, bounded redirects, retries and 100 MiB maximum transfer.
constrained/ci/docsSmaller timeout/byte/retry budgets for deterministic CI and documentation. It does not disable TLS or SSRF protection.
large/large-filesLarger byte/time budget while preserving the same security boundaries.
from scikitplot.corpus import AnyDownloader, DownloadPolicy
policy = DownloadPolicy.from_config({
"preset": "constrained",
"max_bytes": 8 * 1024 * 1024,
})
downloader = AnyDownloader.from_policy(
"https://example.com/data.csv",
policy,
)
Unknown configuration keys and non-boolean security flags fail early. This
prevents values such as verify_ssl="false" from being interpreted as a
truthy value. Explicit keyword arguments may still override a policy when the
caller intentionally needs a per-transfer exception.
Plan dispatch without touching the network#
Use AnyDownloader.plan when code, CI or a user interface needs to show
what would happen before any transfer starts. The returned
DownloadPlan uses the same URL-classification/per-URL parameter
resolver as the real specialist construction. Planning intentionally does not
resolve DNS, issue HEAD/GET requests, allocate a temporary directory or import a
remote SDK.
from scikitplot.corpus import AnyDownloader, DownloadPolicy
downloader = AnyDownloader.from_policy(
"https://github.com/org/repo/blob/main/data.csv",
DownloadPolicy.constrained(),
github_token="...",
)
plan = downloader.plan()
print(plan.downloader) # GitHubDownloader
print(plan.max_bytes) # constrained transfer budget
print(plan.github_token_configured) # True; token value is not serialized
DownloadPlan reports whether credentials/custom headers are configured but
never includes their values. It is a dispatch plan, not a security verdict:
SSRF DNS resolution and remote content-type probing belong to execution-time
network boundaries and are intentionally not disguised as side-effect-free
preflight. For batch inputs, AnyDownloader.plan_all always returns one
plan per URL in input order.
Builder integration#
CorpusBuilder no longer owns a second direct-download implementation.
Downloadable URLs flow through _make_downloader() and then through the same
_make_reader() seam used by local files.
This separation matters because transfer policy and parsing policy are different:
URL -> downloader -> DownloadResult(output_path, suffix, MIME, filename)
|
v
DocumentReader
A temporary local filename is implementation detail; provenance can continue to point at the original URL.
Advanced built-in downloader tuning does not require a custom factory. Put
explicit overrides in BuilderConfig downloader_kwargs:
from scikitplot.corpus import BuilderConfig, CorpusBuilder
builder = CorpusBuilder(BuilderConfig(
download_policy="constrained",
downloader_kwargs={
"max_redirects": 2,
"youtube_language": "tr",
},
))
Call-site downloader kwargs take precedence over this mapping; the mapping takes
precedence over ordinary builder defaults. Security-reducing values such as
verify_ssl=False or block_private_ips=False are therefore explicit user
choices rather than hidden fallbacks.
Security defaults#
The built-in downloader layer keeps SSRF, redirect, scheme, TLS and size
controls. CustomDownloader is intentionally a trusted-code escape
hatch: its handler can execute arbitrary Python and could make unrelated
network requests, so the wrapper cannot sandbox it. Instead the wrapper applies
what it can verify independently:
the original input URL still receives the normal SSRF pre-check by default;
timeout, size, TLS, redirect and user-agent policy are passed to the handler;
the returned path must exist and resolve to a regular file;
it must remain inside the configured output directory unless
allow_external_output=Trueis explicit;returned file size is checked against
max_bytesby default even if the handler ignored the hint.
A handler remains responsible for applying those transfer-policy arguments to
its own redirects/subrequests. Do not accept an untrusted downloaded Python
callable as a CustomDownloader handler. If a private network or custom
transport is intentionally allowed, make that decision in the custom
downloader/policy rather than weakening the global default.
Runtime plans#
RuntimePolicy defaults to allow_network=False. A Fluent plan with a
URL source therefore requires an explicit network permission at execution time.
This is independent of backend/model download policy: allowing an input URL does
not automatically mean an OCR/ASR/model backend may fetch assets.
Policy/legacy configuration ambiguity#
downloader_kwargs remains the deliberate per-build override layer.
Do not mix an explicit download_policy with customized legacy scalar
fields such as download_timeout or max_download_bytes. That is
ambiguous and BuilderConfig rejects it instead of silently ignoring
one side. Put the value in DownloadPolicy, or use
downloader_kwargs when an intentional one-build override is required.