Skip to content

Commit

Permalink
[DRAFT] Add version to metadata hook, use version to retrieve deps
Browse files Browse the repository at this point in the history
This is a very draft proposal on how to fix #1348 and #1349 - more
to see if this is a good direction. It should likely be split to
two PRs (and of course tests and docs are needed):

* extending custom metadata plugin to allow passing version through
  hook's version property to distinguish standard and editable builds
  (also including extending the hatchling CLIs.

* adding version to CLI where hatch queries hatchling to include standard/
  editable version when querying for available dependencies.

Not all changes have been yet applied, this is more to check if
this is the right direction.
  • Loading branch information
potiuk committed May 27, 2024
1 parent 9cd28d3 commit 762d7a4
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 20 deletions.
18 changes: 8 additions & 10 deletions backend/src/hatchling/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def build_sdist(sdist_directory: str, config_settings: dict[str, Any] | None = N
"""
from hatchling.builders.sdist import SdistBuilder

builder = SdistBuilder(os.getcwd())
return os.path.basename(next(builder.build(directory=sdist_directory, versions=['standard'])))
builder = SdistBuilder(os.getcwd(), versions=['standard'])
return os.path.basename(next(builder.build(directory=sdist_directory)))


def get_requires_for_build_wheel(config_settings: dict[str, Any] | None = None) -> list[str]: # noqa: ARG001
Expand All @@ -54,8 +54,8 @@ def build_wheel(
"""
from hatchling.builders.wheel import WheelBuilder

builder = WheelBuilder(os.getcwd())
return os.path.basename(next(builder.build(directory=wheel_directory, versions=['standard'])))
builder = WheelBuilder(os.getcwd(), versions=['standard'])
return os.path.basename(next(builder.build(directory=wheel_directory)))


def get_requires_for_build_editable(config_settings: dict[str, Any] | None = None) -> list[str]: # noqa: ARG001
Expand All @@ -79,9 +79,8 @@ def build_editable(
"""
from hatchling.builders.wheel import WheelBuilder

builder = WheelBuilder(os.getcwd())
return os.path.basename(next(builder.build(directory=wheel_directory, versions=['editable'])))

builder = WheelBuilder(os.getcwd(), versions=['editable'])
return os.path.basename(next(builder.build(directory=wheel_directory)))

# Any builder that has build-time hooks like Hatchling and setuptools cannot technically keep PEP 517's identical
# metadata promise e.g. C extensions would require different tags in the `WHEEL` file. Therefore, we consider the
Expand All @@ -106,8 +105,7 @@ def prepare_metadata_for_build_wheel(
https://peps.python.org/pep-0517/#prepare-metadata-for-build-wheel
"""
from hatchling.builders.wheel import WheelBuilder

builder = WheelBuilder(os.getcwd())
builder = WheelBuilder(os.getcwd(), versions=["standard"])

directory = os.path.join(metadata_directory, f'{builder.artifact_project_id}.dist-info')
if not os.path.isdir(directory):
Expand All @@ -128,7 +126,7 @@ def prepare_metadata_for_build_editable(
from hatchling.builders.constants import EDITABLES_REQUIREMENT
from hatchling.builders.wheel import WheelBuilder

builder = WheelBuilder(os.getcwd())
builder = WheelBuilder(os.getcwd(), versions=['editable'])

directory = os.path.join(metadata_directory, f'{builder.artifact_project_id}.dist-info')
if not os.path.isdir(directory):
Expand Down
9 changes: 5 additions & 4 deletions backend/src/hatchling/builders/plugin/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ def __init__(
config: dict[str, Any] | None = None,
metadata: ProjectMetadata | None = None,
app: Application | None = None,
versions: list[str] | None = None,
) -> None:
self.__root = root
self.__plugin_manager = cast(PluginManagerBound, plugin_manager)
self.__raw_config = config
self.__metadata = metadata
self.__app = app
self.__config = cast(BuilderConfigBound, None)
self.__versions = versions
self.__project_config: dict[str, Any] | None = None
self.__hatch_config: dict[str, Any] | None = None
self.__build_config: dict[str, Any] | None = None
Expand All @@ -80,7 +82,6 @@ def build(
self,
*,
directory: str | None = None,
versions: list[str] | None = None,
hooks_only: bool | None = None,
clean: bool | None = None,
clean_hooks_after: bool | None = None,
Expand All @@ -101,7 +102,7 @@ def build(

version_api = self.get_version_api()

versions = versions or self.config.versions
versions = self.__versions or self.config.versions
if versions:
unknown_versions = set(versions) - set(version_api)
if unknown_versions:
Expand Down Expand Up @@ -290,8 +291,8 @@ def metadata(self) -> ProjectMetadata:
if self.__metadata is None:
from hatchling.metadata.core import ProjectMetadata

self.__metadata = ProjectMetadata(self.root, self.plugin_manager, self.__raw_config)

self.__metadata = ProjectMetadata(self.root, self.plugin_manager, self.__raw_config,
self.__versions)
return self.__metadata

@property
Expand Down
1 change: 0 additions & 1 deletion backend/src/hatchling/builders/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,6 @@ def build_editable_detection(self, directory: str, **build_data: Any) -> str:
from editables import EditableProject

build_data['tag'] = self.get_default_tag()

with WheelArchive(
self.artifact_project_id, reproducible=self.config.reproducible
) as archive, RecordFile() as records:
Expand Down
4 changes: 3 additions & 1 deletion backend/src/hatchling/cli/metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def metadata_impl(
called_by_app: bool, # noqa: ARG001
field: str,
compact: bool,
version: str = "standard",
) -> None:
import json
import os
Expand All @@ -22,7 +23,7 @@ def metadata_impl(

root = os.getcwd()
plugin_manager = PluginManager()
project_metadata = ProjectMetadata(root, plugin_manager)
project_metadata = ProjectMetadata(root, plugin_manager, build_versions=[version])

metadata = resolve_metadata_fields(project_metadata)
if field: # no cov
Expand Down Expand Up @@ -55,4 +56,5 @@ def metadata_command(
parser.add_argument('field', nargs='?')
parser.add_argument('-c', '--compact', action='store_true')
parser.add_argument('--app', dest='called_by_app', action='store_true', help=argparse.SUPPRESS)
parser.add_argument('--version', dest='version', default="standard")
parser.set_defaults(func=metadata_impl)
17 changes: 14 additions & 3 deletions backend/src/hatchling/metadata/core.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

import contextlib
import os
import sys
from contextlib import suppress
from copy import deepcopy
from typing import TYPE_CHECKING, Any, Generic, cast
from typing import TYPE_CHECKING, Any, Generic, cast, Generator

from hatchling.metadata.utils import (
format_dependency,
Expand Down Expand Up @@ -36,12 +37,20 @@ def load_toml(path: str) -> dict[str, Any]:
return tomllib.loads(f.read())


@contextlib.contextmanager
def set_version(hook: MetadataHookInterface, version: str) -> Generator[None, None, None]:
old_version = hook.version
hook.version = version
yield
hook.version = old_version

class ProjectMetadata(Generic[PluginManagerBound]):
def __init__(
self,
root: str,
plugin_manager: PluginManagerBound | None,
config: dict[str, Any] | None = None,
build_versions: list[str] | None = None,
) -> None:
self.root = root
self.plugin_manager = plugin_manager
Expand All @@ -57,6 +66,7 @@ def __init__(
self._name: str | None = None
self._version: str | None = None
self._project_file: str | None = None
self._build_versions = build_versions

# App already loaded config
if config is not None and root is not None:
Expand Down Expand Up @@ -193,8 +203,9 @@ def core(self) -> CoreMetadata:

if metadata.dynamic:
for metadata_hook in metadata_hooks.values():
metadata_hook.update(self.core_raw_metadata)
metadata.add_known_classifiers(metadata_hook.get_known_classifiers())
with set_version(metadata_hook, self.version):
metadata_hook.update(self.core_raw_metadata)
metadata.add_known_classifiers(metadata_hook.get_known_classifiers())

new_fields = set(self.core_raw_metadata) - static_fields
for new_field in new_fields:
Expand Down
15 changes: 15 additions & 0 deletions backend/src/hatchling/metadata/plugin/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def hatch_register_metadata_hook():
def __init__(self, root: str, config: dict) -> None:
self.__root = root
self.__config = config
self.__version: str = "standard"

@property
def root(self) -> str:
Expand All @@ -53,6 +54,20 @@ def config(self) -> dict:
"""
return self.__config

@property
def version(self) -> str:
"""
Gets the version of build (standard/editable) that is being run.
"""
return self.__version

@version.setter
def version(self, version: str) -> None:
"""
This sets the version of build (standard/editable) that is being run.
"""
self.__version = version

@abstractmethod
def update(self, metadata: dict) -> None:
"""
Expand Down
3 changes: 2 additions & 1 deletion src/hatch/utils/dep.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def get_project_dependencies_complex(
from packaging.requirements import Requirement

with environment.root.as_cwd(), environment.build_environment(environment.metadata.build.requires):
command = ['python', '-u', '-W', 'ignore', '-m', 'hatchling', 'metadata', '--compact']
command = ['python', '-u', '-W', 'ignore', '-m', 'hatchling', 'metadata', '--compact',
"--version", "editable" if environment.dev_mode else "standard"]
output = environment.platform.check_command_output(
command,
# Only capture stdout
Expand Down

0 comments on commit 762d7a4

Please sign in to comment.