Skip to content

Commit f9398b1

Browse files
authored
Mark abstract base classes and methods (#4503)
* Mark abstract base classes and methods
1 parent ab8290c commit f9398b1

5 files changed

Lines changed: 49 additions & 30 deletions

File tree

newsfragments/4503.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Mark abstract base classes and methods with `abc.ABC` and `abc.abstractmethod` -- by :user:`Avasam`

pkg_resources/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from __future__ import annotations
2424

25+
from abc import ABC
2526
import sys
2627

2728
if sys.version_info < (3, 8): # noqa: UP036 # Check for unsupported versions
@@ -311,7 +312,7 @@ def get_supported_platform():
311312
]
312313

313314

314-
class ResolutionError(Exception):
315+
class ResolutionError(Exception, ABC):
315316
"""Abstract base for dependency resolution errors"""
316317

317318
def __repr__(self):

setuptools/__init__.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
from abc import ABC, abstractmethod
56
import functools
67
import os
78
import re
@@ -119,7 +120,7 @@ def setup(**attrs):
119120
_Command = monkey.get_unpatched(distutils.core.Command)
120121

121122

122-
class Command(_Command):
123+
class Command(_Command, ABC):
123124
"""
124125
Setuptools internal actions are organized using a *command design pattern*.
125126
This means that each action (or group of closely related actions) executed during
@@ -132,42 +133,25 @@ class Command(_Command):
132133
When creating a new command from scratch, custom defined classes **SHOULD** inherit
133134
from ``setuptools.Command`` and implement a few mandatory methods.
134135
Between these mandatory methods, are listed:
135-
136-
.. method:: initialize_options(self)
137-
138-
Set or (reset) all options/attributes/caches used by the command
139-
to their default values. Note that these values may be overwritten during
140-
the build.
141-
142-
.. method:: finalize_options(self)
143-
144-
Set final values for all options/attributes used by the command.
145-
Most of the time, each option/attribute/cache should only be set if it does not
146-
have any value yet (e.g. ``if self.attr is None: self.attr = val``).
147-
148-
.. method:: run(self)
149-
150-
Execute the actions intended by the command.
151-
(Side effects **SHOULD** only take place when ``run`` is executed,
152-
for example, creating new files or writing to the terminal output).
136+
:meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`.
153137
154138
A useful analogy for command classes is to think of them as subroutines with local
155-
variables called "options". The options are "declared" in ``initialize_options()``
156-
and "defined" (given their final values, aka "finalized") in ``finalize_options()``,
139+
variables called "options". The options are "declared" in :meth:`initialize_options`
140+
and "defined" (given their final values, aka "finalized") in :meth:`finalize_options`,
157141
both of which must be defined by every command class. The "body" of the subroutine,
158-
(where it does all the work) is the ``run()`` method.
159-
Between ``initialize_options()`` and ``finalize_options()``, ``setuptools`` may set
142+
(where it does all the work) is the :meth:`run` method.
143+
Between :meth:`initialize_options` and :meth:`finalize_options`, ``setuptools`` may set
160144
the values for options/attributes based on user's input (or circumstance),
161145
which means that the implementation should be careful to not overwrite values in
162-
``finalize_options`` unless necessary.
146+
:meth:`finalize_options` unless necessary.
163147
164148
Please note that other commands (or other parts of setuptools) may also overwrite
165149
the values of the command's options/attributes multiple times during the build
166150
process.
167-
Therefore it is important to consistently implement ``initialize_options()`` and
168-
``finalize_options()``. For example, all derived attributes (or attributes that
151+
Therefore it is important to consistently implement :meth:`initialize_options` and
152+
:meth:`finalize_options`. For example, all derived attributes (or attributes that
169153
depend on the value of other attributes) **SHOULD** be recomputed in
170-
``finalize_options``.
154+
:meth:`finalize_options`.
171155
172156
When overwriting existing commands, custom defined classes **MUST** abide by the
173157
same APIs implemented by the original class. They also **SHOULD** inherit from the
@@ -238,6 +222,33 @@ def reinitialize_command(
238222
vars(cmd).update(kw)
239223
return cmd
240224

225+
@abstractmethod
226+
def initialize_options(self) -> None:
227+
"""
228+
Set or (reset) all options/attributes/caches used by the command
229+
to their default values. Note that these values may be overwritten during
230+
the build.
231+
"""
232+
raise NotImplementedError
233+
234+
@abstractmethod
235+
def finalize_options(self) -> None:
236+
"""
237+
Set final values for all options/attributes used by the command.
238+
Most of the time, each option/attribute/cache should only be set if it does not
239+
have any value yet (e.g. ``if self.attr is None: self.attr = val``).
240+
"""
241+
raise NotImplementedError
242+
243+
@abstractmethod
244+
def run(self) -> None:
245+
"""
246+
Execute the actions intended by the command.
247+
(Side effects **SHOULD** only take place when :meth:`run` is executed,
248+
for example, creating new files or writing to the terminal output).
249+
"""
250+
raise NotImplementedError
251+
241252

242253
def _find_all_simple(path):
243254
"""

setuptools/command/setopt.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from abc import ABC, abstractmethod
12
from distutils.util import convert_path
23
from distutils import log
34
from distutils.errors import DistutilsOptionError
@@ -68,7 +69,7 @@ def edit_config(filename, settings, dry_run=False):
6869
opts.write(f)
6970

7071

71-
class option_base(Command):
72+
class option_base(Command, ABC):
7273
"""Abstract base class for commands that mess with config files"""
7374

7475
user_options = [
@@ -103,6 +104,10 @@ def finalize_options(self):
103104
)
104105
(self.filename,) = filenames
105106

107+
@abstractmethod
108+
def run(self) -> None:
109+
raise NotImplementedError
110+
106111

107112
class setopt(option_base):
108113
"""Save command-line options to a file"""

setuptools/sandbox.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from abc import ABC
34
import os
45
import sys
56
import tempfile
@@ -265,7 +266,7 @@ def run_setup(setup_script, args):
265266
# Normal exit, just return
266267

267268

268-
class AbstractSandbox:
269+
class AbstractSandbox(ABC):
269270
"""Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
270271

271272
_active = False

0 commit comments

Comments
 (0)