diff --git a/pydra/engine/helpers.py b/pydra/engine/helpers.py
index 1a441f1d..3cd9c65a 100644
--- a/pydra/engine/helpers.py
+++ b/pydra/engine/helpers.py
@@ -10,7 +10,7 @@ from hashlib import sha256
 import subprocess as sp
 
 from .specs import Runtime, File, attr_fields
-from .helpers_file import is_existing_file, hash_file, copyfile, is_existing_file
+from .helpers_file import hash_file, copyfile, is_existing_file
 
 
 def ensure_list(obj, tuple2list=False):
@@ -444,12 +444,14 @@ def hash_function(obj):
 
 def hash_value(value, tp=None, metadata=None):
     """calculating hash or returning values recursively"""
+    if metadata is None:
+        metadata = {}
     if isinstance(value, (tuple, list)):
         return [hash_value(el, tp, metadata) for el in value]
     elif isinstance(value, dict):
         dict_hash = {k: hash_value(v, tp, metadata) for (k, v) in value.items()}
         # returning a sorted object
-        return sorted(dict_hash.items(), key=lambda x: x[0])
+        return [list(el) for el in sorted(dict_hash.items(), key=lambda x: x[0])]
     else:  # not a container
         if (
             (tp is File or "pydra.engine.specs.File" in str(tp))
@@ -457,8 +459,6 @@ def hash_value(value, tp=None, metadata=None):
             and "container_path" not in metadata
         ):
             return hash_file(value)
-        elif isinstance(value, tuple):
-            return list(value)
         else:
             return value
 

review what should be included checksum
for example I'm removing bindings and input that have output_file_template
diff --git a/pydra/engine/tests/test_helpers.py b/pydra/engine/tests/test_helpers.py
index 862f6b1d..50437354 100644
--- a/pydra/engine/tests/test_helpers.py
+++ b/pydra/engine/tests/test_helpers.py
@@ -4,17 +4,18 @@ import pytest
 import cloudpickle as cp
 
 from .utils import multiply
-from .. import helpers
+from ..helpers import hash_value, hash_function, save, create_pyscript
 from .. import helpers_file
+from ..specs import File
 
 
 def test_save(tmpdir):
     outdir = Path(tmpdir)
     with pytest.raises(ValueError):
-        helpers.save(tmpdir)
+        save(tmpdir)
     foo = multiply(name="mult", x=1, y=2)
     # save task
-    helpers.save(outdir, task=foo)
+    save(outdir, task=foo)
     del foo
     # load saved task
     task_pkl = outdir / "_task.pklz"
@@ -24,7 +25,7 @@ def test_save(tmpdir):
     # execute task and save result
     res = foo()
     assert res.output.out == 2
-    helpers.save(outdir, result=res)
+    save(outdir, result=res)
     del res
     # load saved result
     res_pkl = outdir / "_result.pklz"
@@ -35,10 +36,10 @@ def test_save(tmpdir):
 def test_create_pyscript(tmpdir):
     outdir = Path(tmpdir)
     with pytest.raises(Exception):
-        helpers.create_pyscript(outdir, "checksum")
+        create_pyscript(outdir, "checksum")
     foo = multiply(name="mult", x=1, y=2)
-    helpers.save(outdir, task=foo)
-    pyscript = helpers.create_pyscript(outdir, foo.checksum)
+    save(outdir, task=foo)
+    pyscript = create_pyscript(outdir, foo.checksum)
     assert pyscript.exists()
 
 
@@ -50,3 +51,76 @@ def test_hash_file(tmpdir):
         helpers_file.hash_file(outdir / "test.file")
         == "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
     )
+
+
+def test_hashfun_float():
+    import math
+
+    pi_50 = 3.14159265358979323846264338327950288419716939937510
+    pi_15 = 3.141592653589793
+    pi_10 = 3.1415926536
+    # comparing for x that have the same x.as_integer_ratio()
+    assert (
+        math.pi.as_integer_ratio()
+        == pi_50.as_integer_ratio()
+        == pi_15.as_integer_ratio()
+    )
+    assert hash_function(math.pi) == hash_function(pi_15) == hash_function(pi_50)
+    # comparing for x that have different x.as_integer_ratio()
+    assert math.pi.as_integer_ratio() != pi_10.as_integer_ratio()
+    assert hash_function(math.pi) != hash_function(pi_10)
+
+
+def test_hash_value_dict():
+    dict1 = {"a": 10, "b": 5}
+    dict2 = {"b": 5, "a": 10}
+    assert (
+        hash_value(dict1)
+        == hash_value(dict2)
+        == [["a", hash_value(10)], ["b", hash_value(5)]]
+        == [["a", 10], ["b", 5]]
+    )
+
+
+def test_hash_value_list_tpl():
+    lst = [2, 5.6, "ala"]
+    tpl = (2, 5.6, "ala")
+    assert hash_value(lst) == [hash_value(2), hash_value(5.6), hash_value("ala")] == lst
+    assert hash_value(lst) == hash_value(tpl)
+
+
+def test_hash_value_list_dict():
+    lst = [2, {"a": "ala", "b": 1}]
+    hash_value(lst)
+    assert (
+        hash_value(lst)
+        == [hash_value(2), hash_value([["a", "ala"], ["b", 1]])]
+        == [2, [["a", "ala"], ["b", 1]]]
+    )
+
+
+def test_hash_value_files(tmpdir):
+    file_1 = tmpdir.join("file_1.txt")
+    file_2 = tmpdir.join("file_2.txt")
+    with open(file_1, "w") as f:
+        f.write("hello")
+    with open(file_2, "w") as f:
+        f.write("hello")
+
+    assert hash_value(file_1, tp=File) == hash_value(file_2, tp=File)
+    assert hash_value(file_1, tp=str) != hash_value(file_2, tp=str)
+    assert hash_value(file_1) != hash_value(file_2)
+
+
+def test_hash_value_files_list(tmpdir):
+    file_1 = tmpdir.join("file_1.txt")
+    file_2 = tmpdir.join("file_2.txt")
+    with open(file_1, "w") as f:
+        f.write("hello")
+    with open(file_2, "w") as f:
+        f.write("hi")
+
+    assert hash_value([file_1, file_2], tp=File) == [
+        hash_value(file_1, tp=File),
+        hash_value(file_2, tp=File),
+    ]
diff --git a/pydra/engine/tests/test_specs.py b/pydra/engine/tests/test_specs.py
index 67ea41ea..da98ff88 100644
--- a/pydra/engine/tests/test_specs.py
+++ b/pydra/engine/tests/test_specs.py
@@ -289,7 +289,7 @@ def test_input_file_hash_4(tmpdir):
 
     # checking specific hash value
     hash1 = inputs(in_file=[{"file": file, "int": 3}]).hash
-    assert hash1 == "0148961a66106e15e55de3ab7e8c49e6227b6db7cdc614dcbae20406feb2548a"
+    assert hash1 == "e0555e78a40a02611674b0f48da97cdd28eee7e9885ecc17392b560c14826f06"
 
     # the same file, but int field changes
     hash1a = inputs(in_file=[{"file": file, "int": 5}]).hash

pip install -e .[dev]
parse_log_pytest
pytest
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
asttokens==3.0.0
attrs==25.3.0
beautifulsoup4==4.13.3
black==25.1.0
bleach==6.2.0
cachetools==5.5.2
certifi==2025.1.31
cfgv==3.4.0
charset-normalizer==3.4.1
ci-info==0.3.0
click==8.1.8
cloudpickle==3.1.1
codecov==2.1.13
comm==0.2.2
coverage==7.8.0
debugpy==1.8.13
decorator==5.2.1
defusedxml==0.7.1
distlib==0.3.9
entrypoints==0.4
etelemetry==0.3.1
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
execnet==2.1.1
executing==2.2.0
fastjsonschema==2.21.1
filelock==3.18.0
frozendict==2.4.6
identify==2.6.9
idna==3.10
importlib_metadata==8.6.1
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
ipykernel==6.9.2
ipython==8.18.1
ipython-genutils==0.2.0
ipywidgets==8.1.5
jedi==0.19.2
Jinja2==3.1.6
json5==0.10.0
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
jupyter==1.1.1
jupyter-client==7.1.2
jupyter-console==6.4.4
jupyter-contrib-nbextensions==0.5.1
jupyter-highlight-selected-word==0.2.0
jupyter_contrib_core==0.4.2
jupyter_core==5.7.2
jupyter_latex_envs==1.4.6
jupyter_nbextensions_configurator==0.4.1
jupyterlab==2.3.2
jupyterlab-server==1.2.0
jupyterlab_pygments==0.3.0
jupyterlab_widgets==3.0.13
lxml==5.3.1
MarkupSafe==3.0.2
matplotlib-inline==0.1.7
mistune==3.1.3
mypy-extensions==1.0.0
nbclient==0.10.2
nbconvert==7.16.6
nbformat==5.10.4
nest-asyncio==1.6.0
nodeenv==1.9.1
notebook==5.7.8
numpy==2.0.2
packaging @ file:///croot/packaging_1734472117206/work
pandocfilters==1.5.1
parso==0.8.4
pathspec==0.12.1
pexpect==4.9.0
platformdirs==4.3.7
pluggy @ file:///croot/pluggy_1733169602837/work
pre_commit==4.2.0
prometheus_client==0.21.1
prompt_toolkit==3.0.50
psutil==7.0.0
ptyprocess==0.7.0
pure_eval==0.2.3
-e git+https://github.com/nipype/pydra.git@349d954add42e4297ce127c2fa61f49346cc19a1#egg=pydra
Pygments==2.19.1
PyLD==2.0.4
pytest @ file:///croot/pytest_1738938843180/work
pytest-cov==6.0.0
pytest-env==1.1.5
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
PyYAML==6.0.2
pyzmq==26.3.0
referencing==0.36.2
requests==2.32.3
rpds-py==0.24.0
Send2Trash==1.8.3
six==1.17.0
soupsieve==2.6
stack-data==0.6.3
terminado==0.13.3
tinycss2==1.4.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
tornado==4.5.3
traitlets==5.14.3
typing_extensions==4.13.0
urllib3==2.3.0
virtualenv==20.29.3
wcwidth==0.2.13
webencodings==0.5.1
widgetsnbextension==4.0.13
zipp==3.21.0

name: pydra
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - asttokens==3.0.0
      - attrs==25.3.0
      - beautifulsoup4==4.13.3
      - black==25.1.0
      - bleach==6.2.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cfgv==3.4.0
      - charset-normalizer==3.4.1
      - ci-info==0.3.0
      - click==8.1.8
      - cloudpickle==3.1.1
      - codecov==2.1.13
      - comm==0.2.2
      - coverage==7.8.0
      - debugpy==1.8.13
      - decorator==5.2.1
      - defusedxml==0.7.1
      - distlib==0.3.9
      - entrypoints==0.4
      - etelemetry==0.3.1
      - execnet==2.1.1
      - executing==2.2.0
      - fastjsonschema==2.21.1
      - filelock==3.18.0
      - frozendict==2.4.6
      - identify==2.6.9
      - idna==3.10
      - importlib-metadata==8.6.1
      - ipykernel==6.9.2
      - ipython==8.18.1
      - ipython-genutils==0.2.0
      - ipywidgets==8.1.5
      - jedi==0.19.2
      - jinja2==3.1.6
      - json5==0.10.0
      - jsonschema==4.23.0
      - jsonschema-specifications==2024.10.1
      - jupyter==1.1.1
      - jupyter-client==7.1.2
      - jupyter-console==6.4.4
      - jupyter-contrib-core==0.4.2
      - jupyter-contrib-nbextensions==0.5.1
      - jupyter-core==5.7.2
      - jupyter-highlight-selected-word==0.2.0
      - jupyter-latex-envs==1.4.6
      - jupyter-nbextensions-configurator==0.4.1
      - jupyterlab==2.3.2
      - jupyterlab-pygments==0.3.0
      - jupyterlab-server==1.2.0
      - jupyterlab-widgets==3.0.13
      - lxml==5.3.1
      - markupsafe==3.0.2
      - matplotlib-inline==0.1.7
      - mistune==3.1.3
      - mypy-extensions==1.0.0
      - nbclient==0.10.2
      - nbconvert==7.16.6
      - nbformat==5.10.4
      - nest-asyncio==1.6.0
      - nodeenv==1.9.1
      - notebook==5.7.8
      - numpy==2.0.2
      - pandocfilters==1.5.1
      - parso==0.8.4
      - pathspec==0.12.1
      - pexpect==4.9.0
      - platformdirs==4.3.7
      - pre-commit==4.2.0
      - prometheus-client==0.21.1
      - prompt-toolkit==3.0.50
      - psutil==7.0.0
      - ptyprocess==0.7.0
      - pure-eval==0.2.3
      - pydra==0.3.1+19.g349d954a
      - pygments==2.19.1
      - pyld==2.0.4
      - pytest-cov==6.0.0
      - pytest-env==1.1.5
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.2
      - pyzmq==26.3.0
      - referencing==0.36.2
      - requests==2.32.3
      - rpds-py==0.24.0
      - send2trash==1.8.3
      - six==1.17.0
      - soupsieve==2.6
      - stack-data==0.6.3
      - terminado==0.13.3
      - tinycss2==1.4.0
      - tornado==4.5.3
      - traitlets==5.14.3
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - virtualenv==20.29.3
      - wcwidth==0.2.13
      - webencodings==0.5.1
      - widgetsnbextension==4.0.13
      - zipp==3.21.0
prefix: /opt/conda/envs/pydra

pydra/engine/tests/test_helpers.py::test_hash_value_dict pydra/engine/tests/test_helpers.py::test_hash_value_list_dict pydra/engine/tests/test_helpers.py::test_hash_value_files pydra/engine/tests/test_helpers.py::test_hash_value_files_list pydra/engine/tests/test_specs.py::test_input_file_hash_4
pydra/engine/tests/test_helpers.py::test_save pydra/engine/tests/test_helpers.py::test_create_pyscript pydra/engine/tests/test_helpers.py::test_hash_file pydra/engine/tests/test_helpers.py::test_hashfun_float pydra/engine/tests/test_helpers.py::test_hash_value_list_tpl pydra/engine/tests/test_specs.py::test_basespec pydra/engine/tests/test_specs.py::test_runtime pydra/engine/tests/test_specs.py::test_result pydra/engine/tests/test_specs.py::test_shellspec pydra/engine/tests/test_specs.py::test_container pydra/engine/tests/test_specs.py::test_docker pydra/engine/tests/test_specs.py::test_singularity pydra/engine/tests/test_specs.py::test_lazy_inp pydra/engine/tests/test_specs.py::test_lazy_out pydra/engine/tests/test_specs.py::test_laxy_errorattr pydra/engine/tests/test_specs.py::test_lazy_getvale pydra/engine/tests/test_specs.py::test_input_file_hash_1 pydra/engine/tests/test_specs.py::test_input_file_hash_2 pydra/engine/tests/test_specs.py::test_input_file_hash_2a pydra/engine/tests/test_specs.py::test_input_file_hash_3
pydra/engine/helpers.py
pydra/engine/helpers.py