metatomic-torch 0.1.0__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. metatomic/__init__.py +0 -0
  2. metatomic/torch/__init__.py +49 -0
  3. metatomic/torch/_c_lib.py +134 -0
  4. metatomic/torch/_extensions.py +202 -0
  5. metatomic/torch/ase_calculator.py +774 -0
  6. metatomic/torch/documentation.py +523 -0
  7. metatomic/torch/io.py +164 -0
  8. metatomic/torch/model.py +794 -0
  9. metatomic/torch/outputs.py +337 -0
  10. metatomic/torch/systems_to_torch.py +125 -0
  11. metatomic/torch/torch-2.1/bin/metatomic_torch.dll +0 -0
  12. metatomic/torch/torch-2.1/include/metatomic/torch/exports.h +43 -0
  13. metatomic/torch/torch-2.1/include/metatomic/torch/misc.hpp +15 -0
  14. metatomic/torch/torch-2.1/include/metatomic/torch/model.hpp +334 -0
  15. metatomic/torch/torch-2.1/include/metatomic/torch/system.hpp +299 -0
  16. metatomic/torch/torch-2.1/include/metatomic/torch/version.h +11 -0
  17. metatomic/torch/torch-2.1/include/metatomic/torch.hpp +6 -0
  18. metatomic/torch/torch-2.1/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  19. metatomic/torch/torch-2.1/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  20. metatomic/torch/torch-2.1/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  21. metatomic/torch/torch-2.1/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  22. metatomic/torch/torch-2.1/lib/metatomic_torch.lib +0 -0
  23. metatomic/torch/torch-2.2/bin/metatomic_torch.dll +0 -0
  24. metatomic/torch/torch-2.2/include/metatomic/torch/exports.h +43 -0
  25. metatomic/torch/torch-2.2/include/metatomic/torch/misc.hpp +15 -0
  26. metatomic/torch/torch-2.2/include/metatomic/torch/model.hpp +334 -0
  27. metatomic/torch/torch-2.2/include/metatomic/torch/system.hpp +299 -0
  28. metatomic/torch/torch-2.2/include/metatomic/torch/version.h +11 -0
  29. metatomic/torch/torch-2.2/include/metatomic/torch.hpp +6 -0
  30. metatomic/torch/torch-2.2/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  31. metatomic/torch/torch-2.2/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  32. metatomic/torch/torch-2.2/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  33. metatomic/torch/torch-2.2/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  34. metatomic/torch/torch-2.2/lib/metatomic_torch.lib +0 -0
  35. metatomic/torch/torch-2.3/bin/metatomic_torch.dll +0 -0
  36. metatomic/torch/torch-2.3/include/metatomic/torch/exports.h +43 -0
  37. metatomic/torch/torch-2.3/include/metatomic/torch/misc.hpp +15 -0
  38. metatomic/torch/torch-2.3/include/metatomic/torch/model.hpp +334 -0
  39. metatomic/torch/torch-2.3/include/metatomic/torch/system.hpp +299 -0
  40. metatomic/torch/torch-2.3/include/metatomic/torch/version.h +11 -0
  41. metatomic/torch/torch-2.3/include/metatomic/torch.hpp +6 -0
  42. metatomic/torch/torch-2.3/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  43. metatomic/torch/torch-2.3/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  44. metatomic/torch/torch-2.3/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  45. metatomic/torch/torch-2.3/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  46. metatomic/torch/torch-2.3/lib/metatomic_torch.lib +0 -0
  47. metatomic/torch/torch-2.4/bin/metatomic_torch.dll +0 -0
  48. metatomic/torch/torch-2.4/include/metatomic/torch/exports.h +43 -0
  49. metatomic/torch/torch-2.4/include/metatomic/torch/misc.hpp +15 -0
  50. metatomic/torch/torch-2.4/include/metatomic/torch/model.hpp +334 -0
  51. metatomic/torch/torch-2.4/include/metatomic/torch/system.hpp +299 -0
  52. metatomic/torch/torch-2.4/include/metatomic/torch/version.h +11 -0
  53. metatomic/torch/torch-2.4/include/metatomic/torch.hpp +6 -0
  54. metatomic/torch/torch-2.4/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  55. metatomic/torch/torch-2.4/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  56. metatomic/torch/torch-2.4/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  57. metatomic/torch/torch-2.4/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  58. metatomic/torch/torch-2.4/lib/metatomic_torch.lib +0 -0
  59. metatomic/torch/torch-2.5/bin/metatomic_torch.dll +0 -0
  60. metatomic/torch/torch-2.5/include/metatomic/torch/exports.h +43 -0
  61. metatomic/torch/torch-2.5/include/metatomic/torch/misc.hpp +15 -0
  62. metatomic/torch/torch-2.5/include/metatomic/torch/model.hpp +334 -0
  63. metatomic/torch/torch-2.5/include/metatomic/torch/system.hpp +299 -0
  64. metatomic/torch/torch-2.5/include/metatomic/torch/version.h +11 -0
  65. metatomic/torch/torch-2.5/include/metatomic/torch.hpp +6 -0
  66. metatomic/torch/torch-2.5/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  67. metatomic/torch/torch-2.5/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  68. metatomic/torch/torch-2.5/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  69. metatomic/torch/torch-2.5/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  70. metatomic/torch/torch-2.5/lib/metatomic_torch.lib +0 -0
  71. metatomic/torch/torch-2.6/bin/metatomic_torch.dll +0 -0
  72. metatomic/torch/torch-2.6/include/metatomic/torch/exports.h +43 -0
  73. metatomic/torch/torch-2.6/include/metatomic/torch/misc.hpp +15 -0
  74. metatomic/torch/torch-2.6/include/metatomic/torch/model.hpp +334 -0
  75. metatomic/torch/torch-2.6/include/metatomic/torch/system.hpp +299 -0
  76. metatomic/torch/torch-2.6/include/metatomic/torch/version.h +11 -0
  77. metatomic/torch/torch-2.6/include/metatomic/torch.hpp +6 -0
  78. metatomic/torch/torch-2.6/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  79. metatomic/torch/torch-2.6/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  80. metatomic/torch/torch-2.6/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  81. metatomic/torch/torch-2.6/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  82. metatomic/torch/torch-2.6/lib/metatomic_torch.lib +0 -0
  83. metatomic/torch/torch-2.7/bin/metatomic_torch.dll +0 -0
  84. metatomic/torch/torch-2.7/include/metatomic/torch/exports.h +43 -0
  85. metatomic/torch/torch-2.7/include/metatomic/torch/misc.hpp +15 -0
  86. metatomic/torch/torch-2.7/include/metatomic/torch/model.hpp +334 -0
  87. metatomic/torch/torch-2.7/include/metatomic/torch/system.hpp +299 -0
  88. metatomic/torch/torch-2.7/include/metatomic/torch/version.h +11 -0
  89. metatomic/torch/torch-2.7/include/metatomic/torch.hpp +6 -0
  90. metatomic/torch/torch-2.7/lib/cmake/metatomic_torch/metatomic_torch-config-version.cmake +85 -0
  91. metatomic/torch/torch-2.7/lib/cmake/metatomic_torch/metatomic_torch-config.cmake +23 -0
  92. metatomic/torch/torch-2.7/lib/cmake/metatomic_torch/metatomic_torch-targets-release.cmake +19 -0
  93. metatomic/torch/torch-2.7/lib/cmake/metatomic_torch/metatomic_torch-targets.cmake +108 -0
  94. metatomic/torch/torch-2.7/lib/metatomic_torch.lib +0 -0
  95. metatomic/torch/utils.py +52 -0
  96. metatomic/torch/version.py +4 -0
  97. metatomic_torch-0.1.0.dist-info/METADATA +39 -0
  98. metatomic_torch-0.1.0.dist-info/RECORD +102 -0
  99. metatomic_torch-0.1.0.dist-info/WHEEL +5 -0
  100. metatomic_torch-0.1.0.dist-info/licenses/AUTHORS +2 -0
  101. metatomic_torch-0.1.0.dist-info/licenses/LICENSE +28 -0
  102. metatomic_torch-0.1.0.dist-info/top_level.txt +2 -0
metatomic/__init__.py ADDED
File without changes
@@ -0,0 +1,49 @@
1
+ import os
2
+
3
+ import torch
4
+
5
+ from ._c_lib import _load_library
6
+ from .version import __version__ # noqa: F401
7
+
8
+
9
+ if os.environ.get("METATOMIC_IMPORT_FOR_SPHINX", "0") != "0":
10
+ from .documentation import (
11
+ ModelCapabilities,
12
+ ModelEvaluationOptions,
13
+ ModelMetadata,
14
+ ModelOutput,
15
+ NeighborListOptions,
16
+ System,
17
+ check_atomistic_model,
18
+ load_model_extensions,
19
+ read_model_metadata,
20
+ register_autograd_neighbors,
21
+ unit_conversion_factor,
22
+ )
23
+
24
+ else:
25
+ _load_library()
26
+
27
+ System = torch.classes.metatomic.System
28
+ NeighborListOptions = torch.classes.metatomic.NeighborListOptions
29
+
30
+ ModelOutput = torch.classes.metatomic.ModelOutput
31
+ ModelEvaluationOptions = torch.classes.metatomic.ModelEvaluationOptions
32
+ ModelCapabilities = torch.classes.metatomic.ModelCapabilities
33
+ ModelMetadata = torch.classes.metatomic.ModelMetadata
34
+
35
+ read_model_metadata = torch.ops.metatomic.read_model_metadata
36
+ load_model_extensions = torch.ops.metatomic.load_model_extensions
37
+ check_atomistic_model = torch.ops.metatomic.check_atomistic_model
38
+
39
+ register_autograd_neighbors = torch.ops.metatomic.register_autograd_neighbors
40
+ unit_conversion_factor = torch.ops.metatomic.unit_conversion_factor
41
+
42
+ from .io import load_system, save # noqa: F401
43
+ from .model import ( # noqa: F401
44
+ AtomisticModel,
45
+ ModelInterface,
46
+ is_atomistic_model,
47
+ load_atomistic_model, # noqa: F401
48
+ )
49
+ from .systems_to_torch import systems_to_torch # noqa: F401
@@ -0,0 +1,134 @@
1
+ import glob
2
+ import importlib
3
+ import os
4
+ import sys
5
+
6
+ import metatensor.torch
7
+ import torch
8
+
9
+ from .utils import parse_version, version_compatible
10
+ from .version import __version__
11
+
12
+
13
+ _HERE = os.path.realpath(os.path.dirname(__file__))
14
+
15
+
16
+ def _lib_path():
17
+ torch_version = parse_version(torch.__version__)
18
+ install_prefix = os.path.join(
19
+ _HERE, f"torch-{torch_version.major}.{torch_version.minor}"
20
+ )
21
+
22
+ if os.path.exists(install_prefix):
23
+ # check if we are using an externally-provided version of the shared library
24
+ external_path = os.path.join(install_prefix, "_external.py")
25
+ if os.path.exists(external_path):
26
+ spec = importlib.util.spec_from_file_location("_external", external_path)
27
+ module = importlib.util.module_from_spec(spec)
28
+ spec.loader.exec_module(module)
29
+ return module.EXTERNAL_METATOMIC_TORCH_PATH
30
+
31
+ if sys.platform.startswith("darwin"):
32
+ path = os.path.join(install_prefix, "lib", "libmetatomic_torch.dylib")
33
+ windows = False
34
+ elif sys.platform.startswith("linux"):
35
+ path = os.path.join(install_prefix, "lib", "libmetatomic_torch.so")
36
+ windows = False
37
+ elif sys.platform.startswith("win"):
38
+ path = os.path.join(install_prefix, "bin", "metatomic_torch.dll")
39
+ windows = True
40
+ else:
41
+ raise ImportError("Unknown platform. Please edit this file")
42
+
43
+ if os.path.isfile(path):
44
+ if windows:
45
+ _check_dll(path)
46
+ return path
47
+ else:
48
+ raise ImportError(
49
+ "Could not find metatomic_torch shared library at " + path
50
+ )
51
+
52
+ # gather which torch version(s) the current install was built
53
+ # with to create the error message
54
+ existing_versions = []
55
+ for prefix in glob.glob(os.path.join(_HERE, "torch-*")):
56
+ existing_versions.append(os.path.basename(prefix)[6:])
57
+
58
+ if len(existing_versions) == 1:
59
+ raise ImportError(
60
+ f"Trying to load metatomic-torch with torch v{torch.__version__}, "
61
+ f"but it was compiled against torch v{existing_versions[0]}, which "
62
+ "is not ABI compatible"
63
+ )
64
+ else:
65
+ all_versions = ", ".join(map(lambda version: f"v{version}", existing_versions))
66
+ raise ImportError(
67
+ f"Trying to load metatomic-torch with torch v{torch.__version__}, "
68
+ f"we found builds for torch {all_versions}; which are not ABI compatible.\n"
69
+ "You can try to re-install from source with "
70
+ "`pip install metatomic-torch --no-binary=metatomic-torch`"
71
+ )
72
+
73
+
74
+ def _check_dll(path):
75
+ """
76
+ Check if the DLL pointer size matches Python (32-bit or 64-bit)
77
+ """
78
+ import platform
79
+ import struct
80
+
81
+ IMAGE_FILE_MACHINE_I386 = 332
82
+ IMAGE_FILE_MACHINE_AMD64 = 34404
83
+
84
+ machine = None
85
+ with open(path, "rb") as fd:
86
+ header = fd.read(2).decode(encoding="utf-8", errors="strict")
87
+ if header != "MZ":
88
+ raise ImportError(path + " is not a DLL")
89
+ else:
90
+ fd.seek(60)
91
+ header = fd.read(4)
92
+ header_offset = struct.unpack("<L", header)[0]
93
+ fd.seek(header_offset + 4)
94
+ header = fd.read(2)
95
+ machine = struct.unpack("<H", header)[0]
96
+
97
+ arch = platform.architecture()[0]
98
+ if arch == "32bit":
99
+ if machine != IMAGE_FILE_MACHINE_I386:
100
+ raise ImportError("Python is 32-bit, but this DLL is not")
101
+ elif arch == "64bit":
102
+ if machine != IMAGE_FILE_MACHINE_AMD64:
103
+ raise ImportError("Python is 64-bit, but this DLL is not")
104
+ else:
105
+ raise ImportError("Could not determine pointer size of Python")
106
+
107
+
108
+ def _load_library():
109
+ # Load metatensor_torch shared library in the process first, to ensure
110
+ # the metatomic_torch shared library can find it
111
+ metatensor.torch._c_lib._load_library()
112
+
113
+ # load the C++ operators and custom classes
114
+ try:
115
+ torch.ops.load_library(_lib_path())
116
+ except Exception as e:
117
+ if "undefined symbol" in str(e):
118
+ file_name = os.path.basename(_lib_path())
119
+ raise ImportError(
120
+ f"{file_name} is not compatible with the current PyTorch "
121
+ "installation.\nThis can happen if PyTorch comes from one source "
122
+ "(pip, conda, custom), but metatomic-torch comes from a different "
123
+ "one.\nIn this case, you can try to compile metatomic-torch yourself "
124
+ "with `pip install --no-binary=metatomic-torch metatomic-torch`"
125
+ ) from e
126
+ else:
127
+ raise e
128
+
129
+ lib_version = torch.ops.metatomic.version()
130
+ if not version_compatible(lib_version, __version__):
131
+ raise ImportError(
132
+ f"Trying to load the Python package metatomic-torch v{__version__} "
133
+ f"with the incompatible metatomic-torch C++ library v{lib_version}"
134
+ )
@@ -0,0 +1,202 @@
1
+ import glob
2
+ import hashlib
3
+ import os
4
+ import shutil
5
+ import site
6
+ import sys
7
+ import warnings
8
+
9
+ import metatensor.torch
10
+ import torch
11
+
12
+ from . import _c_lib
13
+
14
+
15
+ METATOMIC_TORCH_LIB_PATH = _c_lib._lib_path()
16
+ METATENSOR_TORCH_LIB_PATH = metatensor.torch._c_lib._lib_path()
17
+
18
+
19
+ def _rascaline_lib_path():
20
+ # This is kept for backward compatibility, but rascaline is now named featomic.
21
+ # This code should be removed by the middle of 2025.
22
+ import rascaline
23
+
24
+ return [rascaline._c_lib._lib_path()]
25
+
26
+
27
+ def _featomic_deps_path():
28
+ import featomic
29
+
30
+ deps_path = [featomic._c_lib._lib_path()]
31
+
32
+ libgomp_path = _find_openmp_dep("featomic_torch.libs")
33
+ if libgomp_path is not None:
34
+ deps_path.insert(0, libgomp_path)
35
+
36
+ return deps_path
37
+
38
+
39
+ def _sphericart_deps_path():
40
+ import sphericart.torch
41
+
42
+ deps_path = []
43
+
44
+ libgomp_path = _find_openmp_dep("sphericart_torch.libs")
45
+ if libgomp_path is not None:
46
+ deps_path.append(libgomp_path)
47
+
48
+ if sys.platform.startswith("linux"):
49
+ # sphericart uses a separate library to get the CUDA stream corresponding to a
50
+ # tensor, see https://github.com/lab-cosmo/sphericart/pull/164
51
+ sphericart_torch_path = sphericart.torch._lib_path()
52
+ lib_dir = os.path.dirname(sphericart_torch_path)
53
+
54
+ cuda_stream_lib = os.path.join(lib_dir, "libsphericart_torch_cuda_stream.so")
55
+ if os.path.exists(cuda_stream_lib):
56
+ deps_path.append(cuda_stream_lib)
57
+
58
+ return deps_path
59
+
60
+
61
+ def _find_openmp_dep(search_dir):
62
+ """
63
+ When building code that uses OpenMP on linux, we typically dynamically link to
64
+ libgomp. `cibuildwheel` then copies ``libgomp.so`` to
65
+ ``<wheel_name>.libs/libgomp-<hash>.so``, so we need to find and add this shared
66
+ library to the extensions dependencies.
67
+ """
68
+ if sys.platform.startswith("linux"):
69
+ libs_list = []
70
+
71
+ for prefix in site.getsitepackages():
72
+ libs_dir = os.path.join(prefix, search_dir)
73
+ if os.path.exists(libs_dir):
74
+ libs_list = glob.glob(os.path.join(libs_dir, "libgomp-*.so*"))
75
+ if len(libs_list) != 0:
76
+ # found it!
77
+ break
78
+
79
+ if len(libs_list) == 0:
80
+ warnings.warn(
81
+ f"No libgomp shared library found in '{search_dir}'. "
82
+ "This may cause issues when loading and running the model.",
83
+ stacklevel=2,
84
+ )
85
+ elif len(libs_list) > 1:
86
+ raise RuntimeError(
87
+ f"Multiple libgomp shared libraries found in '{search_dir}': "
88
+ f"{libs_list}. Try to re-install in a fresh environment."
89
+ )
90
+ else: # len(libs_list) == 1
91
+ return libs_list[0]
92
+
93
+
94
+ # Manual definition of which TorchScript extensions have their own dependencies. The
95
+ # dependencies should be returned in the order they need to be loaded.
96
+ EXTENSIONS_WITH_DEPENDENCIES = {
97
+ "rascaline_torch": _rascaline_lib_path,
98
+ "featomic_torch": _featomic_deps_path,
99
+ "sphericart_torch": _sphericart_deps_path,
100
+ }
101
+
102
+
103
+ def _collect_extensions(extensions_path):
104
+ """
105
+ Record the list of loaded TorchScript extensions (and their dependencies), to check
106
+ that they are also loaded when executing the model.
107
+ """
108
+ if extensions_path is not None:
109
+ if os.path.exists(extensions_path):
110
+ shutil.rmtree(extensions_path)
111
+ os.makedirs(extensions_path)
112
+ # TODO: the extensions are currently collected in a separate directory,
113
+ # should we store the files directly inside the model file? This would makes
114
+ # the model platform-specific but much more convenient (since the end user
115
+ # does not have to move a model around)
116
+
117
+ extensions = []
118
+ extensions_deps = []
119
+ for library in torch.ops.loaded_libraries:
120
+ assert os.path.exists(library)
121
+
122
+ # these should be provided by the simulation engine
123
+ if os.path.samefile(library, METATENSOR_TORCH_LIB_PATH):
124
+ continue
125
+ if os.path.samefile(library, METATOMIC_TORCH_LIB_PATH):
126
+ continue
127
+
128
+ path = _copy_extension(library, extensions_path)
129
+ info = _extension_info(library)
130
+ info["path"] = path
131
+ extensions.append(info)
132
+
133
+ for extra in EXTENSIONS_WITH_DEPENDENCIES.get(info["name"], lambda: [])():
134
+ path = _copy_extension(extra, extensions_path)
135
+ info = _extension_info(extra)
136
+ info["path"] = path
137
+ extensions_deps.append(info)
138
+
139
+ return extensions, extensions_deps
140
+
141
+
142
+ def _copy_extension(full_path, extensions_dir):
143
+ full_path = os.path.realpath(full_path)
144
+
145
+ prefixes = site.getsitepackages()
146
+ if site.ENABLE_USER_SITE:
147
+ prefixes.append(site.getusersitepackages())
148
+
149
+ # this also takes care of extensions installed directly in the same prefix as
150
+ # Python, for example when installing a standalone libmetatensor_torch with conda.
151
+ prefixes.append(sys.prefix)
152
+
153
+ path = full_path
154
+ for prefix in prefixes:
155
+ prefix = os.path.realpath(prefix)
156
+ assert os.path.isabs(prefix)
157
+
158
+ # Remove any local prefix
159
+ if path.startswith(prefix):
160
+ path = os.path.relpath(path, prefix)
161
+ break
162
+
163
+ if extensions_dir is not None:
164
+ collect_path = os.path.realpath(os.path.join(extensions_dir, path))
165
+ if collect_path == path:
166
+ raise RuntimeError(
167
+ f"extensions directory '{extensions_dir}' would overwrite files, "
168
+ "you should set it to a local path instead"
169
+ )
170
+
171
+ if os.path.exists(collect_path):
172
+ raise RuntimeError(
173
+ f"more than one extension would be collected at {collect_path}"
174
+ )
175
+
176
+ os.makedirs(os.path.dirname(collect_path), exist_ok=True)
177
+ shutil.copyfile(full_path, collect_path)
178
+
179
+ return path
180
+
181
+
182
+ def _extension_info(path):
183
+ # get the name of the library, excluding any shared object prefix/suffix
184
+ name = os.path.basename(path)
185
+ if name.startswith("lib"):
186
+ name = name[3:]
187
+
188
+ if name.endswith(".so"):
189
+ name = name[:-3]
190
+
191
+ if name.endswith(".dll"):
192
+ name = name[:-4]
193
+
194
+ if name.endswith(".dylib"):
195
+ name = name[:-6]
196
+
197
+ # Collect the hash of the extension shared library. We don't currently use
198
+ # this, but it would allow for binary-level reproducibility later.
199
+ with open(path, "rb") as fd:
200
+ sha256 = hashlib.sha256(fd.read()).hexdigest()
201
+
202
+ return {"name": name, "sha256": sha256}