Skip to content
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

Deprecate HashableDict in favor of frozendict #5284

Merged
merged 4 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 8 additions & 9 deletions conda_build/jinja_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,18 @@
import time
from functools import partial
from io import StringIO, TextIOBase
from typing import TYPE_CHECKING
from warnings import warn

import jinja2
import yaml

try:
import tomllib # Python 3.11
except:
import tomli as tomllib

from typing import TYPE_CHECKING
from frozendict import deepfreeze

from . import _load_setup_py_data
from .environ import get_dict as get_environ
from .exceptions import CondaBuildException
from .render import get_env_dependencies
from .utils import (
HashableDict,
apply_pin_expressions,
check_call_env,
copy_into,
Expand All @@ -38,6 +32,11 @@
)
from .variants import DEFAULT_COMPILERS

try:
import tomllib # Python 3.11
except:
import tomli as tomllib

if TYPE_CHECKING:
from typing import IO, Any

Expand Down Expand Up @@ -298,7 +297,7 @@ def pin_compatible(
# There are two cases considered here (so far):
# 1. Good packages that follow semver style (if not philosophy). For example, 1.2.3
# 2. Evil packages that cram everything alongside a single major version. For example, 9b
key = (m.name(), HashableDict(m.config.variant))
key = (m.name(), deepfreeze(m.config.variant))
if key in cached_env_dependencies:
pins = cached_env_dependencies[key]
else:
Expand Down
68 changes: 22 additions & 46 deletions conda_build/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from bs4 import UnicodeDammit
from conda.base.context import context
from conda.gateways.disk.read import compute_sum
from frozendict import deepfreeze

from . import exceptions, utils, variants
from .conda_interface import MatchSpec
Expand All @@ -26,7 +27,6 @@
from .license_family import ensure_valid_license_family
from .utils import (
DEFAULT_SUBDIRS,
HashableDict,
ensure_list,
expand_globs,
find_recipe,
Expand Down Expand Up @@ -956,15 +956,8 @@ def finalize_outputs_pass(
fm = om
if not output_d.get("type") or output_d.get("type").startswith("conda"):
outputs[
(
fm.name(),
HashableDict(
{
k: copy.deepcopy(fm.config.variant[k])
for k in fm.get_used_vars()
}
),
)
fm.name(),
deepfreeze({k: fm.config.variant[k] for k in fm.get_used_vars()}),
] = (output_d, fm)
except exceptions.DependencyNeedsBuildingError as e:
if not permit_unsatisfiable_variants:
Expand All @@ -976,28 +969,22 @@ def finalize_outputs_pass(
f"{e.packages}"
)
outputs[
(
metadata.name(),
HashableDict(
{
k: copy.deepcopy(metadata.config.variant[k])
for k in metadata.get_used_vars()
}
),
)
metadata.name(),
deepfreeze(
{
k: metadata.config.variant[k]
for k in metadata.get_used_vars()
}
),
] = (output_d, metadata)
# in-place modification
base_metadata.other_outputs = outputs
base_metadata.final = False
final_outputs = OrderedDict()
for k, (out_d, m) in outputs.items():
final_outputs[
(
m.name(),
HashableDict(
{k: copy.deepcopy(m.config.variant[k]) for k in m.get_used_vars()}
),
)
m.name(),
deepfreeze({k: m.config.variant[k] for k in m.get_used_vars()}),
] = (out_d, m)
return final_outputs

Expand Down Expand Up @@ -2540,17 +2527,15 @@ def get_output_metadata_set(
# also refine this collection as each output metadata object is
# finalized - see the finalize_outputs_pass function
all_output_metadata[
(
out_metadata.name(),
HashableDict(
{
k: copy.deepcopy(out_metadata.config.variant[k])
for k in out_metadata.get_used_vars()
}
),
)
out_metadata.name(),
deepfreeze(
{
k: out_metadata.config.variant[k]
for k in out_metadata.get_used_vars()
}
),
] = (out, out_metadata)
out_metadata_map[HashableDict(out)] = out_metadata
out_metadata_map[deepfreeze(out)] = out_metadata
ref_metadata.other_outputs = out_metadata.other_outputs = (
all_output_metadata
)
Expand All @@ -2577,12 +2562,7 @@ def get_output_metadata_set(
):
conda_packages[
m.name(),
HashableDict(
{
k: copy.deepcopy(m.config.variant[k])
for k in m.get_used_vars()
}
),
deepfreeze({k: m.config.variant[k] for k in m.get_used_vars()}),
] = (output_d, m)
elif output_d.get("type") == "wheel":
if not output_d.get("requirements", {}).get("build") or not any(
Expand Down Expand Up @@ -2719,11 +2699,7 @@ def get_used_vars(self, force_top_level=False, force_global=False):
global used_vars_cache
recipe_dir = self.path

# `HashableDict` does not handle lists of other dictionaries correctly. Also it
# is constructed inplace, taking references to sub-elements of the input dict
# and thus corrupting it. Also, this was being called in 3 places in this function
# so caching it is probably a good thing.
hashed_variants = HashableDict(copy.deepcopy(self.config.variant))
hashed_variants = deepfreeze(self.config.variant)
if hasattr(self.config, "used_vars"):
used_vars = self.config.used_vars
elif (
Expand Down
4 changes: 4 additions & 0 deletions conda_build/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
win_path_to_unix,
)
from .conda_interface import rm_rf as _rm_rf
from .deprecations import deprecated
from .exceptions import BuildLockError

if TYPE_CHECKING:
Expand Down Expand Up @@ -1407,6 +1408,7 @@ def get_installed_packages(path):
return installed


@deprecated("24.5", "24.7", addendum="Use `frozendict.deepfreeze` instead.")
def _convert_lists_to_sets(_dict):
for k, v in _dict.items():
if hasattr(v, "keys"):
Expand All @@ -1419,6 +1421,7 @@ def _convert_lists_to_sets(_dict):
return _dict


@deprecated("24.5", "24.7", addendum="Use `frozendict.deepfreeze` instead.")
class HashableDict(dict):
"""use hashable frozen dictionaries for resources and resource types so that they can be in sets"""

Expand All @@ -1430,6 +1433,7 @@ def __hash__(self):
return hash(json.dumps(self, sort_keys=True))


@deprecated("24.5", "24.7", addendum="Use `frozendict.deepfreeze` instead.")
def represent_hashabledict(dumper, data):
value = []

Expand Down
21 changes: 21 additions & 0 deletions news/5284-deprecate-HashableDict
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
### Enhancements

* <news item>

### Bug fixes

* <news item>

### Deprecations

* Deprecate `conda_build.utils.HashableDict`. Use `frozendict.deepfreeze` instead. (#5284)
* Deprecate `conda_build.utils._convert_lists_to_sets`. Use `frozendict.deepfreeze` instead. (#5284)
* Deprecate `conda_build.utils.represent_hashabledict`. Use `frozendict.deepfreeze` instead. (#5284)

### Docs

* <news item>

### Other

* <news item>
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies = [
"conda-index >=0.4.0",
"conda-package-handling >=1.3",
"filelock",
"frozendict >=2.4.2",
"jinja2",
"jsonschema >=4.19",
"libarchive-c",
Expand Down
1 change: 1 addition & 0 deletions recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ requirements:
- conda-index >=0.4.0
- conda-package-handling >=1.3
- filelock
- frozendict >=2.4.2
- jinja2
- jsonschema >=4.19
- m2-patch >=2.6 # [win]
Expand Down
1 change: 1 addition & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ conda-index >=0.4.0
conda-libmamba-solver # ensure we use libmamba
conda-package-handling >=1.3
filelock
frozendict >=2.4.2
jinja2
jsonschema >=4.19
menuinst >=2
Expand Down
6 changes: 3 additions & 3 deletions tests/test_jinja_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from typing import TYPE_CHECKING

import pytest
from frozendict import deepfreeze

from conda_build import jinja_context
from conda_build.utils import HashableDict

if TYPE_CHECKING:
from pathlib import Path
Expand Down Expand Up @@ -99,7 +99,7 @@ def test_pin_subpackage_exact(testing_metadata):
testing_metadata.meta["outputs"] = [output_dict]
fm = testing_metadata.get_output_metadata(output_dict)
testing_metadata.other_outputs = {
(name, HashableDict(testing_metadata.config.variant)): (output_dict, fm)
(name, deepfreeze(testing_metadata.config.variant)): (output_dict, fm)
}
pin = jinja_context.pin_subpackage(testing_metadata, name, exact=True)
assert len(pin.split()) == 3
Expand All @@ -111,7 +111,7 @@ def test_pin_subpackage_expression(testing_metadata):
testing_metadata.meta["outputs"] = [output_dict]
fm = testing_metadata.get_output_metadata(output_dict)
testing_metadata.other_outputs = {
(name, HashableDict(testing_metadata.config.variant)): (output_dict, fm)
(name, deepfreeze(testing_metadata.config.variant)): (output_dict, fm)
}
pin = jinja_context.pin_subpackage(testing_metadata, name)
assert len(pin.split()) == 2
Expand Down