Movatterモバイル変換


[0]ホーム

URL:


ContentsMenuExpandLight modeDark modeAuto light/dark, in light modeAuto light/dark, in dark modeSkip to content
setuptools 80.9.0 documentation
Logo

Links

Project

Back to top

Extending or Customizing Setuptools

Setuptools design is based on thedistutils package originally distributedas part of Python’s standard library, effectively serving as its successor(as established inPEP 632).

This means thatsetuptools strives to honor the extension mechanismsprovided bydistutils, and allows developers to create third party packagesthat modify or augment the build process behavior.

A simple way of doing that is to hook in new or existingcommands andsetup() arguments just by defining “entry points”. Theseare mappings from command or argument names to a specification of where toimport a handler from. (See the section onAdvertising Behavior for some more background on entry points).

The following sections describe the most common procedures for extendingthedistutils functionality used bysetuptools.

Important

Any entry-point defined in yoursetup.cfg,setup.py orpyproject.toml files are not immediately available for use. Yourpackage needs to be installed first, thensetuptools will be able toaccess these entry points. For example consider aProject-A thatdefines entry points. When buildingProject-A, these will not beavailable. IfProject-B declares abuild system requirement onProject-A, thensetuptoolswill be able to useProject-A’ customizations.

Customizing Commands

Bothsetuptools anddistutils are structured around thecommand designpattern. This means that each main action executed when building adistribution package (such as creating asdistorwheel) correspond to the implementation of a Python class.

Originally indistutils, these commands would correspond to actual CLIarguments that could be passed to thesetup.py script to trigger adifferent aspect of the build. Insetuptools, however, these commandobjects are just a design abstraction that encapsulate logic and help toorganise the code.

You can overwrite existing commands (or add new ones) by defining entrypoints in thedistutils.commands group. For example, if you wanted to addafoo command, you might add something like this to your project:

# setup.cfg...[options.entry_points]distutils.commands=foo=mypackage.some_module:foo

Assuming, of course, that thefoo class inmypackage.some_module isasetuptools.Command subclass (documented below).

Once a project containing such entry points has been activated onsys.path,(e.g. by runningpipinstall) the command(s) will be available to anysetuptools-based project. In fact, this ishow setuptools’ own commands are installed: the setuptools project’s setupscript defines entry points for them!

The commandssdist,build_py andbuild_ext are especially usefulto customizesetuptools builds. Note however that when overwriting existingcommands, you should be very careful to maintain API compatibility.Custom commands should try to replicate the same overall behavior as theoriginal classes, and when possible, even inherit from them.

You should also consider handling exceptions such asCompileError,LinkError,LibError, among others. These exceptions are available inthesetuptools.errors module.

classsetuptools.Command(dist:Distribution,**kw)

Setuptools internal actions are organized using acommand design pattern.This means that each action (or group of closely related actions) executed duringthe build should be implemented as aCommand subclass.

These commands are abstractions and do not necessarily correspond to a command thatcan (or should) be executed via a terminal, in a CLI fashion (although historicallythey would).

When creating a new command from scratch, custom defined classesSHOULD inheritfromsetuptools.Command and implement a few mandatory methods.Between these mandatory methods, are listed:initialize_options(),finalize_options() andrun().

A useful analogy for command classes is to think of them as subroutines with localvariables called “options”. The options are “declared” ininitialize_options()and “defined” (given their final values, aka “finalized”) infinalize_options(),both of which must be defined by every command class. The “body” of the subroutine,(where it does all the work) is therun() method.Betweeninitialize_options() andfinalize_options(),setuptools may setthe values for options/attributes based on user’s input (or circumstance),which means that the implementation should be careful to not overwrite values infinalize_options() unless necessary.

Please note that other commands (or other parts of setuptools) may also overwritethe values of the command’s options/attributes multiple times during the buildprocess.Therefore it is important to consistently implementinitialize_options() andfinalize_options(). For example, all derived attributes (or attributes thatdepend on the value of other attributes)SHOULD be recomputed infinalize_options().

When overwriting existing commands, custom defined classesMUST abide by thesame APIs implemented by the original class. They alsoSHOULD inherit from theoriginal class.

abstractmethodfinalize_options()None

Set final values for all options/attributes used by the command.Most of the time, each option/attribute/cache should only be set if it does nothave any value yet (e.g.ifself.attrisNone:self.attr=val).

abstractmethodinitialize_options()None

Set or (reset) all options/attributes/caches used by the commandto their default values. Note that these values may be overwritten duringthe build.

abstractmethodrun()None

Execute the actions intended by the command.(Side effectsSHOULD only take place whenrun() is executed,for example, creating new files or writing to the terminal output).

Supporting sdists and editable installs inbuild sub-commands

build sub-commands (likebuild_py andbuild_ext)are encouraged to implement the following protocol:

classsetuptools.command.build.SubCommand(*args,**kwargs)

In order to support editable installations (seePEP 660) allbuild subcommandsSHOULD implement this protocol. They alsoMUST inheritfromsetuptools.Command.

When creating aneditable wheel,setuptools will try to evaluatecustombuild subcommands using the following procedure:

  1. setuptools will set theeditable_mode attribute toTrue

  2. setuptools will execute therun() command.

    Important

    SubcommandsSHOULD take advantage ofeditable_mode=True to adequateits behaviour or perform optimisations.

    For example, if a subcommand doesn’t need to generate an extra file andall it does is to copy a source file into the build directory,run()SHOULD simply “early return”.

    Similarly, if the subcommand creates files that would be placed alongsidePython files in the final distribution, during an editable installthe commandSHOULD generate these files “in place” (i.e. write them tothe original source directory, instead of using the build directory).Note thatget_output_mapping() should reflect that and include mappingsfor “in place” builds accordingly.

  3. setuptools use any knowledge it can derive from the return values ofget_outputs() andget_output_mapping() to create an editable wheel.When relevantsetuptoolsMAY attempt to use file links based on the valueofget_output_mapping(). Alternatively,setuptoolsMAY attempt to useimport hooks to redirect any attempt to importto the directory with the original source code and other files built in place.

Please note that custom sub-commandsSHOULD NOT rely onrun() beingexecuted (or not) to provide correct return values forget_outputs(),get_output_mapping() orget_source_files(). Theget_* methods shouldwork independently ofrun().

build_lib:str

String representing the directory where the build artifacts should be stored,e.g.build/lib.For example, if a distribution wants to provide a Python module namedpkg.mod,then a corresponding file should be written to{build_lib}/package/module.py.A way of thinking about this is that the files saved underbuild_libwould be eventually copied to one of the directories insite.PREFIXESupon installation.

A command that produces platform-independent files (e.g. compiling text templatesinto Python functions),CAN initializebuild_lib by copying its value fromthebuild_py command. On the other hand, a command that producesplatform-specific filesCAN initializebuild_lib by copying its value fromthebuild_ext command. In general this is done inside thefinalize_optionsmethod with the help of theset_undefined_options command:

deffinalize_options(self):self.set_undefined_options("build_py",("build_lib","build_lib"))...
editable_mode:bool=False

Boolean flag that will be set toTrue when setuptools is used for aneditable installation (seePEP 660).ImplementationsSHOULD explicitly set the default value of this attribute toFalse.When subcommands run, they can use this flag to perform optimizations or changetheir behaviour accordingly.

finalize_options()None

(Required by the originalsetuptools.Command interface)

get_output_mapping()dict[str,str]

Return a mapping between destination files as they would be produced by thebuild (dict keys) into the respective existing (source) files (dict values).Existing (source) files should be represented as strings relative to the projectroot directory.Destination files should be strings in the form of"{build_lib}/destination/file/path".

get_outputs()list[str]

Return a list of files intended for distribution as they would have beenproduced by the build.These files should be strings in the form of"{build_lib}/destination/file/path".

Note

The return value ofget_output() should include all files used as keysinget_output_mapping() plus files that are generated during the buildand don’t correspond to any source file already present in the project.

get_source_files()list[str]

Return a list of all files that are used by the command to create the expectedoutputs.For example, if your build command transpiles Java files into Python, you shouldlist here all the Java files.The primary purpose of this function is to help populating thesdistwith all the files necessary to build the distribution.All files should be strings relative to the project root directory.

initialize_options()None

(Required by the originalsetuptools.Command interface)

run()None

(Required by the originalsetuptools.Command interface)

Adding Arguments

Warning

Adding arguments to setup is discouraged as such argumentsare only supported through imperative execution and not supported throughdeclarative config.

Sometimes, your commands may need additional arguments to thesetup()call. You can enable this by defining entry points in thedistutils.setup_keywords group. For example, if you wanted asetup()argument calledbar_baz, you might add something like this to yourextension project:

# setup.cfg...[options.entry_points]distutils.commands=foo=mypackage.some_module:foodistutils.setup_keywords=bar_baz=mypackage.some_module:validate_bar_baz

The idea here is that the entry point defines a function that will be calledto validate thesetup() argument, if it’s supplied. TheDistributionobject will have the initial value of the attribute set toNone, and thevalidation function will only be called if thesetup() call sets it toa non-None value. Here’s an example validation function:

defassert_bool(dist,attr,value):"""Verify that value is True, False, 0, or 1"""ifbool(value)!=value:raiseSetupError(f"{attr!r} must be a boolean value (got{value!r}")

Your function should accept three arguments: theDistribution object,the attribute name, and the attribute value. It should raise aSetupError (from thesetuptools.errors module) if the argumentis invalid. Remember, your function will only be called with non-None values,and the default value of arguments defined this way is alwaysNone. So, yourcommands should always be prepared for the possibility that the attribute willbeNone when they access it later.

If more than one active distribution defines an entry point for the samesetup() argument,all of them will be called. This allows multipleextensions to define a common argument, as long as they agree onwhat values of that argument are valid.

Customizing Distribution Options

Plugins may wish to extend or alter the options on aDistribution object tosuit the purposes of that project. For example, a tool that infers theDistribution.version from SCM-metadata may need to hook into theoption finalization. To enable this feature, Setuptools offers an entrypointsetuptools.finalize_distribution_options. That entry point mustbe a callable taking one argument (theDistribution instance).

If the callable has an.order property, that value will be used todetermine the order in which the hook is called. Lower numbers are calledfirst and the default is zero (0).

Plugins may read, alter, and set properties on the distribution, but eachplugin is encouraged to load the configuration/settings for their behaviorindependently.

Defining Additional Metadata

Some extensible applications and frameworks may need to define their own kindsof metadata, which they can then access using theimportlib.metadata APIs.Ordinarily, this is done by having plugindevelopers include additional files in theirProjectName.egg-infodirectory. However, since it can be tedious to create such files by hand, youmay want to create an extension that will create the necessary filesfrom arguments tosetup(), in much the same way thatsetuptools doesfor many of thesetup() arguments it adds. See the section below for moredetails.

Adding new EGG-INFO Files

Some extensible applications or frameworks may want to allow third parties todevelop plugins with application or framework-specific metadata included inthe plugins’ EGG-INFO directory, for easy access via thepkg_resourcesmetadata API. The easiest way to allow this is to create an extensionto be used from the plugin projects’ setup scripts (viasetup_requires)that defines a new setup keyword, and then uses that data to write an EGG-INFOfile when theegg_info command is run.

Theegg_info command looks for extension points in anegg_info.writersgroup, and calls them to write the files. Here’s a simple example of anextension defining a setup argumentfoo_bar, which is a list oflines that will be written tofoo_bar.txt in the EGG-INFO directory of anyproject that uses the argument:

# setup.cfg...[options.entry_points]distutils.setup_keywords=foo_bar=setuptools.dist:assert_string_listegg_info.writers=foo_bar.txt=setuptools.command.egg_info:write_arg

This simple example makes use of two utility functions defined by setuptoolsfor its own use: a routine to validate that a setup keyword is a sequence ofstrings, and another one that looks up a setup argument and writes it toa file. Here’s what the writer utility looks like:

defwrite_arg(cmd,basename,filename):argname=os.path.splitext(basename)[0]value=getattr(cmd.distribution,argname,None)ifvalueisnotNone:value="\n".join(value)+"\n"cmd.write_or_delete_file(argname,filename,value)

As you can see,egg_info.writers entry points must be a function takingthree arguments: aegg_info command instance, the basename of the file towrite (e.g.foo_bar.txt), and the actual full filename that should bewritten to.

In general, writer functions should honor the command object’sdry_runsetting when writing files, and uselogging to do any console output.The easiest way to conform to this requirement is to usethecmd object’swrite_file(),delete_file(), andwrite_or_delete_file() methods exclusively for your file operations.See those methods’ docstrings for more details.

Adding Support for Revision Control Systems

If the files you want to include in the source distribution are tracked usingGit, Mercurial or SVN, you can use the following packages to achieve that:

If you would like to create a plugin forsetuptools to find files trackedby another revision control system, you can do so by adding an entry point tothesetuptools.file_finders group. The entry point should be a functionaccepting a single directory name, and should yield all the filenames withinthat directory (and any subdirectories thereof) that are under revisioncontrol.

For example, if you were going to create a plugin for a revision control systemcalled “foobar”, you would write a function something like this:

deffind_files_for_foobar(dirname):...# loop to yield paths that start with `dirname`

And you would register it in a setup script using something like this:

# setup.cfg...[options.entry_points]setuptools.file_finders=foobar=my_foobar_module:find_files_for_foobar

Then, anyone who wants to use your plugin can simply install it, and theirlocal setuptools installation will be able to find the necessary files.

It is not necessary to distribute source control plugins with projects thatsimply use the other source control system, or to specify the plugins insetup_requires. When you create a source distribution with thesdistcommand, setuptools automatically records what files were found in theSOURCES.txt file. That way, recipients of source distributions don’t needto have revision control at all. However, if someone is working on a packageby checking out with that system, they will need the same plugin(s) that theoriginal author is using.

A few important points for writing revision control file finders:

  • Your finder function MUST return relative paths, created by appending to thepassed-in directory name. Absolute paths are NOT allowed, nor are relativepaths that reference a parent directory of the passed-in directory.

  • Your finder function MUST accept an empty string as the directory name,meaning the current directory. You MUST NOT convert this to a dot; justyield relative paths. So, yielding a subdirectory namedsome/dir underthe current directory should NOT be rendered as./some/dir or/somewhere/some/dir, butalways as simplysome/dir

  • Your finder function SHOULD NOT raise any errors, and SHOULD deal gracefullywith the absence of needed programs (i.e., ones belonging to the revisioncontrol system itself. Itmay, however, uselogging.warning() toinform the user of the missing program(s).

Final Remarks

  • To use asetuptools plugin, your users will need to add your package as abuild requirement to their build-system configuration. Please check out ourguides onDependencies Management in Setuptools for more information.

  • Directly callingpythonsetup.py... is considered adeprecated practice.You should not add new commands tosetuptools expecting them to be runvia this interface.

On this page

[8]ページ先頭

©2009-2025 Movatter.jp