Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pypa/hatch
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: hatchling-v1.23.0
Choose a base ref
...
head repository: pypa/hatch
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: hatchling-v1.24.0
Choose a head ref
  • 3 commits
  • 20 files changed
  • 1 contributor

Commits on Apr 16, 2024

  1. Depend on UV (#1393)

    ofek authored Apr 16, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a229af7 View commit details
  2. Add shared_data and shared_scripts build data for the wheel tar…

    …get (#1394)
    ofek authored Apr 16, 2024
    Copy the full SHA
    b198f97 View commit details
  3. release Hatchling v1.24.0

    ofek committed Apr 16, 2024
    Copy the full SHA
    8648544 View commit details
2 changes: 1 addition & 1 deletion backend/src/hatchling/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.23.0'
__version__ = '1.24.0'
20 changes: 14 additions & 6 deletions backend/src/hatchling/builders/wheel.py
Original file line number Diff line number Diff line change
@@ -585,25 +585,31 @@ def build_editable_explicit(self, directory: str, **build_data: Any) -> str:
def write_data(
self, archive: WheelArchive, records: RecordFile, build_data: dict[str, Any], extra_dependencies: Sequence[str]
) -> None:
self.add_shared_data(archive, records)
self.add_shared_scripts(archive, records)
self.add_shared_data(archive, records, build_data)
self.add_shared_scripts(archive, records, build_data)

# Ensure metadata is written last, see https://peps.python.org/pep-0427/#recommended-archiver-features
self.write_metadata(archive, records, build_data, extra_dependencies=extra_dependencies)

def add_shared_data(self, archive: WheelArchive, records: RecordFile) -> None:
for shared_file in self.recurse_explicit_files(self.config.shared_data):
def add_shared_data(self, archive: WheelArchive, records: RecordFile, build_data: dict[str, Any]) -> None:
shared_data = dict(self.config.shared_data)
shared_data.update(normalize_inclusion_map(build_data['shared_data'], self.root))

for shared_file in self.recurse_explicit_files(shared_data):
record = archive.add_shared_file(shared_file)
records.write(record)

def add_shared_scripts(self, archive: WheelArchive, records: RecordFile) -> None:
def add_shared_scripts(self, archive: WheelArchive, records: RecordFile, build_data: dict[str, Any]) -> None:
import re
from io import BytesIO

# https://packaging.python.org/en/latest/specifications/binary-distribution-format/#recommended-installer-features
shebang = re.compile(rb'^#!.*(?:pythonw?|pypyw?)[0-9.]*(.*)', flags=re.DOTALL)

for shared_script in self.recurse_explicit_files(self.config.shared_scripts):
shared_scripts = dict(self.config.shared_scripts)
shared_scripts.update(normalize_inclusion_map(build_data['shared_scripts'], self.root))

for shared_script in self.recurse_explicit_files(shared_scripts):
with open(shared_script.path, 'rb') as f:
content = BytesIO()
for line in f:
@@ -784,6 +790,8 @@ def get_default_build_data(self) -> dict[str, Any]: # noqa: PLR6301
'dependencies': [],
'force_include_editable': {},
'extra_metadata': {},
'shared_data': {},
'shared_scripts': {},
}

def get_forced_inclusion_map(self, build_data: dict[str, Any]) -> dict[str, str]:
9 changes: 9 additions & 0 deletions docs/config/internal/testing.md
Original file line number Diff line number Diff line change
@@ -106,3 +106,12 @@ The `run` script is the default behavior while the `run-cov` script is used inst

!!! note
The `HATCH_TEST_ARGS` environment variable is how the [`test`](../../cli/reference.md#hatch-test) command's flags are translated and internally populated without affecting the user's arguments. This is also the way that [extra arguments](#extra-arguments) are passed.

### Installer

By default, [UV is enabled](../../how-to/environment/select-installer.md). You may disable that behavior as follows:

```toml config-example
[tool.hatch.envs.hatch-test]
uv = false
```
6 changes: 6 additions & 0 deletions docs/history/hatchling.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## Unreleased

## [1.24.0](https://github.com/pypa/hatch/releases/tag/hatchling-v1.24.0) - 2024-04-16 ## {: #hatchling-v1.24.0 }

***Added:***

- Add `shared_data` and `shared_scripts` build data for the `wheel` target

## [1.23.0](https://github.com/pypa/hatch/releases/tag/hatchling-v1.23.0) - 2024-04-14 ## {: #hatchling-v1.23.0 }

***Added:***
19 changes: 7 additions & 12 deletions docs/how-to/environment/select-installer.md
Original file line number Diff line number Diff line change
@@ -9,17 +9,15 @@ The [virtual](../../plugins/environment/virtual.md) environment type by default
!!! warning "caveat"
UV is under active development and may not work for all dependencies.

To do so, enable the `uv` [option](../../plugins/environment/virtual.md#options). For example, if you wanted to enable this functionality for your [test environments](../../config/internal/testing.md#customize-environment), you could set the following:
To do so, enable the `uv` [option](../../plugins/environment/virtual.md#options). For example, if you wanted to enable this functionality for the [default](../../config/environment/overview.md#inheritance) environment, you could set the following:

```toml config-example
[tool.hatch.envs.hatch-test]
[tool.hatch.envs.default]
uv = true
```

The next time you use environments, Hatch will create a dedicated environment for UV (if it does not yet exist) which will be used for all subsequent environment creation and dependency resolution & installation.

!!! tip
All environments that enable UV will have `uv` available on PATH.
All environments that enable UV will have the path to `uv` available as the `HATCH_UV` environment variable.

## Configuring the version

@@ -34,7 +32,7 @@ dependencies = [

## Externally managed

If you want to manage UV yourself, you can expose it to Hatch by setting the `HATCH_ENV_TYPE_VIRTUAL_UV_PATH` environment variable. This should be the absolute path to a UV binary which Hatch will use instead of the internal environment. This implicitly [enables](#enabling-uv) the `uv` option.
If you want to manage UV yourself, you can expose it to Hatch by setting the `HATCH_ENV_TYPE_VIRTUAL_UV_PATH` environment variable which should be the absolute path to a UV binary for Hatch to use instead. This implicitly [enables](#enabling-uv) the `uv` option.

## Installer script alias

@@ -50,16 +48,13 @@ matrix.installer.uv = [
{ value = false, if = ["pip"] },
]
matrix.installer.scripts = [
{ key = "pip", value = "uv pip {args}", if = ["uv"] },
{ key = "pip", value = "{env:HATCH_UV} pip {args}", if = ["uv"] },
]
```

Another common use case is to enable UV for all [test environments](../../config/internal/testing.md). In this case, you often wouldn't want to modify the `scripts` mapping directly but rather add an [extra script](../../config/environment/overview.md#extra-scripts):
Another common use case is to expose UV to all [test environments](../../config/internal/testing.md). In this case, you often wouldn't want to modify the `scripts` mapping directly but rather add an [extra script](../../config/environment/overview.md#extra-scripts):

```toml config-example
[tool.hatch.envs.hatch-test]
uv = true

[tool.hatch.envs.hatch-test.extra-scripts]
pip = "uv pip {args}"
pip = "{env:HATCH_UV} pip {args}"
```
2 changes: 2 additions & 0 deletions docs/plugins/builder/wheel.md
Original file line number Diff line number Diff line change
@@ -56,5 +56,7 @@ This is data that can be modified by [build hooks](../build-hook/reference.md).
| `infer_tag` | `#!python False` | When `tag` is not set, this may be enabled to use the one most specific to the platform, Python interpreter, and ABI |
| `pure_python` | `#!python True` | Whether or not to write metadata indicating that the package does not contain any platform-specific files |
| `dependencies` | | Extra [project dependencies](../../config/metadata.md#required) |
| `shared_data` | | Additional [`shared-data`](#options) entries, which take precedence in case of conflicts |
| `shared_scripts` | | Additional [`shared-scripts`](#options) entries, which take precedence in case of conflicts |
| `extra_metadata` | | Additional [`extra-metadata`](#options) entries, which take precedence in case of conflicts |
| `force_include_editable` | | Similar to the [`force_include` option](../build-hook/reference.md#build-data) but specifically for the `editable` [version](#versions) and takes precedence |
2 changes: 1 addition & 1 deletion docs/plugins/environment/virtual.md
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ type = "virtual"
| `python-sources` | `['external', 'internal']` | This may be set to an array of strings that are either the literal `internal` or `external`. External considers only Python executables that are already on `PATH`. Internal considers only [internally managed Python distributions](#internal-distributions). |
| `path` | | An explicit path to the virtual environment. The path may be absolute or relative to the project root. Any environments that [inherit](../../config/environment/overview.md#inheritance) this option will also use this path. The environment variable `HATCH_ENV_TYPE_VIRTUAL_PATH` may be used, which will take precedence. |
| `system-packages` | `false` | Whether or not to give the virtual environment access to the system `site-packages` directory |
| `uv` | `false` | Whether or not to use [UV](https://github.com/astral-sh/uv) in place of virtualenv & pip for virtual environment creation and dependency management, respectively. By default, Hatch will manage UV itself in an isolated environment. If you intend to provide UV instead, you may set the `HATCH_ENV_TYPE_VIRTUAL_UV_PATH` environment variable which should be the absolute path to a UV binary. This environment variable implicitly enables the `uv` option (if unset). |
| `uv` | `false` | Whether or not to use [UV](https://github.com/astral-sh/uv) in place of virtualenv & pip for virtual environment creation and dependency management, respectively. If you intend to provide UV yourself, you may set the `HATCH_ENV_TYPE_VIRTUAL_UV_PATH` environment variable which should be the absolute path to a UV binary. This environment variable implicitly enables the `uv` option (if unset). |

## Location

4 changes: 1 addition & 3 deletions hatch.toml
Original file line number Diff line number Diff line change
@@ -8,12 +8,10 @@ post-install-commands = [
]

[envs.hatch-test]
uv = true
extra-dependencies = [
"filelock",
"pyfakefs",
"trustme",
"uv",
# Hatchling dynamic dependency
"editables",
]
@@ -23,7 +21,7 @@ post-install-commands = [
extra-args = ["--dist", "worksteal"]

[envs.hatch-test.extra-scripts]
pip = "uv pip {args}"
pip = "{env:HATCH_UV} pip {args}"

[envs.coverage]
detached = true
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ dependencies = [
"tomli-w>=1.0",
"tomlkit>=0.11.1",
"userpath~=1.7",
"uv>=0.1.32",
"virtualenv>=20.16.2",
"zstandard<1",
]
31 changes: 4 additions & 27 deletions src/hatch/cli/application.py
Original file line number Diff line number Diff line change
@@ -76,6 +76,8 @@ def get_environment(self, env_name: str | None = None) -> EnvironmentInterface:
if environment_class is None:
self.abort(f'Environment `{env_name}` has unknown type: {environment_type}')

from hatch.env.internal import is_isolated_environment

if self.project.location.is_file():
data_directory = isolated_data_directory = self.data_dir / 'env' / environment_type / '.scripts'
elif is_isolated_environment(env_name, config):
@@ -344,6 +346,8 @@ def _write(self, environment: EnvironmentInterface, metadata: dict[str, Any]) ->
metadata_file.write_text(json.dumps(metadata))

def _metadata_file(self, environment: EnvironmentInterface) -> Path:
from hatch.env.internal import is_isolated_environment

if is_isolated_environment(environment.name, environment.config):
return self.__data_dir / '.internal' / f'{environment.name}.json'

@@ -352,30 +356,3 @@ def _metadata_file(self, environment: EnvironmentInterface) -> Path:
@cached_property
def _storage_dir(self) -> Path:
return self.__data_dir / self.__project_path.id


def is_isolated_environment(env_name: str, config: dict[str, Any]) -> bool:
# Provide super isolation and immunity to project-level environment removal only when the environment:
#
# 1. Does not require the project being installed
# 2. The default configuration is used
#
# For example, the environment for static analysis depends only on Ruff at a specific default
# version. This environment does not require the project and can be reused by every project to
# improve responsiveness. However, if the user for some reason chooses to override the dependencies
# to use a different version of Ruff, then the project would get its own environment.
if not config.get('skip-install', False):
return False

from hatch.env.internal import get_internal_env_config

internal_config = get_internal_env_config().get(env_name)
if not internal_config:
return False

# Only consider things that would modify the actual installation, other options like extra scripts don't matter
for key in ('dependencies', 'extra-dependencies', 'features'):
if config.get(key) != internal_config.get(key):
return False

return True
30 changes: 30 additions & 0 deletions src/hatch/env/internal/__init__.py
Original file line number Diff line number Diff line change
@@ -20,3 +20,33 @@ def get_internal_env_config() -> dict[str, Any]:
internal_config[env_name] = env_config

return internal_config


def is_isolated_environment(env_name: str, config: dict[str, Any]) -> bool:
# Provide super isolation and immunity to project-level environment removal only when the environment:
#
# 1. Does not require the project being installed
# 2. The default configuration is used
#
# For example, the environment for static analysis depends only on Ruff at a specific default
# version. This environment does not require the project and can be reused by every project to
# improve responsiveness. However, if the user for some reason chooses to override the dependencies
# to use a different version of Ruff, then the project would get its own environment.
return config.get('skip-install', False) and is_default_environment(env_name, config)


def is_default_environment(env_name: str, config: dict[str, Any]) -> bool:
# Standalone environment
internal_config = get_internal_env_config().get(env_name)
if not internal_config:
# Environment generated from matrix
internal_config = get_internal_env_config().get(env_name.split('.')[0])
if not internal_config:
return False

# Only consider things that would modify the actual installation, other options like extra scripts don't matter
for key in ('dependencies', 'extra-dependencies', 'features'):
if config.get(key) != internal_config.get(key):
return False

return True
5 changes: 4 additions & 1 deletion src/hatch/env/internal/build.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from __future__ import annotations

from typing import Any

def get_default_config() -> dict:

def get_default_config() -> dict[str, Any]:
return {
'skip-install': True,
'uv': True,
'dependencies': ['build[virtualenv]>=1.0.3'],
'scripts': {
'build-all': 'python -m build',
5 changes: 4 additions & 1 deletion src/hatch/env/internal/static_analysis.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from __future__ import annotations

from typing import Any

def get_default_config() -> dict:

def get_default_config() -> dict[str, Any]:
return {
'skip-install': True,
'uv': True,
'dependencies': [f'ruff=={RUFF_DEFAULT_VERSION}'],
'scripts': {
'format-check': 'ruff format{env:HATCH_FMT_ARGS:} --check --diff {args:.}',
5 changes: 4 additions & 1 deletion src/hatch/env/internal/test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import annotations

from typing import Any

def get_default_config() -> dict:

def get_default_config() -> dict[str, Any]:
return {
'uv': True,
'dependencies': [
'coverage-enable-subprocess==1.0',
'coverage[toml]~=7.4',
6 changes: 4 additions & 2 deletions src/hatch/env/internal/uv.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

from typing import Any

def get_default_config() -> dict:

def get_default_config() -> dict[str, Any]:
return {
'skip-install': True,
'dependencies': ['uv==0.1.31'],
'uv': True,
}
Loading