[Python-checkins] r45552 - peps/trunk/pep-0359.txt peps/trunk/pep-3002.txt
david.goodger
python-checkins at python.org
Wed Apr 19 01:25:48 CEST 2006
Author: david.goodger
Date: Wed Apr 19 01:25:47 2006
New Revision: 45552
Modified:
peps/trunk/pep-0359.txt
peps/trunk/pep-3002.txt
Log:
updates from Steven Bethard
Modified: peps/trunk/pep-0359.txt
==============================================================================
--- peps/trunk/pep-0359.txt (original)
+++ peps/trunk/pep-0359.txt Wed Apr 19 01:25:47 2006
@@ -8,7 +8,7 @@
Content-Type: text/x-rst
Created: 05-Apr-2006
Python-Version: 2.6
-Post-History: 05-Apr-2006, 06-Apr-2006
+Post-History: 05-Apr-2006, 06-Apr-2006, 13-Apr-2006
Abstract
@@ -26,6 +26,19 @@
<name> = <callable>("<name>", <tuple>, <namespace>)
where ``<namespace>`` is the dict created by executing ``<block>``.
+This is mostly syntactic sugar for::
+
+ class <name> <tuple>:
+ __metaclass__ = <callable>
+ <block>
+
+and is intended to help more clearly express the intent of the
+statement when something other than a class is being created. Of
+course, other syntax for such a statement is possible, but it is hoped
+that by keeping a strong parallel to the class statement, an
+understanding of how classes and metaclasses work will translate into
+an understanding of how the make-statement works as well.
+
The PEP is based on a suggestion [1]_ from Michele Simionato on the
python-dev list.
@@ -35,12 +48,11 @@
Class statements provide two nice facilities to Python:
- (1) They are the standard Python means of creating a namespace. All
- statements within a class body are executed, and the resulting
- local name bindings are passed as a dict to the metaclass.
+(1) They execute a block of statements and provide the resulting
+ bindings as a dict to the metaclass.
- (2) They encourage DRY (don't repeat yourself) by allowing the class
- being created to know the name it is being assigned.
+(2) They encourage DRY (don't repeat yourself) by allowing the class
+ being created to know the name it is being assigned.
Thus in a simple class statement like::
@@ -59,13 +71,153 @@
creation of the dict by allowing it to be expressed as a series of
statements.
-Historically, type instances (a.k.a. class objects) have been the only
-objects blessed with this sort of syntactic support. But other sorts
-of objects could benefit from such support. For example, property
-objects take three function arguments, but because the property type
-cannot be passed a namespace, these functions, though relevant only to
-the property, must be declared before it and then passed as arguments
-to the property call, e.g.::
+Historically, type instances (a.k.a. class objects) have been the
+only objects blessed with this sort of syntactic support. The make
+statement aims to extend this support to other sorts of objects where
+such syntax would also be useful.
+
+
+Example: simple namespaces
+--------------------------
+
+Let's say I have some attributes in a module that I access like::
+
+ mod.thematic_roletype
+ mod.opinion_roletype
+
+ mod.text_format
+ mod.html_format
+
+and since "Namespaces are one honking great idea", I'd like to be able
+to access these attributes instead as::
+
+ mod.roletypes.thematic
+ mod.roletypes.opinion
+
+ mod.format.text
+ mod.format.html
+
+I currently have two main options:
+
+(1) Turn the module into a package, turn ``roletypes`` and ``format``
+ into submodules, and move the attributes to the submodules.
+
+(2) Create ``roletypes`` and ``format`` classes, and move the
+ attributes to the classes.
+
+The former is a fair chunk of refactoring work, and produces two tiny
+modules without much content. The latter keeps the attributes local
+to the module, but creates classes when there is no intention of ever
+creating instances of those classes.
+
+In situations like this, it would be nice to simply be able to declare
+a "namespace" to hold the few attributes. With the new make
+statement, I could introduce my new namespaces with something like::
+
+ make namespace roletypes:
+ thematic = ...
+ opinion = ...
+
+ make namespace format:
+ text = ...
+ html = ...
+
+and keep my attributes local to the module without making classes that
+are never intended to be instantiated. One definition of namespace
+that would make this work is::
+
+ class namespace(object):
+ def __init__(self, name, args, kwargs):
+ self.__dict__.update(kwargs)
+
+Given this definition, at the end of the make-statements above,
+``roletypes`` and ``format`` would be namespace instances.
+
+
+Example: GUI objects
+--------------------
+
+In GUI toolkits, objects like frames and panels are often associated
+with attributes and functions. With the make-statement, code that
+looks something like::
+
+ root = Tkinter.Tk()
+ frame = Tkinter.Frame(root)
+ frame.pack()
+ def say_hi():
+ print "hi there, everyone!"
+ hi_there = Tkinter.Button(frame, text="Hello", command=say_hi)
+ hi_there.pack(side=Tkinter.LEFT)
+ root.mainloop()
+
+could be rewritten to group the the Button's function with its
+declaration::
+
+ root = Tkinter.Tk()
+ frame = Tkinter.Frame(root)
+ frame.pack()
+ make Tkinter.Button hi_there(frame):
+ text = "Hello"
+ def command():
+ print "hi there, everyone!"
+ hi_there.pack(side=Tkinter.LEFT)
+ root.mainloop()
+
+
+Example: custom descriptors
+---------------------------
+
+Since descriptors are used to customize access to an attribute, it's
+often useful to know the name of that attribute. Current Python
+doesn't give an easy way to find this name and so a lot of custom
+descriptors, like Ian Bicking's setonce descriptor [2]_, have to hack
+around this somehow. With the make-statement, you could create a
+``setonce`` attribute like::
+
+ class A(object):
+ ...
+ make setonce x:
+ "A's x attribute"
+ ...
+
+where the ``setonce`` descriptor would be defined like::
+
+ class setonce(object):
+
+ def __init__(self, name, args, kwargs):
+ self._name = '_setonce_attr_%s' % name
+ self.__doc__ = kwargs.pop('__doc__', None)
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return getattr(obj, self._name)
+
+ def __set__(self, obj, value):
+ try:
+ getattr(obj, self._name)
+ except AttributeError:
+ setattr(obj, self._name, value)
+ else:
+ raise AttributeError("Attribute already set")
+
+ def set(self, obj, value):
+ setattr(obj, self._name, value)
+
+ def __delete__(self, obj):
+ delattr(obj, self._name)
+
+Note that unlike the original implementation, the private attribute
+name is stable since it uses the name of the descriptor, and therefore
+instances of class A are pickleable.
+
+
+Example: property namespaces
+----------------------------
+
+Python's property type takes three function arguments and a docstring
+argument which, though relevant only to the property, must be declared
+before it and then passed as arguments to the property call, e.g.::
class C(object):
...
@@ -73,60 +225,73 @@
...
def set_x(self):
...
- x = property(get_x, set_x, ...)
+ x = property(get_x, set_x, "the x of the frobulation")
-There have been a few recipes [2]_ trying to work around this
-behavior, but with the new make statement (and an appropriate
-definition of property), the getter and setter functions can be
-defined in the property's namespace like::
+This issue has been brought up before, and Guido [3]_ and others [4]_
+have briefly mused over alternate property syntaxes to make declaring
+properties easier. With the make-statement, the following syntax
+could be supported::
class C(object):
...
- make property x:
- def get(self):
+ make block_property x:
+ '''The x of the frobulation'''
+ def fget(self):
...
- def set(self):
+ def fset(self):
...
-The definition of such a property callable could be as simple as::
-
- def property(name, args, namespace):
- fget = namespace.get('get')
- fset = namespace.get('set')
- fdel = namespace.get('delete')
- doc = namespace.get('__doc__')
- return __builtin__.property(fget, fset, fdel, doc)
-
-Of course, properties are only one of the many possible uses of the
-make statement. The make statement is useful in essentially any
-situation where a name is associated with a namespace. So, for
-example, namespaces could be created as simply as::
-
- make namespace ns:
- """This creates a namespace named ns with a badger attribute
- and a spam function"""
-
- badger = 42
-
- def spam():
- ...
+with the following definition of ``block_property``::
-And if Python acquires interfaces, given an appropriately defined
-``interface`` callable, the make statement can support interface
-creation through the syntax::
-
- make interface C(...):
- ...
+ def block_property(name, args, block_dict):
+ fget = block_dict.pop('fget', None)
+ fset = block_dict.pop('fset', None)
+ fdel = block_dict.pop('fdel', None)
+ doc = block_dict.pop('__doc__', None)
+ assert not block_dict
+ return property(fget, fset, fdel, doc)
+
+
+Example: interfaces
+-------------------
+
+Guido [5]_ and others have occasionally suggested introducing
+interfaces into python. Most suggestions have offered syntax along
+the lines of::
+
+ interface IFoo:
+ """Foo blah blah"""
+
+ def fumble(name, count):
+ """docstring"""
+
+but since there is currently no way in Python to declare an interface
+in this manner, most implementations of Python interfaces use class
+objects instead, e.g. Zope's::
+
+ class IFoo(Interface):
+ """Foo blah blah"""
+
+ def fumble(name, count):
+ """docstring"""
+
+With the new make-statement, these interfaces could instead be
+declared as::
+
+ make Interface IFoo:
+ """Foo blah blah"""
+
+ def fumble(name, count):
+ """docstring"""
-This would mean that interface systems like that of Zope would no
-longer have to abuse the class syntax to create proper interface
-instances.
+which makes the intent (that this is an interface, not a class) much
+clearer.
Specification
=============
-Python will translate a make statement::
+Python will translate a make-statement::
make <callable> <name> <tuple>:
<block>
@@ -139,25 +304,39 @@
The ``<tuple>`` expression is optional; if not present, an empty tuple
will be assumed.
-A patch is available implementing these semantics [3]_.
+A patch is available implementing these semantics [6]_.
-The make statement introduces a new keyword, ``make``. Thus in Python
-2.6, the make statement will have to be enabled using ``from
+The make-statement introduces a new keyword, ``make``. Thus in Python
+2.6, the make-statement will have to be enabled using ``from
__future__ import make_statement``.
Open Issues
===========
+Keyword
+-------
+
Does the ``make`` keyword break too much code? Originally, the make
statement used the keyword ``create`` (a suggestion due to Nick
-Coghlan). However, investigations into the standard library [4]_ and
-Zope+Plone code [5]_ revealed that ``create`` would break a lot more
+Coghlan). However, investigations into the standard library [7]_ and
+Zope+Plone code [8]_ revealed that ``create`` would break a lot more
code, so ``make`` was adopted as the keyword instead. However, there
are still a few instances where ``make`` would break code. Is there a
better keyword for the statement?
-**********
+Some possible keywords and their counts in the standard library (plus
+some installed packages):
+
+* make - 2 (both in tests)
+* create - 19 (including existing function in imaplib)
+* build - 83 (including existing class in distutils.command.build)
+* construct - 0
+* produce - 0
+
+
+The make-statement as an alternate constructor
+----------------------------------------------
Currently, there are not many functions which have the signature
``(name, args, kwargs)``. That means that something like::
@@ -169,10 +348,10 @@
is currently impossible because the dict constructor has a different
signature. Does this sort of thing need to be supported? One
suggestion, by Carl Banks, would be to add a ``__make__`` magic method
-that would be called before ``__call__``. For types, the ``__make__``
-method would be identical to ``__call__`` (and thus unnecessary), but
-dicts could support the make statement by defining a ``__make__``
-method on the dict type that looks something like::
+that if found would be called instead of ``__call__``. For types,
+the ``__make__`` method would be identical to ``__call__`` and thus
+unnecessary, but dicts could support the make-statement by defining a
+``__make__`` method on the dict type that looks something like::
def __make__(cls, name, args, kwargs):
return cls(**kwargs)
@@ -185,6 +364,112 @@
x = 1
y = 2
+So the question is, will many types want to use the make-statement as
+an alternate constructor? And if so, does that alternate constructor
+need to have the same name as the original constructor?
+
+
+Customizing the dict in which the block is executed
+---------------------------------------------------
+
+Should users of the make-statement be able to determine in which dict
+object the code is executed? This would allow the make-statement to
+be used in situations where a normal dict object would not suffice,
+e.g. if order and repeated names must be allowed. Allowing this sort
+of customization could allow XML to be written without repeating
+element names, and with nesting of make-statements corresponding to
+nesting of XML elements::
+
+ make Element html:
+ make Element body:
+ text('before first h1')
+ make Element h1:
+ attrib(style='first')
+ text('first h1')
+ tail('after first h1')
+ make Element h1:
+ attrib(style='second')
+ text('second h1')
+ tail('after second h1')
+
+If the make-statement tried to get the dict in which to execute its
+block by calling the callable's ``__make_dict__`` method, the
+following code would allow the make-statement to be used as above::
+
+ class Element(object):
+
+ class __make_dict__(dict):
+
+ def __init__(self, *args, **kwargs):
+ self._super = super(Element.__make_dict__, self)
+ self._super.__init__(*args, **kwargs)
+ self.elements = []
+ self.text = None
+ self.tail = None
+ self.attrib = {}
+
+ def __getitem__(self, name):
+ try:
+ return self._super.__getitem__(name)
+ except KeyError:
+ if name in ['attrib', 'text', 'tail']:
+ return getattr(self, 'set_%s' % name)
+ else:
+ return globals()[name]
+
+ def __setitem__(self, name, value):
+ self._super.__setitem__(name, value)
+ self.elements.append(value)
+
+ def set_attrib(self, **kwargs):
+ self.attrib = kwargs
+
+ def set_text(self, text):
+ self.text = text
+
+ def set_tail(self, text):
+ self.tail = text
+
+ def __new__(cls, name, args, edict):
+ get_element = etree.ElementTree.Element
+ result = get_element(name, attrib=edict.attrib)
+ result.text = edict.text
+ result.tail = edict.tail
+ for element in edict.elements:
+ result.append(element)
+ return result
+
+Note, however, that the code to support this is somewhat fragile --
+it has to magically populate the namespace with ``attrib``, ``text``
+and ``tail``, and it assumes that every name binding inside the make
+statement body is creating an Element. As it stands, this code would
+break with the introduction of a simple for-loop to any one of the
+make-statement bodies, because the for-loop would bind a name to a
+non-Element object. This could be worked around by adding some sort
+of isinstance check or attribute examination, but this still results
+in a somewhat fragile solution.
+
+It has also been pointed out that the with-statement can provide
+equivalent nesting with a much more explicit syntax::
+
+ with Element('html') as html:
+ with Element('body') as body:
+ body.text = 'before first h1'
+ with Element('h1', style='first') as h1:
+ h1.text = 'first h1'
+ h1.tail = 'after first h1'
+ with Element('h1', style='second') as h1:
+ h1.text = 'second h1'
+ h1.tail = 'after second h1'
+
+And if the repetition of the element names here is too much of a DRY
+violoation, it is also possible to eliminate all as-clauses except for
+the first by adding a few methods to Element. [9]_
+
+So are there real use-cases for executing the block in a dict of a
+different type? And if so, should the make-statement be extended to
+support them?
+
Optional Extensions
===================
@@ -213,7 +498,7 @@
Removing __metaclass__ in Python 3000
-------------------------------------
-As a side-effect of its generality, the make statement mostly
+As a side-effect of its generality, the make-statement mostly
eliminates the need for the ``__metaclass__`` attribute in class
objects. Thus in Python 3000, instead of::
@@ -222,7 +507,7 @@
<block>
metaclasses could be supported by using the metaclass as the callable
-in a make statement::
+in a make-statement::
make <metaclass> <name> <bases-tuple>:
<block>
@@ -234,7 +519,7 @@
Removing class statements in Python 3000
----------------------------------------
-In the most extreme application of make statements, the class
+In the most extreme application of make-statements, the class
statement itself could be deprecated in favor of ``make type``
statements.
@@ -245,18 +530,30 @@
.. [1] Michele Simionato's original suggestion
(http://mail.python.org/pipermail/python-dev/2005-October/057435.html)
-.. [2] Namespace-based property recipe
+.. [2] Ian Bicking's setonce descriptor
+ (http://blog.ianbicking.org/easy-readonly-attributes.html)
+
+.. [3] Guido ponders property syntax
+ (http://mail.python.org/pipermail/python-dev/2005-October/057404.html)
+
+.. [4] Namespace-based property recipe
(http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442418)
-.. [3] Make Statement patch
+.. [5] Python interfaces
+ (http://www.artima.com/weblogs/viewpost.jsp?thread=86641)
+
+.. [6] Make Statement patch
(http://ucsu.colorado.edu/~bethard/py/make_statement.patch)
-.. [4] Instances of create in the stdlib
+.. [7] Instances of create in the stdlib
(http://mail.python.org/pipermail/python-list/2006-April/335159.html)
-.. [5] Instances of create in Zope+Plone
+.. [8] Instances of create in Zope+Plone
(http://mail.python.org/pipermail/python-list/2006-April/335284.html)
+.. [9] Eliminate as-clauses in with-statement XML
+ (http://mail.python.org/pipermail/python-list/2006-April/336774.html)
+
Copyright
=========
Modified: peps/trunk/pep-3002.txt
==============================================================================
--- peps/trunk/pep-3002.txt (original)
+++ peps/trunk/pep-3002.txt Wed Apr 19 01:25:47 2006
@@ -6,8 +6,8 @@
Status: Draft
Type: Process
Content-Type: text/x-rst
-Created: 03-Mar-2006
-Post-History: 03-Mar-2006
+Created: 27-Mar-2006
+Post-History: 27-Mar-2006, 13-Apr-2006
Abstract
More information about the Python-checkins
mailing list