diff --git a/README.md b/README.md index c98afe2..5a4df0b 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,13 @@ If you have an actual file on disk, you can get the most information possible {'directory'} >>> identify.tags_from_path('/path/to/symlink') {'symlink'} +>>> identify.tags_from_path('/path/to/named-pipe') +{'fifo'} ``` When using a file on disk, the checks performed are: -* File type (file, symlink, directory, socket) +* File type (file, symlink, directory, socket, fifo) * Mode (is it executable?) * File name (mostly based on extension) * If executable, the shebang is read and the interpreter interpreted @@ -123,7 +125,8 @@ licenses are sourced from [choosealicense.com]. A call to `tags_from_path` does this: -1. What is the type: file, symlink, directory? If it's not file, stop here. +1. If the path is a directory, symlink, socket, or named pipe, return its type + tag immediately. 2. Is it executable? Add the appropriate tag. 3. Do we recognize the file extension? If so, add the appropriate tags, stop here. These tags would include binary/text. diff --git a/identify/identify.py b/identify/identify.py index 0279ba8..8064239 100644 --- a/identify/identify.py +++ b/identify/identify.py @@ -20,13 +20,14 @@ DIRECTORY = 'directory' SYMLINK = 'symlink' SOCKET = 'socket' +FIFO = 'fifo' FILE = 'file' EXECUTABLE = 'executable' NON_EXECUTABLE = 'non-executable' TEXT = 'text' BINARY = 'binary' -TYPE_TAGS = frozenset((DIRECTORY, FILE, SYMLINK, SOCKET)) +TYPE_TAGS = frozenset((DIRECTORY, FILE, SYMLINK, SOCKET, FIFO)) MODE_TAGS = frozenset((EXECUTABLE, NON_EXECUTABLE)) ENCODING_TAGS = frozenset((BINARY, TEXT)) _ALL_TAGS = {*TYPE_TAGS, *MODE_TAGS, *ENCODING_TAGS} @@ -50,6 +51,8 @@ def tags_from_path(path: str) -> set[str]: return {SYMLINK} if stat.S_ISSOCK(mode): return {SOCKET} + if stat.S_ISFIFO(mode): + return {FIFO} tags = {FILE} diff --git a/tests/identify_test.py b/tests/identify_test.py index a82f353..7d58af4 100644 --- a/tests/identify_test.py +++ b/tests/identify_test.py @@ -20,6 +20,7 @@ def test_all_tags_includes_basic_ones(): assert 'executable' in identify.ALL_TAGS assert 'text' in identify.ALL_TAGS assert 'socket' in identify.ALL_TAGS + assert 'fifo' in identify.ALL_TAGS @pytest.mark.parametrize( @@ -71,6 +72,17 @@ def test_tags_from_path_socket(): assert tags == {'socket'} +@pytest.mark.skipif(not hasattr(os, 'mkfifo'), reason='requires mkfifo') +def test_tags_from_path_fifo(tmpdir): + fifo_path = tmpdir.join('pipe').strpath + os.mkfifo(fifo_path) + + with mock.patch.object(builtins, 'open') as open_mock: + assert identify.tags_from_path(fifo_path) == {'fifo'} + + open_mock.assert_not_called() + + def test_tags_from_path_broken_symlink(tmpdir): x = tmpdir.join('foo') x.mksymlinkto(tmpdir.join('lol'))