Skip to content

Add optional Distribution parameter to walk_revctrl file finder #5056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setuptools/command/egg_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ def add_defaults(self) -> None:
sdist.add_defaults(self)
self.filelist.append(self.template)
self.filelist.append(self.manifest)
rcfiles = list(walk_revctrl())
rcfiles = list(walk_revctrl(distribution=self.distribution))
if rcfiles:
self.filelist.extend(rcfiles)
elif os.path.exists(self.manifest):
Expand Down
42 changes: 40 additions & 2 deletions setuptools/command/sdist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import contextlib
import inspect
import os
import re
from collections.abc import Iterator
Expand All @@ -17,10 +18,47 @@
_default_revctrl = list


def walk_revctrl(dirname='') -> Iterator:
def _call_finder_with_distribution_support(finder_func, dirname='', distribution=None):
"""
Call a file finder function with distribution support if available.

This helper function inspects the finder function's signature to determine
if it accepts a distribution parameter. If it does, the distribution is passed;
otherwise, only the dirname is passed for backward compatibility.

Args:
finder_func: The file finder function to call
dirname: Directory name to search (default: '')
distribution: Distribution object to pass if supported (default: None)

Returns:
Iterator of file paths from the finder function

Raises:
Any exception raised by the finder function itself
"""
try:
sig = inspect.signature(finder_func)
params = list(sig.parameters.keys())

# If function accepts distribution parameter, pass it
if len(params) > 1 and 'distribution' in params:
return finder_func(dirname, distribution=distribution)
else:
# Fall back to dirname-only for backward compatibility
return finder_func(dirname)
except (ValueError, TypeError):
# If signature inspection fails, fall back to dirname-only
return finder_func(dirname)


def walk_revctrl(dirname='', distribution=None) -> Iterator:
"""Find all files under revision control"""
for ep in metadata.entry_points(group='setuptools.file_finders'):
yield from ep.load()(dirname)
finder_func = ep.load()
yield from _call_finder_with_distribution_support(
finder_func, dirname, distribution
)


class sdist(orig.sdist):
Expand Down
108 changes: 107 additions & 1 deletion setuptools/tests/test_sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from setuptools import Command, SetuptoolsDeprecationWarning
from setuptools._importlib import metadata
from setuptools.command.egg_info import manifest_maker
from setuptools.command.sdist import sdist
from setuptools.command.sdist import _call_finder_with_distribution_support, sdist
from setuptools.dist import Distribution
from setuptools.extension import Extension
from setuptools.tests import fail_on_ascii
Expand Down Expand Up @@ -982,3 +982,109 @@ def test_sanity_check_setuptools_own_sdist(setuptools_sdist):
# setuptools sdist should not include the .tox folder
tox_files = [name for name in files if ".tox" in name]
assert len(tox_files) == 0, f"not empty {tox_files}"


class TestCallFinderWithDistributionSupport:
"""Tests for the _call_finder_with_distribution_support helper function"""

def test_finder_with_distribution_parameter(self):
"""Test that finder functions accepting distribution parameter receive it"""

def finder_with_distribution(dirname, distribution=None):
return [f"file_in_{dirname}_with_dist_{distribution}"]

result = list(
_call_finder_with_distribution_support(
finder_with_distribution, dirname="testdir", distribution="test_dist"
)
)

assert result == ["file_in_testdir_with_dist_test_dist"]

def test_finder_without_distribution_parameter(self):
"""Test that finder functions not accepting distribution parameter work normally"""

def finder_without_distribution(dirname):
return [f"file_in_{dirname}"]

result = list(
_call_finder_with_distribution_support(
finder_without_distribution, dirname="testdir", distribution="test_dist"
)
)

assert result == ["file_in_testdir"]

def test_finder_with_single_parameter_only(self):
"""Test that finder functions with only dirname parameter work"""

def finder_single_param(dirname):
return [f"single_{dirname}"]

result = list(
_call_finder_with_distribution_support(finder_single_param, dirname="test")
)

assert result == ["single_test"]

def test_finder_with_distribution_named_differently(self):
"""Test that only functions with 'distribution' parameter name get it"""

def finder_with_other_param(dirname, other_param=None):
return [f"other_{dirname}_{other_param}"]

# Should not pass distribution even though function has 2+ params
result = list(
_call_finder_with_distribution_support(
finder_with_other_param, dirname="test", distribution="dist_obj"
)
)

assert result == ["other_test_None"]

def test_finder_with_signature_inspection_failure(self):
"""Test fallback behavior when signature inspection fails"""

# Create a mock function that raises an error during signature inspection
def problematic_finder(dirname):
return [f"fallback_{dirname}"]

# Mock inspect.signature to raise ValueError
with mock.patch(
'setuptools.command.sdist.inspect.signature',
side_effect=ValueError("test error"),
):
result = list(
_call_finder_with_distribution_support(
problematic_finder, dirname="test", distribution="dist_obj"
)
)

assert result == ["fallback_test"]

def test_finder_with_multiple_parameters_including_distribution(self):
"""Test finder with multiple parameters where one is distribution"""

def complex_finder(dirname, extra_param=None, distribution=None):
return [f"complex_{dirname}_{extra_param}_{distribution}"]

result = list(
_call_finder_with_distribution_support(
complex_finder, dirname="test", distribution="dist_obj"
)
)

assert result == ["complex_test_None_dist_obj"]

def test_finder_function_raises_exception(self):
"""Test that exceptions from finder functions are properly propagated"""

def failing_finder(dirname, distribution=None):
raise RuntimeError("Finder failed")

with pytest.raises(RuntimeError, match="Finder failed"):
list(
_call_finder_with_distribution_support(
failing_finder, dirname="test", distribution="dist_obj"
)
)
Loading