Skip to content

Commit

Permalink
Merge pull request #311 from agriyakhetarpal/types/extension-sources
Browse files Browse the repository at this point in the history
Accept any non-string iterable for `distutils.Extension`'s sources
  • Loading branch information
jaraco authored Dec 27, 2024
2 parents 7a9bbd5 + efeb97c commit b0aea96
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 18 deletions.
36 changes: 22 additions & 14 deletions distutils/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ class Extension:
name : string
the full name of the extension, including any packages -- ie.
*not* a filename or pathname, but Python dotted name
sources : [string | os.PathLike]
list of source filenames, relative to the distribution root
(where the setup script lives), in Unix form (slash-separated)
for portability. Source files may be C, C++, SWIG (.i),
platform-specific resource files, or whatever else is recognized
by the "build_ext" command as source for a Python extension.
sources : Iterable[string | os.PathLike]
iterable of source filenames (except strings, which could be misinterpreted
as a single filename), relative to the distribution root (where the setup
script lives), in Unix form (slash-separated) for portability. Can be any
non-string iterable (list, tuple, set, etc.) containing strings or
PathLike objects. Source files may be C, C++, SWIG (.i), platform-specific
resource files, or whatever else is recognized by the "build_ext" command
as source for a Python extension.
include_dirs : [string]
list of directories to search for C/C++ header files (in Unix
form for portability)
Expand Down Expand Up @@ -105,17 +107,23 @@ def __init__(
**kw, # To catch unknown keywords
):
if not isinstance(name, str):
raise AssertionError("'name' must be a string") # noqa: TRY004
if not (
isinstance(sources, list)
and all(isinstance(v, (str, os.PathLike)) for v in sources)
):
raise AssertionError(
"'sources' must be a list of strings or PathLike objects."
raise TypeError("'name' must be a string") # noqa: TRY004

# handle the string case first; since strings are iterable, disallow them
if isinstance(sources, str):
raise TypeError(
"'sources' must be an iterable of strings or PathLike objects, not a string"
)

# now we check if it's iterable and contains valid types
try:
self.sources = list(map(os.fspath, sources))
except TypeError:
raise TypeError(
"'sources' must be an iterable of strings or PathLike objects"
)

self.name = name
self.sources = list(map(os.fspath, sources))
self.include_dirs = include_dirs or []
self.define_macros = define_macros or []
self.undef_macros = undef_macros or []
Expand Down
18 changes: 14 additions & 4 deletions distutils/tests/test_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,32 @@ def test_read_setup_file(self):

def test_extension_init(self):
# the first argument, which is the name, must be a string
with pytest.raises(AssertionError):
with pytest.raises(TypeError):
Extension(1, [])
ext = Extension('name', [])
assert ext.name == 'name'

# the second argument, which is the list of files, must
# be a list of strings or PathLike objects
with pytest.raises(AssertionError):
# be an iterable of strings or PathLike objects, and not a string
with pytest.raises(TypeError):
Extension('name', 'file')
with pytest.raises(AssertionError):
with pytest.raises(TypeError):
Extension('name', ['file', 1])
ext = Extension('name', ['file1', 'file2'])
assert ext.sources == ['file1', 'file2']
ext = Extension('name', [pathlib.Path('file1'), pathlib.Path('file2')])
assert ext.sources == ['file1', 'file2']

# any non-string iterable of strings or PathLike objects should work
ext = Extension('name', ('file1', 'file2')) # tuple
assert ext.sources == ['file1', 'file2']
ext = Extension('name', {'file1', 'file2'}) # set
assert sorted(ext.sources) == ['file1', 'file2']
ext = Extension('name', iter(['file1', 'file2'])) # iterator
assert ext.sources == ['file1', 'file2']
ext = Extension('name', [pathlib.Path('file1'), 'file2']) # mixed types
assert ext.sources == ['file1', 'file2']

# others arguments have defaults
for attr in (
'include_dirs',
Expand Down

0 comments on commit b0aea96

Please sign in to comment.