Skip to content

Commit 5f73afe

Browse files
authored
Merge pull request #1133 from mathics/hashfunctions
improving support for Hash and FileHash functions
2 parents f6abc00 + fcdb763 commit 5f73afe

2 files changed

Lines changed: 34 additions & 23 deletions

File tree

mathics/builtin/files.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,6 +3813,8 @@ class FileHash(Builtin):
38133813
<dt>'FileHash[$file$, $type$]'
38143814
<dd>returns an integer hash of the specified $type$ for the given $file$.</dd>
38153815
<dd>The types supported are "MD5", "Adler32", "CRC32", "SHA", "SHA224", "SHA256", "SHA384", and "SHA512".</dd>
3816+
<dt>'FileHash[$file$, $type$, $format$]'
3817+
<dd>gives a hash code in the specified format.</dd>
38163818
</dl>
38173819
38183820
>> FileHash["ExampleData/sunflowers.jpg"]
@@ -3841,19 +3843,20 @@ class FileHash(Builtin):
38413843
#> FileHash["ExampleData/sunflowers.jpg", xyzsymbol]
38423844
= FileHash[ExampleData/sunflowers.jpg, xyzsymbol]
38433845
#> FileHash["ExampleData/sunflowers.jpg", "xyzstr"]
3844-
= FileHash[ExampleData/sunflowers.jpg, xyzstr]
3846+
= FileHash[ExampleData/sunflowers.jpg, xyzstr, Integer]
38453847
#> FileHash[xyzsymbol]
38463848
= FileHash[xyzsymbol]
38473849
"""
38483850

38493851
rules = {
3850-
"FileHash[filename_String]": 'FileHash[filename, "MD5"]',
3852+
"FileHash[filename_String]": 'FileHash[filename, "MD5", "Integer"]',
3853+
"FileHash[filename_String, hashtype_String]": 'FileHash[filename, hashtype, "Integer"]',
38513854
}
38523855

38533856
attributes = ("Protected", "ReadProtected")
38543857

3855-
def apply(self, filename, hashtype, evaluation):
3856-
"FileHash[filename_String, hashtype_String]"
3858+
def apply(self, filename, hashtype, format, evaluation):
3859+
"FileHash[filename_String, hashtype_String, format_String]"
38573860
py_filename = filename.get_string_value()
38583861

38593862
try:
@@ -3866,7 +3869,7 @@ def apply(self, filename, hashtype, evaluation):
38663869
e.message(evaluation)
38673870
return
38683871

3869-
return Hash.compute(lambda update: update(dump), hashtype.get_string_value())
3872+
return Hash.compute(lambda update: update(dump), hashtype.get_string_value(), format.get_string_value())
38703873

38713874

38723875
class FileDate(Builtin):
@@ -4802,6 +4805,7 @@ def apply(self, pathname, evaluation):
48024805
return SymbolTrue
48034806
return SymbolFalse
48044807

4808+
48054809
class Needs(Builtin):
48064810
"""
48074811
<dl>
@@ -4921,7 +4925,6 @@ def apply(self, context, evaluation):
49214925
curr_ctxt = evaluation.definitions.get_current_context()
49224926
contextstr = curr_ctxt + contextstr[1:]
49234927
context = String(contextstr)
4924-
49254928
if not valid_context_name(contextstr):
49264929
evaluation.message('Needs', 'ctx', Expression(
49274930
'Needs', context), 1, '`')
@@ -4931,19 +4934,11 @@ def apply(self, context, evaluation):
49314934
if test_loaded.is_true():
49324935
# Already loaded
49334936
return SymbolNull
4934-
4935-
# TODO: Figure out why this raises the message:
4936-
# "Select::normal: Nonatomic expression expected."
4937-
already_loaded = Expression('MemberQ',
4938-
Symbol('System`$Packages'), context)
4939-
already_loaded = already_loaded.evaluate(evaluation).is_true()
4940-
if already_loaded:
4941-
return SymbolNull
4942-
49434937
result = Expression('Get', context).evaluate(evaluation)
49444938

49454939
if result == SymbolFailed:
49464940
evaluation.message("Needs", "nocont", context)
49474941
return SymbolFailed
49484942

49494943
return SymbolNull
4944+

mathics/builtin/numeric.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,8 @@ class Hash(Builtin):
13401340
<dt>'Hash[$expr$, $type$]'
13411341
<dd>returns an integer hash of the specified $type$ for the given $expr$.</dd>
13421342
<dd>The types supported are "MD5", "Adler32", "CRC32", "SHA", "SHA224", "SHA256", "SHA384", and "SHA512".</dd>
1343+
<dt>'Hash[$expr$, $type$, $format$]'
1344+
<dd>Returns the hash in the especified format.</dd>
13431345
</dl>
13441346
13451347
> Hash["The Adventures of Huckleberry Finn"]
@@ -1358,11 +1360,12 @@ class Hash(Builtin):
13581360
= 58042316473471877315442015469706095084
13591361
13601362
>> Hash[{a, b, c}, "xyzstr"]
1361-
= Hash[{a, b, c}, xyzstr]
1363+
= Hash[{a, b, c}, xyzstr, Integer]
13621364
"""
13631365

13641366
rules = {
1365-
"Hash[expr_]": 'Hash[expr, "MD5"]',
1367+
"Hash[expr_]": 'Hash[expr, "MD5", "Integer"]',
1368+
"Hash[expr_, type_String]": 'Hash[expr, type, "Integer"]',
13661369
}
13671370

13681371
attributes = ("Protected", "ReadProtected")
@@ -1380,17 +1383,30 @@ class Hash(Builtin):
13801383
}
13811384

13821385
@staticmethod
1383-
def compute(user_hash, py_hashtype):
1386+
def compute(user_hash, py_hashtype, py_format):
13841387
hash_func = Hash._supported_hashes.get(py_hashtype)
13851388
if hash_func is None: # unknown hash function?
13861389
return # in order to return original Expression
13871390
h = hash_func()
13881391
user_hash(h.update)
1389-
return from_python(int(h.hexdigest(), 16))
1390-
1391-
def apply(self, expr, hashtype, evaluation):
1392-
"Hash[expr_, hashtype_String]"
1393-
return Hash.compute(expr.user_hash, hashtype.get_string_value())
1392+
res = h.hexdigest()
1393+
if py_format in ('HexString', "HexStringLittleEndian") :
1394+
return from_python(res)
1395+
res = int(res, 16)
1396+
if py_format == "DecimalString":
1397+
return from_python(str(res))
1398+
elif py_format == "ByteArray":
1399+
print("Not implemented. Return a string")
1400+
return from_python(str(res))
1401+
# Default: Integer
1402+
return from_python(res)
1403+
1404+
1405+
def apply(self, expr, hashtype, outformat, evaluation):
1406+
"Hash[expr_, hashtype_String, outformat_String]"
1407+
return Hash.compute(expr.user_hash,
1408+
hashtype.get_string_value(),
1409+
outformat.get_string_value())
13941410

13951411

13961412
class TypeEscalation(Exception):

0 commit comments

Comments
 (0)