3 Commits

Author SHA1 Message Date
Campbell Barton
01806a62e3 Cleanup: spelling (make check_spelling_*) 2025-10-07 10:19:46 +11:00
Sybren A. Stüvel
9b0b011f5d HTTP Downloader: ensure __main__.__file__ is not set when spawning process
Ensure that `__main__.__file__` is not set to "<blender string>" when
spawning the subprocess for the background downloader. Blender sets this
value when executing a Python string from C++ code. However, this value
causes issues with Python's `multiprocessing` module, as it expects that
IF the attribute is set, it contains the actual file of the actual main
module. Clearing it works fine, as `__file__` is optional anyway.

Pull Request: https://projects.blender.org/blender/blender/pulls/143985
2025-08-06 14:22:47 +02:00
Sybren A. Stüvel
3d40246e94 Python: add HTTP file downloader
Add a new package `scripts/modules/_bpy_internal/http`, containing
classes to download files via HTTP.

The code is intentionally put into the `_bpy_internal` package, as I
don't intend it to be the end-all-be-all of downloaders for general
use in add-ons. It's been written to support the Remote Asset Library
project (#134495), where it will be used to download JSON files (to
get the list of assets on the server) as well as the asset files
themselves.

The module consists of several parts. The main ones are:

`class ConditionalDownloader`
: File downloader, which downloads a URL to a file on disk.

  It supports conditional requests via `ETag`/`If-None-Match` and
  `Last-Modified`/`If-Modified-Since` HTTP headers (RFC 7273, section 3.
  Precondition Header Fields). A `304 Not Modified` response is
  treated as a succesful download.

  Metadata of the request (the response length in bytes, and the above
  headers) are stored on disk, in a location that is determined by the
  user of the class. Probably in the future it would be nice to have a
  single sqlite database for this (there's a TODO in the code about
  this).

  The downloader uses the Requests library, and manages its own HTTP
  session object. This way it can handle TCP/IP connection reuse,
  automatically retry failing connections, and in the future
  HTTP-level authentication.

`class BackgroundDownloader`
: Wrapper for a `ConditionalDownloader` that manages a background
  process for the actual downloading.

  It runs the downloader in a background process, while ensuring that
  its reporters (see below) get called on the main process. This way
  it's possible to do background downloading, while still receiving
  progress reports in a modal operator, which in turn can directly
  call Blender's Python API. Care was taken to [not use Python
  threads][1]

`class DownloadReporter`
: Protocol class. Objects adhering to the protocol can be given to a
  `ConditionalDownloader` or `BackgroundDownloader`. The protocol has
  functions like `download_starts(…)`, `download_progress(…)`,
  `download_error(…)`, which will be called by the downloader to
  report on what it's doing.

  I chose to make this a protocol, rather than an abstract superclass,
  because then it's possible to make an Operator a DownloadReporter
  without requiring multi-classing.

[1]: https://docs.blender.org/api/main/info_gotchas_threading.html

Pull Request: https://projects.blender.org/blender/blender/pulls/138327
2025-08-01 12:27:56 +02:00