Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #20373 > unrolled thread

Automatic Type Conversion to String

Started byBruce Eckel <lists.eckel@gmail.com>
First post2012-02-13 14:01 -0800
Last post2012-02-15 19:21 +0100
Articles 7 — 4 participants

Back to article view | Back to comp.lang.python


Contents

  Automatic Type Conversion to String Bruce Eckel <lists.eckel@gmail.com> - 2012-02-13 14:01 -0800
    Re: Automatic Type Conversion to String Chris Rebert <clp2@rebertia.com> - 2012-02-13 14:27 -0800
      Re: Automatic Type Conversion to String Bruce Eckel <lists.eckel@gmail.com> - 2012-02-13 15:18 -0800
      Re: Automatic Type Conversion to String Bruce Eckel <lists.eckel@gmail.com> - 2012-02-13 15:18 -0800
        Re: Automatic Type Conversion to String Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-02-14 15:26 +0100
          Re: Automatic Type Conversion to String Bruce Eckel <lists.eckel@gmail.com> - 2012-02-15 06:58 -0800
            Re: Automatic Type Conversion to String Ned Deily <nad@acm.org> - 2012-02-15 19:21 +0100

#20373 — Automatic Type Conversion to String

FromBruce Eckel <lists.eckel@gmail.com>
Date2012-02-13 14:01 -0800
SubjectAutomatic Type Conversion to String
Message-ID<090e2893-7a1c-4b11-9e45-974ed33e7b77@i18g2000yqf.googlegroups.com>
I'm creating a class to encapsulate OS paths, to reduce the visual
noise and typing from the os.path methods. I've got the class and nose
tests below, and everything works except the last test which I've
prefixed with XXX:

    def XXXtest_should_work(self):
        """
        Produces:
        open(self.p2, 'w').write('')
        TypeError: coercing to Unicode: need string or buffer, Path
found
        """
        open(self.p2, 'w').write('')
        assert self.p2

Note that I *have* a __str__(self) method to perform automatic
conversion to string, and I've commented out the __unicode__(self)
method because it wasn't getting called. The problem appears to be
that open() does not seem to be calling __str__ on its first argument,
but instead it appears to want a basestring and this doesn't
automatically call __str__.

I'm trying to write the Path class so I can just hand a Path object
to, for example, open() and have it produce the path string.

Thanks for any insights.
--------------------------- Class and tests, put in file path.py
------------------------------------

import os, os.path

class Path(object):
    def __init__(self, *args):
        """
        Each of *args is a piece of a path. Assemble them together.
        """
        if len(args) == 0:
            self.path = os.path.abspath('.')
        else:
            self.path = str(args[0]) # str() so you can add Path
objects
            if self.path.endswith(':'):
                self.path = os.path.join(self.path, os.sep)
        for a in args[1:]:
            self.path = os.path.join(self.path, str(a))
    def __add__(self, other):
        return Path(os.path.join(self.path, other))
    def __iadd__(self, other): # path += path
        self.path = os.path.join(self.path, other)
        return self # Allows chaining
    def __str__(self):
        return self.path
    # def __repr__(self):
        # assert not self.path, "__repr__"
        # return os.path.join(self.path)
    # def __unicode__(self):
        # assert not self.path, "__unicode__"
        # print "Unicode called %s" % self.path
        # return os.path.join(self.path)
    def __nonzero__(self):
        """
        Boolean test: does this path exist?
        So you can say "if path:"
        """
        return bool(os.path.exists(self.path))

"""
Nose tests. To run, you must first install nose:
pip install nose
Then:
nosetests path.py
"""
def test_platform():
    "First draft tests are Windows-based"
    import platform
    assert platform.system() == 'Windows'

class test_paths():
    def setUp(self):
        self.paths = [
            ("C:\\"),
            ("C:\\", "Users", "Bruce Eckel", "Downloads",
"AtomicScala.zip"),
            ("C:\\", "Users", "Bruce Eckel", "Dropbox",
"AtomicScala"),
        ]
        self.p1 = Path(self.paths[0])
        self.s1 = os.path.join(self.paths[0], "\\")
        self.p2 = Path(*self.paths[1])
        self.s2 = os.path.join(*self.paths[1])
        self.p3 = Path(*self.paths[2])
        self.s3 = os.path.join(*self.paths[2])
        self.p4 = self.p3 + "TestFile1.tmp"
        self.s4 = os.path.join(self.s3, "TestFile1.tmp")
        self.p5 = self.p3 + "TestFile2.tmp"
        self.s5 = os.path.join(self.s3, "TestFile2.tmp")
        self.p6 = Path("foo") + "bar" + "baz"

    def test1(self):
        "Root directory"
        print self.p1
        print self.s1
        assert str(self.p1) == self.s1

    def test2(self):
        "Full path to file"
        print "p2", self.p2
        print "s2", self.s2
        assert str(self.p2) == self.s2

    def test3(self):
        "Directory"
        assert str(self.p3) == self.s3

    def test4(self):
        "Plus operator"
        assert str(self.p4) == self.s4

    def test5(self):
        "Another temp file"
        assert str(self.p5) == self.s5

    def test6(self):
        "Chained operator +"
        assert str(self.p6) == r"foo\bar\baz"

    def test7(self):
        "Operator +="
        self.p6 += "bingo"
        assert str(self.p6) == r"foo\bar\baz\bingo"

    def test8(self):
        "Create a Path from a Path"
        p = Path(self.p3)
        assert p.path == self.s3

class test_existence:
    """
    Test for directory and path existence
    """
    def setUp(self):
        base = Path("C:", "Users", "Bruce Eckel", "Downloads")
        self.p1 = base + "TestFile1.tmp"
        self.p2 = base + "TestFile2.tmp"
        self.p3 = base + "TestFile3.tmp"

    def test_basestring(self):
        print type(self.p1)
        assert isinstance(self.p1.path, basestring)

    def test1(self):
        "p1 existence"
        open(self.p1.path, 'w').write('')
        assert self.p1

    def test2(self):
        "p2 existence"
        open(self.p2.path, 'w').write('')
        assert self.p2

    def test3(self):
        "p3 existence"
        assert not self.p3

    def XXXtest_should_work(self):
        """
        Produces:
        open(self.p2, 'w').write('')
        TypeError: coercing to Unicode: need string or buffer, Path
found
        """
        open(self.p2, 'w').write('')
        assert self.p2

[toc] | [next] | [standalone]


#20374

FromChris Rebert <clp2@rebertia.com>
Date2012-02-13 14:27 -0800
Message-ID<mailman.5782.1329172035.27778.python-list@python.org>
In reply to#20373
On Mon, Feb 13, 2012 at 2:01 PM, Bruce Eckel <lists.eckel@gmail.com> wrote:
> I'm creating a class to encapsulate OS paths, to reduce the visual
> noise and typing from the os.path methods. I've got the class and nose
> tests below, and everything works except the last test which I've
> prefixed with XXX:
>
>    def XXXtest_should_work(self):
>        """
>        Produces:
>        open(self.p2, 'w').write('')
>        TypeError: coercing to Unicode: need string or buffer, Path
> found
>        """
>        open(self.p2, 'w').write('')
>        assert self.p2
>
> Note that I *have* a __str__(self) method to perform automatic
> conversion to string, and I've commented out the __unicode__(self)
> method because it wasn't getting called. The problem appears to be
> that open() does not seem to be calling __str__ on its first argument,
> but instead it appears to want a basestring and this doesn't
> automatically call __str__.

Right. Python is strongly-typed, so it requires a genuine str here (as
opposed to just something that's stringifyable) and doesn't do an
coercion call to str() for you. Just like how str.join() doesn't
automatically call str() on the elements of the list you give it. This
is to prevent silliness like trying to open() e.g. a dict. Your
alternatives are:
- Learn to live with having to write open(str(path_obj))
- Add a .open() method to Path, and use that instead of the open()
built-in function
- Have Path subclass `str` or `unicode`

> I'm trying to write the Path class so I can just hand a Path object
> to, for example, open() and have it produce the path string.

Impossible, short of subclassing `str` or `unicode`.

Cheers,
Chris

[toc] | [prev] | [next] | [standalone]


#20378

FromBruce Eckel <lists.eckel@gmail.com>
Date2012-02-13 15:18 -0800
Message-ID<12584848.58.1329175135396.JavaMail.geo-discussion-forums@ynii32>
In reply to#20374
I'm willing to subclass str, but when I tried it before it became a little confusing -- I think mostly because anytime I assigned to self it seemed like it converted the whole object to a str rather than a Path. I suspect I don't know the proper idiom for doing this -- any hints? Thanks ...

[toc] | [prev] | [next] | [standalone]


#20379

FromBruce Eckel <lists.eckel@gmail.com>
Date2012-02-13 15:18 -0800
Message-ID<mailman.5785.1329175143.27778.python-list@python.org>
In reply to#20374
I'm willing to subclass str, but when I tried it before it became a little confusing -- I think mostly because anytime I assigned to self it seemed like it converted the whole object to a str rather than a Path. I suspect I don't know the proper idiom for doing this -- any hints? Thanks ...

[toc] | [prev] | [next] | [standalone]


#20401

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-02-14 15:26 +0100
Message-ID<eh7q09-69s.ln1@satorlaser.homedns.org>
In reply to#20379
Am 14.02.2012 00:18, schrieb Bruce Eckel:
> I'm willing to subclass str, but when I tried it before it became a
> little confusing -- I think mostly because anytime I assigned to self
> it seemed like it converted the whole object to a str rather than a
> Path. I suspect I don't know the proper idiom for doing this -- any
> hints? Thanks ...

Could it be that you missed the fact that strings are immutable? That 
means that you can't change the content of the object once it is 
initialized. In particular, it means that you e.g. have to override 
__new__ instead of __init__, because the content is already fixed when 
the latter is called.

Python strings rather behave like Java strings than C++ strings.

Uli

[toc] | [prev] | [next] | [standalone]


#20443

FromBruce Eckel <lists.eckel@gmail.com>
Date2012-02-15 06:58 -0800
Message-ID<dc097623-f377-4c7d-a065-13b58bf1cabd@n12g2000yqb.googlegroups.com>
In reply to#20401
> Could it be that you missed the fact that strings are immutable? That
> means that you can't change the content of the object once it is
> initialized. In particular, it means that you e.g. have to override
> __new__ instead of __init__, because the content is already fixed when
> the latter is called.
>
> Uli

Yes, that's what I missed, and it explains why I found examples of str
inheritance using __new__. I think this might end up being a puzzle I
poke at for awhile.

Also, I discovered that the attempt to create a "Path" class goes back
to 2006, where it created a lot of discussion and was finally shelved:
http://www.python.org/dev/peps/pep-0355/

A significant part of the problem seems to be that there was no
inheritance from str at the time, so maybe a lot of the issues they
ran into could be solved now.

[toc] | [prev] | [next] | [standalone]


#20455

FromNed Deily <nad@acm.org>
Date2012-02-15 19:21 +0100
Message-ID<mailman.5839.1329330127.27778.python-list@python.org>
In reply to#20443
In article 
<dc097623-f377-4c7d-a065-13b58bf1cabd@n12g2000yqb.googlegroups.com>,
 Bruce Eckel <lists.eckel@gmail.com> wrote:
> Also, I discovered that the attempt to create a "Path" class goes back
> to 2006, where it created a lot of discussion and was finally shelved:
> http://www.python.org/dev/peps/pep-0355/
> 
> A significant part of the problem seems to be that there was no
> inheritance from str at the time, so maybe a lot of the issues they
> ran into could be solved now.

You might want to take a look at pathlib, a current attempt at providing 
object-oriented paths, written by Antoine Pitrou, one of the Python core 
developers:

http://pypi.python.org/pypi/pathlib

-- 
 Ned Deily,
 nad@acm.org

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web