towards a faster Python
Hi guys, With Guido's blessing, I just commited a change that takes a small step towards where we want to be, IMO. Using __setattr__ on a module to create a global name that shadows a builtin name now raises a DeprecationWarning. E.g. import fileinput fileinput.open = 10 # <- warning will be raised The goal is that the compiler should be able to determine the scope of a name at compile time. Code like the above does not allow that and so non-local names must be searched for in both globals and builtins at run time. Unfortunately the warning is not bulletproof. It's possible to modify the module dict directly and bypass the warning. I'm not sure what to do about that. :-( Eventually we might want to allow optimizations based on known builtin functions. For example, code like for i in range(100000): ... could be rewritten to use an integer counter rather than creating a list and then iterating over it. I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it. The compiler could also warn about references to non-existent names. That's a common error and would be helpful to developers. Looking farther into the future, knowing that a name refers to a specific builtin function would give a type inference system a lot more information to work with. I guess it would help Psyco. Just some ideas to think about. Neil
Neil Schemenauer wrote:
Hi guys,
With Guido's blessing, I just commited a change that takes a small step towards where we want to be, IMO. Using __setattr__ on a module to create a global name that shadows a builtin name now raises a DeprecationWarning. E.g.
import fileinput fileinput.open = 10 # <- warning will be raised
The goal is that the compiler should be able to determine the scope of a name at compile time. Code like the above does not allow that and so non-local names must be searched for in both globals and builtins at run time.
Spiffy.
Unfortunately the warning is not bulletproof. It's possible to modify the module dict directly and bypass the warning. I'm not sure what to do about that. :-(
Probably only way to deal with that would to come up with namespace dicts that can restrictions set on them?
Eventually we might want to allow optimizations based on known builtin functions. For example, code like
for i in range(100000): ...
could be rewritten to use an integer counter rather than creating a list and then iterating over it. I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it.
I am personally fine with this since I too never assign to built-in names, but I can see people raising a stink over this citing that their freedoms are being encroached upon. =)
The compiler could also warn about references to non-existent names. That's a common error and would be helpful to developers.
Isn't this getting into PyChecker's territory? -Brett
Neil> It's possible to modify the module dict directly and bypass the Neil> warning. I'm not sure what to do about that. :-( Make the module dict read-only and some as-yet-to-be-determined time during import? That is, upon initial import of a module the dict collecting names would be made writable, but once it's associated with a module object (or the import is completed), the dict would be flagged read-only or be transmogrified into a read-only dict subclass of dict(). Skip
Make the module dict read-only and some as-yet-to-be-determined time during import? That is, upon initial import of a module the dict collecting names would be made writable, but once it's associated with a module object (or the import is completed), the dict would be flagged read-only or be transmogrified into a read-only dict subclass of dict().
But the dict is still accessible in writable form through globals() and as the func_globals attribute of any function defined in the module. This can't be helped unless we want to totally freeze a module's contents after it's been imported. Another hole in the plan is exec statements, or execfile() calls. I still would like to be able to say "if you add a module global that corresponds to a built-in name used in the module, the module may continue to use the built-in name". As long as we can detect *most* of the ways of inserting such module globals, the remaining ways could be declared illegal without making them impossible. --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
I still would like to be able to say "if you add a module global that corresponds to a built-in name used in the module, the module may continue to use the built-in name". As long as we can detect *most* of the ways of inserting such module globals, the remaining ways could be declared illegal without making them impossible.
I'd phrase this differently: Those cases just get a different semantics then they have now; the module-level name is ignored unless explicitly qualified. In the spirit of C, hiding builtins would cause "undefined behaviour" or "implementation-defined behaviour", and not be "illegal". Regards, Martin
Guido van Rossum wrote:
I still would like to be able to say "if you add a module global that corresponds to a built-in name used in the module, the module may continue to use the built-in name". As long as we can detect *most* of the ways of inserting such module globals, the remaining ways could be declared illegal without making them impossible.
I'd phrase this differently: Those cases just get a different semantics then they have now; the module-level name is ignored unless explicitly qualified. In the spirit of C, hiding builtins would cause "undefined behaviour" or "implementation-defined behaviour", and not be "illegal".
I don't think the spirit of C applies here. I want to be able to tell anyone who does one of these things that their program is illegal (or invalid, or some other pejorative term), not just undefined, and certainly not implementation-defined. Otherwise a future implementation couldn't detect such illegal programs and reject them at compile time or at least raise an exception at run-time. --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido]
I don't think the spirit of C applies here. I want to be able to tell anyone who does one of these things that their program is illegal (or invalid, or some other pejorative term), not just undefined, and certainly not implementation-defined. Otherwise a future implementation couldn't detect such illegal programs and reject them at compile time or at least raise an exception at run-time.
As the C standard uses the word "undefined", compile-time rejection of a program with undefined semantics is fine. Nothing is defined about what happens in the face of undefined behavior, so nobody can object to anything that happens! undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). EXAMPLE An example of undefined behavior is the behavior on integer overflow.
>> Make the module dict read-only ... Guido> But the dict is still accessible in writable form through Guido> globals() and as the func_globals attribute of any function Guido> defined in the module. I was thinking more along the lines of making the dict itself read-only. Skip
Skip Montanaro writes:
I was thinking more along the lines of making the dict itself read-only.
That certainly makes global variables more difficult, doesn't it? The interpreter could worm around it, but it's not clear that this is the right solution. -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
Skip> I was thinking more along the lines of making the dict itself Skip> read-only. Fred> That certainly makes global variables more difficult, doesn't it? Yes, but we all know globals are bad, right? ;-) At first blush, it doesn't seem all that tough to worm around this in the interpreter (postulating a read_only flag for dicts or at least for that variety of dict used as a module dict): case STORE_GLOBAL: w = GETITEM(names, oparg); v = POP(); ro = f->f_globals->read_only; f->f_globals->read_only = 0; err = PyDict_SetItem(f->f_globals, w, v); f->f_globals->read_only = ro; Py_DECREF(v); break; or something like that. I think the STORE_GLOBAL instruction will only be executed to set a global in the current module. Any foreign setting, e.g. "fileinput.open = 10", will be handled by STORE_ATTR. Can executing PyDict_SetItem() cause arbitrary Python code to execute? (I'm almost purposely adopting an XP approach here - do just enough to address the current breakage and see what the next problem is which pops up.) Skip
Skip Montanaro wrote:
Make the module dict read-only and some as-yet-to-be-determined time during import?
I don't think read-only will fly. Perhaps we could use a subclass of dict for the module dict. It could have an optimized interface for use by the runtime internals. The standard dict interface would be slower and could check for monkey business. Neil
At 03:02 PM 6/9/03 -0500, Skip Montanaro wrote:
Neil> It's possible to modify the module dict directly and bypass the Neil> warning. I'm not sure what to do about that. :-(
Make the module dict read-only and some as-yet-to-be-determined time during import? That is, upon initial import of a module the dict collecting names would be made writable, but once it's associated with a module object (or the import is completed), the dict would be flagged read-only or be transmogrified into a read-only dict subclass of dict().
You're forgetting about globals; functions have a func_globals reference to their globals dictionary, and if the function modifies any globals, it has to be writable. In any case, I'm not sure what the use case is here for stopping all modifications of a module __dict__. Presumably, if somebody writes to __dict__ in order to bypass the warning, they either know what they're doing or deserve what they get. :)
Phillip J. Eby wrote:
Presumably, if somebody writes to __dict__ in order to bypass the warning, they either know what they're doing or deserve what they get.
I'm not sure about that. Think about using globals(). Eg. exec somecode in globals() or: d = globals() d['range'] = 'ha ha' I especially worried about code that provides an interactive interface (e.g IDLE and pyrepl). Neil
Neil Schemenauer <nas@python.ca>:
Unfortunately the warning is not bulletproof. It's possible to modify the module dict directly and bypass the warning. I'm not sure what to do about that. :-(
I'd say don't bother doing anything. Anyone who messes around with things at that low a level deserves whatever they get. :-) Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
Neil Schemenauer wrote:
The goal is that the compiler should be able to determine the scope of a name at compile time. Code like the above does not allow that and so non-local names must be searched for in both globals and builtins at run time. Unfortunately the warning is not bulletproof. It's possible to modify the module dict directly and bypass the warning. I'm not sure what to do about that. :-(
Eventually we might want to allow optimizations based on known builtin functions. For example, code like
for i in range(100000): ...
could be rewritten to use an integer counter rather than creating a list and then iterating over it. I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it.
All very nice, but you'll have to think about two cases which could cause trouble: 1. Extending builtins should still be possible like it is now. Of course, the extensions wouldn't take part in the optimizations you have in mind, but they should still be found. 2. What happens if a new Python version introduces new builtins that are in use by some modules out there for other purposes ? The problem here is similar to that of introducing new keywords. Modules should still be able to override builtin names in their globals; directly, via the global statement and also dynamically (modules are often used as container for global variables and state). -- Marc-Andre Lemburg eGenix.com Professional Python Software directly from the Source (#1, Jun 10 2003)
Python/Zope Products & Consulting ... http://www.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
EuroPython 2003, Charleroi, Belgium: 14 days left
1. Extending builtins should still be possible like it is now.
Of course, the extensions wouldn't take part in the optimizations you have in mind, but they should still be found.
I'm not sure what you mean by "extending builtins", but if it is what I think it is, I am strongly opposed. The builtins should have fixed capabilities (in a particular Python version). If you want a function that behaves just like a builtin but also can do something else, give it a different name and import it explicitly, don't override the existing entry in __builtin__. If you have a new function that you want to use in lots of code, resist the temptation to add it to the __builtin__ module. Zope did this e.g. with get_transaction() and the net result was reader confusion and intractable dependencies on import order.
2. What happens if a new Python version introduces new builtins that are in use by some modules out there for other purposes ?
*** THIS IS NOT AFFECTED! *** There is no proposal on the table to ban the use of identifiers known to be built-ins for any purpose. If a module defines a class, function or variable that shadows a builtin name, that's fine, because the parser has no problem detecting this situation. The *only* thing being banned is insertion of a new name that shadows a builtin from outside a module. IOW: # module foo def open(): return ... is fine, but import foo foo.open = 42 is not. --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
IOW:
# module foo def open(): return ...
is fine, but
import foo foo.open = 42
is not.
How about this:
# module foo def open(): ...
# module bar import foo foo.open = 42
?
(I would assume it to be ok, since the builtin name was already shadowed.)
Yes, this wouldn't break in any new ways due to the proposed optimization, and you won't get the warning because it only warns about inserting new names. --Guido van Rossum (home page: http://www.python.org/~guido/)
At 09:51 10.06.2003 -0400, Guido van Rossum wrote:
1. Extending builtins should still be possible like it is now.
Of course, the extensions wouldn't take part in the optimizations you have in mind, but they should still be found.
I'm not sure what you mean by "extending builtins", but if it is what I think it is, I am strongly opposed. The builtins should have fixed capabilities (in a particular Python version). If you want a function that behaves just like a builtin but also can do something else, give it a different name and import it explicitly, don't override the existing entry in __builtin__. If you have a new function that you want to use in lots of code, resist the temptation to add it to the __builtin__ module. Zope did this e.g. with get_transaction() and the net result was reader confusion and intractable dependencies on import order.
what about substituing the built-in entirely for security reasons, Zope3 does that with its wrapping versions: def ri_exec(self, code): # XXX What is the type of code? self.globals['__builtins__'] = RestrictedBuiltins exec code in self.globals regards.
what about substituing the built-in entirely for security reasons, Zope3 does that with its wrapping versions:
def ri_exec(self, code): # XXX What is the type of code? self.globals['__builtins__'] = RestrictedBuiltins exec code in self.globals
regards.
Restricted execution is a different case; possibly the compiler will need to know when it is compiling for restricted mode. --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
1. Extending builtins should still be possible like it is now.
Of course, the extensions wouldn't take part in the optimizations you have in mind, but they should still be found.
I'm not sure what you mean by "extending builtins", but if it is what I think it is, I am strongly opposed. The builtins should have fixed capabilities (in a particular Python version). If you want a function that behaves just like a builtin but also can do something else, give it a different name and import it explicitly, don't override the existing entry in __builtin__. If you have a new function that you want to use in lots of code, resist the temptation to add it to the __builtin__ module. Zope did this e.g. with get_transaction() and the net result was reader confusion and intractable dependencies on import order.
I am talking about what mxTools is doing: adding new builtins to the interpreter by placing them into the __builtins__ dictionary. While I agree that it is usually better to use something like 'from x import *' or even naming the tools explicitly, some features in mxTools do warrant being made builtins, e.g. irange() has been most helpful in the past :-) Note that I'm not talking about overriding existing builtins. mxTools just adds a few more (that's what I meant with "extending builtins").
2. What happens if a new Python version introduces new builtins that are in use by some modules out there for other purposes ?
*** THIS IS NOT AFFECTED! ***
There is no proposal on the table to ban the use of identifiers known to be built-ins for any purpose. If a module defines a class, function or variable that shadows a builtin name, that's fine, because the parser has no problem detecting this situation.
The *only* thing being banned is insertion of a new name that shadows a builtin from outside a module.
IOW:
# module foo def open(): return ...
is fine, but
import foo foo.open = 42
is not.
I presume you mean the case that foo.open is not already defined in foo. That's good. -- Marc-Andre Lemburg eGenix.com Professional Python Software directly from the Source (#1, Jun 10 2003)
Python/Zope Products & Consulting ... http://www.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
EuroPython 2003, Charleroi, Belgium: 14 days left
I am talking about what mxTools is doing: adding new builtins to the interpreter by placing them into the __builtins__ dictionary. While I agree that it is usually better to use something like 'from x import *' or even naming the tools explicitly, some features in mxTools do warrant being made builtins, e.g. irange() has been most helpful in the past :-)
You're not going to convince me to endorse that practice. End of story. --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
I am talking about what mxTools is doing: adding new builtins to the interpreter by placing them into the __builtins__ dictionary. While I agree that it is usually better to use something like 'from x import *' or even naming the tools explicitly, some features in mxTools do warrant being made builtins, e.g. irange() has been most helpful in the past :-)
You're not going to convince me to endorse that practice. End of story.
I'm not asking you for endorsement :-) Just wanted to note that Python's dynamic features are actually being used in this case (and have been for years). BTW, I don't see any problem with optimizing the builtins access regardeless of whether adding builtins is considered good or bad. Caching the lookups for globals in code objects seems like a good way to increase performance without having to change a single line of code and builtins a great candidate for cacheable globals (I'd even make all functions and classes at module top-level cacheable per default if Python is run under -OO). -- Marc-Andre Lemburg eGenix.com Professional Python Software directly from the Source (#1, Jun 10 2003)
Python/Zope Products & Consulting ... http://www.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
EuroPython 2003, Charleroi, Belgium: 14 days left
On Tue, 10 Jun 2003, Guido van Rossum wrote:
I am talking about what mxTools is doing: adding new builtins to the interpreter by placing them into the __builtins__ dictionary. While I agree that it is usually better to use something like 'from x import *' or even naming the tools explicitly, some features in mxTools do warrant being made builtins, e.g. irange() has been most helpful in the past :-)
You're not going to convince me to endorse that practice. End of story.
While not trying to convince you, I do have to say that we use this trick to provide implementations of builtins to code running under older Python versions. This allows us to write code using useful features from Python 2.3 like enumerate, sum, basestring, etc. that can run under Python 2.2. For example: def export(name, obj): import __builtin__ setattr(__builtin__,name,obj) try: enumerate except NameError: def enumerate(l): '''Given a sequence l, generates an indexed series: (0,l[0]), (1,l[1])...''' i = 0 next = iter(l).next while 1: yield (i, next()) i += 1 export('enumerate',enumerate) It isn't the prettiest thing, but it does get the job done. -Kevin -- -- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
While not trying to convince you, I do have to say that we use this trick to provide implementations of builtins to code running under older Python versions.
Whatever works for a given Python version works. Going forward, builtins may become more static than they currently are (but only if the performance benefits turn out to be really worth it). --Guido van Rossum (home page: http://www.python.org/~guido/)
On Tue, 10 Jun 2003, Guido van Rossum wrote:
While not trying to convince you, I do have to say that we use this trick to provide implementations of builtins to code running under older Python versions.
Whatever works for a given Python version works. Going forward, builtins may become more static than they currently are (but only if the performance benefits turn out to be really worth it).
I understand, though I suppose I'd still like to have the option of a dynamic builtin namespace as a fallback to a new fast/static builtin namespace. Either that or we go all out and get rid of all builtins in Python 3K, though I'm not sure how much I'd like typing 'from System.Types import None', etc. in every file. -Kevin -- -- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
Kevin Jacobs <jacobs@penguin.theopalgroup.com> writes:
On Tue, 10 Jun 2003, Guido van Rossum wrote:
I am talking about what mxTools is doing: adding new builtins to the interpreter by placing them into the __builtins__ dictionary. While I agree that it is usually better to use something like 'from x import *' or even naming the tools explicitly, some features in mxTools do warrant being made builtins, e.g. irange() has been most helpful in the past :-)
You're not going to convince me to endorse that practice. End of story.
While not trying to convince you, I do have to say that we use this trick to provide implementations of builtins to code running under older Python versions.
There's even precedence in the standard library for modifying __builtins__: (gettext.py, lines 219ff): def install(self, unicode=False): import __builtin__ __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext Bernhard -- Intevation GmbH http://intevation.de/ Sketch http://sketch.sourceforge.net/ MapIt! http://www.mapit.de/
There's even precedence in the standard library for modifying __builtins__:
(gettext.py, lines 219ff):
The standard library often goes beyond what the language standard promises. --Guido van Rossum (home page: http://www.python.org/~guido/)
On Tue, 2003-06-10 at 11:57, Bernhard Herzog wrote:
There's even precedence in the standard library for modifying __builtins__:
(gettext.py, lines 219ff):
def install(self, unicode=False): import __builtin__ __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext
Yes, but this sucks. :) -Barry
At 10:26 10.06.2003 +0200, M.-A. Lemburg wrote:
(modules are often used as container for global variables and state).
and that kind of usage is penalized maybe too much by the patch as it stands: e.g [probably an application scripting scenario] import new import sys userdata = new.module('userdata') execfile('startupdata',userdata.__dict__) sys.modules['userdata'] = userdata user scripts: import userdata userdata.x=... regards
Neil Schemenauer <nas@python.ca> writes:
I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it.
I'm not sure quite what you mean here, but I had plenty of variables called `file' pre-2.2 (and probably still do). Cheers, M. -- Every day I send overnight packages filled with rabid weasels to people who use frames for no good reason. -- The Usenet Oracle, Oracularity #1017-1
I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it.
I'm not sure quite what you mean here, but I had plenty of variables called `file' pre-2.2 (and probably still do).
Several standard library modules export a function named 'open' as part of their APIs. Arguments and variables named 'dir', 'str' or 'type' are common. And so on. Built-ins will always be the last namespace to be searched. The goal here is to move this search from run time to compile time. --Guido van Rossum (home page: http://www.python.org/~guido/)
Michael Hudson wrote:
Neil Schemenauer <nas@python.ca> writes:
I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it.
I'm not sure quite what you mean here,
What I mean by reassigning builtins is: import __builtin__ __builtin__.int = something
but I had plenty of variables called `file' pre-2.2 (and probably still do).
That's fine and there will be no warning. Neil
Neil Schemenauer <nas@python.ca> writes:
Michael Hudson wrote:
Neil Schemenauer <nas@python.ca> writes:
I'm not sure how much resistance there would be in the community to disallowing reassignment of builtin names though. I don't think I've ever reassigned a builtin name so I think it would be worth it.
I'm not sure quite what you mean here,
What I mean by reassigning builtins is:
import __builtin__ __builtin__.int = something
Ah. I think Guido dislikes this one (as in, would like to see this practice banned), not sure about the community :-)
but I had plenty of variables called `file' pre-2.2 (and probably still do).
That's fine and there will be no warning.
Goody. Cheers, M. -- [3] Modem speeds being what they are, large .avi files were generally downloaded to the shell server instead[4]. [4] Where they were usually found by the technical staff, and burned to CD. -- Carlfish, asr
import __builtin__ __builtin__.int = something
Ah. I think Guido dislikes this one (as in, would like to see this practice banned), not sure about the community :-)
Indeed, I strongly dislike this, because the scope of the change is the entire Python process. Sooner or later this practice will cause someone to rely on a change to a built-in that breaks a standard library module that they didn't even know they were relying on. --Guido van Rossum (home page: http://www.python.org/~guido/)
On Tue, 2003-06-10 at 10:36, Neil Schemenauer wrote:
What I mean by reassigning builtins is:
import __builtin__ __builtin__.int = something
IMO the only defensible reason to do that is for debugging purposes. Although I haven't had a need for this in many years, it has occasionally been useful to reassign built-in open() to catch or log a particular file action. (IIRC, the last time I did this was before the resulting IOError included the filename argument.) A warning is fine -- even desirable. I'd want to make sure such debugging hacks didn't make it into a release. :) -Barry
Barry Warsaw wrote:
On Tue, 2003-06-10 at 10:36, Neil Schemenauer wrote:
What I mean by reassigning builtins is:
import __builtin__ __builtin__.int = something
IMO the only defensible reason to do that is for debugging purposes. Although I haven't had a need for this in many years, it has occasionally been useful to reassign built-in open() to catch or log a particular file action. (IIRC, the last time I did this was before the resulting IOError included the filename argument.)
I've done it to find out how often and when open() is called in a large application.
A warning is fine -- even desirable. I'd want to make sure such debugging hacks didn't make it into a release. :)
In this case too, I wouldn't mind warnings. I would mind it if I had to hack my libc to track python-level open() calls =). --david
I've done it to find out how often and when open() is called in a large application.
A warning is fine -- even desirable. I'd want to make sure such debugging hacks didn't make it into a release. :)
In this case too, I wouldn't mind warnings. I would mind it if I had to hack my libc to track python-level open() calls =).
In any case, the bytecode compiler isn't going to be able to generate more efficient bytecode for open() calls... --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (17)
-
"Martin v. Löwis"
-
Barry Warsaw
-
Bernhard Herzog
-
Brett C.
-
David Ascher
-
Fred L. Drake, Jr.
-
Greg Ewing
-
Guido van Rossum
-
Just van Rossum
-
Kevin Jacobs
-
M.-A. Lemburg
-
Michael Hudson
-
Neil Schemenauer
-
Phillip J. Eby
-
Samuele Pedroni
-
Skip Montanaro
-
Tim Peters