Skip to content

Commit

Permalink
Deprecate HashableDict in favor of frozendict >=2.4.2 (#5284)
Browse files Browse the repository at this point in the history
  • Loading branch information
kenodegard committed Apr 15, 2024
1 parent 1b2f1e0 commit b1310ca
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 58 deletions.
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

0 comments on commit b1310ca

Please sign in to comment.