0
# Core Classes
1
2
Essential setuptools classes that provide the foundation for package building, extension compilation, and command execution. These classes extend distutils functionality with enhanced capabilities for modern Python packaging.
3
4
## Capabilities
5
6
### Distribution Class
7
8
The Distribution class is the enhanced version of distutils.Distribution that handles setuptools-specific metadata and configuration.
9
10
```python { .api }
11
class Distribution:
12
"""
13
Enhanced distribution class that extends distutils.Distribution.
14
15
This class handles package metadata, command execution, and configuration
16
parsing with setuptools-specific enhancements.
17
18
Key Attributes:
19
- metadata: Package metadata
20
- packages: List of packages to include
21
- py_modules: List of Python modules
22
- ext_modules: List of C/C++ extensions
23
- scripts: List of script files
24
- data_files: List of data files
25
- cmdclass: Dictionary of command classes
26
27
Key Methods:
28
"""
29
30
def __init__(self, attrs=None):
31
"""
32
Initialize Distribution with package attributes.
33
34
Parameters:
35
- attrs (dict): Package attributes and metadata
36
"""
37
38
def parse_config_files(self, filenames=None):
39
"""
40
Parse setup.cfg and other configuration files.
41
42
Parameters:
43
- filenames (list): Configuration files to parse
44
45
Returns:
46
None
47
"""
48
49
def get_command_class(self, command):
50
"""
51
Get command class by name.
52
53
Parameters:
54
- command (str): Command name (e.g., 'build', 'install')
55
56
Returns:
57
type: Command class
58
"""
59
60
def get_command_obj(self, command, create=1):
61
"""
62
Get command instance, creating if necessary.
63
64
Parameters:
65
- command (str): Command name
66
- create (int): Whether to create if not exists (default: 1)
67
68
Returns:
69
Command: Command instance
70
"""
71
72
def run_command(self, command):
73
"""
74
Execute a command by name.
75
76
Parameters:
77
- command (str): Command name to run
78
79
Returns:
80
None
81
"""
82
83
def run_commands(self):
84
"""Run all commands in command line order."""
85
86
def get_command_list(self):
87
"""Get list of available commands."""
88
89
def finalize_options(self):
90
"""Finalize and validate all options."""
91
```
92
93
### Command Base Class
94
95
The Command class is the abstract base class for all setuptools commands, providing the interface and common functionality.
96
97
```python { .api }
98
class Command:
99
"""
100
Abstract base class for all setuptools commands.
101
102
All setuptools commands must inherit from this class and implement
103
the three required methods: initialize_options, finalize_options, and run.
104
105
Attributes:
106
- distribution: Reference to Distribution instance
107
- _dry_run: Whether to perform actual operations
108
- verbose: Verbosity level
109
- force: Whether to force operations
110
"""
111
112
def __init__(self, dist):
113
"""
114
Initialize command with distribution reference.
115
116
Parameters:
117
- dist (Distribution): Distribution instance
118
"""
119
120
def initialize_options(self):
121
"""
122
Set default values for all command options.
123
124
This method must be implemented by subclasses to set default
125
values for all options that the command supports.
126
"""
127
128
def finalize_options(self):
129
"""
130
Validate and process command options.
131
132
This method must be implemented by subclasses to validate
133
option values and perform any necessary processing.
134
"""
135
136
def run(self):
137
"""
138
Execute the command.
139
140
This method must be implemented by subclasses to perform
141
the actual command operation.
142
"""
143
144
def announce(self, msg, level=1):
145
"""
146
Announce a message at given verbosity level.
147
148
Parameters:
149
- msg (str): Message to announce
150
- level (int): Verbosity level (default: 1)
151
"""
152
153
def debug_print(self, msg):
154
"""Print debug message if in debug mode."""
155
156
def ensure_dirname(self, option):
157
"""Ensure an option value is a directory name."""
158
159
def ensure_filename(self, option):
160
"""Ensure an option value is a filename."""
161
162
def ensure_string(self, option, default=None):
163
"""Ensure an option value is a string."""
164
165
def ensure_string_list(self, option):
166
"""Ensure an option value is a list of strings."""
167
168
def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1):
169
"""Copy a file with optional preservation of metadata."""
170
171
def copy_tree(self, indir, outdir, preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1):
172
"""Copy an entire directory tree."""
173
174
def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1):
175
"""Make a file by calling a function if inputs are newer than output."""
176
177
def spawn(self, cmd, search_path=1, level=1):
178
"""Spawn a subprocess command."""
179
180
def mkpath(self, name, mode=0o777):
181
"""Create a directory path."""
182
183
def execute(self, func, args, msg=None, level=1):
184
"""Execute a function with optional message."""
185
```
186
187
### Extension Class
188
189
The Extension class describes C/C++ extension modules to be built.
190
191
```python { .api }
192
class Extension:
193
"""
194
Enhanced C/C++ extension module descriptor.
195
196
Extends distutils.Extension with additional features like Cython support,
197
limited API support, and enhanced configuration options.
198
199
Attributes:
200
- name: Extension module name
201
- sources: List of source files
202
- include_dirs: Include directories
203
- define_macros: Preprocessor macros
204
- undef_macros: Macros to undefine
205
- library_dirs: Library search directories
206
- libraries: Libraries to link against
207
- runtime_library_dirs: Runtime library directories
208
- extra_objects: Additional object files
209
- extra_compile_args: Extra compiler arguments
210
- extra_link_args: Extra linker arguments
211
- export_symbols: Symbols to export (Windows)
212
- swig_opts: SWIG options
213
- depends: Dependency files
214
- language: Programming language ('c' or 'c++')
215
- optional: Whether extension is optional
216
- py_limited_api: Whether to use stable ABI
217
"""
218
219
def __init__(self, name, sources, include_dirs=None, define_macros=None,
220
undef_macros=None, library_dirs=None, libraries=None,
221
runtime_library_dirs=None, extra_objects=None,
222
extra_compile_args=None, extra_link_args=None,
223
export_symbols=None, swig_opts=None, depends=None,
224
language=None, optional=None, py_limited_api=False, **kw):
225
"""
226
Initialize Extension with configuration.
227
228
Parameters:
229
- name (str): Fully qualified extension name (e.g., 'package.module')
230
- sources (list): List of source file paths
231
- include_dirs (list): Directories to search for header files
232
- define_macros (list): Macros to define (list of tuples)
233
- undef_macros (list): Macros to undefine
234
- library_dirs (list): Directories to search for libraries
235
- libraries (list): Libraries to link against
236
- runtime_library_dirs (list): Runtime library search directories
237
- extra_objects (list): Additional object files to link
238
- extra_compile_args (list): Additional compiler arguments
239
- extra_link_args (list): Additional linker arguments
240
- export_symbols (list): Symbols to export on Windows
241
- swig_opts (list): Options for SWIG
242
- depends (list): Files that extension depends on
243
- language (str): Programming language ('c' or 'c++')
244
- optional (bool): Whether extension is optional
245
- py_limited_api (bool): Whether to use Python stable ABI
246
"""
247
```
248
249
### Library Class
250
251
The Library class describes C/C++ libraries to be built (similar to Extension but for libraries).
252
253
```python { .api }
254
class Library:
255
"""
256
C/C++ library descriptor.
257
258
Like Extension but for building libraries instead of extension modules.
259
Libraries are built and can be used by extension modules.
260
261
Attributes: Same as Extension
262
"""
263
264
def __init__(self, name, sources, **kw):
265
"""
266
Initialize Library with configuration.
267
268
Parameters: Same as Extension.__init__
269
"""
270
```
271
272
### Package Discovery Classes
273
274
Classes that provide automatic discovery of Python packages and modules within a project directory structure.
275
276
```python { .api }
277
class PackageFinder:
278
"""
279
Standard package discovery for traditional package layouts.
280
281
Finds regular Python packages (directories with __init__.py files).
282
"""
283
284
@classmethod
285
def find(cls, where='.', exclude=(), include=('*',)):
286
"""
287
Find all packages in the given directory.
288
289
Parameters:
290
- where (str): Root directory to search (default: '.')
291
- exclude (tuple): Glob patterns for packages to exclude
292
- include (tuple): Glob patterns for packages to include (default: ('*',))
293
294
Returns:
295
list[str]: List of discovered package names
296
"""
297
298
class PEP420PackageFinder(PackageFinder):
299
"""
300
Discovery for PEP 420 implicit namespace packages.
301
302
Finds both regular packages and namespace packages (directories without __init__.py).
303
"""
304
305
@classmethod
306
def find(cls, where='.', exclude=(), include=('*',)):
307
"""
308
Find all packages including PEP 420 namespace packages.
309
310
Parameters:
311
- where (str): Root directory to search (default: '.')
312
- exclude (tuple): Glob patterns for packages to exclude
313
- include (tuple): Glob patterns for packages to include (default: ('*',))
314
315
Returns:
316
list[str]: List of discovered package names (including namespace packages)
317
"""
318
319
class ModuleFinder:
320
"""
321
Discovery for standalone Python modules.
322
323
Finds individual .py files that should be included as modules.
324
"""
325
326
@classmethod
327
def find(cls, where='.', exclude=(), include=('*',)):
328
"""
329
Find all Python modules in the given directory.
330
331
Parameters:
332
- where (str): Root directory to search (default: '.')
333
- exclude (tuple): Glob patterns for modules to exclude
334
- include (tuple): Glob patterns for modules to include (default: ('*',))
335
336
Returns:
337
list[str]: List of discovered module names
338
"""
339
340
class FlatLayoutPackageFinder(PEP420PackageFinder):
341
"""
342
Package discovery for flat-layout projects.
343
344
Specialized finder for projects that don't use src-layout but have packages
345
directly under the project root.
346
"""
347
348
class FlatLayoutModuleFinder(ModuleFinder):
349
"""
350
Module discovery for flat-layout projects.
351
352
Specialized finder for discovering standalone modules in flat-layout projects.
353
"""
354
355
class ConfigDiscovery:
356
"""
357
Automatic configuration discovery for setuptools.
358
359
Provides automatic discovery of package configuration including packages,
360
modules, entry points, and other metadata from project structure.
361
"""
362
363
def __init__(self, distribution=None):
364
"""
365
Initialize configuration discovery.
366
367
Parameters:
368
- distribution (Distribution, optional): Distribution instance
369
"""
370
371
def __call__(self, dist=None):
372
"""
373
Perform automatic discovery and apply to distribution.
374
375
Parameters:
376
- dist (Distribution, optional): Distribution to configure
377
378
Returns:
379
dict: Discovered configuration
380
"""
381
```
382
383
## Usage Examples
384
385
### Custom Command Class
386
387
```python
388
from setuptools import setup, Command
389
import subprocess
390
391
class CustomCommand(Command):
392
"""Custom command example."""
393
394
description = 'Run custom command'
395
user_options = [
396
('option=', 'o', 'Custom option'),
397
]
398
399
def initialize_options(self):
400
"""Set default option values."""
401
self.option = None
402
403
def finalize_options(self):
404
"""Validate and process options."""
405
if self.option is None:
406
self.option = 'default_value'
407
408
def run(self):
409
"""Execute the custom command."""
410
self.announce(f'Running custom command with option: {self.option}')
411
# Custom command logic here
412
subprocess.run(['echo', f'Custom option: {self.option}'])
413
414
setup(
415
name='my-package',
416
cmdclass={
417
'custom': CustomCommand,
418
},
419
)
420
```
421
422
### C Extension with Cython
423
424
```python
425
from setuptools import setup, Extension
426
from Cython.Build import cythonize
427
428
extensions = [
429
Extension(
430
'my_package.fast_module',
431
sources=['src/fast_module.pyx'], # Cython source
432
include_dirs=['src/include'],
433
libraries=['m'],
434
extra_compile_args=['-O3'],
435
language='c++',
436
py_limited_api=True, # Use stable ABI
437
)
438
]
439
440
setup(
441
name='my-package',
442
ext_modules=cythonize(extensions),
443
)
444
```
445
446
### Complex Extension Configuration
447
448
```python
449
from setuptools import setup, Extension
450
import numpy
451
452
ext_modules = [
453
Extension(
454
'my_package.numerical',
455
sources=[
456
'src/numerical.c',
457
'src/utils.c',
458
],
459
include_dirs=[
460
'src/include',
461
numpy.get_include(), # NumPy headers
462
],
463
library_dirs=['/usr/local/lib'],
464
libraries=['blas', 'lapack'],
465
define_macros=[
466
('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION'),
467
('VERSION', '"1.0.0"'),
468
],
469
extra_compile_args=['-O3', '-fopenmp'],
470
extra_link_args=['-fopenmp'],
471
depends=['src/numerical.h', 'src/utils.h'],
472
optional=False, # Required extension
473
)
474
]
475
476
setup(
477
name='numerical-package',
478
ext_modules=ext_modules,
479
zip_safe=False,
480
)
481
```
482
483
### Distribution Subclass
484
485
```python
486
from setuptools import setup
487
from setuptools.dist import Distribution
488
489
class CustomDistribution(Distribution):
490
"""Custom distribution class."""
491
492
def __init__(self, attrs=None):
493
# Custom initialization
494
super().__init__(attrs)
495
# Add custom behavior
496
497
def run_commands(self):
498
"""Override command execution."""
499
self.announce('Running custom distribution')
500
super().run_commands()
501
502
setup(
503
name='my-package',
504
distclass=CustomDistribution,
505
)
506
```
507
508
### Library Building
509
510
```python
511
from setuptools import setup
512
from setuptools.extension import Library, Extension
513
514
libraries = [
515
Library(
516
'myutils',
517
sources=['src/utils.c', 'src/helpers.c'],
518
include_dirs=['src/include'],
519
)
520
]
521
522
extensions = [
523
Extension(
524
'my_package.core',
525
sources=['src/core.c'],
526
libraries=['myutils'], # Link against our library
527
library_dirs=['.'],
528
)
529
]
530
531
setup(
532
name='my-package',
533
libraries=libraries,
534
ext_modules=extensions,
535
)
536
```