Skip to content

Commit

Permalink
Merge 114d415 into 710daf2
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Jan 30, 2019
2 parents 710daf2 + 114d415 commit 04e53a4
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 3 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -5,12 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.2.2] - Unreleased
## [2.3.0] - 2018-01-30

### Fixed

- IllegalBackReference had mangled error message

### Added

- FS.hash method

## [2.2.1] - 2018-01-06

### Fixed
Expand Down
3 changes: 2 additions & 1 deletion docs/source/implementers.rst
Expand Up @@ -130,8 +130,9 @@ These methods SHOULD NOT be implemented.

Implementing these is highly unlikely to be worthwhile.

* :meth:`~fs.base.FS.check`
* :meth:`~fs.base.FS.getbasic`
* :meth:`~fs.base.FS.getdetails`
* :meth:`~fs.base.FS.check`
* :meth:`~fs.base.FS.hash`
* :meth:`~fs.base.FS.match`
* :meth:`~fs.base.FS.tree`
1 change: 1 addition & 0 deletions docs/source/interface.rst
Expand Up @@ -25,6 +25,7 @@ The following is a complete list of methods on PyFilesystem objects.
* :meth:`~fs.base.FS.gettype` Get the type of a resource.
* :meth:`~fs.base.FS.geturl` Get a URL to a resource, if one exists.
* :meth:`~fs.base.FS.hassyspath` Check if a resource maps to the OS filesystem.
* :meth:`~fs.base.FS.hash` Get the hash of a file's contents.
* :meth:`~fs.base.FS.hasurl` Check if a resource has a URL.
* :meth:`~fs.base.FS.isclosed` Check if the filesystem is closed.
* :meth:`~fs.base.FS.isempty` Check if a directory is empty.
Expand Down
2 changes: 1 addition & 1 deletion fs/_version.py
@@ -1,3 +1,3 @@
"""Version, used in module and setup.py.
"""
__version__ = "2.2.2a0"
__version__ = "2.3.0"
38 changes: 38 additions & 0 deletions fs/base.py
Expand Up @@ -9,6 +9,7 @@
from __future__ import absolute_import, print_function, unicode_literals

import abc
import hashlib
import itertools
import os
import threading
Expand Down Expand Up @@ -65,6 +66,7 @@
def _new_name(method, old_name):
"""Return a method with a deprecation warning."""
# Looks suspiciously like a decorator, but isn't!

@wraps(method)
def _method(*args, **kwargs):
warnings.warn(
Expand All @@ -75,6 +77,14 @@ def _method(*args, **kwargs):
)
return method(*args, **kwargs)

_method.__doc__ += """
Note:
.. deprecated:: 2.2.0
Please use `~{}`
""".format(
method.__name__
)

return _method


Expand Down Expand Up @@ -1593,3 +1603,31 @@ def tree(self, **kwargs):
from .tree import render

render(self, **kwargs)

def hash(self, path, name):
# type: (Text, Text) -> Text
"""Get the hash of a file's contents.
Arguments:
path(str): A path on the filesystem.
name(str): One of the algorithms supported by the hashlib module, e.g. `"md5"`
Returns:
str: The hex digest of the hash.
Raises:
fs.errors.UnsupportedHash: If the requested hash is not supported.
"""
_path = self.validatepath(path)
try:
hash_object = hashlib.new(name)
except ValueError:
raise errors.UnsupportedHash("hash '{}' is not supported".format(name))
with self.openbin(path) as binary_file:
while True:
chunk = binary_file.read(1024 * 1024)
if not chunk:
break
hash_object.update(chunk)
return hash_object.hexdigest()
9 changes: 9 additions & 0 deletions fs/errors.py
Expand Up @@ -364,3 +364,12 @@ def __init__(self, path):

def __reduce__(self):
return type(self), (self.path,)


class UnsupportedHash(ValueError):
"""The requested hash algorithm is not supported.
This exception will be thrown if a hash algorithm is requested that is
not supported by hashlib.
"""
2 changes: 2 additions & 0 deletions fs/info.py
Expand Up @@ -45,6 +45,8 @@ class Info(object):
"""

__slots__ = ["raw", "_to_datetime", "namespaces"]

def __init__(self, raw_info, to_datetime=epoch_to_datetime):
# type: (RawInfo, ToDatetime) -> None
"""Create a resource info object from a raw info dict.
Expand Down
7 changes: 7 additions & 0 deletions fs/test.py
Expand Up @@ -1834,3 +1834,10 @@ def test_case_sensitive(self):
def test_glob(self):
self.assertIsInstance(self.fs.glob, glob.BoundGlobber)

def test_hash(self):
self.fs.writebytes("hashme.txt", b"foobar" * 1024)
self.assertEqual(
self.fs.hash("hashme.txt", "md5"), "9fff4bb103ab8ce4619064109c54cb9c"
)
with self.assertRaises(errors.UnsupportedHash):
self.fs.hash("hashme.txt", "nohash")
1 change: 1 addition & 0 deletions tests/test_new_name.py
Expand Up @@ -9,6 +9,7 @@

class TestNewNameDecorator(unittest.TestCase):
def double(self, n):
"Double a number"
return n * 2

times_2 = _new_name(double, "times_2")
Expand Down

0 comments on commit 04e53a4

Please sign in to comment.