mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	Merged revisions 59488-59511 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r59489 | christian.heimes | 2007-12-14 03:33:57 +0100 (Fri, 14 Dec 2007) | 1 line Silence a warning about an unsed variable in debug builds ........ r59490 | christian.heimes | 2007-12-14 03:35:23 +0100 (Fri, 14 Dec 2007) | 2 lines Fixed bug #1620: New @spam.getter property syntax modifies the property in place. I added also the feature that a @prop.getter decorator does not overwrite the doc string of the property if it was given as an argument to property(). ........ r59491 | raymond.hettinger | 2007-12-14 03:49:47 +0100 (Fri, 14 Dec 2007) | 1 line Cleaner method naming convention ........ r59492 | christian.heimes | 2007-12-14 04:02:34 +0100 (Fri, 14 Dec 2007) | 1 line Fixed a warning in _codecs_iso2022.c and some non C89 conform // comments. ........ r59493 | christian.heimes | 2007-12-14 05:38:13 +0100 (Fri, 14 Dec 2007) | 1 line Fixed warning in ssl module ........ r59500 | raymond.hettinger | 2007-12-14 19:08:20 +0100 (Fri, 14 Dec 2007) | 1 line Add line spacing for readability ........ r59501 | raymond.hettinger | 2007-12-14 19:12:21 +0100 (Fri, 14 Dec 2007) | 3 lines Update method names for named tuples. ........ r59503 | georg.brandl | 2007-12-14 20:03:36 +0100 (Fri, 14 Dec 2007) | 3 lines Add a section about nested listcomps to the tutorial. Thanks to Ian Bruntlett and Robert Lehmann. ........ r59504 | raymond.hettinger | 2007-12-14 20:19:59 +0100 (Fri, 14 Dec 2007) | 1 line Faster and simpler _replace() method ........ r59505 | raymond.hettinger | 2007-12-14 22:51:50 +0100 (Fri, 14 Dec 2007) | 1 line Add usage note ........ r59507 | andrew.kuchling | 2007-12-14 23:41:18 +0100 (Fri, 14 Dec 2007) | 1 line Remove warning about URL ........ r59510 | andrew.kuchling | 2007-12-14 23:52:36 +0100 (Fri, 14 Dec 2007) | 1 line Bump the version number, and make a few small edits ........ r59511 | christian.heimes | 2007-12-15 00:42:36 +0100 (Sat, 15 Dec 2007) | 2 lines Fixed bug #1628 The detection now works on Unix with Makefile, Makefile with VPATH and on Windows. ........
This commit is contained in:
		
							parent
							
								
									54cc54c1fe
								
							
						
					
					
						commit
						0449f63f53
					
				
					 16 changed files with 299 additions and 103 deletions
				
			
		|  | @ -28,6 +28,7 @@ docs@python.org), and we'll be glad to correct the problem. | ||||||
|    * Aaron Brancotti |    * Aaron Brancotti | ||||||
|    * Georg Brandl |    * Georg Brandl | ||||||
|    * Keith Briggs |    * Keith Briggs | ||||||
|  |    * Ian Bruntlett | ||||||
|    * Lee Busby |    * Lee Busby | ||||||
|    * Lorenzo M. Catucci |    * Lorenzo M. Catucci | ||||||
|    * Carl Cerecke |    * Carl Cerecke | ||||||
|  |  | ||||||
|  | @ -3,12 +3,10 @@ | ||||||
| ******************************** | ******************************** | ||||||
| 
 | 
 | ||||||
| :Author: \A. M. Kuchling | :Author: \A. M. Kuchling | ||||||
| :Release: 0.30 | :Release: 0.31 | ||||||
| 
 | 
 | ||||||
| (This is a first draft.  Please send comments/error reports/suggestions to | (This is a first draft.  Please send comments/error reports/suggestions to | ||||||
| amk@amk.ca.  This URL is probably not going to be the final location of the | amk@amk.ca.) | ||||||
| document, so be careful about linking to it -- you may want to add a |  | ||||||
| disclaimer.) |  | ||||||
| 
 | 
 | ||||||
| In this document, we'll take a tour of Python's features suitable for | In this document, we'll take a tour of Python's features suitable for | ||||||
| implementing programs in a functional style.  After an introduction to the | implementing programs in a functional style.  After an introduction to the | ||||||
|  | @ -49,17 +47,19 @@ Programming languages support decomposing problems in several different ways: | ||||||
|   functional languages include the ML family (Standard ML, OCaml, and other |   functional languages include the ML family (Standard ML, OCaml, and other | ||||||
|   variants) and Haskell. |   variants) and Haskell. | ||||||
| 
 | 
 | ||||||
| The designers of some computer languages have chosen one approach to programming | The designers of some computer languages choose to emphasize one | ||||||
| that's emphasized.  This often makes it difficult to write programs that use a | particular approach to programming.  This often makes it difficult to | ||||||
| different approach.  Other languages are multi-paradigm languages that support | write programs that use a different approach.  Other languages are | ||||||
| several different approaches.  Lisp, C++, and Python are multi-paradigm; you can | multi-paradigm languages that support several different approaches. | ||||||
| write programs or libraries that are largely procedural, object-oriented, or | Lisp, C++, and Python are multi-paradigm; you can write programs or | ||||||
| functional in all of these languages.  In a large program, different sections | libraries that are largely procedural, object-oriented, or functional | ||||||
| might be written using different approaches; the GUI might be object-oriented | in all of these languages.  In a large program, different sections | ||||||
| while the processing logic is procedural or functional, for example. | might be written using different approaches; the GUI might be | ||||||
|  | object-oriented while the processing logic is procedural or | ||||||
|  | functional, for example. | ||||||
| 
 | 
 | ||||||
| In a functional program, input flows through a set of functions. Each function | In a functional program, input flows through a set of functions. Each function | ||||||
| operates on its input and produces some output.  Functional style frowns upon | operates on its input and produces some output.  Functional style discourages | ||||||
| functions with side effects that modify internal state or make other changes | functions with side effects that modify internal state or make other changes | ||||||
| that aren't visible in the function's return value.  Functions that have no side | that aren't visible in the function's return value.  Functions that have no side | ||||||
| effects at all are called **purely functional**.  Avoiding side effects means | effects at all are called **purely functional**.  Avoiding side effects means | ||||||
|  | @ -616,7 +616,7 @@ Built-in functions | ||||||
| 
 | 
 | ||||||
| Let's look in more detail at built-in functions often used with iterators. | Let's look in more detail at built-in functions often used with iterators. | ||||||
| 
 | 
 | ||||||
| Two Python's built-in functions, :func:`map` and :func:`filter`, are somewhat | Two of Python's built-in functions, :func:`map` and :func:`filter`, are somewhat | ||||||
| obsolete; they duplicate the features of list comprehensions but return actual | obsolete; they duplicate the features of list comprehensions but return actual | ||||||
| lists instead of iterators. | lists instead of iterators. | ||||||
| 
 | 
 | ||||||
|  | @ -842,8 +842,8 @@ Fredrik Lundh once suggested the following set of rules for refactoring uses of | ||||||
| 4) Convert the lambda to a def statement, using that name. | 4) Convert the lambda to a def statement, using that name. | ||||||
| 5) Remove the comment. | 5) Remove the comment. | ||||||
| 
 | 
 | ||||||
| I really like these rules, but you're free to disagree that this lambda-free | I really like these rules, but you're free to disagree  | ||||||
| style is better. | about whether this lambda-free style is better. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| The itertools module | The itertools module | ||||||
|  |  | ||||||
|  | @ -403,8 +403,8 @@ they add the ability to access fields by name instead of position index. | ||||||
|    can be specified as a list of strings (such as ['x', 'y']). |    can be specified as a list of strings (such as ['x', 'y']). | ||||||
| 
 | 
 | ||||||
|    Any valid Python identifier may be used for a fieldname except for names |    Any valid Python identifier may be used for a fieldname except for names | ||||||
|    starting and ending with double underscores.  Valid identifiers consist of |    starting with an underscore.  Valid identifiers consist of letters, digits, | ||||||
|    letters, digits, and underscores but do not start with a digit and cannot be |    and underscores but do not start with a digit or underscore and cannot be | ||||||
|    a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, *print*, |    a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, *print*, | ||||||
|    or *raise*. |    or *raise*. | ||||||
| 
 | 
 | ||||||
|  | @ -418,18 +418,25 @@ Example:: | ||||||
|    >>> Point = namedtuple('Point', 'x y', verbose=True) |    >>> Point = namedtuple('Point', 'x y', verbose=True) | ||||||
|    class Point(tuple): |    class Point(tuple): | ||||||
|            'Point(x, y)' |            'Point(x, y)' | ||||||
|  | 
 | ||||||
|            __slots__ = () |            __slots__ = () | ||||||
|            __fields__ = ('x', 'y') | 
 | ||||||
|  |            _fields = ('x', 'y') | ||||||
|  | 
 | ||||||
|            def __new__(cls, x, y): |            def __new__(cls, x, y): | ||||||
|                return tuple.__new__(cls, (x, y)) |                return tuple.__new__(cls, (x, y)) | ||||||
|  | 
 | ||||||
|            def __repr__(self): |            def __repr__(self): | ||||||
|                return 'Point(x=%r, y=%r)' % self |                return 'Point(x=%r, y=%r)' % self | ||||||
|            def __asdict__(self): | 
 | ||||||
|                'Return a new dict mapping field names to their values' |            def _asdict(self): | ||||||
|  |                'Return a new dict which maps field names to their values' | ||||||
|                return dict(zip(('x', 'y'), self)) |                return dict(zip(('x', 'y'), self)) | ||||||
|            def __replace__(self, **kwds): | 
 | ||||||
|  |            def _replace(self, **kwds): | ||||||
|                'Return a new Point object replacing specified fields with new values' |                'Return a new Point object replacing specified fields with new values' | ||||||
|                return Point(**dict(zip(('x', 'y'), self), **kwds)) |                return Point(*map(kwds.get, ('x', 'y'), self)) | ||||||
|  | 
 | ||||||
|            x = property(itemgetter(0)) |            x = property(itemgetter(0)) | ||||||
|            y = property(itemgetter(1)) |            y = property(itemgetter(1)) | ||||||
| 
 | 
 | ||||||
|  | @ -477,43 +484,51 @@ When casting a dictionary to a named tuple, use the double-star-operator:: | ||||||
| In addition to the methods inherited from tuples, named tuples support | In addition to the methods inherited from tuples, named tuples support | ||||||
| two additonal methods and a read-only attribute. | two additonal methods and a read-only attribute. | ||||||
| 
 | 
 | ||||||
| .. method:: somenamedtuple.__asdict__() | .. method:: somenamedtuple._asdict() | ||||||
| 
 | 
 | ||||||
|    Return a new dict which maps field names to their corresponding values: |    Return a new dict which maps field names to their corresponding values: | ||||||
| 
 | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|       >>> p.__asdict__() |       >>> p._asdict() | ||||||
|       {'x': 11, 'y': 22} |       {'x': 11, 'y': 22} | ||||||
|        |        | ||||||
| .. method:: somenamedtuple.__replace__(kwargs) | .. method:: somenamedtuple._replace(kwargs) | ||||||
| 
 | 
 | ||||||
|    Return a new instance of the named tuple replacing specified fields with new values: |    Return a new instance of the named tuple replacing specified fields with new values: | ||||||
| 
 | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|       >>> p = Point(x=11, y=22) |       >>> p = Point(x=11, y=22) | ||||||
|       >>> p.__replace__(x=33) |       >>> p._replace(x=33) | ||||||
|       Point(x=33, y=22) |       Point(x=33, y=22) | ||||||
| 
 | 
 | ||||||
|       >>> for partnum, record in inventory.items(): |       >>> for partnum, record in inventory.items(): | ||||||
|       ...     inventory[partnum] = record.__replace__(price=newprices[partnum], updated=time.now()) |       ...     inventory[partnum] = record._replace(price=newprices[partnum], updated=time.now()) | ||||||
| 
 | 
 | ||||||
| .. attribute:: somenamedtuple.__fields__ | .. attribute:: somenamedtuple._fields | ||||||
| 
 | 
 | ||||||
|    Return a tuple of strings listing the field names.  This is useful for introspection |    Return a tuple of strings listing the field names.  This is useful for introspection | ||||||
|    and for creating new named tuple types from existing named tuples. |    and for creating new named tuple types from existing named tuples. | ||||||
| 
 | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|       >>> p.__fields__                                  # view the field names |       >>> p._fields            # view the field names | ||||||
|       ('x', 'y') |       ('x', 'y') | ||||||
| 
 | 
 | ||||||
|       >>> Color = namedtuple('Color', 'red green blue') |       >>> Color = namedtuple('Color', 'red green blue') | ||||||
|       >>> Pixel = namedtuple('Pixel', Point.__fields__ + Color.__fields__) |       >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) | ||||||
|       >>> Pixel(11, 22, 128, 255, 0) |       >>> Pixel(11, 22, 128, 255, 0) | ||||||
|       Pixel(x=11, y=22, red=128, green=255, blue=0)' |       Pixel(x=11, y=22, red=128, green=255, blue=0)' | ||||||
| 
 | 
 | ||||||
|  | To retrieve a field whose name is stored in a string, use the :func:`getattr` | ||||||
|  | function: | ||||||
|  | 
 | ||||||
|  | :: | ||||||
|  | 
 | ||||||
|  |     >>> getattr(p, 'x') | ||||||
|  |     11 | ||||||
|  | 
 | ||||||
| Since a named tuple is a regular Python class, it is easy to add or change | Since a named tuple is a regular Python class, it is easy to add or change | ||||||
| functionality.  For example, the display format can be changed by overriding | functionality.  For example, the display format can be changed by overriding | ||||||
| the :meth:`__repr__` method: | the :meth:`__repr__` method: | ||||||
|  | @ -522,17 +537,17 @@ the :meth:`__repr__` method: | ||||||
| 
 | 
 | ||||||
|     >>> Point = namedtuple('Point', 'x y') |     >>> Point = namedtuple('Point', 'x y') | ||||||
|     >>> Point.__repr__ = lambda self: 'Point(%.3f, %.3f)' % self |     >>> Point.__repr__ = lambda self: 'Point(%.3f, %.3f)' % self | ||||||
|     >>> Point(x=10, y=20) |     >>> Point(x=11, y=22) | ||||||
|     Point(10.000, 20.000) |     Point(11.000, 22.000) | ||||||
| 
 | 
 | ||||||
| Default values can be implemented by starting with a prototype instance | Default values can be implemented by starting with a prototype instance | ||||||
| and customizing it with :meth:`__replace__`: | and customizing it with :meth:`_replace`: | ||||||
| 
 | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|     >>> Account = namedtuple('Account', 'owner balance transaction_count') |     >>> Account = namedtuple('Account', 'owner balance transaction_count') | ||||||
|     >>> model_account = Account('<owner name>', 0.0, 0) |     >>> model_account = Account('<owner name>', 0.0, 0) | ||||||
|     >>> johns_account = model_account.__replace__(owner='John') |     >>> johns_account = model_account._replace(owner='John') | ||||||
| 
 | 
 | ||||||
| .. rubric:: Footnotes | .. rubric:: Footnotes | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -277,6 +277,48 @@ List comprehensions can be applied to complex expressions and nested functions:: | ||||||
|    ['3.1', '3.14', '3.142', '3.1416', '3.14159'] |    ['3.1', '3.14', '3.142', '3.1416', '3.14159'] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Nested List Comprehensions | ||||||
|  | -------------------------- | ||||||
|  | 
 | ||||||
|  | If you've got the stomach for it, list comprehensions can be nested. They are a | ||||||
|  | powerful tool but -- like all powerful tools -- they need to be used carefully, | ||||||
|  | if at all. | ||||||
|  | 
 | ||||||
|  | Consider the following example of a 3x3 matrix held as a list containing three  | ||||||
|  | lists, one list per row:: | ||||||
|  | 
 | ||||||
|  |     >>> mat = [ | ||||||
|  |     ...        [1, 2, 3], | ||||||
|  |     ...        [4, 5, 6], | ||||||
|  |     ...        [7, 8, 9], | ||||||
|  |     ...       ] | ||||||
|  | 
 | ||||||
|  | Now, if you wanted to swap rows and columns, you could use a list  | ||||||
|  | comprehension:: | ||||||
|  | 
 | ||||||
|  |     >>> print [[row[i] for row in mat] for i in [0, 1, 2]] | ||||||
|  |     [[1, 4, 7], [2, 5, 8], [3, 6, 9]] | ||||||
|  | 
 | ||||||
|  | Special care has to be taken for the *nested* list comprehension: | ||||||
|  | 
 | ||||||
|  |     To avoid apprehension when nesting list comprehensions, read from right to | ||||||
|  |     left. | ||||||
|  | 
 | ||||||
|  | A more verbose version of this snippet shows the flow explicitly:: | ||||||
|  | 
 | ||||||
|  |     for i in [0, 1, 2]: | ||||||
|  |         for row in mat: | ||||||
|  |             print row[i], | ||||||
|  |         print | ||||||
|  | 
 | ||||||
|  | In real world, you should prefer builtin functions to complex flow statements.  | ||||||
|  | The :func:`zip` function would do a great job for this use case:: | ||||||
|  | 
 | ||||||
|  |     >>> zip(*mat) | ||||||
|  |     [(1, 4, 7), (2, 5, 8), (3, 6, 9)] | ||||||
|  | 
 | ||||||
|  | See :ref:`tut-unpacking-arguments` for details on the asterisk in this line. | ||||||
|  | 
 | ||||||
| .. _tut-del: | .. _tut-del: | ||||||
| 
 | 
 | ||||||
| The :keyword:`del` statement | The :keyword:`del` statement | ||||||
|  |  | ||||||
|  | @ -528,7 +528,7 @@ complete list of changes, or look through the CVS logs for all the details. | ||||||
|      ...             'id name type size') |      ...             'id name type size') | ||||||
|      # Names are separated by spaces or commas. |      # Names are separated by spaces or commas. | ||||||
|      # 'id, name, type, size' would also work. |      # 'id, name, type, size' would also work. | ||||||
|      >>> var_type.__fields__ |      >>> var_type._fields | ||||||
|      ('id', 'name', 'type', 'size') |      ('id', 'name', 'type', 'size') | ||||||
| 
 | 
 | ||||||
|      >>> var = var_type(1, 'frequency', 'int', 4) |      >>> var = var_type(1, 'frequency', 'int', 4) | ||||||
|  | @ -536,9 +536,9 @@ complete list of changes, or look through the CVS logs for all the details. | ||||||
|      1 1 |      1 1 | ||||||
|      >>> print var[2], var.type          # Equivalent |      >>> print var[2], var.type          # Equivalent | ||||||
|      int int |      int int | ||||||
|      >>> var.__asdict__() |      >>> var._asdict() | ||||||
|      {'size': 4, 'type': 'int', 'id': 1, 'name': 'frequency'} |      {'size': 4, 'type': 'int', 'id': 1, 'name': 'frequency'} | ||||||
|      >>> v2 = var.__replace__('name', 'amplitude') |      >>> v2 = var._replace('name', 'amplitude') | ||||||
|      >>> v2 |      >>> v2 | ||||||
|      variable(id=1, name='amplitude', type='int', size=4) |      variable(id=1, name='amplitude', type='int', size=4) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,12 +26,12 @@ def namedtuple(typename, field_names, verbose=False): | ||||||
|     (11, 22) |     (11, 22) | ||||||
|     >>> p.x + p.y                       # fields also accessable by name |     >>> p.x + p.y                       # fields also accessable by name | ||||||
|     33 |     33 | ||||||
|     >>> d = p.__asdict__()              # convert to a dictionary |     >>> d = p._asdict()                 # convert to a dictionary | ||||||
|     >>> d['x'] |     >>> d['x'] | ||||||
|     11 |     11 | ||||||
|     >>> Point(**d)                      # convert from a dictionary |     >>> Point(**d)                      # convert from a dictionary | ||||||
|     Point(x=11, y=22) |     Point(x=11, y=22) | ||||||
|     >>> p.__replace__(x=100)            # __replace__() is like str.replace() but targets named fields |     >>> p._replace(x=100)               # _replace() is like str.replace() but targets named fields | ||||||
|     Point(x=100, y=22) |     Point(x=100, y=22) | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
|  | @ -49,8 +49,8 @@ def namedtuple(typename, field_names, verbose=False): | ||||||
|             raise ValueError('Type names and field names cannot start with a number: %r' % name) |             raise ValueError('Type names and field names cannot start with a number: %r' % name) | ||||||
|     seen_names = set() |     seen_names = set() | ||||||
|     for name in field_names: |     for name in field_names: | ||||||
|         if name.startswith('__') and name.endswith('__') and len(name) > 3: |         if name.startswith('_'): | ||||||
|             raise ValueError('Field names cannot start and end with double underscores: %r' % name) |             raise ValueError('Field names cannot start with an underscore: %r' % name) | ||||||
|         if name in seen_names: |         if name in seen_names: | ||||||
|             raise ValueError('Encountered duplicate field name: %r' % name) |             raise ValueError('Encountered duplicate field name: %r' % name) | ||||||
|         seen_names.add(name) |         seen_names.add(name) | ||||||
|  | @ -59,19 +59,19 @@ def namedtuple(typename, field_names, verbose=False): | ||||||
|     argtxt = repr(field_names).replace("'", "")[1:-1]   # tuple repr without parens or quotes |     argtxt = repr(field_names).replace("'", "")[1:-1]   # tuple repr without parens or quotes | ||||||
|     reprtxt = ', '.join('%s=%%r' % name for name in field_names) |     reprtxt = ', '.join('%s=%%r' % name for name in field_names) | ||||||
|     template = '''class %(typename)s(tuple): |     template = '''class %(typename)s(tuple): | ||||||
|         '%(typename)s(%(argtxt)s)' |         '%(typename)s(%(argtxt)s)' \n | ||||||
|         __slots__ = () |         __slots__ = () \n | ||||||
|         __fields__ = property(lambda self: %(field_names)r) |         _fields = property(lambda self: %(field_names)r) \n | ||||||
|         def __new__(cls, %(argtxt)s): |         def __new__(cls, %(argtxt)s): | ||||||
|             return tuple.__new__(cls, (%(argtxt)s)) |             return tuple.__new__(cls, (%(argtxt)s)) \n | ||||||
|         def __repr__(self): |         def __repr__(self): | ||||||
|             return '%(typename)s(%(reprtxt)s)' %% self |             return '%(typename)s(%(reprtxt)s)' %% self \n | ||||||
|         def __asdict__(self, dict=dict, zip=zip): |         def _asdict(self, dict=dict, zip=zip): | ||||||
|             'Return a new dict mapping field names to their values' |             'Return a new dict which maps field names to their values' | ||||||
|             return dict(zip(%(field_names)r, self)) |             return dict(zip(%(field_names)r, self)) \n | ||||||
|         def __replace__(self, **kwds): |         def _replace(self, **kwds): | ||||||
|             'Return a new %(typename)s object replacing specified fields with new values' |             'Return a new %(typename)s object replacing specified fields with new values' | ||||||
|             return %(typename)s(**dict(zip(%(field_names)r, self), **kwds))  \n''' % locals() |             return %(typename)s(*map(kwds.get, %(field_names)r, self)) \n\n''' % locals() | ||||||
|     for i, name in enumerate(field_names): |     for i, name in enumerate(field_names): | ||||||
|         template += '        %s = property(itemgetter(%d))\n' % (name, i) |         template += '        %s = property(itemgetter(%d))\n' % (name, i) | ||||||
|     if verbose: |     if verbose: | ||||||
|  |  | ||||||
|  | @ -31,8 +31,10 @@ | ||||||
| # python_build: (Boolean) if true, we're either building Python or | # python_build: (Boolean) if true, we're either building Python or | ||||||
| # building an extension with an un-installed Python, so we use | # building an extension with an un-installed Python, so we use | ||||||
| # different (hard-wired) directories. | # different (hard-wired) directories. | ||||||
| python_build = os.path.isfile(os.path.join(project_base, "Modules", | # Setup.local is available for Makefile builds including VPATH builds, | ||||||
|                                            "Setup.local")) | # Setup.dist is available on Windows | ||||||
|  | python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn)) | ||||||
|  |                    for fn in ("Setup.dist", "Setup.local")) | ||||||
| 
 | 
 | ||||||
| def get_python_version(): | def get_python_version(): | ||||||
|     """Return a string containing the major and minor Python version, |     """Return a string containing the major and minor Python version, | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ def test_get_config_h_filename(self): | ||||||
| 
 | 
 | ||||||
|     def test_get_python_lib(self): |     def test_get_python_lib(self): | ||||||
|         lib_dir = sysconfig.get_python_lib() |         lib_dir = sysconfig.get_python_lib() | ||||||
|         # XXX doesn't work on Inux when Python was never installed before |         # XXX doesn't work on Linux when Python was never installed before | ||||||
|         #self.assert_(os.path.isdir(lib_dir), lib_dir) |         #self.assert_(os.path.isdir(lib_dir), lib_dir) | ||||||
|         # test for pythonxx.lib? |         # test for pythonxx.lib? | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,11 +28,11 @@ def test_factory(self): | ||||||
|         self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi')       # field with non-alpha char |         self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi')       # field with non-alpha char | ||||||
|         self.assertRaises(ValueError, namedtuple, 'abc', 'abc class')      # field has keyword |         self.assertRaises(ValueError, namedtuple, 'abc', 'abc class')      # field has keyword | ||||||
|         self.assertRaises(ValueError, namedtuple, 'abc', '8efg 9ghi')      # field starts with digit |         self.assertRaises(ValueError, namedtuple, 'abc', '8efg 9ghi')      # field starts with digit | ||||||
|         self.assertRaises(ValueError, namedtuple, 'abc', '__efg__ ghi')    # field with double underscores |         self.assertRaises(ValueError, namedtuple, 'abc', '_efg ghi')       # field with leading underscore | ||||||
|         self.assertRaises(ValueError, namedtuple, 'abc', 'efg efg ghi')    # duplicate field |         self.assertRaises(ValueError, namedtuple, 'abc', 'efg efg ghi')    # duplicate field | ||||||
| 
 | 
 | ||||||
|         namedtuple('Point0', 'x1 y2')   # Verify that numbers are allowed in names |         namedtuple('Point0', 'x1 y2')   # Verify that numbers are allowed in names | ||||||
|         namedtuple('_', '_ __ ___')     # Verify that underscores are allowed |         namedtuple('_', 'a b c')        # Test leading underscores in a typename | ||||||
| 
 | 
 | ||||||
|     def test_instance(self): |     def test_instance(self): | ||||||
|         Point = namedtuple('Point', 'x y') |         Point = namedtuple('Point', 'x y') | ||||||
|  | @ -49,17 +49,17 @@ def test_instance(self): | ||||||
|         self.assertEqual(repr(p), 'Point(x=11, y=22)') |         self.assertEqual(repr(p), 'Point(x=11, y=22)') | ||||||
|         self.assert_('__dict__' not in dir(p))                              # verify instance has no dict |         self.assert_('__dict__' not in dir(p))                              # verify instance has no dict | ||||||
|         self.assert_('__weakref__' not in dir(p)) |         self.assert_('__weakref__' not in dir(p)) | ||||||
|         self.assertEqual(p.__fields__, ('x', 'y'))                          # test __fields__ attribute |         self.assertEqual(p._fields, ('x', 'y'))                             # test _fields attribute | ||||||
|         self.assertEqual(p.__replace__(x=1), (1, 22))                       # test __replace__ method |         self.assertEqual(p._replace(x=1), (1, 22))                          # test _replace method | ||||||
|         self.assertEqual(p.__asdict__(), dict(x=11, y=22))                  # test __dict__ method |         self.assertEqual(p._asdict(), dict(x=11, y=22))                     # test _asdict method | ||||||
| 
 | 
 | ||||||
|         # Verify that __fields__ is read-only |         # Verify that _fields is read-only | ||||||
|         try: |         try: | ||||||
|             p.__fields__ = ('F1' ,'F2') |             p._fields = ('F1' ,'F2') | ||||||
|         except AttributeError: |         except AttributeError: | ||||||
|             pass |             pass | ||||||
|         else: |         else: | ||||||
|             self.fail('The __fields__ attribute needs to be read-only') |             self.fail('The _fields attribute needs to be read-only') | ||||||
| 
 | 
 | ||||||
|         # verify that field string can have commas |         # verify that field string can have commas | ||||||
|         Point = namedtuple('Point', 'x, y') |         Point = namedtuple('Point', 'x, y') | ||||||
|  |  | ||||||
							
								
								
									
										98
									
								
								Lib/test/test_property.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								Lib/test/test_property.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | ||||||
|  | # Test case for property | ||||||
|  | # more tests are in test_descr | ||||||
|  | 
 | ||||||
|  | import unittest | ||||||
|  | from test.test_support import run_unittest | ||||||
|  | 
 | ||||||
|  | class PropertyBase(Exception): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class PropertyGet(PropertyBase): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class PropertySet(PropertyBase): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class PropertyDel(PropertyBase): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class BaseClass(object): | ||||||
|  |     def __init__(self): | ||||||
|  |         self._spam = 5 | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def spam(self): | ||||||
|  |         """BaseClass.getter""" | ||||||
|  |         return self._spam | ||||||
|  | 
 | ||||||
|  |     @spam.setter | ||||||
|  |     def spam(self, value): | ||||||
|  |         self._spam = value | ||||||
|  | 
 | ||||||
|  |     @spam.deleter | ||||||
|  |     def spam(self): | ||||||
|  |         del self._spam | ||||||
|  | 
 | ||||||
|  | class SubClass(BaseClass): | ||||||
|  | 
 | ||||||
|  |     @BaseClass.spam.getter | ||||||
|  |     def spam(self): | ||||||
|  |         """SubClass.getter""" | ||||||
|  |         raise PropertyGet(self._spam) | ||||||
|  | 
 | ||||||
|  |     @spam.setter | ||||||
|  |     def spam(self, value): | ||||||
|  |         raise PropertySet(self._spam) | ||||||
|  | 
 | ||||||
|  |     @spam.deleter | ||||||
|  |     def spam(self): | ||||||
|  |         raise PropertyDel(self._spam) | ||||||
|  | 
 | ||||||
|  | class PropertyDocBase(object): | ||||||
|  |     _spam = 1 | ||||||
|  |     def _get_spam(self): | ||||||
|  |         return self._spam | ||||||
|  |     spam = property(_get_spam, doc="spam spam spam") | ||||||
|  | 
 | ||||||
|  | class PropertyDocSub(PropertyDocBase): | ||||||
|  |     @PropertyDocBase.spam.getter | ||||||
|  |     def spam(self): | ||||||
|  |         """The decorator does not use this doc string""" | ||||||
|  |         return self._spam | ||||||
|  | 
 | ||||||
|  | class PropertyTests(unittest.TestCase): | ||||||
|  |     def test_property_decorator_baseclass(self): | ||||||
|  |         # see #1620 | ||||||
|  |         base = BaseClass() | ||||||
|  |         self.assertEqual(base.spam, 5) | ||||||
|  |         self.assertEqual(base._spam, 5) | ||||||
|  |         base.spam = 10 | ||||||
|  |         self.assertEqual(base.spam, 10) | ||||||
|  |         self.assertEqual(base._spam, 10) | ||||||
|  |         delattr(base, "spam") | ||||||
|  |         self.assert_(not hasattr(base, "spam")) | ||||||
|  |         self.assert_(not hasattr(base, "_spam")) | ||||||
|  |         base.spam = 20 | ||||||
|  |         self.assertEqual(base.spam, 20) | ||||||
|  |         self.assertEqual(base._spam, 20) | ||||||
|  |         self.assertEqual(base.__class__.spam.__doc__, "BaseClass.getter") | ||||||
|  | 
 | ||||||
|  |     def test_property_decorator_subclass(self): | ||||||
|  |         # see #1620 | ||||||
|  |         sub = SubClass() | ||||||
|  |         self.assertRaises(PropertyGet, getattr, sub, "spam") | ||||||
|  |         self.assertRaises(PropertySet, setattr, sub, "spam", None) | ||||||
|  |         self.assertRaises(PropertyDel, delattr, sub, "spam") | ||||||
|  |         self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter") | ||||||
|  | 
 | ||||||
|  |     def test_property_decorator_doc(self): | ||||||
|  |         base = PropertyDocBase() | ||||||
|  |         sub = PropertyDocSub() | ||||||
|  |         self.assertEqual(base.__class__.spam.__doc__, "spam spam spam") | ||||||
|  |         self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam") | ||||||
|  | 
 | ||||||
|  | def test_main(): | ||||||
|  |     run_unittest(PropertyTests) | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     test_main() | ||||||
|  | @ -256,7 +256,7 @@ ffi_closure_SYSV_inner (closure, respp, args) | ||||||
|      void **respp; |      void **respp; | ||||||
|      void *args; |      void *args; | ||||||
| { | { | ||||||
|   // our various things...
 |   /* our various things... */ | ||||||
|   ffi_cif       *cif; |   ffi_cif       *cif; | ||||||
|   void         **arg_area; |   void         **arg_area; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -656,7 +656,7 @@ _get_peer_alt_names (X509 *certificate) { | ||||||
| 	char buf[2048]; | 	char buf[2048]; | ||||||
| 	char *vptr; | 	char *vptr; | ||||||
| 	int len; | 	int len; | ||||||
| 	unsigned char *p; | 	const unsigned char *p; | ||||||
| 
 | 
 | ||||||
| 	if (certificate == NULL) | 	if (certificate == NULL) | ||||||
| 		return peer_alt_names; | 		return peer_alt_names; | ||||||
|  |  | ||||||
|  | @ -232,7 +232,7 @@ typedef struct { | ||||||
| #define DBSequenceObject_Check(v)  ((v)->ob_type == bsddb_api->dbsequence_type) | #define DBSequenceObject_Check(v)  ((v)->ob_type == bsddb_api->dbsequence_type) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #endif // COMPILING_BSDDB_C
 | #endif /* COMPILING_BSDDB_C */ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #endif // _BSDDB_H_
 | #endif /* _BSDDB_H_ */ | ||||||
|  |  | ||||||
|  | @ -203,7 +203,7 @@ ENCODER(iso2022) | ||||||
| 				} else | 				} else | ||||||
| 					encoded = dsg->encoder(&c, &length); | 					encoded = dsg->encoder(&c, &length); | ||||||
| #else | #else | ||||||
| 				encoded = dsg->encoder(*inbuf, &length); | 				encoded = dsg->encoder(&c, &length); | ||||||
| #endif | #endif | ||||||
| 				if (encoded != MAP_UNMAPPABLE) { | 				if (encoded != MAP_UNMAPPABLE) { | ||||||
| 					insize = length; | 					insize = length; | ||||||
|  |  | ||||||
|  | @ -1065,8 +1065,12 @@ typedef struct { | ||||||
| 	PyObject *prop_set; | 	PyObject *prop_set; | ||||||
| 	PyObject *prop_del; | 	PyObject *prop_del; | ||||||
| 	PyObject *prop_doc; | 	PyObject *prop_doc; | ||||||
|  | 	int getter_doc; | ||||||
| } propertyobject; | } propertyobject; | ||||||
| 
 | 
 | ||||||
|  | static PyObject * property_copy(PyObject *, PyObject *, PyObject *, | ||||||
|  | 				  PyObject *, PyObject *); | ||||||
|  | 
 | ||||||
| static PyMemberDef property_members[] = { | static PyMemberDef property_members[] = { | ||||||
| 	{"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY}, | 	{"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY}, | ||||||
| 	{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY}, | 	{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY}, | ||||||
|  | @ -1075,53 +1079,37 @@ static PyMemberDef property_members[] = { | ||||||
| 	{0} | 	{0} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| PyDoc_STRVAR(getter_doc, | PyDoc_STRVAR(getter_doc, | ||||||
| 	     "Descriptor to change the getter on a property."); | 	     "Descriptor to change the getter on a property."); | ||||||
| 
 | 
 | ||||||
| PyObject * | PyObject * | ||||||
| property_getter(PyObject *self, PyObject *getter) | property_getter(PyObject *self, PyObject *getter) | ||||||
| { | { | ||||||
| 	Py_XDECREF(((propertyobject *)self)->prop_get); | 	return property_copy(self, getter, NULL, NULL, NULL); | ||||||
| 	if (getter == Py_None) |  | ||||||
| 		getter = NULL; |  | ||||||
| 	Py_XINCREF(getter); |  | ||||||
| 	((propertyobject *)self)->prop_get = getter; |  | ||||||
| 	Py_INCREF(self); |  | ||||||
| 	return self; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| PyDoc_STRVAR(setter_doc, | PyDoc_STRVAR(setter_doc, | ||||||
| 	     "Descriptor to change the setter on a property.\n"); | 	     "Descriptor to change the setter on a property."); | ||||||
| 
 | 
 | ||||||
| PyObject * | PyObject * | ||||||
| property_setter(PyObject *self, PyObject *setter) | property_setter(PyObject *self, PyObject *setter) | ||||||
| { | { | ||||||
| 	Py_XDECREF(((propertyobject *)self)->prop_set); | 	return property_copy(self, NULL, setter, NULL, NULL); | ||||||
| 	if (setter == Py_None) |  | ||||||
| 		setter = NULL; |  | ||||||
| 	Py_XINCREF(setter); |  | ||||||
| 	((propertyobject *)self)->prop_set = setter; |  | ||||||
| 	Py_INCREF(self); |  | ||||||
| 	return self; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| PyDoc_STRVAR(deleter_doc, | PyDoc_STRVAR(deleter_doc, | ||||||
| 	     "Descriptor to change the deleter on a property."); | 	     "Descriptor to change the deleter on a property."); | ||||||
| 
 | 
 | ||||||
| PyObject * | PyObject * | ||||||
| property_deleter(PyObject *self, PyObject *deleter) | property_deleter(PyObject *self, PyObject *deleter) | ||||||
| { | { | ||||||
| 	Py_XDECREF(((propertyobject *)self)->prop_del); | 	return property_copy(self, NULL, NULL, deleter, NULL); | ||||||
| 	if (deleter == Py_None) |  | ||||||
| 		deleter = NULL; |  | ||||||
| 	Py_XINCREF(deleter); |  | ||||||
| 	((propertyobject *)self)->prop_del = deleter; |  | ||||||
| 	Py_INCREF(self); |  | ||||||
| 	return self; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| static PyMethodDef property_methods[] = { | static PyMethodDef property_methods[] = { | ||||||
| 	{"getter", property_getter, METH_O, getter_doc}, | 	{"getter", property_getter, METH_O, getter_doc}, | ||||||
| 	{"setter", property_setter, METH_O, setter_doc}, | 	{"setter", property_setter, METH_O, setter_doc}, | ||||||
|  | @ -1186,12 +1174,59 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static PyObject * | ||||||
|  | property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del, | ||||||
|  | 		PyObject *doc) | ||||||
|  | { | ||||||
|  | 	propertyobject *pold = (propertyobject *)old; | ||||||
|  | 	propertyobject *pnew = NULL; | ||||||
|  | 	PyObject *new, *type; | ||||||
|  | 
 | ||||||
|  | 	type = PyObject_Type(old); | ||||||
|  | 	if (type == NULL) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	if (get == NULL || get == Py_None) { | ||||||
|  | 		Py_XDECREF(get); | ||||||
|  | 		get = pold->prop_get ? pold->prop_get : Py_None; | ||||||
|  | 	} | ||||||
|  | 	if (set == NULL || set == Py_None) { | ||||||
|  | 		Py_XDECREF(set); | ||||||
|  | 		set = pold->prop_set ? pold->prop_set : Py_None; | ||||||
|  | 	} | ||||||
|  | 	if (del == NULL || del == Py_None) { | ||||||
|  | 		Py_XDECREF(del); | ||||||
|  | 		del = pold->prop_del ? pold->prop_del : Py_None; | ||||||
|  | 	} | ||||||
|  | 	if (doc == NULL || doc == Py_None) { | ||||||
|  | 		Py_XDECREF(doc); | ||||||
|  | 		doc = pold->prop_doc ? pold->prop_doc : Py_None; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	new =  PyObject_CallFunction(type, "OOOO", get, set, del, doc); | ||||||
|  | 	if (new == NULL) | ||||||
|  | 		return NULL; | ||||||
|  | 	pnew = (propertyobject *)new; | ||||||
|  | 	 | ||||||
|  | 	if (pold->getter_doc && get != Py_None) { | ||||||
|  | 		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__"); | ||||||
|  | 		if (get_doc != NULL) { | ||||||
|  | 			Py_XDECREF(pnew->prop_doc); | ||||||
|  | 			pnew->prop_doc = get_doc;  /* get_doc already INCREF'd by GetAttr */ | ||||||
|  | 			pnew->getter_doc = 1; | ||||||
|  | 		} else { | ||||||
|  | 			PyErr_Clear(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int | static int | ||||||
| property_init(PyObject *self, PyObject *args, PyObject *kwds) | property_init(PyObject *self, PyObject *args, PyObject *kwds) | ||||||
| { | { | ||||||
| 	PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL; | 	PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL; | ||||||
| 	static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0}; | 	static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0}; | ||||||
| 	propertyobject *gs = (propertyobject *)self; | 	propertyobject *prop = (propertyobject *)self; | ||||||
| 	 | 	 | ||||||
| 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", | 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", | ||||||
| 					 kwlist, &get, &set, &del, &doc)) | 					 kwlist, &get, &set, &del, &doc)) | ||||||
|  | @ -1209,22 +1244,24 @@ property_init(PyObject *self, PyObject *args, PyObject *kwds) | ||||||
| 	Py_XINCREF(del); | 	Py_XINCREF(del); | ||||||
| 	Py_XINCREF(doc); | 	Py_XINCREF(doc); | ||||||
| 
 | 
 | ||||||
|  | 	prop->prop_get = get; | ||||||
|  | 	prop->prop_set = set; | ||||||
|  | 	prop->prop_del = del; | ||||||
|  | 	prop->prop_doc = doc; | ||||||
|  | 	prop->getter_doc = 0; | ||||||
|  | 
 | ||||||
| 	/* if no docstring given and the getter has one, use that one */ | 	/* if no docstring given and the getter has one, use that one */ | ||||||
| 	if ((doc == NULL || doc == Py_None) && get != NULL) { | 	if ((doc == NULL || doc == Py_None) && get != NULL) { | ||||||
| 		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__"); | 		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__"); | ||||||
| 		if (get_doc != NULL) { | 		if (get_doc != NULL) { | ||||||
| 			Py_XDECREF(doc); | 			Py_XDECREF(prop->prop_doc); | ||||||
| 			doc = get_doc;  /* get_doc already INCREF'd by GetAttr */ | 			prop->prop_doc = get_doc;  /* get_doc already INCREF'd by GetAttr */ | ||||||
|  | 			prop->getter_doc = 1; | ||||||
| 		} else { | 		} else { | ||||||
| 			PyErr_Clear(); | 			PyErr_Clear(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	gs->prop_get = get; |  | ||||||
| 	gs->prop_set = set; |  | ||||||
| 	gs->prop_del = del; |  | ||||||
| 	gs->prop_doc = doc; |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -668,8 +668,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) | ||||||
| #define STACKADJ(n)	{ (void)(BASIC_STACKADJ(n), \ | #define STACKADJ(n)	{ (void)(BASIC_STACKADJ(n), \ | ||||||
|                                lltrace && prtrace(TOP(), "stackadj")); \ |                                lltrace && prtrace(TOP(), "stackadj")); \ | ||||||
|                                assert(STACK_LEVEL() <= co->co_stacksize); } |                                assert(STACK_LEVEL() <= co->co_stacksize); } | ||||||
| #define EXT_POP(STACK_POINTER) (lltrace && prtrace((STACK_POINTER)[-1], \ | #define EXT_POP(STACK_POINTER) ((void)(lltrace && \ | ||||||
| 				"ext_pop"), *--(STACK_POINTER)) | 				prtrace((STACK_POINTER)[-1], "ext_pop")), \ | ||||||
|  | 				*--(STACK_POINTER)) | ||||||
| #else | #else | ||||||
| #define PUSH(v)		BASIC_PUSH(v) | #define PUSH(v)		BASIC_PUSH(v) | ||||||
| #define POP()		BASIC_POP() | #define POP()		BASIC_POP() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Christian Heimes
						Christian Heimes