3 from typing
import TYPE_CHECKING
, List
, Dict
4 from distutils
.command
.build
import build
as _build
6 from setuptools
import SetuptoolsDeprecationWarning
8 if sys
.version_info
>= (3, 8):
9 from typing
import Protocol
11 from typing_extensions
import Protocol
13 from abc
import ABC
as Protocol
16 _ORIGINAL_SUBCOMMANDS
= {"build_py", "build_clib", "build_ext", "build_scripts"}
20 # copy to avoid sharing the object with parent class
21 sub_commands
= _build
.sub_commands
[:]
23 def get_sub_commands(self
):
24 subcommands
= {cmd
[0] for cmd
in _build
.sub_commands
}
25 if subcommands
- _ORIGINAL_SUBCOMMANDS
:
27 It seems that you are using `distutils.command.build` to add
28 new subcommands. Using `distutils` directly is considered deprecated,
29 please use `setuptools.command.build`.
31 warnings
.warn(msg
, SetuptoolsDeprecationWarning
)
32 self
.sub_commands
= _build
.sub_commands
33 return super().get_sub_commands()
36 class SubCommand(Protocol
):
37 """In order to support editable installations (see :pep:`660`) all
38 build subcommands **SHOULD** implement this protocol. They also **MUST** inherit
39 from ``setuptools.Command``.
41 When creating an :pep:`editable wheel <660>`, ``setuptools`` will try to evaluate
42 custom ``build`` subcommands using the following procedure:
44 1. ``setuptools`` will set the ``editable_mode`` attribute to ``True``
45 2. ``setuptools`` will execute the ``run()`` command.
48 Subcommands **SHOULD** take advantage of ``editable_mode=True`` to adequate
49 its behaviour or perform optimisations.
51 For example, if a subcommand doesn't need to generate an extra file and
52 all it does is to copy a source file into the build directory,
53 ``run()`` **SHOULD** simply "early return".
55 Similarly, if the subcommand creates files that would be placed alongside
56 Python files in the final distribution, during an editable install
57 the command **SHOULD** generate these files "in place" (i.e. write them to
58 the original source directory, instead of using the build directory).
59 Note that ``get_output_mapping()`` should reflect that and include mappings
60 for "in place" builds accordingly.
62 3. ``setuptools`` use any knowledge it can derive from the return values of
63 ``get_outputs()`` and ``get_output_mapping()`` to create an editable wheel.
64 When relevant ``setuptools`` **MAY** attempt to use file links based on the value
65 of ``get_output_mapping()``. Alternatively, ``setuptools`` **MAY** attempt to use
66 :doc:`import hooks <python:reference/import>` to redirect any attempt to import
67 to the directory with the original source code and other files built in place.
69 Please note that custom sub-commands **SHOULD NOT** rely on ``run()`` being
70 executed (or not) to provide correct return values for ``get_outputs()``,
71 ``get_output_mapping()`` or ``get_source_files()``. The ``get_*`` methods should
72 work independently of ``run()``.
75 editable_mode
: bool = False
76 """Boolean flag that will be set to ``True`` when setuptools is used for an
77 editable installation (see :pep:`660`).
78 Implementations **SHOULD** explicitly set the default value of this attribute to
80 When subcommands run, they can use this flag to perform optimizations or change
81 their behaviour accordingly.
85 """String representing the directory where the build artifacts should be stored,
87 For example, if a distribution wants to provide a Python module named ``pkg.mod``,
88 then a corresponding file should be written to ``{build_lib}/package/module.py``.
89 A way of thinking about this is that the files saved under ``build_lib``
90 would be eventually copied to one of the directories in :obj:`site.PREFIXES`
93 A command that produces platform-independent files (e.g. compiling text templates
94 into Python functions), **CAN** initialize ``build_lib`` by copying its value from
95 the ``build_py`` command. On the other hand, a command that produces
96 platform-specific files **CAN** initialize ``build_lib`` by copying its value from
97 the ``build_ext`` command. In general this is done inside the ``finalize_options``
98 method with the help of the ``set_undefined_options`` command::
100 def finalize_options(self):
101 self.set_undefined_options("build_py", ("build_lib", "build_lib"))
105 def initialize_options(self
):
106 """(Required by the original :class:`setuptools.Command` interface)"""
108 def finalize_options(self
):
109 """(Required by the original :class:`setuptools.Command` interface)"""
112 """(Required by the original :class:`setuptools.Command` interface)"""
114 def get_source_files(self
) -> List
[str]:
116 Return a list of all files that are used by the command to create the expected
118 For example, if your build command transpiles Java files into Python, you should
119 list here all the Java files.
120 The primary purpose of this function is to help populating the ``sdist``
121 with all the files necessary to build the distribution.
122 All files should be strings relative to the project root directory.
125 def get_outputs(self
) -> List
[str]:
127 Return a list of files intended for distribution as they would have been
128 produced by the build.
129 These files should be strings in the form of
130 ``"{build_lib}/destination/file/path"``.
133 The return value of ``get_output()`` should include all files used as keys
134 in ``get_output_mapping()`` plus files that are generated during the build
135 and don't correspond to any source file already present in the project.
138 def get_output_mapping(self
) -> Dict
[str, str]:
140 Return a mapping between destination files as they would be produced by the
141 build (dict keys) into the respective existing (source) files (dict values).
142 Existing (source) files should be represented as strings relative to the project
144 Destination files should be strings in the form of
145 ``"{build_lib}/destination/file/path"``.