Skip to main content

postprocess-sdist-make-rust-ext-opt

CI badge PyPI package and version badge Supported Python versions badge

This is a small tool to "post-process" Python source distributions ("sdists") containing setuptools-rust-based Rust extensions so that these extensions are marked as "optional" (cf. optional parameter in the setuptools-rust API docs).

In other words, it takes an sdist with a setup.py that looks like this

from setuptools import setup

setup(
    ...
    rust_extensions=[
        RustExtension(...),
    ]
    ...
)

and turns it into one that looks like this:

from setuptools import setup

setup(
    ...
    rust_extensions=[
        RustExtension(..., optional=True),
    ]
    ...
)

What? Why?

What does it mean for an extension to be optional?

An extension (Rust or otherwise) being optional means that if the build fails when installing the package, the installation of the remainder of the package will proceed anyway and be considered successful. The package can then deal with the extension's absence at runtime, e.g. by providing pure-Python fallbacks for its functionality.

Why set it as optional in a postprocessing step and not from the start?

Because you'll probably want to build binary packages (wheels) from the project as well, but if your extension is marked as optional, any errors during their build will be ignored. So you don't generally want to have it set as optional when building wheels. It only really makes sense to have it set for the sdist, nothing else.

Isn't there a way to do this dynamically without post-processing?

There is, and in fact that is the approach recommended by cibuildwheel: You just make the optional setting in your setup.py dependent on some environment variable that tells you whether a wheel is being built or not.

I thought about similar approaches before I wrote this utility, but figured you'd want to keep the number of "dynamic" values in setup.py to a minimum so it will be easy to port it to declarative alternatives like setup.cfg or pyproject.toml eventually. But being able to specify any kind of extension (even C ones) declaratively is nowhere close to being implemented in setuptools, and once it is, this tool in its current form won't help you anyway because it's just setup.py focused (although that could be changed then), so, to be honest, it's not much of an argument.

If I had known about these things or thought them through properly, I probably wouldn't have written it 🤷

Back to postprocessing: Why not do it the other way round?

Another option would be to set the extension as optional from the start but change it to non-optional before the wheel build. But the issue with that is that if you're using tools like setuptools-scm that automatically determine your package's version from its state as determined by a version control system (VCS) like Git, changing anything about the code will cause the version to be considered "dirty", which will be represented in the version string. A way to work around this would be to manipulate the VCS history in this case, but that is even more of a hack than the postprocessing.

Why not change it prior to building the sdist?

The same reason as above (dirty repo when building => modified automatically-determined version).

Installation

pip install postprocess-sdist-make-rust-ext-opt

Usage

$ postprocess-sdist-make-rust-ext-opt --help
Usage: postprocess-sdist-make-rust-ext-opt [OPTIONS] SDIST_PATHS...

  Post-process a Python sdist to make its Rust extensions optional

Arguments:
  SDIST_PATHS...  path(s) of sdist .tar.gz archive(s) to post-process
                  [required]

Options:
  -O, --output-dir DIRECTORY      directory in which to place the post-
                                  processed archives (default: directory named
                                  'postprocess' in input sdist directory)
  -C, --create-output-dir         whether to create the output directory (and
                                  its parents) if it does not exist
  -f, --force                     whether to overwrite existing files in the
                                  output directory
  --install-completion [bash|zsh|fish|powershell|pwsh]
                                  Install completion for the specified shell.
  --show-completion [bash|zsh|fish|powershell|pwsh]
                                  Show completion for the specified shell, to
                                  copy it or customize the installation.
  --help                          Show this message and exit.

Unless otherwise specified, a processed sdist will be written to a folder named postprocessed in the same directory as the input sdist. Its filename will be the same as that of the input sdist.

To modify sdists in-place, you can use the combination of options -fO dir dir/*.tar.gz, where dir is the directory cotaining the sdists.

Caveats

The RustExtension calls for which the optional argument should be set to True must be placed directly inside the list that is assigned to the rust_extensions parameter of the top-level setup() call like so:

from setuptools import setup

setup(
    ...
    rust_extensions=[
        RustExtension(...),
    ]
    ...
)

Anything more indirect than that, e.g. assigning a RustExtension instance to a variable and then placing that inside the rust_extensions list, will cause the tool to exit with an error.

This is because the transformation is implemented at the syntax tree level and no static analysis is performed to trace arguments back to their origins.

Acknowledgements

The transformation is performed using RedBaron's full syntax tree (FST) representation of the sdist's setup.py.

Metadata

Release files for postprocess-sdist-make-rust-ext-opt 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for postprocess-sdist-make-rust-ext-opt 0.2.2
File Size Uploaded Provenance Actions
postprocess_sdist_make_rust_ext_opt-0.2.2.tar.gz 5.5 kB Without provenance Details for postprocess_sdist_make_rust_ext_opt-0.2.2.tar.gz

Release files / postprocess_sdist_make_rust_ext_opt-0.2.2.tar.gz

Download URL postprocess_sdist_make_rust_ext_opt-0.2.2.tar.gz
Size 5.5 kB
Tags Source
SHA-256 checksum
How to use checksums
ba1d5fb87e56da0fdf63c0c8bf36e2c32afb3347c21864f5a670df0958a198ae
BLAKE2b-256 checksum
How to use checksums
91ef9eba7bf07be7d9f4d5a02c2589179daf315205443e094b4dd2114542b76b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.15

Security for postprocess-sdist-make-rust-ext-opt 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page