Skip to content

Commit

Permalink
Fix exclude counting toward inclusion selection (#1134)
Browse files Browse the repository at this point in the history
  • Loading branch information
ofek authored Dec 13, 2023
1 parent 35f8ffd commit e0449be
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 24 deletions.
26 changes: 11 additions & 15 deletions backend/src/hatchling/builders/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,46 +170,42 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:

@cached_property
def default_file_selection_options(self) -> FileSelectionOptions:
if include := self.target_config.get('include', self.build_config.get('include', [])):
return FileSelectionOptions(include, [], [], [])
include = self.target_config.get('include', self.build_config.get('include', []))
exclude = self.target_config.get('exclude', self.build_config.get('exclude', []))
packages = self.target_config.get('packages', self.build_config.get('packages', []))
only_include = self.target_config.get('only-include', self.build_config.get('only-include', []))

if exclude := self.target_config.get('exclude', self.build_config.get('exclude', [])):
return FileSelectionOptions([], exclude, [], [])

if packages := self.target_config.get('packages', self.build_config.get('packages', [])):
return FileSelectionOptions([], [], packages, [])

if only_include := self.target_config.get('only-include', self.build_config.get('only-include', [])):
return FileSelectionOptions([], [], [], only_include)
if include or packages or only_include:
return FileSelectionOptions(include, exclude, packages, only_include)

for project_name in (
self.builder.normalize_file_name_component(self.builder.metadata.core.raw_name),
self.builder.normalize_file_name_component(self.builder.metadata.core.name),
):
if os.path.isfile(os.path.join(self.root, project_name, '__init__.py')):
normalized_project_name = self.get_raw_fs_path_name(self.root, project_name)
return FileSelectionOptions([], [], [normalized_project_name], [])
return FileSelectionOptions([], exclude, [normalized_project_name], [])

if os.path.isfile(os.path.join(self.root, 'src', project_name, '__init__.py')):
normalized_project_name = self.get_raw_fs_path_name(os.path.join(self.root, 'src'), project_name)
return FileSelectionOptions([], [], [f'src/{normalized_project_name}'], [])
return FileSelectionOptions([], exclude, [f'src/{normalized_project_name}'], [])

module_file = f'{project_name}.py'
if os.path.isfile(os.path.join(self.root, module_file)):
normalized_project_name = self.get_raw_fs_path_name(self.root, module_file)
return FileSelectionOptions([], [], [], [module_file])
return FileSelectionOptions([], exclude, [], [module_file])

from glob import glob

possible_namespace_packages = glob(os.path.join(self.root, '*', project_name, '__init__.py'))
if len(possible_namespace_packages) == 1:
relative_path = os.path.relpath(possible_namespace_packages[0], self.root)
namespace = relative_path.split(os.sep)[0]
return FileSelectionOptions([], [], [namespace], [])
return FileSelectionOptions([], exclude, [namespace], [])

if self.build_artifact_spec is not None or self.get_force_include():
self.set_exclude_all()
return FileSelectionOptions([], [], [], [])
return FileSelectionOptions([], exclude, [], [])

message = (
'Unable to determine which files to ship inside the wheel using the following heuristics: '
Expand Down
4 changes: 4 additions & 0 deletions docs/history/hatchling.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## Unreleased

***Fixed:***

- Fix regression in 1.19.1 that allowed `exclude` to count toward inclusion selection, thus bypassing the default inclusion selection heuristics

## [1.19.1](https://github.com/pypa/hatch/releases/tag/hatchling-v1.19.1) - 2023-12-12 ## {: #hatchling-v1.19.1 }

***Fixed:***
Expand Down
58 changes: 49 additions & 9 deletions tests/backend/builders/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,36 @@ def test_default_versions(isolation):


class TestDefaultFileSelection:
def test_already_defined(self, temp_dir):
config = {
'project': {'name': 'my-app', 'version': '0.0.1'},
'tool': {
'hatch': {
'build': {
'targets': {
'wheel': {
'include': ['foo'],
'exclude': ['bar'],
'packages': ['foo', 'bar', 'baz'],
'only-include': ['baz'],
}
}
}
}
},
}
builder = WheelBuilder(str(temp_dir), config=config)

assert builder.config.default_include() == ['foo']
assert builder.config.default_exclude() == ['bar']
assert builder.config.default_packages() == ['foo', 'bar', 'baz']
assert builder.config.default_only_include() == ['baz']

def test_flat_layout(self, temp_dir):
config = {'project': {'name': 'my-app', 'version': '0.0.1'}}
config = {
'project': {'name': 'my-app', 'version': '0.0.1'},
'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}},
}
builder = WheelBuilder(str(temp_dir), config=config)

flat_root = temp_dir / 'my_app' / '__init__.py'
Expand All @@ -54,12 +82,15 @@ def test_flat_layout(self, temp_dir):
namespace_root.touch()

assert builder.config.default_include() == []
assert builder.config.default_exclude() == []
assert builder.config.default_exclude() == ['foobarbaz']
assert builder.config.default_packages() == ['my_app']
assert builder.config.default_only_include() == []

def test_src_layout(self, temp_dir):
config = {'project': {'name': 'my-app', 'version': '0.0.1'}}
config = {
'project': {'name': 'my-app', 'version': '0.0.1'},
'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}},
}
builder = WheelBuilder(str(temp_dir), config=config)

src_root = temp_dir / 'src' / 'my_app' / '__init__.py'
Expand All @@ -74,12 +105,15 @@ def test_src_layout(self, temp_dir):
namespace_root.touch()

assert builder.config.default_include() == []
assert builder.config.default_exclude() == []
assert builder.config.default_exclude() == ['foobarbaz']
assert builder.config.default_packages() == ['src/my_app']
assert builder.config.default_only_include() == []

def test_single_module(self, temp_dir):
config = {'project': {'name': 'my-app', 'version': '0.0.1'}}
config = {
'project': {'name': 'my-app', 'version': '0.0.1'},
'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}},
}
builder = WheelBuilder(str(temp_dir), config=config)

single_module_root = temp_dir / 'my_app.py'
Expand All @@ -90,25 +124,31 @@ def test_single_module(self, temp_dir):
namespace_root.touch()

assert builder.config.default_include() == []
assert builder.config.default_exclude() == []
assert builder.config.default_exclude() == ['foobarbaz']
assert builder.config.default_packages() == []
assert builder.config.default_only_include() == ['my_app.py']

def test_namespace(self, temp_dir):
config = {'project': {'name': 'my-app', 'version': '0.0.1'}}
config = {
'project': {'name': 'my-app', 'version': '0.0.1'},
'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}},
}
builder = WheelBuilder(str(temp_dir), config=config)

namespace_root = temp_dir / 'ns' / 'my_app' / '__init__.py'
namespace_root.ensure_parent_dir_exists()
namespace_root.touch()

assert builder.config.default_include() == []
assert builder.config.default_exclude() == []
assert builder.config.default_exclude() == ['foobarbaz']
assert builder.config.default_packages() == ['ns']
assert builder.config.default_only_include() == []

def test_default_error(self, temp_dir):
config = {'project': {'name': 'my-app', 'version': '0.0.1'}}
config = {
'project': {'name': 'my-app', 'version': '0.0.1'},
'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}},
}
builder = WheelBuilder(str(temp_dir), config=config)

for method in (
Expand Down

0 comments on commit e0449be

Please sign in to comment.