236781615dd35adca1dbed54f2393196fbd14523 — Stefan Schwarzer 6 years ago c6d0136
Add `__repr__` so that a `StatResult` looks like a named tuple (#91)

Contrary to the suggestion in ticket #91, I can't turn `StatResult`
into a `namedtuple`. Since `StatResult` inherits from `tuple`, its
constructor takes a tuple of the n items to use:

  StatResult((item1, item2, ...))

On the other hand, the constructor of a `namedtuple` takes n arguments
for the tuple items, so the API would become

  StatResult(item1, item2, ...)

Since `StatResult` is a public API for implementation of custom parsers,
I can't just change it.

However, since the main idea behind using a `namedtuple` is its
representation, I added a `__repr__` method so that the representation
of a `StatResult` looks like the representation of a `namedtuple`.

Note that the `__repr__` result is different under Python 2 and 3. The
strings in the `StatResult` are always unicode strings and those have
a `u` prefix under Python 2. I had considered removing the prefix for
Python 2, but I think this could be very confusing during debugging
(including for me).
2 files changed, 31 insertions(+), 2 deletions(-)

M ftputil/stat.py
M test/test_stat.py
M ftputil/stat.py => ftputil/stat.py +13 -1
@@ 1,4 1,4 @@
# Copyright (C) 2002-2014, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# Copyright (C) 2002-2015, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# and ftputil contributors (see `doc/contributors.txt`)
# See the file LICENSE for licensing terms.

@@ 63,6 63,18 @@ class StatResult(tuple):
            raise AttributeError("'StatResult' object has no attribute '{0}'".

    def __repr__(self):
        # "Invert" `_index_mapping` so that we can look up the names
        # for the tuple indices.
        index_to_name = dict((v, k) for k, v in self._index_mapping.items())
        argument_strings = []
        for index, item in enumerate(self):
        return "{0}({1})".format(type(self).__name__,
                                 ", ".join(argument_strings))

# FTP directory parsers

M test/test_stat.py => test/test_stat.py +18 -1
@@ 1,4 1,4 @@
# Copyright (C) 2003-2014, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# Copyright (C) 2003-2015, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# and ftputil contributors (see `doc/contributors.txt`)
# See the file LICENSE for licensing terms.

@@ 11,6 11,7 @@ import time
import unittest

import ftputil
import ftputil.compat
import ftputil.error
import ftputil.stat

@@ 365,6 366,22 @@ class TestLstatAndStat(unittest.TestCase):
        self.stat = \

    def test_repr(self):
        """Test if the `repr` result looks like a named tuple."""
        stat_result = self.stat._lstat("/home/sschwarzer/index.html")
        # Only under Python 2, unicode strings have the `u` prefix.
        if ftputil.compat.python_version == 2:
            expected_result = (
              "StatResult(st_mode=33188, st_ino=None, st_dev=None, "
              "st_nlink=1, st_uid=u'45854', st_gid=u'200', st_size=4604, "
              "st_atime=None, st_mtime=1421705460.0, st_ctime=None)")
            expected_result = (
              "StatResult(st_mode=33188, st_ino=None, st_dev=None, "
              "st_nlink=1, st_uid='45854', st_gid='200', st_size=4604, "
              "st_atime=None, st_mtime=1421705460.0, st_ctime=None)")
        self.assertEqual(repr(stat_result), expected_result)

    def test_failing_lstat(self):
        """Test whether `lstat` fails for a nonexistent path."""