Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

isinstance(.., file) for Python 3

1,169 views
Skip to first unread message

Ulrich Eckhardt

unread,
Nov 8, 2012, 7:05:22 AM11/8/12
to
Hi!

I have two problems that are related and that I'd like to solve together.

Firstly, I have code that allows either a file or a string representing
its content as parameter. If the parameter is a file, the content is
read from the file. In Python 2, I used "isinstance(p, file)" to
determine whether the parameter p is a file. In Python 3, the
returnvalue of open() is of type _io.TextIOWrapper, while the built-in
class file doesn't exist, so I can't use that code.

Secondly, checking for the type is kind-of ugly, because it means that I
can't use an object that fits but that doesn't have the right type. In
other words, it breaks duck-typing. This is already broken in the Python
2 code, but since I have to touch the code anyway, I might as well fix
it on the way.

If possible, I'm looking for a solution that works for Pythons 2 and 3,
since I'm not fully through the conversion yet and have clients that
might use the older snake for some time before shedding their skin.

Suggestions?

Uli


MRAB

unread,
Nov 8, 2012, 7:54:15 AM11/8/12
to pytho...@python.org
Instead of checking whether it's a file, check whether it's a string!

Steven D'Aprano

unread,
Nov 8, 2012, 7:56:57 AM11/8/12
to
On Thu, 08 Nov 2012 13:05:22 +0100, Ulrich Eckhardt wrote:

> Firstly, I have code that allows either a file or a string representing
> its content as parameter. If the parameter is a file, the content is
> read from the file. In Python 2, I used "isinstance(p, file)" to
> determine whether the parameter p is a file. In Python 3, the
> returnvalue of open() is of type _io.TextIOWrapper,

Incorrect.

py> type(open('x', 'wb'))
<class '_io.BufferedWriter'>

The type returned by open will depend on what you open and how you open
it.

> while the built-in
> class file doesn't exist, so I can't use that code.

import io
file = io._IOBase

will probably work. But consider it a little smelly, since you're relying
on an implementation detail.


> Secondly, checking for the type is kind-of ugly, because it means that I
> can't use an object that fits but that doesn't have the right type. In
> other words, it breaks duck-typing. This is already broken in the Python
> 2 code, but since I have to touch the code anyway, I might as well fix
> it on the way.

if hasattr(obj, 'read'):
# object is file-like enough to treat as a file
pass

That means that you can also use io.StringIO objects as pseudo-files too.



--
Steven

Chris Angelico

unread,
Nov 8, 2012, 7:57:00 AM11/8/12
to pytho...@python.org
On Thu, Nov 8, 2012 at 11:05 PM, Ulrich Eckhardt
<ulrich....@dominolaser.com> wrote:
> Firstly, I have code that allows either a file or a string representing its
> content as parameter. If the parameter is a file, the content is read from
> the file. In Python 2, I used "isinstance(p, file)" to determine whether the
> parameter p is a file...

Can you use the inverted check "isinstance(p, str)"? It's more likely
that you'll want to pass a file-like object than a string-like object.
This would work on Python 2 as well, though it's semantically
different; to safely check for both Unicode and bytes strings on both
Py2 and Py3, this may work:

# Once-off:
try:
basestring
except NameError:
basestring = (str, bytes)

# Is p a string?
if isinstance(p, basestring):
pass

It abuses the fact that isinstance will happily accept the
'basestring' common supertype of both strings in Python 2, but will
equally happily accept a tuple of types.

ChrisA

Peter Otten

unread,
Nov 8, 2012, 8:02:23 AM11/8/12
to pytho...@python.org
In order of obviousness:
hasattr(p, "read")
not isinstance(p, str)
iter(p) is p

Or you change the interface

def f(*, contents=None, file=None):
if contents is None:
with open(file) as f:
contents = f.read()
... # work with contents



Duncan Booth

unread,
Nov 8, 2012, 8:58:11 AM11/8/12
to
Ulrich Eckhardt <ulrich....@dominolaser.com> wrote:

> If possible, I'm looking for a solution that works for Pythons 2 and 3,
> since I'm not fully through the conversion yet and have clients that
> might use the older snake for some time before shedding their skin.
>
> Suggestions?

Why bother checking types at all?

def foo(file_or_string):
try:
data = file_or_string.read()
except AttributeError:
data = file_or_string
... use data ...

--
Duncan Booth http://kupuguy.blogspot.com

Stefan Behnel

unread,
Nov 8, 2012, 10:14:37 AM11/8/12
to pytho...@python.org
Duncan Booth, 08.11.2012 14:58:
> Ulrich Eckhardt wrote:
>> If possible, I'm looking for a solution that works for Pythons 2 and 3,
>> since I'm not fully through the conversion yet and have clients that
>> might use the older snake for some time before shedding their skin.
>>
>> Suggestions?
>
> Why bother checking types at all?
>
> def foo(file_or_string):
> try:
> data = file_or_string.read()
> except AttributeError:
> data = file_or_string
> ... use data ...

Or, a tiny bit more safely:

try:
read = file_or_string.read
except AttributeError:
data = file_or_string
else:
data = read()

I'd rather go with one of the previous solutions, though.

Stefan


0 new messages