When developing and testing add-ons it can be useful to get to the
folder containing such add-on.
When Developer Extras is enabled, show a folder icon next to the
extension's path to quickly open it using the system file browser.
Also display it in the list of duplicate add-ons, if any, so user can
access them more conveniently.
Pull Request: https://projects.blender.org/blender/blender/pulls/128474
The logic to generalize over downloading a URL or reading data from
the file-system included static values in the iterator along with the
data being read - so the caller could access the total expected size of
the data being read. While this worked, the result didn't read well.
Now these iterators yield the data being read, a new type has been
added for the caller to read the size hint from.
Also correct invalid doc-string.
Accessing the internal extensions command would warn that the processes
stdout wasn't closed on completion. While it's harmless at the moment,
ensure it's closed using a context manager.
Regression in [0] caused extensions with an empty wheel list
to raise an unhandled exception when extracting the Python
version from the wheel list.
Besides resolving this error, account for errors using the wheel
list since it isn't type checked in this code-path.
[0]: cfc10b0232565642afbfdc5a867f027640ce8274
Add error handling for rare but possible failures on installation.
- Account for the destination extension directory being a file.
- Handle the exception if renaming the temporary directory fails.
When an extension could could be removed but it's directory could be
renamed, the install operation would fail anyway.
Resolve by completing the installation when the directory can be moved.
Report as part of #129884.
Regression in [0] caused wheels with an older Python version
that used CPython's stable ABI to be considered incompatible.
Resolve using the stable ABI for version checks.
[0]: cfc10b0232565642afbfdc5a867f027640ce8274
addon_utils.enable/disable now handle wheels so that the functions can
be used with extension add-ons.
A new argument `refresh_handled` supports scripts handing refresh
themselves which is needed to avoid refreshing many times in cases
when there are multiple calls to enable/disable.
This is mostly useful for internal operations.
Support multiple extensions with different Python versions in a similar
way to how "platforms" are handled now.
This will be needed when Blender upgrades to a newer version of Python
so extensions which have a binary dependency on Python 3.11 don't
attempt to install in a Blender built with a newer Python version.
Details:
- Extensions from incompatible Python versions are not listed.
- Python versions are extracted from wheel names.
- An extension may include wheels for multiple Python versions,
only the compatible wheels will be installed.
- Links may include a "python_versions" field which is checked
when dropping into Blender.
- JSON generated by the "server-generate" command include the:
"python_versions" field for extensions that include wheels.
- The "python_versions" field is optional, when omitted no version
checks are performed.
Also correct watch_test make target.
Ref #128750.
Python wheels from extensions were not being removed after
install/uninstall in some cases - although installing an extension
afterwards that used wheels would recalculate deps & remove them.
- Installing an extension didn't include the extension in the
compatibility-cache, causing uninstalling not to remove deps.
- Uninstalling an extension wasn't re-calculating the deps,
leaving them as-is.
Always write the compatibility-cache after installing and uninstalling
so changes are detected & handled.
Building an extension when the manifest didn't define a "type"
would assert instead of reporting the missing field.
Return earlier when there are errors to prevent the assertion.
On Windows an entire directory may be locked when any files inside it
are opened by another process. This can cause operations that
recursively remove a directory (uninstalling & updating) to fail
with a partially removed extension.
The case of uninstalling was already handled, where failure to remove
a directory would stage the extension for later removal.
In the case of updating however, the user could be left with a broken
(partially removed) extension where some files were removed, as the
directory was locked, the update would fail to extract new files.
Address this issue by renaming the directory before recursive removal.
The following logic has been implemented:
- If any files in the directory are locked, renaming will fail.
So even though the operation fails the extension is left intact.
- If renaming succeeds, it's possible to apply the update.
While it's possible (albeit unlikely) recursive removal fails,
which could be caused by file-system permissions issues corruption or
a process could open a file between rename & removal.
In this case the renamed directory is staged for later removal.
Other changes:
- Resolve a related problem where the user could install an
extension previously staged for removal, now installing an extension
ensured it's not removed later.
This would occur if uninstalling failed, the user resolves
directory-lock, uninstalls again, then re-installs the extension.
- When an extension fails to be removed, don't attempt to remove
user configuration for that extension.
Prefer to keep the extension & it's settings in their "current state"
if it can't be removed.
Do not report error when local repo does not have manifest and we are
asking for remote repositories.
If the errors list is filled with any error the operation is considered
failure.
Ref: !127360
Since [0] missing repository directories printed a warning on startup.
When checking for blocked extensions on startup, skip directories
that don't exist & don't print any warnings if the repository data
hasn't been downloaded.
[0]: 656fe6d3e4
When deleting files on WIN32, open files cannot be removed.
This is especially a problem for compiled Python modules which
remain open once imported.
Previously it was not as common for add-ons to include compiled Python
modules however with extensions supporting Python-wheels,
it's increasingly likely users run into this.
Workaround the problem by:
- Scheduling the files for removal next time Blender starts.
- Rename paths that cannot be removed to avoid collisions when
the paths is reused (re-installing for example).
This is supported for:
- Extensions.
- Python wheels.
- Legacy user add-ons.
- App-templates.
Details:
- On startup, a file exists that indicates cleanup is needed.
In the common case the file doesn't exist.
Otherwise module paths are scanned for files to remove.
- Since errors resolving paths to remove could result in user data loss,
ensure the paths are always within the (extension/addon/app-template)
directory.
- File locking isn't used, if multiple Blender instances start at the
same time and try to remove the same files, this won't cause errors.
Even so, remove the checking file immediately avoid unnecessary
file-system access overhead for other Blender instances.
Also resolves#125049.
Expose arguments to use when creating a Python sub-process.
Python could fail to start when loaded in a customized environment,
with PYTHONPATH set for e.g. Blender ignores these and loads but a
Python sub-process attempts to use these environment variables which
may point to incompatible Python versions.
Resolve the root cause of #124731.
Workaround: `[ASN1] nested asn1 error` error when making HTTPS
connections on systems with certificates that OpenSSL cannot parse
are installed.
This is a general issue with Python, resolve by applying a proposed
fix [0] to the extensions Python process at run-time.
(this doesn't impact Blender's Python run-time).
The down side is HTTPS connections will only work for extensions
on systems with this problem so this needs to be resolved by Python
long term.
While any changes to Python's SSL checks is worth avoiding,
this simply skips SSL certificates in the windows store that OpenSSL
can't parse instead of failing all SSL connections.
See related issues:
- https://github.com/python/cpython/issues/79846
- https://github.com/openssl/openssl/issues/25023
[0]: https://github.com/python/cpython/pull/91740
Ref !124943.
Support blocking extensions so there is a way for the maintainers of
the remote repository to notify the user if one of their installed
extensions blocked along with a reason for blocking.
Blocked extensions cannot be installed from the preferences or by
dropping a URL.
When an installed & blocked extension is found:
- An icon int the status bar shows an alert,
clicking on the icon shows the blocked extensions.
- The extensions preferences show a warning.
- The extensions & add-ons UI shows an alert icon
and "details" section shows the reason.
Details:
- Blocked & installed extensions are shown first in the installed
extensions panel.
- The internal "install" logic prevents downloading & installing
blocked extensions.
- Blocked extensions can still be downloaded & installed from disk.
- The "list" command includes an error message if any installed
extensions are blocked.
- The "server-generate" command can optionally take a configuration
file that includes the blocklist for the generated JSON.
See design #124954.