Skip to content

Commit

Permalink
Allow using an empty string for the sources option to add a prefix …
Browse files Browse the repository at this point in the history
…to distribution paths (#1064)

* Allow using empty string to add prefixes to the distribution path

Closes #802

* update example

Co-Authored-By: Vincent Hatakeyama <[email protected]>

* add release note

Co-Authored-By: Vincent Hatakeyama <[email protected]>

* fix lint

Co-Authored-By: Vincent Hatakeyama <[email protected]>

---------

Co-authored-by: Vincent Hatakeyama <[email protected]>
Co-authored-by: Vincent Hatakeyama <[email protected]>
  • Loading branch information
3 people authored Nov 26, 2023
1 parent 0326e47 commit feacd65
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 12 deletions.
11 changes: 5 additions & 6 deletions backend/src/hatchling/builders/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -675,11 +675,7 @@ def sources(self) -> dict[str, str]:

sources[normalize_relative_directory(source)] = ''
elif isinstance(raw_sources, dict):
for i, (source, path) in enumerate(raw_sources.items(), 1):
if not source:
message = f'Source #{i} in field `{sources_location}` cannot be an empty string'
raise ValueError(message)

for source, path in raw_sources.items():
if not isinstance(path, str):
message = f'Path for source `{source}` in field `{sources_location}` must be a string'
raise TypeError(message)
Expand All @@ -690,7 +686,7 @@ def sources(self) -> dict[str, str]:
else:
normalized_path += os.sep

sources[normalize_relative_directory(source)] = normalized_path
sources[normalize_relative_directory(source) if source else source] = normalized_path
else:
message = f'Field `{sources_location}` must be a mapping or array of strings'
raise TypeError(message)
Expand Down Expand Up @@ -806,6 +802,9 @@ def only_include(self) -> dict[str, str]:
def get_distribution_path(self, relative_path: str) -> str:
# src/foo/bar.py -> foo/bar.py
for source, replacement in self.sources.items():
if not source:
return replacement + relative_path

if relative_path.startswith(source):
return relative_path.replace(source, replacement, 1)

Expand Down
9 changes: 9 additions & 0 deletions docs/config/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ If you want to remove path prefixes entirely, rather than setting each to an emp
sources = ["src"]
```

If you want to add a prefix to paths, you can use an empty string. For example, the following configuration:

```toml config-example
[tool.hatch.build.sources]
"" = "foo"
```

would distribute the file `bar/file.ext` as `foo/bar/file.ext`.

The [packages](#packages) option itself relies on sources. Defining `#!toml packages = ["src/foo"]` for the `wheel` target is equivalent to the following:

```toml config-example
Expand Down
1 change: 1 addition & 0 deletions docs/history/hatchling.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- Fix parsing dependencies for environments when warnings are emitted
- Properly handle non-zero version epoch for the `standard` version scheme
- Allow using an empty string for the `sources` option to add a prefix to distribution paths

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

Expand Down
13 changes: 7 additions & 6 deletions tests/backend/builders/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,10 @@ def test_global_mapping_source_empty_string(self, isolation):
config = {'tool': {'hatch': {'build': {'sources': {'': 'renamed'}}}}}
builder = MockBuilder(str(isolation), config=config)

with pytest.raises(ValueError, match='Source #1 in field `tool.hatch.build.sources` cannot be an empty string'):
_ = builder.config.sources
assert len(builder.config.sources) == 1
assert builder.config.sources[''] == pjoin('renamed', '')
assert builder.config.get_distribution_path('bar.py') == pjoin('renamed', 'bar.py')
assert builder.config.get_distribution_path(pjoin('foo', 'bar.py')) == pjoin('renamed', 'foo', 'bar.py')

def test_global_mapping_path_empty_string(self, isolation):
config = {'tool': {'hatch': {'build': {'sources': {'src/foo': ''}}}}}
Expand Down Expand Up @@ -757,10 +759,9 @@ def test_target_mapping_source_empty_string(self, isolation):
builder = MockBuilder(str(isolation), config=config)
builder.PLUGIN_NAME = 'foo'

with pytest.raises(
ValueError, match='Source #1 in field `tool.hatch.build.targets.foo.sources` cannot be an empty string'
):
_ = builder.config.sources
assert len(builder.config.sources) == 1
assert builder.config.sources[''] == pjoin('renamed', '')
assert builder.config.get_distribution_path(pjoin('bar.py')) == pjoin('renamed', 'bar.py')

def test_target_mapping_path_empty_string(self, isolation):
config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': {'src/foo': ''}}}}}}}
Expand Down

0 comments on commit feacd65

Please sign in to comment.