Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom commands in setup.py #767

Open
dtromb opened this issue Jun 4, 2024 · 8 comments
Open

custom commands in setup.py #767

dtromb opened this issue Jun 4, 2024 · 8 comments

Comments

@dtromb
Copy link

dtromb commented Jun 4, 2024

Problem description

Struggling to understand this mess.... I want to do a relatively straightforward thing - define a custom command and set the options for an invocation (or multiple invocations) from setup.py. The documentation for setuptools is confusing to the point of useless - it gives us tutorials and recipes without explaining the WHY and HOW (what are the object lifecycles? what are the steps of a build? what are the apis involed? documentation for setup(), etc, etc, etc.)

Here is my hypothetical setup.py


class CMake(Command):

    def run(self):
        pass

    def initialize_options(self):
        raise AssertionError("HOW DO I SET OPTIONS AFTER DEFINING THEM HERE")

    def finalize_options(self):
        raise AssertionError("What does this even do??")


class Build(build):
    sub_commands = [("compile_subproject", None), ("compile_subproject_2,None), *build.sub_commands]


setup(
    cmdclass={"build": Build, "compile_subproject": CMake, "compile_subproject_2": CMake}, 
)

As you can see, I've defined a command CMake, which will be invoked twice. I want to pass different options to each invocation. How can this be done?

setup() is terribly, terribly, documented! I would MUCH PREFER to use a class API, .toml, or even setup.cfg to do this! But the official docs are totally silent and a string of old Stack Overflow posts have led me to believe that one /must/ use setup(). Is this true? How can I accomplish this simple task? How can I continue using setuptools without going compltely mad?

Reverse engineering has led me to believe that the second member of those sub_commands tuples must be a callable, and when it is present the CMake option functions are not invoked. No clue how or why this exists, and I could not find any documentation at all on it, even in the source. Is it related?

@henryiii
Copy link
Contributor

henryiii commented Jun 4, 2024

Please, please, please use a proper build backend designed for CMake like scikit-build-core. There are long list of benefits that you’ll have to solve yourself if you do it yourself. It even has an experimental setuptools plug-in if you really want to go that way.

@dtromb
Copy link
Author

dtromb commented Jun 4, 2024

I am trying to understand setuptools, because I must. Please answer my question instead of assuming I don't "really" want to do what my hypothetical implies to you I am doing. I'd like to know how to define a custom command and set the options for an invocation (or multiple invocations) from setup.py... just as the question asks. I'd be happy to edit to change the hypothetical from CMake invocations to Grapefruit and Tangerine invocations which "juice" instead of "compile", if it makes you happier...

(also notice that if I did use some other 'backend' - scare quotes because setuptools documentation doesn't even really inform us what a build backend even is - I'd likely still need to know how to customize it in this way)

@henryiii
Copy link
Contributor

henryiii commented Jun 4, 2024

If you are asking a question specifically of a specific tool (setuptools), why are you asking it here instead of at github.com/pypa/setuptools? You literally said "I would MUCH PREFER to use a class API, .toml," - that sounds like you'd be happy with a solution that uses TOML (scikti-build-core). If you'd rather a "designed" API instead you could try Hatchling - it has an API for build plugins. If you are “going mad” over setuptools, why not at least try something else? Since you mentioned CMake, scikit-build-core is a logical thing to try, and it even includes both hatchling and setuptools plugins if you "have to use" setuptools.

General Python packaging is defined in terms of standards (like PEP 517, etc) that do not use setuptools commands; calling commands directly is deprecated, and the command structure is an internal design of setuptools that is not really intended to be stable. For example, bdist_wheel is not considered public at all. Many commands are highly deprecated, etc. If you have suggestions to improve setuptools, feel free to open an issue with them. Though improving anything without breaking anything and without further increasing the size and complexity is very hard precisely because the entire internals is being used like this.

Anyway, to answer the question without using an external package, as a general resource for a lack of documentation of the internals, I'd recommend looking at examples of packages that extend setuptools. https://github.com/scikit-build/scikit-build-core/tree/main/src/scikit_build_core/setuptools was inspired by the excellent https://setuptools-rust.readthedocs.io (which I think is still the best setuptools plugin I've seen). The internals can and do change as setuptools tries to improve, especially now that it "owns" distutils.

I believe in your case you'll need a factory pattern; though for a small number of commands, I'd just do simple subclasses:

class CMake1(CMake):
    OPT = 1

Anything larger and you could do a proper factory:

type('CMake1', (CMake,), {'OPT': 1})

Drop this in where you define the command.

@jeanas
Copy link

jeanas commented Jun 4, 2024

@dtromb setuptools custom commands are officially deprecated.

@henryiii
Copy link
Contributor

henryiii commented Jun 4, 2024

(Oh, and on the edit:)

Also notice that if I did use some other 'backend' - scare quotes because setuptools documentation doesn't even really inform us what a build backend even is

Setuptools is a build backend, I'm not sure why they need to tell you about others. Vs. here, where we cover four of them (hatchling, flit-core, pdm-backend, and setuptools), and link to others, like scikit-build-core, meson-python, and maturin. See https://packaging.python.org/en/latest/glossary/#term-Build-Backend.

Also, if you want to customize it, hatchling probably has the best docs: https://hatch.pypa.io/latest/plugins/build-hook/reference/.

@henryiii
Copy link
Contributor

henryiii commented Jun 4, 2024

@jeanas This was describing custom build steps, which are not deprecated (next entry). The thing that is deprecated is using the "Commands" as commands. If they are steps, that's fine. A bit fragile, but fine.

@dtromb
Copy link
Author

dtromb commented Jun 4, 2024

@jeanas Is that the official opinion of an official source? It's very different from what appears at the link you provide:

Although the usage of setup.py as an executable script is deprecated, its usage as a configuration file for setuptools is absolutely fine.

^^ ie. exactly what I'm asking about. I'm not trying to run python setup.py <command>, ever. I understand it's deprecated.

Also please note the /entire section in that manual/ fully dedicated to custom commands: https://setuptools.pypa.io/en/latest/userguide/extension.html

Maybe I should be inquiring over at the setuptools github, instead?

@jeanas
Copy link

jeanas commented Jun 4, 2024

Sorry, there were so many questions about python setup.py invocations that I replied in semi-automatic mode. My bad. If it's just a configuration, whatever the setuptools internals to do it are called, then yes, it's fine.

It's absolutely not true that you must use setuptools though (don't believe the old SO posts), and part of the reason why pip was changed to be able to use other build backends (a term explained here) is that setuptools is, indeed, poorly documented and difficult to extend. If you want to continue the setuptools way, then you should indeed ask on their repo, though I would personally advise switching to scikit-build or similar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants