py3k concerns. An example

A

Aaron Watters

Why is the migration to py3k a concern?
For example I have libraries which use string%dictionary
substitution where the dictionary is actually an object
which emulates a dictionary. The __getitem__ for
the object can be very expensive and is only called when
needed by the string substitution.

In py3k string%dictionary is going away. Why?
I have no idea.

The replacement is a string.format(...) method
which supports dictionary calling.
string.format(**dictionary)
But dictionary
calling doesn't support dictionary emulation.
So in the example below the substitution works
but the call fails.

=== code

class fdict(dict):
def __getitem__(self, item):
return "got("+item+")"

def fn(**d):
print d["boogie"]

if __name__=="__main__":
fd = fdict()
print "attempting string substitution with fake dictionary"
print
print "hello there %(boogie)s" % fd # <-- works
print
print "now attempting function call with fake dictionary"
print
fn(**fd) # <-- fails

=== output

% python2.6 dtest.py
attempting string substitution with fake dictionary

hello there got(boogie)

now attempting function call with fake dictionary

Traceback (most recent call last):
File "dtest.py", line 17, in <module>
fn(**fd)
File "dtest.py", line 7, in fn
print d["boogie"]
KeyError: 'boogie'

==== end of output

Consequently there is no simple way to translate
my code, I think. I suspect you will find this kind of subtle
issue in many places. Or worse, you won't find it
until after your program has been installed
in production.

It's a damn shame because
if string%dict was just left in it wouldn't be an issue.

Also, if making f(**d) support dict emulation
has any negative performance implications
then I don't want it please.

sigh. -- Aaron Watters

===
http://www.xfeedme.com/nucular/pydistro.py/go?FREETEXT=crack+open
 
J

J. Cliff Dyer

Why is the migration to py3k a concern?
For example I have libraries which use string%dictionary
substitution where the dictionary is actually an object
which emulates a dictionary. The __getitem__ for
the object can be very expensive and is only called when
needed by the string substitution.

In py3k string%dictionary is going away. Why?
I have no idea.

The replacement is a string.format(...) method
which supports dictionary calling.
string.format(**dictionary)
But dictionary
calling doesn't support dictionary emulation.
So in the example below the substitution works
but the call fails.

=== code

class fdict(dict):
def __getitem__(self, item):
return "got("+item+")"

def fn(**d):
print d["boogie"]

if __name__=="__main__":
fd = fdict()
print "attempting string substitution with fake dictionary"
print
print "hello there %(boogie)s" % fd # <-- works
print
print "now attempting function call with fake dictionary"
print
fn(**fd) # <-- fails

=== output

% python2.6 dtest.py
attempting string substitution with fake dictionary

hello there got(boogie)

now attempting function call with fake dictionary

Traceback (most recent call last):
File "dtest.py", line 17, in <module>
fn(**fd)
File "dtest.py", line 7, in fn
print d["boogie"]
KeyError: 'boogie'

==== end of output

Consequently there is no simple way to translate
my code, I think. I suspect you will find this kind of subtle
issue in many places. Or worse, you won't find it
until after your program has been installed
in production.

It's a damn shame because
if string%dict was just left in it wouldn't be an issue.

Also, if making f(**d) support dict emulation
has any negative performance implications
then I don't want it please.

sigh. -- Aaron Watters

===
http://www.xfeedme.com/nucular/pydistro.py/go?FREETEXT=crack+open

I was with you on this issue right up until that last paragraph. You
want it, but only if its free. That's ridiculous. Every thing a
computer does requires processor cycles.

Do you really mean to tell me that string interpolation has been a major
bottleneck for you? Now I think you're just whining because you like to
hear yourself whine. Try coming up with a real standard for evaluation.
How much of a performance hit will actually cause you trouble? 1% extra
on string interpolation? 10%? 50%? 200%?

You do provide a link to a website called xfeedme.com. And I just fed
you. IHBT. HAND. :-/
 
A

Aaron Watters

Try coming up with a real standard for evaluation.
How much of a performance hit will actually cause you trouble? 1% extra
on string interpolation? 10%? 50%? 200%?

You misread my comment. I don't want function calls to support
dictionary emulation if there is a speed penalty because this is
such a special case and function calls are so important.
I don't really know, but I think "fixing" the above issue for
string.format(...) might involve changing the representation of
every python stack frame... not worth it in this case if there
is any penalty (afaik there isn't).

An alternative is to provide an alternate interface to string.format
so
that you could pass in an object which might emulate a dictionary,
like string.formatDict(D) -- or you could even adopt a shortcut
notation like string % D -- hey there's an idea!
-- Aaron Watters

===
http://www.xfeedme.com/nucular/pydistro.py/go?FREETEXT=ignore+warnings
 
K

Kay Schluehr

I don't really know, but I think "fixing" the above issue for
string.format(...) might involve changing the representation of
every python stack frame... not worth it in this case if there
is any penalty (afaik there isn't).

In Python 2.6 the format function simply calls PyDict_GetItem on the
dictionary object and raises a KeyError if it fails. A dict emulation
would require one more lookup if the fast lookup has no result. It
would be a speed penalty in the general case if this lookup would be
implemented in PyDict_GetItem but handling a special case in the
format implementation might not look that bad.
 
M

Matimus

Why is the migration to py3k a concern?
For example I have libraries which use string%dictionary
substitution where the dictionary is actually an object
which emulates a dictionary. The __getitem__ for
the object can be very expensive and is only called when
needed by the string substitution.

In py3k string%dictionary is going away. Why?
I have no idea.

The replacement is a string.format(...) method
which supports dictionary calling.
string.format(**dictionary)
But dictionary
calling doesn't support dictionary emulation.
So in the example below the substitution works
but the call fails.

=== code

class fdict(dict):
def __getitem__(self, item):
return "got("+item+")"

def fn(**d):
print d["boogie"]

if __name__=="__main__":
fd = fdict()
print "attempting string substitution with fake dictionary"
print
print "hello there %(boogie)s" % fd # <-- works
print
print "now attempting function call with fake dictionary"
print
fn(**fd) # <-- fails

=== output

% python2.6 dtest.py
attempting string substitution with fake dictionary

hello there got(boogie)

now attempting function call with fake dictionary

Traceback (most recent call last):
File "dtest.py", line 17, in <module>
fn(**fd)
File "dtest.py", line 7, in fn
print d["boogie"]
KeyError: 'boogie'

==== end of output

Consequently there is no simple way to translate
my code, I think. I suspect you will find this kind of subtle
issue in many places. Or worse, you won't find it
until after your program has been installed
in production.

It's a damn shame because
if string%dict was just left in it wouldn't be an issue.

Also, if making f(**d) support dict emulation
has any negative performance implications
then I don't want it please.

sigh. -- Aaron Watters

===http://www.xfeedme.com/nucular/pydistro.py/go?FREETEXT=crack+open

The reason it doesn't work is that you are unpacking the dictionary
with **, and you have done nothing to define any keys or define a
length. I would describe the way you are using dict as a hack, and not
part of any standard feature. You make a good point that it breaks
your code, but at the same time the format facility gives you the
ability to do something similar but in a standard way. You simply
define a class with a __format__ method and pass that in instead of
your dict.


class MyClass:
def __format__(self, spec):
return "got({0})".format(spec)

c = MyClass()
print ("hey everybody {0:some words}".format(c))
print ("lets try this {0:more words} {0:even more words}".format(c))

should produce:

hey everybody got(some words)
lets try this got(more words) got(even more words)

My point is that instead of exploiting the string formatting of
dictionaries feature to create a custom format specifier, you actually
get to define your own format specifiers, something which is much more
powerful.

And actually, you can do this too... which is even simpler and allows
you to use your a portion of your existing solution:

class fdict(dict):
def __getitem__(self, item):
return "got("+item+")"

fd = fdict()
print ("hello there {0[boogie]} hello there {0[george])".format(fd))

Which should result in:

hello there got(boogie) hello there got(george)

* Keep in mind that I have not tested any of this code, there may be
bugs. I don't have Py3k or 2.6 installed locally.

I think this is a good trade-off.

Adding to that... don't worry about py3k. Nobody is forcing you to
switch. In fact, you are encouraged not to until you are comfortable.
Py3k won't _break_ your code. You wrote the code for Python 2.x use it
in 2.x. Python 2.x probably has a good 5-10 years remaining.

Matt
 
K

Kay Schluehr

The reason it doesn't work is that you are unpacking the dictionary
with **, and you have done nothing to define any keys or define a
length.

This is a non-issue. The class derives from dict; it has all the
desired attributes. It is also not a problem in particular because
these properties are not requested by format ( at least not in the
code I have examined which was admittedly just a critical section that
caused the exception ).
Adding to that... don't worry about py3k. Nobody is forcing you to
switch. In fact, you are encouraged not to until you are comfortable.
Py3k won't _break_ your code. You wrote the code for Python 2.x use it
in 2.x. Python 2.x probably has a good 5-10 years remaining.

These advices start to get annoying.

Software hardly ever exists in isolation for the sake of the beauty of
the algorithm but is supplementary to a large framework/engine/
library. So if e.g. Django switches to 3 everyone who works with it
has to switch sooner or later as well or lose track otherwise, no
matter how long Python 1.5.2 or Python 2.5.2 or whatever version will
be maintained. If Pythons code base becomes fragmented it will be
harmful and affect almost everyones work.
 
I

Ivan Illarionov

This is a non-issue. The class derives from dict; it has all the desired
attributes. It is also not a problem in particular because these
properties are not requested by format ( at least not in the code I have
examined which was admittedly just a critical section that caused the
exception ).


These advices start to get annoying.

Software hardly ever exists in isolation for the sake of the beauty of
the algorithm but is supplementary to a large framework/engine/ library.
So if e.g. Django switches to 3 everyone who works with it has to switch
sooner or later as well or lose track otherwise, no matter how long
Python 1.5.2 or Python 2.5.2 or whatever version will be maintained. If
Pythons code base becomes fragmented it will be harmful and affect
almost everyones work.

This Py3k-Django-related FUD starts to get annoying. AFAIK Django is
going to support everything from 2.3 to 3.0 from single codebase. 2to3
tool will be called from setup.py and code will have few 'if
sys.version_info < (3, 0)/else' tricks. Proof of concept already exists:
http://wiki.python.org/moin/PortingDjangoTo3k

I work with Django as well as *on* Django and 3rd party Django addons and
don't see any reason to worry.

Your particular problem may be solved by using a real dictionary with
real keys which will probably have positive performance impact on your
code and make it more clean.

Even if Python code base will become fragmented why is it harmfull? This
is a way our life work - you rise and get better or die. It is essential
part of progress and evolution. IMHO, we'll only get better Python and
better Python libraries.
 
J

Jason Scheirer

This is a non-issue. The class derives from dict; it has all the
desired attributes. It is also not a problem in particular because
these properties are not requested by format ( at least not in the
code I have examined which was admittedly just a critical section that
caused the exception ).


These advices start to get annoying.

Software hardly ever exists in isolation for the sake of the beauty of
the algorithm but is supplementary to a large framework/engine/
library. So if e.g. Django switches to 3 everyone who works with it
has to switch sooner or later as well or lose track otherwise, no
matter how long Python 1.5.2 or Python 2.5.2 or whatever version will
be maintained. If Pythons code base becomes fragmented it will be
harmful and affect almost everyones work.

This has happened before, though -- I remember the pain of moving to
2.0 from the 1.5 branch way back when, and it wasn't getting my 1.5
code to work in 2.0, it was being jealous of all the cool features of
2.0 that I had to wait to get. I was working in production with 1.5 in
2000 and all the libraries available for Python gradually stopped
supporting 1.5 to pick up interesting 2.0 features that actually made
them easier to work with, and new libraries all began to assume you
were using a 2.0+ Python version because that's what was current. By
2003-2004 everyone I knew had switched over to 2.0, but by then I had
had nearly 5 years to port my legacy 1.5 code to 2.0, take advantage
of the 2.0 version's features, and do plenty of testing of my 1.5
codebase in 2.0 before I switched my production systems over. Not to
mention the fact that plenty of warning was offered BEFORE 2.0 was
released and 1.5 was not abruptly ended, but gradually phased out
until only the teeniest far ends of the long tail were using it. The
2.6->3.0 process is going to be even less of a pain than the 1.5->2.0
conversion, which was not hard at all going forward into it. You may
not want to switch, but by the time you decide to it will be pretty
easy to move on -- the extreme opposite reality being your application
will be so frozen that both your Python version and your codebase will
be fossils, left to hum on completely unchanged on some forgotten
server like so much other legacy code.
 
C

Carl Banks

Why is the migration to py3k a concern?
For example I have libraries which use string%dictionary
substitution where the dictionary is actually an object
which emulates a dictionary. The __getitem__ for
the object can be very expensive and is only called when
needed by the string substitution.

In py3k string%dictionary is going away. Why?
I have no idea.

The replacement is a string.format(...) method
which supports dictionary calling.
string.format(**dictionary)
But dictionary
calling doesn't support dictionary emulation.
So in the example below the substitution works
but the call fails.

=== code

class fdict(dict):
def __getitem__(self, item):
return "got("+item+")"

def fn(**d):
print d["boogie"]

if __name__=="__main__":
fd = fdict()
print "attempting string substitution with fake dictionary"
print
print "hello there %(boogie)s" % fd # <-- works
print
print "now attempting function call with fake dictionary"
print
fn(**fd) # <-- fails

=== output

% python2.6 dtest.py
attempting string substitution with fake dictionary

hello there got(boogie)

now attempting function call with fake dictionary

Traceback (most recent call last):
File "dtest.py", line 17, in <module>
fn(**fd)
File "dtest.py", line 7, in fn
print d["boogie"]
KeyError: 'boogie'

==== end of output

Consequently there is no simple way to translate
my code, I think. I suspect you will find this kind of subtle
issue in many places. Or worse, you won't find it
until after your program has been installed
in production.

It's a damn shame because
if string%dict was just left in it wouldn't be an issue.

Also, if making f(**d) support dict emulation
has any negative performance implications
then I don't want it please.

sigh. -- Aaron Watters


If you don't like Python 3, DON'T USE IT.

It's been stated repeatedly that 2.x and 3.x are going to be supported
in parallel for years.

Refusing to use 3, thus casting your brain-share vote against it, is
far more likely to have an effect than you coming here and making
everyone's life miserable with your pitiful whining.


Carl Banks
 
G

Gabriel Genellina

Why is the migration to py3k a concern?
For example I have libraries which use string%dictionary
substitution where the dictionary is actually an object
which emulates a dictionary. The __getitem__ for
the object can be very expensive and is only called when
needed by the string substitution.

In py3k string%dictionary is going away. Why?
I have no idea.

But not soon. It's not listed in PEP 3100 and according to this message
http://mail.python.org/pipermail/python-3000/2008-April/013094.html
%s formatting will not disappear until Python 3.3
You have plenty of time to evaluate alternatives. Your code may become obsolete even before 3.3 is shipped.
 
P

Paul McGuire

If you don't like Python 3, DON'T USE IT.

I've read this position a number of times in this and related threads,
and it overlooks one constituency of Python developers - those who
develop and support modules for use by other Python users. As the
supporter of pyparsing, I really can't just "not use" Py3 - ignoring
Py3 means shutting out/holding back those of my users who do want to
use it, and pretty much consigning my module to eventual dustbin
status. Ideally, I can implement some form of cross-compatible code
so that I need maintain only a single code base, and I have managed to
do so on a number of fronts (with the help of Robert A. Clark):
- add support for both __bool__ and __nonzero__ (__nonzero__ calls
__bool__, so that upgrading to Py3 actually saves a function call)
- convert isinstance(x,basestring) to isinstance(x,__BASESTRING__) and
dynamically set __BASESTRING__ to basestring or str
- similar treatment for sys.maxint/maxsize -> __MAX_INT__

I dodged a bullet when 3.0a3 added back in support for the 2.x form of
"except" for exception handling. 3.0a2 only supported "except varname
as ExceptionType:" and there was no way I could do this in a multi-
version compatible way.

My remaining hassle is print as function vs. print as statement. I
provide a number of default diagnostic methods, and I have not fully
gotten all to print nice - converting "print x" to "print (x)" is
simple enough, and "print (x,y)" replaces "print x,y" well enough when
running under Py3, but the same code in Py2.x now prints a tuple
instead of a nice string like before. I will probably punt on the
whole issue in the next release and just use sys.write.stdout/stderr
throughout, and " ".join() the args (another function call!) before
calling.

I wasn't aware of the coming deprecation of '%' string interpolation,
but at least it is deferred until 3.3, which does give me a few years
I should think before I absolutely must address it. This is really
not so much an issue for me as it is for my "customers." Pyparsing
returns its parsed tokens using a class that is dict-like in behavior,
but without extending dict (duck-typing at its finest!). I really
like that my users can parse an expression and access any named fields
directly and neatly in an interpolated string using "%(field_name)s".
If this is removed, pyparsing will continue to work as-is, but I feel
like a nice ease-of-access mode will have been lost to those who use
it.

Overall, I think I'm getting off pretty easy, but then pyparsing is a
small module with very limited use of the standard lib. I can
imagine that maintainers of larger libraries are having some serious
fits trying to support both versions with a single code base. And as
much as we all love Python-the-language, language features alone do
not help a language and its community of users to grow and
proliferate. I think most would agree that it is the cornucopia of
libraries that really make Python an environment for developing
production applications.

-- Paul
 
S

Stefan Behnel

Gabriel said:
You have plenty of time to evaluate alternatives. Your code may become obsolete even before 3.3 is shipped.

Sure, and don't forget to save two bytes when storing the year. ;)

Stefan
 
R

Ross Ridge

Carl Banks said:
If you don't like Python 3, DON'T USE IT.

That's the plan.

Paul McGuire said:
I've read this position a number of times in this and related threads,
and it overlooks one constituency of Python developers - those who
develop and support modules for use by other Python users. As the
supporter of pyparsing, I really can't just "not use" Py3 - ignoring
Py3 means shutting out/holding back those of my users who do want to
use it, and pretty much consigning my module to eventual dustbin
status.

Eh. You can ingore it until your users start asking for it.
Ideally, I can implement some form of cross-compatible code
so that I need maintain only a single code base, and I have managed to
do so on a number of fronts (with the help of Robert A. Clark):
- add support for both __bool__ and __nonzero__ (__nonzero__ calls
__bool__, so that upgrading to Py3 actually saves a function call)

Doing sometthing like the following would save the function call in
both cases:

class C(object):
def __bool__(self):
return False

__nonzero__ = __bool__
- convert isinstance(x,basestring) to isinstance(x,__BASESTRING__) and
dynamically set __BASESTRING__ to basestring or str
- similar treatment for sys.maxint/maxsize -> __MAX_INT__

I don't thiink using double underscores here is appropriate. It suggests
it's part of the language. Since "basestring" is no longer part of the
language, you could do:

if "basestring" not in globals():
basestring = str

Overall, I think I'm getting off pretty easy, but then pyparsing is a
small module with very limited use of the standard lib.

Has the standard library changed that much? I thought was it mainly the
deletion of old seldom used modules that happens in new releases anyways.
[...] And as much as we all love Python-the-language, language features
alone do not help a language and its community of users to grow
and proliferate. I think most would agree that it is the cornucopia
of libraries that really make Python an environment for developing
production applications.

Definately.

Ross Ridge
 
G

Gabriel Genellina

En Mon, 21 Apr 2008 16:42:41 -0300, Ross Ridge

Perhaps you can manage to keep your code compatible with all versions, but
AFAIK the reccomended strategy is to write code compatible with Python 2.6
and use the 2to3 tool to generate the 3.0 source. And *not* edit the 3.0
code unless one wants to maintain two branches.
Has the standard library changed that much? I thought was it mainly the
deletion of old seldom used modules that happens in new releases anyways.

*and* renaming of old module names that don't follow PEP8, and merging
others into packages for better structure.
That's another point where using the 2to3 tool is necesary -it takes care
of such changes- unless one wants to maintain two branches.
 
M

Martin v. Löwis

In py3k string%dictionary is going away.

Why do you say that? It's not going away in Python 3.0.

Regards,
Martin
 
H

Hrvoje Niksic

Martin v. Löwis said:
Why do you say that? It's not going away in Python 3.0.

I also got the impression that it was going away. PEP 3101's abstract
says:

This PEP proposes a new system for built-in string formatting
operations, intended as a replacement [sic] for the existing '%'
string formatting operator.

Under "Backward compatibility" it says that both systems can coexist
"until it comes time to deprecate the older system".
 
S

Stefan Behnel

Gabriel said:
*and* renaming of old module names that don't follow PEP8, and merging
others into packages for better structure.
That's another point where using the 2to3 tool is necesary -it takes
care of such changes- unless one wants to maintain two branches.

conditional imports were never a problem in Python. Once things are in their
final place, you can add a try-except and import from two different places -
unless (I assume) you want to use 2to3, which might even break this approach.

Stefan
 
M

Martin v. Löwis

In py3k string%dictionary is going away.
Why do you say that? It's not going away in Python 3.0.

I also got the impression that it was going away. PEP 3101's abstract
says:

This PEP proposes a new system for built-in string formatting
operations, intended as a replacement [sic] for the existing '%'
string formatting operator.

Under "Backward compatibility" it says that both systems can coexist
"until it comes time to deprecate the older system".

The PEP may say that it's going away, but it doesn't say that it
goes away in 3.0 - and indeed, it won't.

At some point in the future, somebody will likely propose that the PEP
will be executed. At that time, huge flame wars will start. I expect
that they settle in changing the PEP to explain that the old mechanism
gets removed in Python 4, to be release in 2018 :)

Regards,
Martin
 
H

Hrvoje Niksic

Martin v. Löwis said:
In py3k string%dictionary is going away.
Why do you say that? It's not going away in Python 3.0.

I also got the impression that it was going away. PEP 3101's abstract
says:

This PEP proposes a new system for built-in string formatting
operations, intended as a replacement [sic] for the existing '%'
string formatting operator.

Under "Backward compatibility" it says that both systems can coexist
"until it comes time to deprecate the older system".

The PEP may say that it's going away, but it doesn't say that it
goes away in 3.0 - and indeed, it won't.

Thanks for clarifying it. It is certainly unclear from the wording of
the PEP, given that 3.0 is regarded as the Python version allowed to
break backward compatibility.
At some point in the future, somebody will likely propose that the
PEP will be executed. At that time, huge flame wars will start. I
expect that they settle in changing the PEP to explain that the old
mechanism gets removed in Python 4, to be release in 2018 :)

Indeed.
 
P

Paul McGuire

Perhaps you can manage to keep your code compatible with all versions, but  
AFAIK the reccomended strategy is to write code compatible with Python 2.6  
and use the 2to3 tool to generate the 3.0 source. And *not* edit the 3.0  
code unless one wants to maintain two branches.

Gabriel -

(Thanks for chiming in on this sub-thread, I really enjoy reading your
posts.)

My point is that the recommended strategy MAY work for those who write
end point applications (I consider maintaining 2 branches to be in the
"not working" category), but it does NOT WORK for people who maintain
modules for other people to use, because those people may be on a
range of Python versions that extend beyond 2.6-3.0. So if I upgrade
my module to 2.6, those running on earlier versions can no longer use
it. At some point in the future, I'll probably be able to say "no
more support for pre-2.6", but it is a bit early to start saying that
now.

Likewise, I don't want to say "no support for 3.0" - people DO want to
try 3.0 out, and I WANT them to want and be able to use my module too.

Given the recommended strategy, and ruling out dual codebase, whom do
I tell that they can't use the next version of my module?

Again, to me, this is a non-issue because I've been able to create a
cross-version compatible single codebase for pyparsing. But it was a
bit dicey there for a while, and I think other module developers/
maintainers may not be so lucky.

So, I feel that the recommended strategy was devised with a narrow
group of developers in mind, and leaves module developers/maintainers,
who wish to target as broad a set of users as possible, faced with
choosing one of these strategies:
- create (if possible) single cross-version compatible code
- forego support of 3.0 users
- discontinue pre-2.6 support for future versions of their module
- maintain dual codebase

-- Paul
 

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

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,526
Members
44,997
Latest member
mileyka

Latest Threads

Top