Automatic Type Conversion to String

B

Bruce Eckel

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
 
C

Chris Rebert

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 orbuffer, 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
 
B

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 ...
 
B

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 ...
 
U

Ulrich Eckhardt

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
 
B

Bruce Eckel

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.
 
N

Ned Deily

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top