changeset 273:c02a57df2a29

treesum: - WalkDirEntry tests with a FIFO special file - WalkDirEntry.is_reg Also all to be handled special files in the notes document.
author Franz Glasner <fzglas.hg@dom66.de>
date Wed, 19 Feb 2025 09:12:30 +0100
parents b4137ebd8e79
children 224725fd9f2f
files cutils/util/walk.py docs/notes.rst tests/_test_setup.py tests/test_walk.py
diffstat 4 files changed, 103 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/cutils/util/walk.py	Tue Feb 18 19:30:40 2025 +0100
+++ b/cutils/util/walk.py	Wed Feb 19 09:12:30 2025 +0100
@@ -21,6 +21,7 @@
         from scandir import scandir
     except ImportError:
         scandir = None
+import stat
 import sys
 
 from . import PY2
@@ -53,8 +54,8 @@
     """
 
     __slots__ = ("_name", "_path",     # encoded as given in the ctor
-                 "_is_symlink", "_is_dir", "_stat_result", "_stat_errno",
-                 "_stat_errstr",
+                 "_is_symlink", "_is_reg", "_is_dir", "_stat_result",
+                 "_stat_errno", "_stat_errstr",
                  "_alt_fsname", "_alt_u8name")
 
     def __init__(self, name, path):
@@ -62,7 +63,7 @@
         """The name exactly as given in the ctor"""
         self._path = _unix_path(path)
         """The path as given in the ctor -- but normalized to have slashes"""
-        self._is_symlink = self._is_dir = self._stat_result = \
+        self._is_symlink = self._is_reg = self._is_dir = self._stat_result = \
             self._stat_errno = self._stat_errstr = None
         self._alt_fsname = self._alt_u8name = _notset
 
@@ -227,6 +228,10 @@
         return self._is_symlink
 
     @property
+    def is_reg(self):
+        return self._is_reg
+
+    @property
     def is_dir(self):
         return self._is_dir
 
@@ -278,6 +283,9 @@
             w._stat_result = None
             w._stat_errno = e.errno
             w._stat_errstr = e.strerror
+            w._is_reg = False
+        else:
+            w._is_reg = stat.S_ISREG(w._stat_result.st_mode)
         return w
 
     @classmethod
@@ -307,6 +315,9 @@
                 w._stat_result = None
                 w._stat_errno = e.errno
                 w._stat_errstr = e.strerror
+                w._is_reg = False
+            else:
+                w._is_reg = stat.S_ISREG(w._stat_result.st_mode)
         return w
 
     @classmethod
--- a/docs/notes.rst	Tue Feb 18 19:30:40 2025 +0100
+++ b/docs/notes.rst	Wed Feb 19 09:12:30 2025 +0100
@@ -19,11 +19,45 @@
 
       Symlink to a directory
 
-  ``/./@`
+  ``/./@``
 
       Symlink to other filesystem object.
       Also used if the link is broken and the target type cannot determined.
 
+  ``/./|``, ``/./@|``
+
+      FIFO and symlink to FIFO
+
+  ``/./=``, ``/./@=``
+
+      Socket and symlink to socket
+
+  ``/./>``,  ``/./@>``
+
+      Door (Solaris) and symlink to door
+
+  ``/./%``,  ``/./@%``
+
+      Whiteout and symlink to whiteout (used by union filesystems) (BSD)
+
+Nonstandard path endings are:
+
+   ``/./:``, ``/./@:``
+
+      Character special and symlink to character special
+
+   ``/./;``, ``/./@;``
+
+      Block special and symlink to block special
+
+  ``/./)``, ``/./@)``
+
+      Event port (Solaris, Illumos) and symlink to event port
+
+
+.. note:: :command:`ls` can also use ``*`` for regular files that are
+          executable.
+
 
 Fields:
 
--- a/tests/_test_setup.py	Tue Feb 18 19:30:40 2025 +0100
+++ b/tests/_test_setup.py	Wed Feb 19 09:12:30 2025 +0100
@@ -9,6 +9,7 @@
 
 TESTDIR = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
 DATADIR = os.path.join(TESTDIR, "data")
+TMPDIR = os.path.join(DATADIR, "_tmp")
 
 
 sys.path.insert(0, os.path.join(TESTDIR, ".."))
--- a/tests/test_walk.py	Tue Feb 18 19:30:40 2025 +0100
+++ b/tests/test_walk.py	Wed Feb 19 09:12:30 2025 +0100
@@ -6,10 +6,11 @@
 from __future__ import absolute_import, print_function
 
 import os
+import shutil
 import sys
 import unittest
 
-from _test_setup import DATADIR
+from _test_setup import (DATADIR, TMPDIR)
 
 from cutils.util import walk
 
@@ -206,6 +207,7 @@
         entry = walk.WalkDirEntry.from_path_name(
             DATADIR, "tree-1-s")
         self.assertTrue(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
         self.assertTrue(entry.is_dir)
         self.assertIsNotNone(entry.stat)
 
@@ -213,6 +215,7 @@
         entry = walk.WalkDirEntry.from_path_name(
             DATADIR, "tree-nn-s")
         self.assertTrue(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
         self.assertFalse(entry.is_dir)
         self.assertIsNone(entry.stat)
 
@@ -220,6 +223,7 @@
         entry = walk.WalkDirEntry.from_path_name(
             DATADIR, "tree-1")
         self.assertFalse(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
         self.assertTrue(entry.is_dir)
         self.assertIsNotNone(entry.stat)
 
@@ -227,6 +231,7 @@
         entry = walk.WalkDirEntry.from_path_name(
             os.path.join(DATADIR, "tree-1"), "file-1-1-s.txt")
         self.assertTrue(entry.is_symlink)
+        self.assertTrue(entry.is_reg)
         self.assertFalse(entry.is_dir)
         self.assertIsNotNone(entry.stat)
 
@@ -234,6 +239,7 @@
         entry = walk.WalkDirEntry.from_path_name(
             os.path.join(DATADIR, "tree-1"), "file-1-nn-s.txt")
         self.assertTrue(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
         self.assertFalse(entry.is_dir)
         self.assertIsNone(entry.stat)
 
@@ -241,6 +247,7 @@
         entry = walk.WalkDirEntry.from_path_name(
             os.path.join(DATADIR, "tree-1"), "file-1-1.txt")
         self.assertFalse(entry.is_symlink)
+        self.assertTrue(entry.is_reg)
         self.assertFalse(entry.is_dir)
         self.assertIsNotNone(entry.stat)
 
@@ -248,9 +255,54 @@
         entry = walk.WalkDirEntry.from_path_name(
             os.path.join(DATADIR, "tree-1-s"), "file-1-1.txt")
         self.assertFalse(entry.is_symlink)
+        self.assertTrue(entry.is_reg)
         self.assertFalse(entry.is_dir)
         self.assertIsNotNone(entry.stat)
 
 
+@unittest.skipIf(not hasattr(os, "mkfifo"), "Needs os.mkfifo()")
+class FIFOTests(unittest.TestCase):
+
+    def setUp(self):
+        if not os.path.isdir(TMPDIR):
+            os.mkdir(TMPDIR)
+
+    def tearDown(self):
+        if os.path.isdir(TMPDIR):
+            shutil.rmtree(TMPDIR)
+
+    def test_real_mkfifo(self):
+        fifopath = os.path.join(TMPDIR, "test.fifo")
+        os.mkfifo(fifopath)
+        entry = walk.WalkDirEntry.from_path_name(TMPDIR, "test.fifo")
+        self.assertFalse(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
+        self.assertFalse(entry.is_dir)
+        self.assertIsNotNone(entry.stat)
+
+    def test_symlink_to_mkfifo(self):
+        fifopath = os.path.join(TMPDIR, "test.fifo")
+        fifo_s_path = os.path.join(TMPDIR, "test-s.fifo")
+        os.mkfifo(fifopath)
+        os.symlink(fifopath, fifo_s_path)
+        entry = walk.WalkDirEntry.from_path_name(TMPDIR, "test-s.fifo")
+        self.assertTrue(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
+        self.assertFalse(entry.is_dir)
+        self.assertIsNotNone(entry.stat)
+
+    def test_broken_symlink_to_mkfifo(self):
+        fifopath = os.path.join(TMPDIR, "test.fifo")
+        fifo_s_path = os.path.join(TMPDIR, "test-s.fifo")
+        os.mkfifo(fifopath)
+        os.symlink(fifopath, fifo_s_path)
+        os.unlink(fifopath)
+        entry = walk.WalkDirEntry.from_path_name(TMPDIR, "test-s.fifo")
+        self.assertTrue(entry.is_symlink)
+        self.assertFalse(entry.is_reg)
+        self.assertFalse(entry.is_dir)
+        self.assertIsNone(entry.stat)
+
+
 if __name__ == "__main__":
     sys.exit(unittest.main())