Repository: jmcgeheeiv/pyfakefs (python)

Issue:
Support for st_blocks field
**Is your feature request related to a problem? Please describe.**
I have a code that crawls filesystem. One of most important field that I read from stat is `st_blocks` (see: https://docs.python.org/3/library/os.html#os.stat_result.st_blocks)
Without that field my code fails with: `AttributeError: 'FakeStatResult' object has no attribute 'st_blocks'`

I know that theoretically, it doesn't have to be there but I haven't met POSIX filesystem that wouldn't have that field yet. 

**Describe the solution you'd like**
Add `st_blocks` field to FakeStatResult.

**Describe alternatives you've considered**
The only alternative I see right now is not to use pyfakefs and mock os calls by myself.


Pull request:
Add some support for st_blocks in stat result
- closes #722

Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 78da2e0..84e6bda 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,7 +3,11 @@ The released versions correspond to PyPi releases.
 
 ## Unreleased
 
-### Fixes 
+### New Features
+* added some support for `st_blocks` in stat result
+  (see [#722](../../issues/722))
+
+### Fixes
 * fixed handling of `O_TMPFILE` in `os.open` (caused handling of 
   `O_DIRECTORY` as `O_TMPFILE`) (see [#723](../../issues/723))
 
diff --git a/pyfakefs/helpers.py b/pyfakefs/helpers.py
index 3e1ae69..35bf67b 100644
--- a/pyfakefs/helpers.py
+++ b/pyfakefs/helpers.py
@@ -213,6 +213,23 @@ class FakeStatResult:
     def st_size(self, val: int) -> None:
         self._st_size = val
 
+    @property
+    def st_blocks(self) -> int:
+        """Return the number of 512-byte blocks allocated for the file.
+        Assumes a page size of 4096 (matches most systems).
+        Ignores that this may not be available under some systems,
+        and that the result may differ if the file has holes.
+        """
+        if self.is_windows:
+            raise AttributeError(
+                "'os.stat_result' object has no attribute 'st_blocks'")
+        page_size = 4096
+        blocks_in_page = page_size // 512
+        pages = self._st_size // page_size
+        if self._st_size % page_size:
+            pages += 1
+        return pages * blocks_in_page
+
     @property
     def st_file_attributes(self) -> int:
         if not self.is_windows:


Test patch:
diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py
index 6a34038..ecd856b 100644
--- a/pyfakefs/tests/fake_os_test.py
+++ b/pyfakefs/tests/fake_os_test.py
@@ -247,6 +247,31 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode)
         self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE])
 
+    def test_st_blocks(self):
+        self.check_posix_only()
+        file_path = self.make_path('foo1')
+        self.create_file(file_path, contents=b"")
+        self.assertEqual(0, self.os.stat(file_path).st_blocks)
+        file_path = self.make_path('foo2')
+        self.create_file(file_path, contents=b"t")
+        self.assertEqual(8, self.os.stat(file_path).st_blocks)
+        file_path = self.make_path('foo3')
+        self.create_file(file_path, contents=b"t" * 4095)
+        self.assertEqual(8, self.os.stat(file_path).st_blocks)
+        file_path = self.make_path('foo4')
+        self.create_file(file_path, contents=b"t" * 4096)
+        self.assertEqual(8, self.os.stat(file_path).st_blocks)
+        file_path = self.make_path('foo5')
+        self.create_file(file_path, contents=b"t" * 4097)
+        self.assertEqual(16, self.os.stat(file_path).st_blocks)
+
+    def test_no_st_blocks_in_windows(self):
+        self.check_windows_only()
+        file_path = self.make_path('foo')
+        self.create_file(file_path, contents=b"")
+        with self.assertRaises(AttributeError):
+            self.os.stat(file_path).st_blocks
+
     def test_stat_with_unc_path(self):
         self.skip_real_fs()
         self.check_windows_only()