|
6 | 6 | from __future__ import annotations |
7 | 7 |
|
8 | 8 | import os |
9 | | -import warnings |
10 | 9 | from collections.abc import Iterable |
| 10 | +from dataclasses import field, fields |
| 11 | + |
| 12 | +from ._dataclass import lenient_dataclass |
11 | 13 |
|
12 | 14 | # This class is really only used by the "build_ext" command, so it might |
13 | 15 | # make sense to put it in distutils.command.build_ext. However, that |
|
20 | 22 | # order to do anything. |
21 | 23 |
|
22 | 24 |
|
| 25 | +@lenient_dataclass() |
23 | 26 | class Extension: |
24 | 27 | """Just a collection of attributes that describes an extension |
25 | 28 | module and everything needed to build it (hopefully in a portable |
26 | 29 | way, but there are hooks that let you be as unportable as you need). |
| 30 | + """ |
| 31 | + |
| 32 | + name: str |
| 33 | + """ |
| 34 | + the full name of the extension, including any packages -- ie. |
| 35 | + *not* a filename or pathname, but Python dotted name |
| 36 | + """ |
| 37 | + |
| 38 | + sources: Iterable[str | os.PathLike[str]] |
| 39 | + """ |
| 40 | + iterable of source filenames (except strings, which could be misinterpreted |
| 41 | + as a single filename), relative to the distribution root (where the setup |
| 42 | + script lives), in Unix form (slash-separated) for portability. Can be any |
| 43 | + non-string iterable (list, tuple, set, etc.) containing strings or |
| 44 | + PathLike objects. Source files may be C, C++, SWIG (.i), platform-specific |
| 45 | + resource files, or whatever else is recognized by the "build_ext" command |
| 46 | + as source for a Python extension. |
| 47 | + """ |
| 48 | + |
| 49 | + include_dirs: list[str] = field(default_factory=list) |
| 50 | + """ |
| 51 | + list of directories to search for C/C++ header files (in Unix |
| 52 | + form for portability) |
| 53 | + """ |
27 | 54 |
|
28 | | - Instance attributes: |
29 | | - name : string |
30 | | - the full name of the extension, including any packages -- ie. |
31 | | - *not* a filename or pathname, but Python dotted name |
32 | | - sources : Iterable[string | os.PathLike] |
33 | | - iterable of source filenames (except strings, which could be misinterpreted |
34 | | - as a single filename), relative to the distribution root (where the setup |
35 | | - script lives), in Unix form (slash-separated) for portability. Can be any |
36 | | - non-string iterable (list, tuple, set, etc.) containing strings or |
37 | | - PathLike objects. Source files may be C, C++, SWIG (.i), platform-specific |
38 | | - resource files, or whatever else is recognized by the "build_ext" command |
39 | | - as source for a Python extension. |
40 | | - include_dirs : [string] |
41 | | - list of directories to search for C/C++ header files (in Unix |
42 | | - form for portability) |
43 | | - define_macros : [(name : string, value : string|None)] |
44 | | - list of macros to define; each macro is defined using a 2-tuple, |
45 | | - where 'value' is either the string to define it to or None to |
46 | | - define it without a particular value (equivalent of "#define |
47 | | - FOO" in source or -DFOO on Unix C compiler command line) |
48 | | - undef_macros : [string] |
49 | | - list of macros to undefine explicitly |
50 | | - library_dirs : [string] |
51 | | - list of directories to search for C/C++ libraries at link time |
52 | | - libraries : [string] |
53 | | - list of library names (not filenames or paths) to link against |
54 | | - runtime_library_dirs : [string] |
55 | | - list of directories to search for C/C++ libraries at run time |
56 | | - (for shared extensions, this is when the extension is loaded) |
57 | | - extra_objects : [string] |
58 | | - list of extra files to link with (eg. object files not implied |
59 | | - by 'sources', static library that must be explicitly specified, |
60 | | - binary resource files, etc.) |
61 | | - extra_compile_args : [string] |
62 | | - any extra platform- and compiler-specific information to use |
63 | | - when compiling the source files in 'sources'. For platforms and |
64 | | - compilers where "command line" makes sense, this is typically a |
65 | | - list of command-line arguments, but for other platforms it could |
66 | | - be anything. |
67 | | - extra_link_args : [string] |
68 | | - any extra platform- and compiler-specific information to use |
69 | | - when linking object files together to create the extension (or |
70 | | - to create a new static Python interpreter). Similar |
71 | | - interpretation as for 'extra_compile_args'. |
72 | | - export_symbols : [string] |
73 | | - list of symbols to be exported from a shared extension. Not |
74 | | - used on all platforms, and not generally necessary for Python |
75 | | - extensions, which typically export exactly one symbol: "init" + |
76 | | - extension_name. |
77 | | - swig_opts : [string] |
78 | | - any extra options to pass to SWIG if a source file has the .i |
79 | | - extension. |
80 | | - depends : [string] |
81 | | - list of files that the extension depends on |
82 | | - language : string |
83 | | - extension language (i.e. "c", "c++", "objc"). Will be detected |
84 | | - from the source extensions if not provided. |
85 | | - optional : boolean |
86 | | - specifies that a build failure in the extension should not abort the |
87 | | - build process, but simply not install the failing extension. |
| 55 | + define_macros: list[tuple[str, str | None]] = field(default_factory=list) |
| 56 | + """ |
| 57 | + list of macros to define; each macro is defined using a 2-tuple, |
| 58 | + where 'value' is either the string to define it to or None to |
| 59 | + define it without a particular value (equivalent of "#define |
| 60 | + FOO" in source or -DFOO on Unix C compiler command line) |
| 61 | + """ |
| 62 | + |
| 63 | + undef_macros: list[str] = field(default_factory=list) |
| 64 | + """list of macros to undefine explicitly""" |
| 65 | + |
| 66 | + library_dirs: list[str] = field(default_factory=list) |
| 67 | + """list of directories to search for C/C++ libraries at link time""" |
| 68 | + |
| 69 | + libraries: list[str] = field(default_factory=list) |
| 70 | + """list of library names (not filenames or paths) to link against""" |
| 71 | + |
| 72 | + runtime_library_dirs: list[str] = field(default_factory=list) |
| 73 | + """ |
| 74 | + list of directories to search for C/C++ libraries at run time |
| 75 | + (for shared extensions, this is when the extension is loaded) |
| 76 | + """ |
| 77 | + |
| 78 | + extra_objects: list[str] = field(default_factory=list) |
| 79 | + """ |
| 80 | + list of extra files to link with (eg. object files not implied |
| 81 | + by 'sources', static library that must be explicitly specified, |
| 82 | + binary resource files, etc.) |
88 | 83 | """ |
89 | 84 |
|
90 | | - # When adding arguments to this constructor, be sure to update |
91 | | - # setup_keywords in core.py. |
92 | | - def __init__( |
93 | | - self, |
94 | | - name: str, |
95 | | - sources: Iterable[str | os.PathLike[str]], |
96 | | - include_dirs: list[str] | None = None, |
97 | | - define_macros: list[tuple[str, str | None]] | None = None, |
98 | | - undef_macros: list[str] | None = None, |
99 | | - library_dirs: list[str] | None = None, |
100 | | - libraries: list[str] | None = None, |
101 | | - runtime_library_dirs: list[str] | None = None, |
102 | | - extra_objects: list[str] | None = None, |
103 | | - extra_compile_args: list[str] | None = None, |
104 | | - extra_link_args: list[str] | None = None, |
105 | | - export_symbols: list[str] | None = None, |
106 | | - swig_opts: list[str] | None = None, |
107 | | - depends: list[str] | None = None, |
108 | | - language: str | None = None, |
109 | | - optional: bool | None = None, |
110 | | - **kw, # To catch unknown keywords |
111 | | - ): |
112 | | - if not isinstance(name, str): |
| 85 | + extra_compile_args: list[str] = field(default_factory=list) |
| 86 | + """ |
| 87 | + any extra platform- and compiler-specific information to use |
| 88 | + when compiling the source files in 'sources'. For platforms and |
| 89 | + compilers where "command line" makes sense, this is typically a |
| 90 | + list of command-line arguments, but for other platforms it could |
| 91 | + be anything. |
| 92 | + """ |
| 93 | + |
| 94 | + extra_link_args: list[str] = field(default_factory=list) |
| 95 | + """ |
| 96 | + any extra platform- and compiler-specific information to use |
| 97 | + when linking object files together to create the extension (or |
| 98 | + to create a new static Python interpreter). Similar |
| 99 | + interpretation as for 'extra_compile_args'. |
| 100 | + """ |
| 101 | + |
| 102 | + export_symbols: list[str] = field(default_factory=list) |
| 103 | + """ |
| 104 | + list of symbols to be exported from a shared extension. Not |
| 105 | + used on all platforms, and not generally necessary for Python |
| 106 | + extensions, which typically export exactly one symbol: "init" + |
| 107 | + extension_name. |
| 108 | + """ |
| 109 | + |
| 110 | + swig_opts: list[str] = field(default_factory=list) |
| 111 | + """ |
| 112 | + any extra options to pass to SWIG if a source file has the .i |
| 113 | + extension. |
| 114 | + """ |
| 115 | + |
| 116 | + depends: list[str] = field(default_factory=list) |
| 117 | + """list of files that the extension depends on""" |
| 118 | + |
| 119 | + language: str | None = None |
| 120 | + """ |
| 121 | + extension language (i.e. "c", "c++", "objc"). Will be detected |
| 122 | + from the source extensions if not provided. |
| 123 | + """ |
| 124 | + |
| 125 | + optional: bool = False |
| 126 | + """ |
| 127 | + specifies that a build failure in the extension should not abort the |
| 128 | + build process, but simply not install the failing extension. |
| 129 | + """ |
| 130 | + |
| 131 | + def __post_init__(self): |
| 132 | + if not isinstance(self.name, str): |
113 | 133 | raise TypeError("'name' must be a string") |
114 | 134 |
|
115 | 135 | # handle the string case first; since strings are iterable, disallow them |
116 | | - if isinstance(sources, str): |
| 136 | + if isinstance(self.sources, str): |
117 | 137 | raise TypeError( |
118 | 138 | "'sources' must be an iterable of strings or PathLike objects, not a string" |
119 | 139 | ) |
120 | 140 |
|
121 | 141 | # now we check if it's iterable and contains valid types |
122 | 142 | try: |
123 | | - self.sources = list(map(os.fspath, sources)) |
| 143 | + self.sources = list(map(os.fspath, self.sources)) |
124 | 144 | except TypeError: |
125 | 145 | raise TypeError( |
126 | 146 | "'sources' must be an iterable of strings or PathLike objects" |
127 | 147 | ) |
128 | 148 |
|
129 | | - self.name = name |
130 | | - self.include_dirs = include_dirs or [] |
131 | | - self.define_macros = define_macros or [] |
132 | | - self.undef_macros = undef_macros or [] |
133 | | - self.library_dirs = library_dirs or [] |
134 | | - self.libraries = libraries or [] |
135 | | - self.runtime_library_dirs = runtime_library_dirs or [] |
136 | | - self.extra_objects = extra_objects or [] |
137 | | - self.extra_compile_args = extra_compile_args or [] |
138 | | - self.extra_link_args = extra_link_args or [] |
139 | | - self.export_symbols = export_symbols or [] |
140 | | - self.swig_opts = swig_opts or [] |
141 | | - self.depends = depends or [] |
142 | | - self.language = language |
143 | | - self.optional = optional |
144 | | - |
145 | | - # If there are unknown keyword options, warn about them |
146 | | - if len(kw) > 0: |
147 | | - options = ', '.join(sorted([repr(option) for option in kw])) |
148 | | - msg = f"Unknown Extension options: {options}" |
149 | | - warnings.warn(msg) |
150 | | - |
151 | | - def __repr__(self): |
152 | | - return f'<{self.__class__.__module__}.{self.__class__.__qualname__}({self.name!r}) at {id(self):#x}>' |
| 149 | + |
| 150 | +_safe = tuple(f.name for f in fields(Extension)) |
| 151 | +"""Legal keyword arguments for the Extension constructor.""" |
153 | 152 |
|
154 | 153 |
|
155 | 154 | def read_setup_file(filename): # noqa: C901 |
|
0 commit comments