Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Devin Jeanpierre
On Sun, Dec 14, 2014 at 11:29 PM, Ben Finney ben+pyt...@benfinney.id.au wrote:
 I'm slowly learning about making Python code that will run under both
 Python 2 (version 2.6 or above) and Python 3 (version 3.2 or above).

 This entails, I believe, the admonition to ensure text literals are
 Unicode by default::

 from __future__ import unicode_literals

Ordinarily, for 2.x/3.3+ code I would suggest not doing this --
instead, b'...' for bytes, u'...' for unicode, and '...' for native
string type (bytes on 2.x, unicode on 3.x). This is the main benefit
of the u'...' syntax addition.

For 3.2, you'll have to do b'...' for bytes, '...' for unicode, and
str('...') for platform-specific strings (bytes on 2.x, unicode on
3.x).  It is in good taste to make an alias of str so it's less
confusing, e.g. native_str = str.

So for example, it's __import__(str(foo)), and getattr(foo, str(bar), baz).

-- Devin
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Steven D'Aprano
Ben Finney wrote:

 Howdy all,
 
 What should I do, in a world where all text literals are Unicode by
 default, to make ‘__import__’ work in both Python 2 and 3?

One approach I frequently use is a conditional import. Off the top of my
head, I'd do something like this:


try:
import builtins  # Python 3.x.
except ImportError:
# We're probably running Python 2.x.
import __builtin__ as builtins

# Untested, just to give you an idea of what I mean.
try:
_ = __import__(sys, fromlist=[version])
except TypeError:
# Shadow the built-in with a patched version.
def __import__(*args, **kwargs):
if fromlist in kwargs:
kwargs[fromlist] = [str(name) for name in kwargs[fromlist]]
return builtins.__import__(*args, **kwargs)

If you're really brave, you can even monkey-patch builtins with your own
version. Obviously you still need to keep the old version somewhere. A
closure would be perfect for that:

# Again, untested.
def patch_import(original_import=__import__):
def __import__(*args, **kwargs):
if fromlist in kwargs:
kwargs[fromlist] = [str(name) for name in kwargs[fromlist]]
return original_import(*args, **kwargs)
builtins.__import__ = __import__

Monkey-patching builtins.__import__ is one of the few
not-completely-frowned-upon uses of monkey-patching.

Perhaps a better approach might be to eschew the use of __import__ and see
whether the functions in imputil (deprecated) or importlib do what you
need.

https://docs.python.org/2/library/imputil.html
https://docs.python.org/3/library/importlib.html

Aside: __import__ is not recommended for user code.

Direct use of __import__() is also discouraged in favor of
importlib.import_module().

https://docs.python.org/3/library/functions.html#__import__



-- 
Steven

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Ethan Furman
On 12/14/2014 11:29 PM, Ben Finney wrote:
 
 This entails, I believe, the admonition to ensure text literals are
 Unicode by default::
 
 from __future__ import unicode_literals
 
 and to specify bytes literals explicitly with ‘b'wibble'’ if needed.

For some scripts this works great (I have some), and for some it does not (I 
have some of those, too).

 The ‘__import__’ built-in function, though, is tripping up.

One work-around I have used is:

  if isinstance(some_var, bytes):
  some_var = some_var.decode('ascii')
  # at this point some_var is unicode in both Pythons

--
~Ethan~



signature.asc
Description: OpenPGP digital signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Portable code: ‘from __future__ import unicode_literals’ a good idea? (was: Portable code: __import__ demands different string types between 2 and 3)

2014-12-15 Thread Ben Finney
Devin Jeanpierre jeanpierr...@gmail.com writes:

 On Sun, Dec 14, 2014 at 11:29 PM, Ben Finney ben+pyt...@benfinney.id.au 
 wrote:
  from __future__ import unicode_literals

 Ordinarily, for 2.x/3.3+ code I would suggest not doing this --
 instead, b'...' for bytes, u'...' for unicode, and '...' for native
 string type (bytes on 2.x, unicode on 3.x). This is the main benefit
 of the u'...' syntax addition.

That latter point would seemingly also apply to ‘from __future__ import
unicode_literals’, so is moot in this context.

As for the advice to avoid such a declaration, you're arguing against
the official guide for porting Python 2 code to 2-and-3 compatible code:

For text you should either use the from __future__ import
unicode_literals statement or add a u prefix to the text literal.


URL:https://docs.python.org/3.4/howto/pyporting.html#text-versus-binary-data

So, the declarative import is specifically recommended. You'll need to
present a case for why I shouldn't follow that recommendation.

-- 
 \   “I disapprove of what you say, but I will defend to the death |
  `\ your right to say it.” —Evelyn Beatrice Hall, _The Friends of |
_o__)  Voltaire_, 1906 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: ‘from __future__ import unicode_literals’ a good idea? (was: Portable code: __import__ demands different string types between 2 and 3)

2014-12-15 Thread Devin Jeanpierre
On Mon, Dec 15, 2014 at 4:42 PM, Ben Finney ben+pyt...@benfinney.id.au wrote:
 Devin Jeanpierre jeanpierr...@gmail.com writes:

 On Sun, Dec 14, 2014 at 11:29 PM, Ben Finney ben+pyt...@benfinney.id.au 
 wrote:
  from __future__ import unicode_literals

 Ordinarily, for 2.x/3.3+ code I would suggest not doing this --
 instead, b'...' for bytes, u'...' for unicode, and '...' for native
 string type (bytes on 2.x, unicode on 3.x). This is the main benefit
 of the u'...' syntax addition.

 That latter point would seemingly also apply to ‘from __future__ import
 unicode_literals’, so is moot in this context.

 As for the advice to avoid such a declaration, you're arguing against
 the official guide for porting Python 2 code to 2-and-3 compatible code:

 For text you should either use the from __future__ import
 unicode_literals statement or add a u prefix to the text literal.

 
 URL:https://docs.python.org/3.4/howto/pyporting.html#text-versus-binary-data

 So, the declarative import is specifically recommended. You'll need to
 present a case for why I shouldn't follow that recommendation.

All the doc is saying there is to use unicode for text, in a way that
works in both 2.x and 3.x. I am saying that the unicode_literals
import is not necessarily the best way to do so.

Python has three categories of strings: things that should be bytes in
2.x and 3.x, things that should be unicode in 2.x and 3.x, and things
that should be bytes on 2.x and unicode on 3.x. The last category
applies mostly to identifiers -- the strings you pass to functions
like getattr or __import__, and the strings that are valid keys in
**kwargs, and so on. You need a way to represent the last kind of
string, whether it's unadorned ... literals, or function calls like
identifier_string(...). If you can solve that, you are good. The
official porting guide offers no advice.

-- Devin
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Ben Finney
Ethan Furman et...@stoneleaf.us writes:

 On 12/14/2014 11:29 PM, Ben Finney wrote:
  The ‘__import__’ built-in function, though, is tripping up.

 One work-around I have used is:

   if isinstance(some_var, bytes):
   some_var = some_var.decode('ascii')
   # at this point some_var is unicode in both Pythons

That's not the issue at all. I know how to declare a literal such that
it is Unicode in Python 2 and Python 3 (that's what the ‘from __future__
import unicode_literals’ does).

Rather, the problem is ‘__import__’ having incompatible expectations:
the ‘fromlist’ parameter's items must be bytes in Python 2, and must be
Unicode in Python 3.

The same parameter value can't satisfy both those requirements in a
single 2-and-3 compatible code base, without either using the
bytes-and-text ambiguity of ‘str’, or some kludge over-riding a simple
‘__import__’ call. Both of those are ugly and make for buggy code.

I'm increasingly of the opinion this is a subtle bug in ‘__import__’
that should be fixed instead of worked around. But it will likely be
rejected because the documentation advises against using ‘__import__’.
Bah.

-- 
 \ “Try adding “as long as you don't breach the terms of service – |
  `\  according to our sole judgement” to the end of any cloud |
_o__)  computing pitch.” —Simon Phipps, 2010-12-11 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Ethan Furman
On 12/15/2014 05:36 PM, Ben Finney wrote:
 
 That's not the issue at all. I know how to declare a literal such that
 it is Unicode in Python 2 and Python 3 (that's what the ‘from __future__
 import unicode_literals’ does).
 
 Rather, the problem is ‘__import__’ having incompatible expectations:
 the ‘fromlist’ parameter's items must be bytes in Python 2, and must be
 Unicode in Python 3.

Ah.  Well, then you do not want the `unicode_literals` import or it won't be 
bytes in Python 2 (as I'm sure you know).


 The same parameter value can't satisfy both those requirements in a
 single 2-and-3 compatible code base, without either using the
 bytes-and-text ambiguity of ‘str’, or some kludge over-riding a simple
 ‘__import__’ call. Both of those are ugly and make for buggy code.

If this is for a large(ish) application, make one module with stuff that needs 
the ambiguity to work correctly.  Comment
the heck out of it.  ;)


 I'm increasingly of the opinion this is a subtle bug in ‘__import__’
 that should be fixed instead of worked around.

Of course.  But you'll still need to work around it for previous versions, 
unless you can say you only support 2.7.10+
(maybe 2.7.9+ if it gets fixed quick enough).

 But it will likely be rejected because the documentation advises against 
 using ‘__import__’.

Functions that should accept str but barf on unicode have a tendency to get 
fixed.

--
~Ethan~



signature.asc
Description: OpenPGP digital signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Steven D'Aprano
Ben Finney wrote:

 I'm increasingly of the opinion this is a subtle bug in ‘__import__’
 that should be fixed instead of worked around. But it will likely be
 rejected because the documentation advises against using ‘__import__’.
 Bah.

I think its worth raising an issue on the bug tracker and see what the core
devs think.

If you don't have and won't open an account on the tracker, if you can come
up with a minimal example that demonstrates the issue, I'll open an issue
for you. (But I'd rather you did it yourself :-)



-- 
Steven

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Ben Finney
Ethan Furman et...@stoneleaf.us writes:

 On 12/15/2014 05:36 PM, Ben Finney wrote:
  I'm increasingly of the opinion this is a subtle bug in ‘__import__’
  that should be fixed instead of worked around.

And other people agree: URL:https://bugs.python.org/issue21720.

 Of course. But you'll still need to work around it for previous
 versions, unless you can say you only support 2.7.10+ (maybe 2.7.9+ if
 it gets fixed quick enough).

If I can eventually drop the kludge by dropping support for Python 2.7
at some future point (instead of waiting until I can drop Python 3),
that would still be an improvement.

 Functions that should accept str but barf on unicode have a tendency
 to get fixed.

I hope you're right. The bug report currently focusses on improving the
error message only.

-- 
 \  “They who can give up essential liberty to obtain a little |
  `\temporary safety, deserve neither liberty nor safety.” |
_o__)   —Benjamin Franklin, 1775-02-17 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-15 Thread Ben Finney
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:

 If you don't have and won't open an account on the tracker, if you can
 come up with a minimal example that demonstrates the issue, I'll open
 an issue for you. (But I'd rather you did it yourself :-)

Thank you for the offer. Fortunately, the Python bug tracker does not
require me to maintain credentials for that site (I can use any OpenID),
so I've got an account there.

-- 
 \ “Our task must be to free ourselves from our prison by widening |
  `\our circle of compassion to embrace all humanity and the whole |
_o__)   of nature in its beauty.” —Albert Einstein |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Portable code: __import__ demands different string types between 2 and 3

2014-12-14 Thread Ben Finney
Howdy all,

What should I do, in a world where all text literals are Unicode by
default, to make ‘__import__’ work in both Python 2 and 3?


I'm slowly learning about making Python code that will run under both
Python 2 (version 2.6 or above) and Python 3 (version 3.2 or above).

This entails, I believe, the admonition to ensure text literals are
Unicode by default::

from __future__ import unicode_literals

and to specify bytes literals explicitly with ‘b'wibble'’ if needed.


The ‘__import__’ built-in function, though, is tripping up.

A contrived, trivial project layout::

$ mkdir fooproject/
$ cd fooproject/

$ mkdir foo/
$ printf   foo/__init__.py
$ mkdir foo/bar/
$ printf   foo/bar/__init__.py

Here's a simple ‘fooproject/setup.py’::

from __future__ import unicode_literals

main_module_name = 'foo'
main_module = __import__(main_module_name, fromlist=['bar'])

assert main_module.bar

That fails under Python 2, but runs fine under Python 3::

$ python2 ./setup.py
Traceback (most recent call last):
  File ./setup.py, line 4, in module
main_module = __import__(main_module_name, fromlist=['bar'])
TypeError: Item in ``from list'' not a string

$ python3 ./setup.py

We've deliberately made unadorned strings Unicode by default. By “not a
string”, I presume Python 2 means “not a ‘bytes’ object”.


Okay, so we'll explicitly set that to a ‘bytes’ literal::

from __future__ import unicode_literals

main_module_name = 'foo'
main_module = __import__(main_module_name, fromlist=[b'bar'])

assert main_module.bar

Now Python 2 is satisfied, but Python 3 complains::

$ python2 ./setup.py

$ python3 ./setup.py
Traceback (most recent call last):
  File ./setup.py, line 4, in module
main_module = __import__(main_module_name, fromlist=[b'bar'])
  File frozen importlib._bootstrap, line 2281, in
_handle_fromlist
TypeError: hasattr(): attribute name must be string


How can I get that ‘__import__’ call, complete with its ‘fromlist’
parameter, working correctly under both Python 2 and Python 3, keeping
the ‘unicode_literals’ setting?

If some kind of kludge is needed to make it work between versions, is
this a bug which should be fixed so “use Unicode for text” remains
applicable advice?

-- 
 \   “I do not believe in immortality of the individual, and I |
  `\consider ethics to be an exclusively human concern with no |
_o__)  superhuman authority behind it.” —Albert Einstein, letter, 1953 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Portable code: __import__ demands different string types between 2 and 3

2014-12-14 Thread Zachary Ware
On Mon, Dec 15, 2014 at 1:29 AM, Ben Finney ben+pyt...@benfinney.id.au wrote:
 How can I get that ‘__import__’ call, complete with its ‘fromlist’
 parameter, working correctly under both Python 2 and Python 3, keeping
 the ‘unicode_literals’ setting?

How about str('bar')?

 If some kind of kludge is needed to make it work between versions, is
 this a bug which should be fixed so “use Unicode for text” remains
 applicable advice?

Fixed in which version?  The problem is that Python 2 mixes the
notions of strings and bytestrings, and the solution is Python 3.

I believe six also has a give me a native string function to help with this.

-- 
Zach
-- 
https://mail.python.org/mailman/listinfo/python-list