[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pyamf-commits
Subject:    [pyamf-commits] r2929 - in pyamf/branches: . bytearray-context-695
From:       SVN commit logs for PyAMF <commits () pyamf ! org>
Date:       2009-10-19 10:33:39
Message-ID: 20091019103339.8037B7C8017 () mail ! collab ! com
[Download RAW message or body]

Author: nick
Date: 2009-10-19 12:33:37 +0200 (Mon, 19 Oct 2009)
New Revision: 2929

Added:
   pyamf/branches/bytearray-context-695/
   pyamf/branches/bytearray-context-695/CHANGES.txt
   pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py
   pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py
Removed:
   pyamf/branches/bytearray-context-695/CHANGES.txt
   pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py
   pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py
Log:
Branching to source:pyamf/branches/bytearray-context-695

Deleted: pyamf/branches/bytearray-context-695/CHANGES.txt
===================================================================
--- pyamf/trunk/CHANGES.txt	2009-10-10 21:51:34 UTC (rev 2927)
+++ pyamf/branches/bytearray-context-695/CHANGES.txt	2009-10-19 10:33:37 UTC (rev \
2929) @@ -1,345 +0,0 @@
-Changelog
-=========
-
-This file contains information about the changes between the different
-versions of PyAMF.
-
-.. contents::
-
-0.6 (unreleased)
-----------------
- - Fixed a small bug in decoding Django relations when declared as static.
-   Thanks to wolver for the patch. (Ticket:693)
- - Removed ``exceptions`` from all ``Context`` and ``pyamf.util.Indexed*``
-   classes (Ticket:660)
- - Empty Django relations are now encoded as ``None``, not ``pyamf.Undefined``
-   (Ticket:659)
- - ``pyamf.ClientType`` removed, as it is not spec compliant (Ticket:651)
-
-0.5.1 (2009-09-19)
-------------------
- - pyamf.register_package can now accept a list of classes (Ticket:650)
- - Fixed a regression in TwistedGateway where services would be called twice
-   (Ticket:648)
- - Fixed a bug with encoding anonymous trait references (Ticket:644)
- - Moved IndexedCollection into cpyamf (Ticket:424)
- - amf3.encode_int now encodes signed 29bit ints, not unsigned. Thanks to
-   gerard for the report, investigation and patch! (Ticket:646)
-
-0.5 (2009-09-07)
-----------------
- - Added the ability to modify the timezones of dates being de/encoded for
-   legacy systems. (Ticket:612)
- - Fixed a Django reference bug where parent.child.parent is parent would
-   encode 3 objects and not 2. (Ticket:642)
- - Setting `None` on a Django DateField would blow up the encoder (Ticket:632)
- - `rootCause` in ErrorFault objects are now populated with the traceback
-   object. (Ticket:637)
- - Support for encoding/decoding BlazeDS specific messages (Ticket:581)
- - Removed tracebacks from exception messages if debug=False (Ticket:552)
- - Support for google.appengine.ext.db.polymodel.PolyModel (Ticket:633)
- - Support for Django File/ImageField (Ticket:631)
- - Support for Django model inheritance (Ticket:626)
- - Rewrote attribute handling code. Added the ability to exclude, set
-   read-only and static attributes. See http://pyamf.org/wiki/AttributeControl
-   for more details. (Ticket:601)
- - Exposed the amf request object if expose_request=True.
-   Twisted/Django/Google have an amf_request property on the http request
-   object.  WSGI has an entry in the environ dict - pyamf.request (Ticket:234)
- - Added support for the `array` module (Ticket:468)
- - Fixed an issue with Django model AutoFields being set to 0 by the Flex
-   client (Ticket:556)
- - Added support for the collections module. (Ticket:474)
- - Added type checks for class objects. (Ticket:473)
- - GAE FloatProperty can now accept int's without issue (Ticket:609)
- - Revamped the lazy imports module to use sys.meta_path instead. Fixes all
-   erroneous import errors (Ticket:485).
- - Django models will now accept dynamic properties (Ticket:575)
- - Django PK properties will be set first to allow related properties to apply
-   correctly (Ticket:599)
- - p.r.encode/p.r.decode both have a new logger kwarg. Supply a logging.Logger
-   instance if you want to see debug output (Ticket:588)
- - pyamf.TypedObjectClassAlias now forces the class type (Ticket:537)
- - If a call to Decoder.readElement results in an IOError exception, the
-   original position of the stream will be restored (Ticket:573)
- - Replaced hardcoded Python implementation title in p.r.gateway.SERVER_NAME
-   (Ticket:541)
- - Provide a user friendly way to handle datetime.time objects (Ticket:498)
- - Removed the useless pyamf.logging module (Ticket:577)
- - Added a new register_package helper function to make bulk class
-   registration easier. Check the docstring for more info. (Ticket:576)
- - cpyamf.amf3._decode_int now propagates exceptions correctly (Ticket:506)
- - Fixed an incorrect OverflowError when encoding large integers in AMF3
-   (Ticket:519)
- - Added `append` to pyamf.util.BufferedByteStream (Ticket:574)
- - Added Epydoc signatures to all util.StringIOProxy, util.DataTypeMixIn and
-   util.BufferedByteStream classes (Ticket:440)
- - Removed dependancy on fpconst. Platforms with broken platforms are detected
-   and handled correctly internally (Ticket:564)
- - amf0.Encoder now supports attribute ordering (Ticket:558)
- - Removed duplicate method Context.reset, in favour of Context.clear
-   (Ticket:527)
- - Rewrote cpyamf.util.BufferedByteStream to not depend on cStringIO. Added a
-   C based api for stream functions (Ticket:513)
- - Unified exceptions for BufferedByteStream so that only IOError is raised.
-   (Ticket:520)
- - Made raising pyamf.ReferenceError optional for all Indexed* and Context
-   classes. Refactored AMF3 so that only one ClassDefinition is created per
-   class (Ticket:524)
- - Strict type checking now on pyamf.util.BufferedByteStream (Ticket:512)
- - Removed the default loggers from p.r.gateway.*. To re-enable supply a
-   logger instance as a keyword arg to the gateway constructor (Ticket:525)
- - Removed pyamf.util.make_classic_instance (as it is not used) (Ticket:521)
- - Removed pyamf.util.get_mro, in favour of inspect.getmro (Ticket:526)
- - Removed pyamf.util.Indexed[Collection|Map].remove (Ticket:518)
-
-0.4.2 (2009-04-20)
-------------------
- - Support for decoding the 'source' property on ArrayCollections (Ticket:488)
- - Fixed an issue in the GAE adapter where dynamic properties would be missing
-   on referenced objects. (Ticket:511)
- - Fixed a critical issue with AMF0 reference counting when encoding remoting
-   responses. (Ticket:510)
- - Strengthened http header handling in the client (Ticket:492)
- - Support for Django i18n ugettext_lazy (Ticket:496)
- - Added support for microseconds for datetime objects. Thanks to Derek Payton
-   for the patch. (Ticket:490)
- - Added support for property types on SQLAlchemy mapped classes. (Ticket:491)
- - Added support for property types for Google AppEngine db.Model and
-   db.Expando. (Ticket:487)
-
-0.4.1 (2009-02-23)
-------------------
- - amf0.Encoder.use_amf3 has been extended to cover all object types
-   (Ticket:453,467)
- - Property types on Django models will now encode as expected. (Ticket:480)
- - Django models.ForeignKey properties will be followed only if previously
-   accessed outside of PyAMF. `_[attr]_cache` is no longer encoded.
-   (Ticket:456)
- - Encoding {0:0, '0':1} will now raise an AttributeError. (Ticket:458)
- - Google AppEngine adapter improvements - see ticket for details (Ticket:479)
- - amf0.Encoder will encode all elements as AMF3 if use_amf3 option is set to
-   True (Ticket:453)
- - Unicode handling in __repr__ functions has been improved (Ticket:455)
- - object attributes and dict keys are now utf8 encoded bytestrings. Python
-   \**kwargs doesn't accept unicode key objects. (Ticket:463)
- - Django models.TimeField, models.DateField will now be converted to the
-   correct type (datetime.time and datetime.date respectively).
-   fields.NOT_PROVIDED is also checked for by converting to pyamf.Undefined
-   and back again. (Ticket:457)
-
-0.4 (2009-01-18)
-----------------
- - cpyamf now deals with exceptional floats the same way as pure python -
-   especially on windows (Ticket:448)
- - Support for SQLAlchemy 0.5.1 (Ticket:449)
- - amf0.Encoder now has a use_amf3 flag which determines which XML type to
-   return to the client (Ticket:435)
- - BufferedByteStream.truncate(length) now actually does something useful
-   (Ticket:444)
- - setup.py now gets the version number from pyamf/__init__.py source
-   (Ticket:429)
-
-0.4rc3 (2009-01-14)
--------------------
- - Support for SQLAlchemy 0.5.0 (Ticket:436)
- - pyamf.util.DataTypeMixIn/cpyamf.util.BufferedByteStream can now
-   encode/decode 24bit un/signed integers. (Ticket:422)
- - pyamf.util.StringIOProxy/cpyamf.util.BufferedByteStream both have new
-   consume methods that will chop of the tail of the stream (already read
-   stream). (Ticket:423)
- - Now checking for all types of supported xml lib types for encoding, but
-   will only use the first implementation for decoding (Ticket:426)
- - fpconst dependancy is now only required if the platform requires it
-   (Ticket:356)
- - Decoding negative timestamps on certain platforms (namely Windows) are now
-   supported (Ticket:390)
-
-0.4rc2 (2009-01-05)
--------------------
- - Support for SQLAlchemy 0.4.0 (Ticket:410)
-
-0.4rc1 (2008-12-31)
--------------------
- - Support for encoding/decoding SQLAlchemy ORM objects with a new adapter.
-   Lots of people involved in this one, but special thanks to Dave Thompson
-   and Michael Van Tellingen for making this happen. (Ticket:277)
- - All gateways now log exceptions when exceptions are raised during
-   en/decoding. (Ticket:394)
- - Flex messaging now uses correct static attributes for encoding. Determining
-   static/dynamic attributes on objects is now easier (Ticket:357)
- - Added use_proxy option to amf3 which will automagically convert ObjectProxy
-   to dict and ArrayCollection to list on decoding, and vice versa on encode.
-   Thanks to dthompso for the excellent patch (Ticket:355)
- - flex.ArrayCollection now subclasses list instead of dict as non-int keys
-   are not allowed. IList interface has been implemented. (Ticket:349)
- - Encoding registered subclass of list that define the 'externalised'
-   metadata will be encoded as an object.
- - Encoders how have a 'strict' mode. Not generally useful for the time being
-   but will help with developments in the future. Type mapped functions now
-   require a second 'encoder' attribute. (Ticket:378)
- - Added adapter to handle the decimal module and, if strict is set to False,
-   silently converts a Decimal instance to a float (Ticket:376)
- - ClassAlias can now be subclassed and three new methods have been added:
-   applyAttributes, createInstance, getAttributes all which help to provide
-   fine control for object/instance manipulation (Ticket:348)
- - Added support for __slots__ (Ticket:347)
- - Fixed problem when decoding objects that map to GAE db.Model objects with
-   required properties (Ticket:342)
- - ByteArray now does not throw an error when used in the Remoting framework
-   (Ticket:379)
- - A new adapter that converts sets.ImmutableSet and sets.Set to tuples before
-   encoding. (Ticket:280)
- - A revamped google app engine adapter that checks for the _key attribute in
-   an aliased class and first loads the object from the datastore and then
-   applies that properties in the object stream. (Ticket:307)
- - New helper function to make it easier to manually add adapters (Ticket:350)
- - Ability to disable the c extension with passing --disable-ext to setup.py
-   (Ticket:381, 391)
- - Python C-extension for the pyamf.util.BufferedByteStream class. Originally
-   contributed by Gerard Escalante (Ticket:225, 405)
- - New API to add headers such as cookies in pyamf.remoting.client
-   (Ticket:337)
- - Now clearing the context between remoting requests (Ticket:309)
- - Fixed issue with AMF3 class definition references (Ticket:341)
- - More helpful description for register_class args check (Ticket:334)
- - pyamf.register_class now checks to ensure that __init__ args do not have
-   any arguments (Ticket:322)
- - Added RemoteObject support for AsyncMessage (Ticket:292)
- - pyamf.remoting.ErrorFault.__repr__ now displays the traceback info (if it
-   exists). (Ticket:331)
- - Both Encoders will now raise pyamf.EncodeError if a function is encoded
-   (Ticket:311)
- - Twisted Gateway would fall over when returning tuples (Ticket:313)
- - The remoting gateways now send a customizable Server header (Ticket:317)
- - The remoting client now sends a customizable User-Agent header (Ticket:306)
- - Added ability to set the HTTP referer in remoting client (Ticket:316)
- - Fixed issue where the AMF3 encoder assumed objects with a 'tag' attribute
-   needed XML encoding. Reported by cy-man (Ticket:303)
- - Solved issue with repr for AbstractMessage. Reported by datafunk
-   (Ticket:283)
- - Content-type was missing in POST requests from the AMF client. Reported by
-   magog (Ticket:304)
- - Added the disconnect Command operation (Ticket:325)
- - Fixed issue with the unit tests for Django (Ticket:281)
- - Removed the NetworkIOMixIn class (Ticket:232)
-
-0.3.1 (2008-05-04)
-------------------
- - Importing module now has tests (Ticket:266)
- - Django model adapter now imported only when django.db.models is imported
-   (Ticket:261)
- - Google Model/Expando encoding now works out of the box
- - Fixed issue with Remote Object destination (Ticket:270)
- - Added a new gateway for the Google App Engine - see
-   pyamf.remoting.gateway.google.WebAppGateway (Ticket:253)
- - amf0 Encoder now takes amf3 contexts into account (Ticket:268)
- - amf*.encode helpers can now accept multiple arguments (Ticket:267)
- - Removed the dependancy of fpconst for Python 2.5 or newer (Ticket:243)
- - Solved issue with AMFPHP exceptions in AMF client (Ticket:258)
- - Fixed issue with url parsing in AMF client (Ticket:256)
- - Client no longer raises httplib.ResponseNotReady when making multiple
-   requests using the same RemotingService (Ticket:254)
-
-0.3 (2008-04-14)
-----------------
- - Added compatibility module for Google App Engine (Ticket:247)
- - Fixed the signed interpretation of compressed integers in AMF3 (Ticket:241)
- - Classic class decoding would throw an AttributeError (Ticket:248)
- - Reloading adapter modules caused errors in Django, Pylons and Google App
-   Engine. Resolved by removing dependancy on Importing module and
-   incorporating into pyamf.util (Ticket:250)
- - Adapter framework can now be fired when only loading submodules
-   (Ticket:246)
- - Made util.BufferedByteStream endian aware (Ticket:231)
- - Fixed issue with Twisted threads (Ticket:233)
-
-0.2 (2008-03-12)
-----------------
- - Removed amfinfo console_script (Ticket:226)
- - Encoders/Decoders now check for __getstate__/__setstate__ respectively
-   (Ticket:209)
- - Gateway import hack has now been removed - permanently (Ticket:224)
- - Encoding/decoding performance has been increased 2x for AMF0 and up-to
-   10x(!) for AMF3 (Ticket:198)
- - Logging is now possible in all the supported gateways (Ticket:173)
- - A new preprocessor function that runs after authentication, but before
-   invoking the service method (Ticket:196)
- - authenticator can now be decorated with expose_request (Ticket:195)
- - Python 2.3 support (Ticket:33)
- - Python 2.6 support (Ticket:222)
- - Made PyAMF distributable as zip-based Python Egg (Ticket:193)
-
-0.1.1 (2008-02-18)
-------------------
- - AMF3 encoder reported incorrect byte length header for non-ASCII string
-   data. Patch supplied by akaihola. (Ticket:194)
- - Decoder context not cleared between reading the remoting header and body.
-   Reported by gerard (Ticket:192)
-
-0.1 (2008-02-11)
-----------------
- - New error handling api useful for registering custom exception classes
-   (Ticket:185)
- - When a client receives a remoting error, an exception is generated
-   (Ticket:167)
- - expose_request per service control vastly improved (Ticket:169)
- - Authentication per service control vastly improved (Ticket:166)
- - uuid is no longer installed when using Python 2.5 or newer (Ticket:182)
- - The inheritance tree was not consulted when encoding attributes (Ticket:172)
- - TypedObjects didn't work with old style classes (Ticket:171)
- - ErrorFault now prints details (Ticket:168)
- - Added expose_request to TwistedGateway (Ticket:165)
- - TwistedGateway now expects deferred from service functions (Ticket:164)
-
-0.1b (2008-01-13)
------------------
- - IExternalizable now takes its methods from the class and fine grain control
-   over attr encoding (Ticket:110)
- - Added an adapter framework that gets imported when the related module is
-   imported. See http://pyamf.org/wiki/AdapterFramework for more info
- - Added 'expose_environ' argument to WSGIGateway to expose the WSGI environ
-   as the first arg in the called services.
- - Implemented Local Shared Object (LSO) support (Ticket:11)
- - ByteArray now implements IDataInput and IDataOutput instead of
-   StringIOProxy (Ticket:151)
- - dicts are now used as the default for anonymous objects (Ticket:131)
- - remoting.client mostly fully supports the predefined headers (defined at
-   http://osflash.org/documentation/amf/envelopes/remoting/headers). The only
-   one missing is amf_server_debug (Ticket:39)
- - LazyImporter objects now set the __file__ attribute to None, so that
-   querying sys.modules don't accidentally import the underlying module.
-   (Ticket:147)
- - Fixed argument positioning for RemoteObject processing. Thanks akaihola!
-   (Ticket:145)
- - ElementTree requirement is now ignored when using Python >= 2.5
-   (Ticket:144)
- - Added tests for TwistedGateway (Ticket:132)
- - Workaround for Python 2.4 float shortcomings (Ticket:78)
- - Service Browser ('DescribeService' header) requests supported. (Ticket:138)
- - Remoting client now supports authentication. (Ticket:137)
- - Proper encoding for aliased subclassed builtin types, specifically
-   flex.ArrayCollection.
- - Added support for easy encoding of Django object queries
-   (Foo.objects.all())
- - Added 'add_type' allowing finer grain control of how a class is encoded in
-   an AMF stream. (Ticket:130)
- - 'authenticator' keyword added to all gateways (moved from ServiceRequest)
-   (Ticket:129)
- - Added 'expose_request' argument to DjangoGateway to expose the underlying
-   HTTP Request object as the first arg in the called services. (Ticket:103)
-
-0.1.0a (2007-12-12)
--------------------
- - AMF0 and AMF3 encoders/decoders
- - Additional support for IExternalizable, ArrayCollection, ObjectProxy,
-   ByteArray, RecordSet and RemoteObject
- - Remoting gateways for Twisted, WSGI, and Django
- - Authentication/setCredentials support
- - zlib compression support for ByteArray
- - Remoting client with httplib
-
-0.0.1 (2007-10-19)
-------------------
- - Started project based on previous work done in the RTMPy (http://rtmpy.org)
-   project.

Copied: pyamf/branches/bytearray-context-695/CHANGES.txt (from rev 2928, \
pyamf/trunk/CHANGES.txt) \
                ===================================================================
--- pyamf/branches/bytearray-context-695/CHANGES.txt	                        (rev 0)
+++ pyamf/branches/bytearray-context-695/CHANGES.txt	2009-10-19 10:33:37 UTC (rev \
2929) @@ -0,0 +1,347 @@
+Changelog
+=========
+
+This file contains information about the changes between the different
+versions of PyAMF.
+
+.. contents::
+
+0.6 (unreleased)
+----------------
+ - Fixed a reference bug with unsaved Django model instances. Thanks to wolver
+   for the patch and tests (Ticket:691).
+ - Fixed a small bug in decoding Django relations when declared as static.
+   Thanks to wolver for the patch. (Ticket:693)
+ - Removed ``exceptions`` from all ``Context`` and ``pyamf.util.Indexed*``
+   classes (Ticket:660)
+ - Empty Django relations are now encoded as ``None``, not ``pyamf.Undefined``
+   (Ticket:659)
+ - ``pyamf.ClientType`` removed, as it is not spec compliant (Ticket:651)
+
+0.5.1 (2009-09-19)
+------------------
+ - pyamf.register_package can now accept a list of classes (Ticket:650)
+ - Fixed a regression in TwistedGateway where services would be called twice
+   (Ticket:648)
+ - Fixed a bug with encoding anonymous trait references (Ticket:644)
+ - Moved IndexedCollection into cpyamf (Ticket:424)
+ - amf3.encode_int now encodes signed 29bit ints, not unsigned. Thanks to
+   gerard for the report, investigation and patch! (Ticket:646)
+
+0.5 (2009-09-07)
+----------------
+ - Added the ability to modify the timezones of dates being de/encoded for
+   legacy systems. (Ticket:612)
+ - Fixed a Django reference bug where parent.child.parent is parent would
+   encode 3 objects and not 2. (Ticket:642)
+ - Setting `None` on a Django DateField would blow up the encoder (Ticket:632)
+ - `rootCause` in ErrorFault objects are now populated with the traceback
+   object. (Ticket:637)
+ - Support for encoding/decoding BlazeDS specific messages (Ticket:581)
+ - Removed tracebacks from exception messages if debug=False (Ticket:552)
+ - Support for google.appengine.ext.db.polymodel.PolyModel (Ticket:633)
+ - Support for Django File/ImageField (Ticket:631)
+ - Support for Django model inheritance (Ticket:626)
+ - Rewrote attribute handling code. Added the ability to exclude, set
+   read-only and static attributes. See http://pyamf.org/wiki/AttributeControl
+   for more details. (Ticket:601)
+ - Exposed the amf request object if expose_request=True.
+   Twisted/Django/Google have an amf_request property on the http request
+   object.  WSGI has an entry in the environ dict - pyamf.request (Ticket:234)
+ - Added support for the `array` module (Ticket:468)
+ - Fixed an issue with Django model AutoFields being set to 0 by the Flex
+   client (Ticket:556)
+ - Added support for the collections module. (Ticket:474)
+ - Added type checks for class objects. (Ticket:473)
+ - GAE FloatProperty can now accept int's without issue (Ticket:609)
+ - Revamped the lazy imports module to use sys.meta_path instead. Fixes all
+   erroneous import errors (Ticket:485).
+ - Django models will now accept dynamic properties (Ticket:575)
+ - Django PK properties will be set first to allow related properties to apply
+   correctly (Ticket:599)
+ - p.r.encode/p.r.decode both have a new logger kwarg. Supply a logging.Logger
+   instance if you want to see debug output (Ticket:588)
+ - pyamf.TypedObjectClassAlias now forces the class type (Ticket:537)
+ - If a call to Decoder.readElement results in an IOError exception, the
+   original position of the stream will be restored (Ticket:573)
+ - Replaced hardcoded Python implementation title in p.r.gateway.SERVER_NAME
+   (Ticket:541)
+ - Provide a user friendly way to handle datetime.time objects (Ticket:498)
+ - Removed the useless pyamf.logging module (Ticket:577)
+ - Added a new register_package helper function to make bulk class
+   registration easier. Check the docstring for more info. (Ticket:576)
+ - cpyamf.amf3._decode_int now propagates exceptions correctly (Ticket:506)
+ - Fixed an incorrect OverflowError when encoding large integers in AMF3
+   (Ticket:519)
+ - Added `append` to pyamf.util.BufferedByteStream (Ticket:574)
+ - Added Epydoc signatures to all util.StringIOProxy, util.DataTypeMixIn and
+   util.BufferedByteStream classes (Ticket:440)
+ - Removed dependancy on fpconst. Platforms with broken platforms are detected
+   and handled correctly internally (Ticket:564)
+ - amf0.Encoder now supports attribute ordering (Ticket:558)
+ - Removed duplicate method Context.reset, in favour of Context.clear
+   (Ticket:527)
+ - Rewrote cpyamf.util.BufferedByteStream to not depend on cStringIO. Added a
+   C based api for stream functions (Ticket:513)
+ - Unified exceptions for BufferedByteStream so that only IOError is raised.
+   (Ticket:520)
+ - Made raising pyamf.ReferenceError optional for all Indexed* and Context
+   classes. Refactored AMF3 so that only one ClassDefinition is created per
+   class (Ticket:524)
+ - Strict type checking now on pyamf.util.BufferedByteStream (Ticket:512)
+ - Removed the default loggers from p.r.gateway.*. To re-enable supply a
+   logger instance as a keyword arg to the gateway constructor (Ticket:525)
+ - Removed pyamf.util.make_classic_instance (as it is not used) (Ticket:521)
+ - Removed pyamf.util.get_mro, in favour of inspect.getmro (Ticket:526)
+ - Removed pyamf.util.Indexed[Collection|Map].remove (Ticket:518)
+
+0.4.2 (2009-04-20)
+------------------
+ - Support for decoding the 'source' property on ArrayCollections (Ticket:488)
+ - Fixed an issue in the GAE adapter where dynamic properties would be missing
+   on referenced objects. (Ticket:511)
+ - Fixed a critical issue with AMF0 reference counting when encoding remoting
+   responses. (Ticket:510)
+ - Strengthened http header handling in the client (Ticket:492)
+ - Support for Django i18n ugettext_lazy (Ticket:496)
+ - Added support for microseconds for datetime objects. Thanks to Derek Payton
+   for the patch. (Ticket:490)
+ - Added support for property types on SQLAlchemy mapped classes. (Ticket:491)
+ - Added support for property types for Google AppEngine db.Model and
+   db.Expando. (Ticket:487)
+
+0.4.1 (2009-02-23)
+------------------
+ - amf0.Encoder.use_amf3 has been extended to cover all object types
+   (Ticket:453,467)
+ - Property types on Django models will now encode as expected. (Ticket:480)
+ - Django models.ForeignKey properties will be followed only if previously
+   accessed outside of PyAMF. `_[attr]_cache` is no longer encoded.
+   (Ticket:456)
+ - Encoding {0:0, '0':1} will now raise an AttributeError. (Ticket:458)
+ - Google AppEngine adapter improvements - see ticket for details (Ticket:479)
+ - amf0.Encoder will encode all elements as AMF3 if use_amf3 option is set to
+   True (Ticket:453)
+ - Unicode handling in __repr__ functions has been improved (Ticket:455)
+ - object attributes and dict keys are now utf8 encoded bytestrings. Python
+   \**kwargs doesn't accept unicode key objects. (Ticket:463)
+ - Django models.TimeField, models.DateField will now be converted to the
+   correct type (datetime.time and datetime.date respectively).
+   fields.NOT_PROVIDED is also checked for by converting to pyamf.Undefined
+   and back again. (Ticket:457)
+
+0.4 (2009-01-18)
+----------------
+ - cpyamf now deals with exceptional floats the same way as pure python -
+   especially on windows (Ticket:448)
+ - Support for SQLAlchemy 0.5.1 (Ticket:449)
+ - amf0.Encoder now has a use_amf3 flag which determines which XML type to
+   return to the client (Ticket:435)
+ - BufferedByteStream.truncate(length) now actually does something useful
+   (Ticket:444)
+ - setup.py now gets the version number from pyamf/__init__.py source
+   (Ticket:429)
+
+0.4rc3 (2009-01-14)
+-------------------
+ - Support for SQLAlchemy 0.5.0 (Ticket:436)
+ - pyamf.util.DataTypeMixIn/cpyamf.util.BufferedByteStream can now
+   encode/decode 24bit un/signed integers. (Ticket:422)
+ - pyamf.util.StringIOProxy/cpyamf.util.BufferedByteStream both have new
+   consume methods that will chop of the tail of the stream (already read
+   stream). (Ticket:423)
+ - Now checking for all types of supported xml lib types for encoding, but
+   will only use the first implementation for decoding (Ticket:426)
+ - fpconst dependancy is now only required if the platform requires it
+   (Ticket:356)
+ - Decoding negative timestamps on certain platforms (namely Windows) are now
+   supported (Ticket:390)
+
+0.4rc2 (2009-01-05)
+-------------------
+ - Support for SQLAlchemy 0.4.0 (Ticket:410)
+
+0.4rc1 (2008-12-31)
+-------------------
+ - Support for encoding/decoding SQLAlchemy ORM objects with a new adapter.
+   Lots of people involved in this one, but special thanks to Dave Thompson
+   and Michael Van Tellingen for making this happen. (Ticket:277)
+ - All gateways now log exceptions when exceptions are raised during
+   en/decoding. (Ticket:394)
+ - Flex messaging now uses correct static attributes for encoding. Determining
+   static/dynamic attributes on objects is now easier (Ticket:357)
+ - Added use_proxy option to amf3 which will automagically convert ObjectProxy
+   to dict and ArrayCollection to list on decoding, and vice versa on encode.
+   Thanks to dthompso for the excellent patch (Ticket:355)
+ - flex.ArrayCollection now subclasses list instead of dict as non-int keys
+   are not allowed. IList interface has been implemented. (Ticket:349)
+ - Encoding registered subclass of list that define the 'externalised'
+   metadata will be encoded as an object.
+ - Encoders how have a 'strict' mode. Not generally useful for the time being
+   but will help with developments in the future. Type mapped functions now
+   require a second 'encoder' attribute. (Ticket:378)
+ - Added adapter to handle the decimal module and, if strict is set to False,
+   silently converts a Decimal instance to a float (Ticket:376)
+ - ClassAlias can now be subclassed and three new methods have been added:
+   applyAttributes, createInstance, getAttributes all which help to provide
+   fine control for object/instance manipulation (Ticket:348)
+ - Added support for __slots__ (Ticket:347)
+ - Fixed problem when decoding objects that map to GAE db.Model objects with
+   required properties (Ticket:342)
+ - ByteArray now does not throw an error when used in the Remoting framework
+   (Ticket:379)
+ - A new adapter that converts sets.ImmutableSet and sets.Set to tuples before
+   encoding. (Ticket:280)
+ - A revamped google app engine adapter that checks for the _key attribute in
+   an aliased class and first loads the object from the datastore and then
+   applies that properties in the object stream. (Ticket:307)
+ - New helper function to make it easier to manually add adapters (Ticket:350)
+ - Ability to disable the c extension with passing --disable-ext to setup.py
+   (Ticket:381, 391)
+ - Python C-extension for the pyamf.util.BufferedByteStream class. Originally
+   contributed by Gerard Escalante (Ticket:225, 405)
+ - New API to add headers such as cookies in pyamf.remoting.client
+   (Ticket:337)
+ - Now clearing the context between remoting requests (Ticket:309)
+ - Fixed issue with AMF3 class definition references (Ticket:341)
+ - More helpful description for register_class args check (Ticket:334)
+ - pyamf.register_class now checks to ensure that __init__ args do not have
+   any arguments (Ticket:322)
+ - Added RemoteObject support for AsyncMessage (Ticket:292)
+ - pyamf.remoting.ErrorFault.__repr__ now displays the traceback info (if it
+   exists). (Ticket:331)
+ - Both Encoders will now raise pyamf.EncodeError if a function is encoded
+   (Ticket:311)
+ - Twisted Gateway would fall over when returning tuples (Ticket:313)
+ - The remoting gateways now send a customizable Server header (Ticket:317)
+ - The remoting client now sends a customizable User-Agent header (Ticket:306)
+ - Added ability to set the HTTP referer in remoting client (Ticket:316)
+ - Fixed issue where the AMF3 encoder assumed objects with a 'tag' attribute
+   needed XML encoding. Reported by cy-man (Ticket:303)
+ - Solved issue with repr for AbstractMessage. Reported by datafunk
+   (Ticket:283)
+ - Content-type was missing in POST requests from the AMF client. Reported by
+   magog (Ticket:304)
+ - Added the disconnect Command operation (Ticket:325)
+ - Fixed issue with the unit tests for Django (Ticket:281)
+ - Removed the NetworkIOMixIn class (Ticket:232)
+
+0.3.1 (2008-05-04)
+------------------
+ - Importing module now has tests (Ticket:266)
+ - Django model adapter now imported only when django.db.models is imported
+   (Ticket:261)
+ - Google Model/Expando encoding now works out of the box
+ - Fixed issue with Remote Object destination (Ticket:270)
+ - Added a new gateway for the Google App Engine - see
+   pyamf.remoting.gateway.google.WebAppGateway (Ticket:253)
+ - amf0 Encoder now takes amf3 contexts into account (Ticket:268)
+ - amf*.encode helpers can now accept multiple arguments (Ticket:267)
+ - Removed the dependancy of fpconst for Python 2.5 or newer (Ticket:243)
+ - Solved issue with AMFPHP exceptions in AMF client (Ticket:258)
+ - Fixed issue with url parsing in AMF client (Ticket:256)
+ - Client no longer raises httplib.ResponseNotReady when making multiple
+   requests using the same RemotingService (Ticket:254)
+
+0.3 (2008-04-14)
+----------------
+ - Added compatibility module for Google App Engine (Ticket:247)
+ - Fixed the signed interpretation of compressed integers in AMF3 (Ticket:241)
+ - Classic class decoding would throw an AttributeError (Ticket:248)
+ - Reloading adapter modules caused errors in Django, Pylons and Google App
+   Engine. Resolved by removing dependancy on Importing module and
+   incorporating into pyamf.util (Ticket:250)
+ - Adapter framework can now be fired when only loading submodules
+   (Ticket:246)
+ - Made util.BufferedByteStream endian aware (Ticket:231)
+ - Fixed issue with Twisted threads (Ticket:233)
+
+0.2 (2008-03-12)
+----------------
+ - Removed amfinfo console_script (Ticket:226)
+ - Encoders/Decoders now check for __getstate__/__setstate__ respectively
+   (Ticket:209)
+ - Gateway import hack has now been removed - permanently (Ticket:224)
+ - Encoding/decoding performance has been increased 2x for AMF0 and up-to
+   10x(!) for AMF3 (Ticket:198)
+ - Logging is now possible in all the supported gateways (Ticket:173)
+ - A new preprocessor function that runs after authentication, but before
+   invoking the service method (Ticket:196)
+ - authenticator can now be decorated with expose_request (Ticket:195)
+ - Python 2.3 support (Ticket:33)
+ - Python 2.6 support (Ticket:222)
+ - Made PyAMF distributable as zip-based Python Egg (Ticket:193)
+
+0.1.1 (2008-02-18)
+------------------
+ - AMF3 encoder reported incorrect byte length header for non-ASCII string
+   data. Patch supplied by akaihola. (Ticket:194)
+ - Decoder context not cleared between reading the remoting header and body.
+   Reported by gerard (Ticket:192)
+
+0.1 (2008-02-11)
+----------------
+ - New error handling api useful for registering custom exception classes
+   (Ticket:185)
+ - When a client receives a remoting error, an exception is generated
+   (Ticket:167)
+ - expose_request per service control vastly improved (Ticket:169)
+ - Authentication per service control vastly improved (Ticket:166)
+ - uuid is no longer installed when using Python 2.5 or newer (Ticket:182)
+ - The inheritance tree was not consulted when encoding attributes (Ticket:172)
+ - TypedObjects didn't work with old style classes (Ticket:171)
+ - ErrorFault now prints details (Ticket:168)
+ - Added expose_request to TwistedGateway (Ticket:165)
+ - TwistedGateway now expects deferred from service functions (Ticket:164)
+
+0.1b (2008-01-13)
+-----------------
+ - IExternalizable now takes its methods from the class and fine grain control
+   over attr encoding (Ticket:110)
+ - Added an adapter framework that gets imported when the related module is
+   imported. See http://pyamf.org/wiki/AdapterFramework for more info
+ - Added 'expose_environ' argument to WSGIGateway to expose the WSGI environ
+   as the first arg in the called services.
+ - Implemented Local Shared Object (LSO) support (Ticket:11)
+ - ByteArray now implements IDataInput and IDataOutput instead of
+   StringIOProxy (Ticket:151)
+ - dicts are now used as the default for anonymous objects (Ticket:131)
+ - remoting.client mostly fully supports the predefined headers (defined at
+   http://osflash.org/documentation/amf/envelopes/remoting/headers). The only
+   one missing is amf_server_debug (Ticket:39)
+ - LazyImporter objects now set the __file__ attribute to None, so that
+   querying sys.modules don't accidentally import the underlying module.
+   (Ticket:147)
+ - Fixed argument positioning for RemoteObject processing. Thanks akaihola!
+   (Ticket:145)
+ - ElementTree requirement is now ignored when using Python >= 2.5
+   (Ticket:144)
+ - Added tests for TwistedGateway (Ticket:132)
+ - Workaround for Python 2.4 float shortcomings (Ticket:78)
+ - Service Browser ('DescribeService' header) requests supported. (Ticket:138)
+ - Remoting client now supports authentication. (Ticket:137)
+ - Proper encoding for aliased subclassed builtin types, specifically
+   flex.ArrayCollection.
+ - Added support for easy encoding of Django object queries
+   (Foo.objects.all())
+ - Added 'add_type' allowing finer grain control of how a class is encoded in
+   an AMF stream. (Ticket:130)
+ - 'authenticator' keyword added to all gateways (moved from ServiceRequest)
+   (Ticket:129)
+ - Added 'expose_request' argument to DjangoGateway to expose the underlying
+   HTTP Request object as the first arg in the called services. (Ticket:103)
+
+0.1.0a (2007-12-12)
+-------------------
+ - AMF0 and AMF3 encoders/decoders
+ - Additional support for IExternalizable, ArrayCollection, ObjectProxy,
+   ByteArray, RecordSet and RemoteObject
+ - Remoting gateways for Twisted, WSGI, and Django
+ - Authentication/setCredentials support
+ - zlib compression support for ByteArray
+ - Remoting client with httplib
+
+0.0.1 (2007-10-19)
+------------------
+ - Started project based on previous work done in the RTMPy (http://rtmpy.org)
+   project.

Deleted: pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py
 ===================================================================
--- pyamf/trunk/pyamf/adapters/_django_db_models_base.py	2009-10-10 21:51:34 UTC (rev \
                2927)
+++ pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py	2009-10-19 \
10:33:37 UTC (rev 2929) @@ -1,283 +0,0 @@
-# Copyright (c) 2007-2009 The PyAMF Project.
-# See LICENSE.txt for details.
-
-"""
-C{django.db.models} adapter module.
-
-@see: U{Django Project<http://www.djangoproject.com>}
-
-@since: 0.4.1
-"""
-
-from django.db.models.base import Model
-from django.db.models import fields
-from django.db.models.fields import related, files
-
-import datetime
-
-import pyamf
-from pyamf.util import imports
-
-
-class DjangoReferenceCollection(dict):
-    """
-    This helper class holds a dict of klass to pk/objects loaded from the
-    underlying db.
-
-    @since: 0.5
-    """
-
-    def _getClass(self, klass):
-        if klass not in self.keys():
-            self[klass] = {}
-
-        return self[klass]
-
-    def getClassKey(self, klass, key):
-        """
-        Return an instance based on klass/key.
-
-        If an instance cannot be found then L{KeyError} is raised.
-
-        @param klass: The class of the instance.
-        @param key: The primary_key of the instance.
-        @return: The instance linked to the C{klass}/C{key}.
-        @rtype: Instance of L{klass}.
-        """
-        d = self._getClass(klass)
-
-        return d[key]
-
-    def addClassKey(self, klass, key, obj):
-        """
-        Adds an object to the collection, based on klass and key.
-
-        @param klass: The class of the object.
-        @param key: The datastore key of the object.
-        @param obj: The loaded instance from the datastore.
-        """
-        d = self._getClass(klass)
-
-        d[key] = obj
-
-
-class DjangoClassAlias(pyamf.ClassAlias):
-    """
-    """
-
-    def getCustomProperties(self):
-        self.fields = {}
-        self.relations = {}
-        self.columns = []
-
-        self.meta = self.klass._meta
-
-        for x in self.meta.local_fields:
-            if isinstance(x, files.FileField):
-                self.readonly_attrs.update([x.name])
-
-            if not isinstance(x, related.ForeignKey):
-                self.fields[x.name] = x
-            else:
-                self.relations[x.name] = x
-
-            self.columns.append(x.attname)
-
-        for k, v in self.klass.__dict__.iteritems():
-            if isinstance(v, related.ReverseManyRelatedObjectsDescriptor):
-                self.fields[k] = v.field
-
-        parent_fields = []
-
-        for field in self.meta.parents.values():
-            parent_fields.append(field.attname)
-            del self.relations[field.name]
-
-        self.exclude_attrs.update(parent_fields)
-
-        props = self.fields.keys()
-
-        self.static_attrs.update(props)
-        self.encodable_properties.update(props)
-        self.decodable_properties.update(props)
-
-    def _compile_base_class(self, klass):
-        if klass is Model:
-            return
-
-        pyamf.ClassAlias._compile_base_class(self, klass)
-
-    def _encodeValue(self, field, value):
-        if value is fields.NOT_PROVIDED:
-            return pyamf.Undefined
-
-        if value is None:
-            return value
-
-        # deal with dates ..
-        if isinstance(field, fields.DateTimeField):
-            return value
-        elif isinstance(field, fields.DateField):
-            return datetime.datetime(value.year, value.month, value.day, 0, 0, 0)
-        elif isinstance(field, fields.TimeField):
-            return datetime.datetime(1970, 1, 1,
-                value.hour, value.minute, value.second, value.microsecond)
-        elif isinstance(value, files.FieldFile):
-            return value.name
-
-        return value
-
-    def _decodeValue(self, field, value):
-        if value is pyamf.Undefined:
-            return fields.NOT_PROVIDED
-
-        if isinstance(field, fields.AutoField) and value == 0:
-            return None
-        elif isinstance(field, fields.DateTimeField):
-            # deal with dates
-            return value
-        elif isinstance(field, fields.DateField):
-            if not value:
-                return None
-
-            return datetime.date(value.year, value.month, value.day)
-        elif isinstance(field, fields.TimeField):
-            if not value:
-                return None
-
-            return datetime.time(value.hour, value.minute, value.second, \
                value.microsecond)
-
-        return value
-
-    def getEncodableAttributes(self, obj, **kwargs):
-        sa, da = pyamf.ClassAlias.getEncodableAttributes(self, obj, **kwargs)
-
-        for name, prop in self.fields.iteritems():
-            if name not in sa:
-                continue
-
-            if isinstance(prop, related.ManyToManyField):
-                sa[name] = [x for x in getattr(obj, name).all()]
-            else:
-                sa[name] = self._encodeValue(prop, getattr(obj, name))
-
-        if not da:
-            da = {}
-
-        keys = da.keys()
-
-        for key in keys:
-            if key.startswith('_'):
-                del da[key]
-            elif key in self.columns:
-                del da[key]
-
-        for name, relation in self.relations.iteritems():
-            if '_%s_cache' % name in obj.__dict__:
-                da[name] = getattr(obj, name)
-
-        if not da:
-            da = None
-
-        return sa, da
-
-    def getDecodableAttributes(self, obj, attrs, **kwargs):
-        attrs = pyamf.ClassAlias.getDecodableAttributes(self, obj, attrs, **kwargs)
-
-        for n in self.decodable_properties:
-            if n in self.relations:
-                continue
-
-            f = self.fields[n]
-
-            attrs[f.attname] = self._decodeValue(f, attrs[n])
-
-        # primary key of django object must always be set first for
-        # relationships with other model objects to work properly
-        # and dict.iteritems() does not guarantee order
-        #
-        # django also forces the use only one attribute as primary key, so
-        # our obj._meta.pk.attname check is sufficient)
-        try:
-            setattr(obj, obj._meta.pk.attname, attrs[obj._meta.pk.attname])
-            del attrs[obj._meta.pk.attname]
-        except KeyError:
-            pass
-
-        return attrs
-
-
-def getDjangoObjects(context):
-    """
-    Returns a reference to the C{django_objects} on the context. If it doesn't
-    exist then it is created.
-
-    @param context: The context to load the C{django_objects} index from.
-    @type context: Instance of L{pyamf.BaseContext}
-    @return: The C{django_objects} index reference.
-    @rtype: Instance of L{DjangoReferenceCollection}
-    @since: 0.5
-    """
-    if not hasattr(context, 'django_objects'):
-        context.django_objects = DjangoReferenceCollection()
-
-    return context.django_objects
-
-
-def writeDjangoObject(self, obj, *args, **kwargs):
-    """
-    The Django ORM creates new instances of objects for each db request.
-    This is a problem for PyAMF as it uses the id(obj) of the object to do
-    reference checking.
-
-    We could just ignore the problem, but the objects are conceptually the
-    same so the effort should be made to attempt to resolve references for a
-    given object graph.
-
-    We create a new map on the encoder context object which contains a dict of
-    C{object.__class__: {key1: object1, key2: object2, .., keyn: objectn}}. We
-    use the primary key to do the reference checking.
-
-    @since: 0.5
-    """
-    if not isinstance(obj, Model):
-        self.writeNonDjangoObject(obj, *args, **kwargs)
-
-        return
-
-    context = self.context
-    kls = obj.__class__
-
-    s = obj.pk
-
-    django_objects = getDjangoObjects(context)
-
-    try:
-        referenced_object = django_objects.getClassKey(kls, s)
-    except KeyError:
-        referenced_object = obj
-        django_objects.addClassKey(kls, s, obj)
-
-    self.writeNonDjangoObject(referenced_object, *args, **kwargs)
-
-
-def install_django_reference_model_hook(mod):
-    """
-    Called when L{pyamf.amf0} or L{pyamf.amf3} are imported. Attaches the
-    L{writeDjangoObject} method to the C{Encoder} class in that module.
-
-    @param mod: The module imported.
-    @since: 0.4.1
-    """
-    if not hasattr(mod.Encoder, 'writeNonDjangoObject'):
-        mod.Encoder.writeNonDjangoObject = mod.Encoder.writeObject
-        mod.Encoder.writeObject = writeDjangoObject
-
-
-# initialise the module here: hook into pyamf
-
-pyamf.register_alias_type(DjangoClassAlias, Model)
-
-# hook the L{writeDjangobject} method to the Encoder class on import
-imports.when_imported('pyamf.amf0', install_django_reference_model_hook)
-imports.when_imported('pyamf.amf3', install_django_reference_model_hook)

Copied: pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py \
(from rev 2928, pyamf/trunk/pyamf/adapters/_django_db_models_base.py) \
                ===================================================================
--- pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py	    \
                (rev 0)
+++ pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py	2009-10-19 \
10:33:37 UTC (rev 2929) @@ -0,0 +1,288 @@
+# Copyright (c) 2007-2009 The PyAMF Project.
+# See LICENSE.txt for details.
+
+"""
+C{django.db.models} adapter module.
+
+@see: U{Django Project<http://www.djangoproject.com>}
+
+@since: 0.4.1
+"""
+
+from django.db.models.base import Model
+from django.db.models import fields
+from django.db.models.fields import related, files
+
+import datetime
+
+import pyamf
+from pyamf.util import imports
+
+
+class DjangoReferenceCollection(dict):
+    """
+    This helper class holds a dict of klass to pk/objects loaded from the
+    underlying db.
+
+    @since: 0.5
+    """
+
+    def _getClass(self, klass):
+        if klass not in self.keys():
+            self[klass] = {}
+
+        return self[klass]
+
+    def getClassKey(self, klass, key):
+        """
+        Return an instance based on klass/key.
+
+        If an instance cannot be found then L{KeyError} is raised.
+
+        @param klass: The class of the instance.
+        @param key: The primary_key of the instance.
+        @return: The instance linked to the C{klass}/C{key}.
+        @rtype: Instance of L{klass}.
+        """
+        d = self._getClass(klass)
+
+        return d[key]
+
+    def addClassKey(self, klass, key, obj):
+        """
+        Adds an object to the collection, based on klass and key.
+
+        @param klass: The class of the object.
+        @param key: The datastore key of the object.
+        @param obj: The loaded instance from the datastore.
+        """
+        d = self._getClass(klass)
+
+        d[key] = obj
+
+
+class DjangoClassAlias(pyamf.ClassAlias):
+    """
+    """
+
+    def getCustomProperties(self):
+        self.fields = {}
+        self.relations = {}
+        self.columns = []
+
+        self.meta = self.klass._meta
+
+        for x in self.meta.local_fields:
+            if isinstance(x, files.FileField):
+                self.readonly_attrs.update([x.name])
+
+            if not isinstance(x, related.ForeignKey):
+                self.fields[x.name] = x
+            else:
+                self.relations[x.name] = x
+
+            self.columns.append(x.attname)
+
+        for k, v in self.klass.__dict__.iteritems():
+            if isinstance(v, related.ReverseManyRelatedObjectsDescriptor):
+                self.fields[k] = v.field
+
+        parent_fields = []
+
+        for field in self.meta.parents.values():
+            parent_fields.append(field.attname)
+            del self.relations[field.name]
+
+        self.exclude_attrs.update(parent_fields)
+
+        props = self.fields.keys()
+
+        self.static_attrs.update(props)
+        self.encodable_properties.update(props)
+        self.decodable_properties.update(props)
+
+    def _compile_base_class(self, klass):
+        if klass is Model:
+            return
+
+        pyamf.ClassAlias._compile_base_class(self, klass)
+
+    def _encodeValue(self, field, value):
+        if value is fields.NOT_PROVIDED:
+            return pyamf.Undefined
+
+        if value is None:
+            return value
+
+        # deal with dates ..
+        if isinstance(field, fields.DateTimeField):
+            return value
+        elif isinstance(field, fields.DateField):
+            return datetime.datetime(value.year, value.month, value.day, 0, 0, 0)
+        elif isinstance(field, fields.TimeField):
+            return datetime.datetime(1970, 1, 1,
+                value.hour, value.minute, value.second, value.microsecond)
+        elif isinstance(value, files.FieldFile):
+            return value.name
+
+        return value
+
+    def _decodeValue(self, field, value):
+        if value is pyamf.Undefined:
+            return fields.NOT_PROVIDED
+
+        if isinstance(field, fields.AutoField) and value == 0:
+            return None
+        elif isinstance(field, fields.DateTimeField):
+            # deal with dates
+            return value
+        elif isinstance(field, fields.DateField):
+            if not value:
+                return None
+
+            return datetime.date(value.year, value.month, value.day)
+        elif isinstance(field, fields.TimeField):
+            if not value:
+                return None
+
+            return datetime.time(value.hour, value.minute, value.second, \
value.microsecond) +
+        return value
+
+    def getEncodableAttributes(self, obj, **kwargs):
+        sa, da = pyamf.ClassAlias.getEncodableAttributes(self, obj, **kwargs)
+
+        for name, prop in self.fields.iteritems():
+            if name not in sa:
+                continue
+
+            if isinstance(prop, related.ManyToManyField):
+                sa[name] = [x for x in getattr(obj, name).all()]
+            else:
+                sa[name] = self._encodeValue(prop, getattr(obj, name))
+
+        if not da:
+            da = {}
+
+        keys = da.keys()
+
+        for key in keys:
+            if key.startswith('_'):
+                del da[key]
+            elif key in self.columns:
+                del da[key]
+
+        for name, relation in self.relations.iteritems():
+            if '_%s_cache' % name in obj.__dict__:
+                da[name] = getattr(obj, name)
+
+        if not da:
+            da = None
+
+        return sa, da
+
+    def getDecodableAttributes(self, obj, attrs, **kwargs):
+        attrs = pyamf.ClassAlias.getDecodableAttributes(self, obj, attrs, **kwargs)
+
+        for n in self.decodable_properties:
+            if n in self.relations:
+                continue
+
+            f = self.fields[n]
+
+            attrs[f.attname] = self._decodeValue(f, attrs[n])
+
+        # primary key of django object must always be set first for
+        # relationships with other model objects to work properly
+        # and dict.iteritems() does not guarantee order
+        #
+        # django also forces the use only one attribute as primary key, so
+        # our obj._meta.pk.attname check is sufficient)
+        try:
+            setattr(obj, obj._meta.pk.attname, attrs[obj._meta.pk.attname])
+            del attrs[obj._meta.pk.attname]
+        except KeyError:
+            pass
+
+        return attrs
+
+
+def getDjangoObjects(context):
+    """
+    Returns a reference to the C{django_objects} on the context. If it doesn't
+    exist then it is created.
+
+    @param context: The context to load the C{django_objects} index from.
+    @type context: Instance of L{pyamf.BaseContext}
+    @return: The C{django_objects} index reference.
+    @rtype: Instance of L{DjangoReferenceCollection}
+    @since: 0.5
+    """
+    if not hasattr(context, 'django_objects'):
+        context.django_objects = DjangoReferenceCollection()
+
+    return context.django_objects
+
+
+def writeDjangoObject(self, obj, *args, **kwargs):
+    """
+    The Django ORM creates new instances of objects for each db request.
+    This is a problem for PyAMF as it uses the id(obj) of the object to do
+    reference checking.
+
+    We could just ignore the problem, but the objects are conceptually the
+    same so the effort should be made to attempt to resolve references for a
+    given object graph.
+
+    We create a new map on the encoder context object which contains a dict of
+    C{object.__class__: {key1: object1, key2: object2, .., keyn: objectn}}. We
+    use the primary key to do the reference checking.
+
+    @since: 0.5
+    """
+    if not isinstance(obj, Model):
+        self.writeNonDjangoObject(obj, *args, **kwargs)
+
+        return
+
+    context = self.context
+    kls = obj.__class__
+
+    s = obj.pk
+
+    if s is None:
+        self.writeNonDjangoObject(obj, *args, **kwargs)
+
+        return
+
+    django_objects = getDjangoObjects(context)
+
+    try:
+        referenced_object = django_objects.getClassKey(kls, s)
+    except KeyError:
+        referenced_object = obj
+        django_objects.addClassKey(kls, s, obj)
+
+    self.writeNonDjangoObject(referenced_object, *args, **kwargs)
+
+
+def install_django_reference_model_hook(mod):
+    """
+    Called when L{pyamf.amf0} or L{pyamf.amf3} are imported. Attaches the
+    L{writeDjangoObject} method to the C{Encoder} class in that module.
+
+    @param mod: The module imported.
+    @since: 0.4.1
+    """
+    if not hasattr(mod.Encoder, 'writeNonDjangoObject'):
+        mod.Encoder.writeNonDjangoObject = mod.Encoder.writeObject
+        mod.Encoder.writeObject = writeDjangoObject
+
+
+# initialise the module here: hook into pyamf
+
+pyamf.register_alias_type(DjangoClassAlias, Model)
+
+# hook the L{writeDjangobject} method to the Encoder class on import
+imports.when_imported('pyamf.amf0', install_django_reference_model_hook)
+imports.when_imported('pyamf.amf3', install_django_reference_model_hook)

Deleted: pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py
===================================================================
--- pyamf/trunk/pyamf/tests/adapters/test_django.py	2009-10-10 21:51:34 UTC (rev \
                2927)
+++ pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py	2009-10-19 \
10:33:37 UTC (rev 2929) @@ -1,829 +0,0 @@
-# Copyright (c) 2007-2009 The PyAMF Project.
-# See LICENSE.txt for details.
-
-"""
-PyAMF Django adapter tests.
-
-@since: 0.3.1
-"""
-
-import unittest
-import sys
-import os
-import new
-import datetime
-
-import pyamf
-from pyamf.tests import util
-
-
-class ModelsBaseTestCase(unittest.TestCase):
-    def setUp(self):
-        self.old_env = os.environ.copy()
-        self.mods = sys.modules.copy()
-
-        if 'DJANGO_SETTINGS_MODULE' in os.environ.keys():
-            from django import conf
-            import copy
-
-            self.mod = copy.deepcopy(conf.settings)
-            mod = conf.settings
-            self.existing = True
-        else:
-            os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-            mod = new.module('settings')
-
-            sys.modules['settings'] = mod
-
-            self.existing = False
-
-        app = new.module('adapters')
-
-        app_models = new.module('adapters.models')
-        setattr(app, 'models', app_models)
-        setattr(app, '__file__', '')
-        setattr(app_models, '__file__', '')
-
-        sys.modules['adapters'] = app
-        sys.modules['adapters.models'] = app_models
-
-        self.app = app
-        self.models = app_models
-
-        setattr(mod, 'DATABASE_ENGINE', 'sqlite3')
-        setattr(mod, 'DATABASE_NAME', ':memory:')
-        setattr(mod, 'INSTALLED_APPS', ('adapters',))
-        setattr(mod, 'USE_I18N', False)
-
-        from pyamf.adapters import _django_db_models_base as models_adapter
-
-        self.adapter = models_adapter
-
-    def tearDown(self):
-        util.replace_dict(os.environ, self.old_env)
-        util.replace_dict(sys.modules, self.mods)
-
-        if self.existing:
-            from django import conf
-            conf.settings = self.mod
-
-    def resetDB(self):
-        from django.db import connection
-        import sys
-
-        old_stderr = sys.stderr
-        sys.stderr = util.NullFileDescriptor()
-
-        self.db_name = connection.creation.create_test_db(0, autoclobber=True)
-
-        sys.stderr = old_stderr
-
-
-class TypeMapTestCase(ModelsBaseTestCase):
-    def test_objects_all(self):
-        from django.db import models
-
-        class Spam(models.Model):
-            pass
-
-        self.resetDB()
-        encoder = pyamf.get_encoder(pyamf.AMF0)
-
-        encoder.writeElement(Spam.objects.all())
-        self.assertEquals(encoder.stream.getvalue(), '\n\x00\x00\x00\x00')
-
-        encoder = pyamf.get_encoder(pyamf.AMF3)
-        encoder.writeElement(Spam.objects.all())
-        self.assertEquals(encoder.stream.getvalue(), '\t\x01\x01')
-
-    def test_NOT_PROVIDED(self):
-        from django.db.models import fields
-
-        encoder = pyamf.get_encoder(pyamf.AMF0)
-
-        encoder.writeElement(fields.NOT_PROVIDED)
-        self.assertEquals(encoder.stream.getvalue(), '\x06')
-
-        encoder = pyamf.get_encoder(pyamf.AMF3)
-        encoder.writeElement(fields.NOT_PROVIDED)
-        self.assertEquals(encoder.stream.getvalue(), '\x00')
-
-
-class ClassAliasTestCase(ModelsBaseTestCase):
-    def test_time(self):
-        from django.db import models
-
-        class TestClass(models.Model):
-            t = models.TimeField()
-            d = models.DateField()
-            dt = models.DateTimeField()
-
-        x = TestClass()
-
-        x.t = datetime.time(12, 12, 12)
-        x.d = datetime.date(2008, 3, 12)
-        x.dt = datetime.datetime(2008, 3, 12, 12, 12, 12)
-
-        alias = self.adapter.DjangoClassAlias(TestClass, None)
-
-        sa, da = alias.getEncodableAttributes(x)
-
-        self.assertEquals(sa, {
-            'id': None,
-            'd': datetime.datetime(2008, 3, 12, 0, 0),
-            'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
-            't': datetime.datetime(1970, 1, 1, 12, 12, 12)
-        })
-
-        self.assertEquals(da, None)
-
-        y = TestClass()
-
-        alias.applyAttributes(y, {
-            'id': None,
-            'd': datetime.datetime(2008, 3, 12, 0, 0),
-            'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
-            't': datetime.datetime(1970, 1, 1, 12, 12, 12)
-        })
-
-        self.assertEquals(y.id, None)
-        self.assertEquals(y.d, datetime.date(2008, 3, 12))
-        self.assertEquals(y.dt, datetime.datetime(2008, 3, 12, 12, 12, 12))
-        self.assertEquals(y.t, datetime.time(12, 12, 12))
-
-        y = TestClass()
-
-        alias.applyAttributes(y, {
-            'id': None,
-            'd': None,
-            'dt': None,
-            't': None
-        })
-
-        self.assertEquals(y.id, None)
-        self.assertEquals(y.d, None)
-        self.assertEquals(y.dt, None)
-        self.assertEquals(y.t, None)
-
-    def test_undefined(self):
-        from django.db import models
-        from django.db.models import fields
-
-        class UndefinedClass(models.Model):
-            pass
-
-        alias = self.adapter.DjangoClassAlias(UndefinedClass, None)
-
-        x = UndefinedClass()
-
-        alias.applyAttributes(x, {
-            'id': pyamf.Undefined
-        })
-
-        self.assertEquals(x.id, fields.NOT_PROVIDED)
-
-        x.id = fields.NOT_PROVIDED
-
-        sa, da = alias.getEncodableAttributes(x)
-
-        self.assertEquals(da, None)
-        self.assertEquals(sa, {'id': pyamf.Undefined})
-
-    def test_non_field_prop(self):
-        from django.db import models
-
-        class Book(models.Model):
-            def _get_number_of_odd_pages(self):
-                return 234
-
-            # note the lack of a setter callable ..
-            numberOfOddPages = property(_get_number_of_odd_pages)
-
-        alias = self.adapter.DjangoClassAlias(Book, 'Book')
-
-        x = Book()
-
-        self.assertEquals(alias.getEncodableAttributes(x), (
-            {'id': None},
-            {'numberOfOddPages': 234}
-        ))
-
-        # now we test sending the numberOfOddPages attribute
-        alias.applyAttributes(x, {'numberOfOddPages': 24, 'id': None})
-
-        # test it hasn't been set
-        self.assertEquals(x.numberOfOddPages, 234)
-
-    def test_dynamic(self):
-        """
-        Test for dynamic property encoding.
-        """
-        from django.db import models
-
-        class Foo(models.Model):
-            pass
-
-        alias = self.adapter.DjangoClassAlias(Foo, 'Book')
-
-        x = Foo()
-        x.spam = 'eggs'
-
-        self.assertEquals(alias.getEncodableAttributes(x), (
-            {'id': None},
-            {'spam': 'eggs'}
-        ))
-
-        # now we test sending the numberOfOddPages attribute
-        alias.applyAttributes(x, {'spam': 'foo', 'id': None})
-
-        # test it has been set
-        self.assertEquals(x.spam, 'foo')
-
-
-class ForeignKeyTestCase(ModelsBaseTestCase):
-    def test_one_to_many(self):
-        from django.db import models
-
-        class Reporter(models.Model):
-            first_name = models.CharField(max_length=30)
-            last_name = models.CharField(max_length=30)
-            email = models.EmailField()
-
-            def __unicode__(self):
-                return u"%s %s" % (self.first_name, self.last_name)
-
-        class Article(models.Model):
-            headline = models.CharField(max_length=100)
-            pub_date = models.DateField()
-            reporter = models.ForeignKey(Reporter)
-
-            def __unicode__(self):
-                return self.headline
-
-        self.resetDB()
-
-        # initialise the db ..
-        r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
-        r.save()
-
-        r2 = Reporter(first_name='Paul', last_name='Jones', \
                email='paul@example.com')
-        r2.save()
-
-        a = Article(headline="This is a test", pub_date=datetime.date(2005, 7, 27), \
                reporter=r)
-        a.save()
-
-        self.assertEquals(a.id, 1)
-
-        del a
-
-        a = Article.objects.filter(pk=1)[0]
-
-        self.assertFalse('_reporter_cache' in a.__dict__)
-        a.reporter
-        self.assertTrue('_reporter_cache' in a.__dict__)
-
-        del a
-
-        a = Article.objects.filter(pk=1)[0]
-        alias = self.adapter.DjangoClassAlias(Article, defer=True)
-
-        self.assertFalse(hasattr(alias, 'fields'))
-        sa, da = alias.getEncodableAttributes(a)
-        self.assertEquals(sa, {
-            'headline': u'This is a test',
-            'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
-            'id': 1,
-        })
-        # note that the reporter attribute does not exist.
-        self.assertEquals(da, None)
-
-        self.assertFalse('_reporter_cache' in a.__dict__)
-        self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
-            '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
-            '\x01\x08\x01BpUYj@\x00\x00\x01')
-
-        del a
-
-        # now with select_related to pull in the reporter object
-        a = Article.objects.select_related().filter(pk=1)[0]
-
-        alias = self.adapter.DjangoClassAlias(Article, defer=True)
-
-        self.assertFalse(hasattr(alias, 'fields'))
-        self.assertEquals(alias.getEncodableAttributes(a), ({
-            'headline': u'This is a test',
-            'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
-            'id': 1,
-        },
-        {
-            'reporter': r,
-        }))
-
-        self.assertTrue('_reporter_cache' in a.__dict__)
-        self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
-            '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
-            '\x01\x08\x01BpUYj@\x00\x00\x11reporter\nK\x01\x0bemail\x15'
-            'first_name\x02\x13last_name\x06!john@example.com\x06\tJohn\x04'
-            '\x01\x06\x0bSmith\x01\x01')
-
-    def test_many_to_many(self):
-        from django.db import models
-
-        class Publication(models.Model):
-            title = models.CharField(max_length=30)
-
-            def __unicode__(self):
-                return self.title
-
-            class Meta:
-                ordering = ('title',)
-
-        class Article2(models.Model):
-            headline = models.CharField(max_length=100)
-            publications = models.ManyToManyField(Publication)
-
-            def __unicode__(self):
-                return self.headline
-
-            class Meta:
-                ordering = ('headline',)
-
-        self.resetDB()
-
-        # install some test data - taken from
-        # http://www.djangoproject.com/documentation/models/many_to_many/
-        p1 = Publication(id=None, title='The Python Journal')
-        p1.save()
-        p2 = Publication(id=None, title='Science News')
-        p2.save()
-        p3 = Publication(id=None, title='Science Weekly')
-        p3.save()
-
-        # Create an Article.
-        a1 = Article2(id=None, headline='Django lets you build Web apps easily')
-        a1.save()
-        self.assertEquals(a1.id, 1)
-
-        # Associate the Article with a Publication.
-        a1.publications.add(p1)
-
-        pub_alias = self.adapter.DjangoClassAlias(Publication, None)
-        art_alias = self.adapter.DjangoClassAlias(Article2, None)
-
-        test_publication = Publication.objects.filter(pk=1)[0]
-        test_article = Article2.objects.filter(pk=1)[0]
-
-        sa, da = pub_alias.getEncodableAttributes(test_publication)
-        self.assertEquals(sa, {'id': 1, 'title': u'The Python Journal'})
-        self.assertEquals(da, None)
-
-        sa, da = art_alias.getEncodableAttributes(test_article)
-        self.assertEquals(sa, {
-            'headline': u'Django lets you build Web apps easily',
-            'id': 1,
-            'publications': [p1]
-        })
-        self.assertEquals(da, None)
-
-        x = Article2()
-        art_alias.applyAttributes(x, {
-            'headline': u'Test',
-            'id': 1,
-            'publications': [p1]
-        })
-
-        self.assertEquals(x.headline, u'Test')
-        self.assertEquals(x.id, 1)
-
-        p = x.publications.all()
-
-        self.assertEquals(len(p), 1)
-        self.assertEquals(p[0], p1)
-
-    def test_nullable_foreign_keys(self):
-        from django.db import models
-
-        class FooBar(models.Model):
-            pass
-
-        class NullForeignKey(models.Model):
-            foobar = models.ForeignKey(FooBar, null=True)
-
-        class BlankForeignKey(models.Model):
-            foobar = models.ForeignKey(FooBar, blank=True)
-
-        self.resetDB()
-
-        x = FooBar()
-        x.save()
-
-        nfk_alias = self.adapter.DjangoClassAlias(NullForeignKey, None)
-        bfk_alias = self.adapter.DjangoClassAlias(BlankForeignKey, None)
-
-        nfk = NullForeignKey()
-
-        sa, da = nfk_alias.getEncodableAttributes(nfk)
-
-        self.assertEquals(sa, {'id': None})
-        self.assertEquals(da, None)
-
-    def test_static_relation(self):
-        """
-        @see: #693
-        """
-        from django.db import models
-        from pyamf import util
-
-        class Gak(models.Model):
-            pass
-
-        class Baz(models.Model):
-            gak = models.ForeignKey(Gak)
-
-            class __amf__:
-                static = ('gak',)
-
-        self.resetDB()
-
-        alias = self.adapter.DjangoClassAlias(Baz, **util.get_class_meta(Baz))
-
-        alias.compile()
-
-        self.assertTrue('gak' in alias.relations)
-        self.assertTrue('gak' in alias.decodable_properties)
-        self.assertTrue('gak' in alias.static_attrs)
-
-        x = Baz()
-
-        alias.getDecodableAttributes(x, {'id': None, 'gak': 'foo'})
-
-
-class I18NTestCase(ModelsBaseTestCase):
-    def test_encode(self):
-        from django.utils.translation import ugettext_lazy
-
-        self.assertEquals(pyamf.encode(ugettext_lazy('Hello')).getvalue(),
-            '\x02\x00\x05Hello')
-
-
-class PKTestCase(ModelsBaseTestCase):
-    """
-    See ticket #599 for this. Check to make sure that django pk fields
-    are set first
-    """
-
-    def test_behaviour(self):
-        from django.db import models
-
-        class Publication(models.Model):
-            title = models.CharField(max_length=30)
-
-            def __unicode__(self):
-                return self.title
-
-            class Meta:
-                ordering = ('title',)
-
-        class Article2(models.Model):
-            headline = models.CharField(max_length=100)
-            publications = models.ManyToManyField(Publication)
-
-            def __unicode__(self):
-                return self.headline
-
-            class Meta:
-                ordering = ('headline',)
-
-        self.resetDB()
-
-        p = Publication(id=None, title='The Python Journal')
-        a = Article2(id=None, headline='Django lets you build Web apps easily')
-
-        # Associate the Article with a Publication.
-        self.assertRaises(ValueError, lambda a, p: a.publications.add(p), a, p)
-
-        p.save()
-        a.save()
-
-        self.assertEquals(a.id, 2)
-
-        article_alias = self.adapter.DjangoClassAlias(Article2, None)
-        x = Article2()
-
-        article_alias.applyAttributes(x, {
-            'headline': 'Foo bar!',
-            'id': 2,
-            'publications': [p]
-        })
-
-    def test_none(self):
-        """
-        See #556. Make sure that PK fields with a value of 0 are actually set
-        to C{None}.
-        """
-        from django.db import models
-
-        class Foo(models.Model):
-            pass
-
-        self.resetDB()
-
-        alias = self.adapter.DjangoClassAlias(Foo, None)
-
-        x = Foo()
-
-        self.assertEquals(x.id, None)
-
-        alias.applyAttributes(x, {
-            'id': 0
-        })
-
-        self.assertEquals(x.id, None)
-
-
-class ModelInheritanceTestCase(ModelsBaseTestCase):
-    """
-    Tests for L{Django model \
inheritance<http://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance>}
                
-    """
-
-    def test_abstract(self):
-        from django.db import models
-
-        class CommonInfo(models.Model):
-            name = models.CharField(max_length=100)
-            age = models.PositiveIntegerField()
-
-            class Meta:
-                abstract = True
-
-        class Student(CommonInfo):
-            home_group = models.CharField(max_length=5)
-
-        self.resetDB()
-
-        alias = self.adapter.DjangoClassAlias(Student)
-
-        x = Student()
-
-        sa, da = alias.getEncodableAttributes(x)
-
-        self.assertEquals(sa, {
-            'age': None,
-            'home_group': '',
-            'id': None,
-            'name': ''
-        })
-
-        self.assertEquals(da, None)
-
-    def test_concrete(self):
-        from django.db import models
-
-        class Place(models.Model):
-            name = models.CharField(max_length=50)
-            address = models.CharField(max_length=80)
-
-        class Restaurant(Place):
-            serves_hot_dogs = models.BooleanField()
-            serves_pizza = models.BooleanField()
-
-        self.resetDB()
-
-        alias = self.adapter.DjangoClassAlias(Place)
-        x = Place()
-
-        sa, da = alias.getEncodableAttributes(x)
-
-        self.assertEquals(sa, {
-            'id': None,
-            'name': '',
-            'address': ''
-        })
-
-        self.assertEquals(da, None)
-
-        alias = self.adapter.DjangoClassAlias(Restaurant)
-        x = Restaurant()
-
-        sa, da = alias.getEncodableAttributes(x)
-
-        self.assertEquals(sa, {
-            'id': None,
-            'name': '',
-            'address': '',
-            'serves_hot_dogs': False,
-            'serves_pizza': False
-        })
-
-        self.assertEquals(da, None)
-
-
-class MockFile(object):
-    """
-    mock for L{django.core.files.base.File}
-    """
-
-    def chunks(self):
-        return []
-
-    def __len__(self):
-        return 0
-
-    def read(self, n):
-        return ''
-
-
-class FieldsTestCase(ModelsBaseTestCase):
-    """
-    Tests for L{fields}
-    """
-
-    def tearDown(self):
-        ModelsBaseTestCase.tearDown(self)
-
-        try:
-            os.unlink(os.path.join(os.getcwd(), 'foo'))
-        except OSError:
-            raise
-            pass
-
-    def test_file(self):
-        from django.db import models
-
-        self.executed = False
-
-        def get_studio_watermark(*args, **kwargs):
-            self.executed = True
-
-            return 'foo'
-
-        class Image(models.Model):
-            file = models.FileField(upload_to=get_studio_watermark)
-            text = models.CharField(max_length=64)
-
-        self.resetDB()
-
-        alias = self.adapter.DjangoClassAlias(Image)
-
-        i = Image()
-        i.file.save('bar', MockFile())
-
-        i.save()
-
-        sa, da = alias.getEncodableAttributes(i)
-
-        self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo'})
-        self.assertEquals(da, None)
-        self.assertTrue(self.executed)
-
-        attrs = alias.getDecodableAttributes(i, sa)
-
-        self.assertEquals(attrs, {'text': ''})
-
-
-class ImageTestCase(ModelsBaseTestCase):
-    """
-    Tests for L{fields}
-    """
-
-    def test_image(self):
-        from django.db import models
-
-        self.executed = False
-
-        def get_studio_watermark(*args, **kwargs):
-            self.executed = True
-
-            return 'foo'
-
-        class Profile(models.Model):
-            file = models.ImageField(upload_to=get_studio_watermark)
-            text = models.CharField(max_length=64)
-
-        self.resetDB()
-
-        alias = self.adapter.DjangoClassAlias(Profile)
-
-        i = Profile()
-        i.file.save('bar', MockFile())
-
-        i.save()
-
-        sa, da = alias.getEncodableAttributes(i)
-
-        self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo_'})
-        self.assertEquals(da, None)
-        self.assertTrue(self.executed)
-
-        attrs = alias.getDecodableAttributes(i, sa)
-
-        self.assertEquals(attrs, {'text': ''})
-
-
-class ReferenceTestCase(ModelsBaseTestCase):
-    """
-    Test case to make sure that the same object from the database is encoded
-    by reference.
-    """
-
-    def setUp(self):
-        ModelsBaseTestCase.setUp(self)
-
-        from django.db import models
-
-        class ParentReference(models.Model):
-            name = models.CharField(max_length=100)
-            bar = models.ForeignKey('ChildReference', null=True)
-
-        class ChildReference(models.Model):
-            name = models.CharField(max_length=100)
-            foo = models.ForeignKey(ParentReference)
-
-        self.ParentReference = ParentReference
-        self.ChildReference = ChildReference
-
-        self.resetDB()
-
-    def tearDown(self):
-        ModelsBaseTestCase.tearDown(self)
-
-    def test_not_referenced(self):
-        """
-        Test to ensure that we observe the correct behaviour in the Django
-        ORM.
-        """
-        f = self.ParentReference()
-        f.name = 'foo'
-
-        b = self.ChildReference()
-        b.name = 'bar'
-
-        f.save()
-        b.foo = f
-        b.save()
-        f.bar = b
-        f.save()
-
-        self.assertEquals(f.id, 1)
-        foo = self.ParentReference.objects.select_related().get(id=1)
-
-        self.assertFalse(foo.bar.foo is foo)
-
-    def test_referenced_encode(self):
-        f = self.ParentReference()
-        f.name = 'foo'
-
-        b = self.ChildReference()
-        b.name = 'bar'
-
-        f.save()
-        b.foo = f
-        b.save()
-        f.bar = b
-        f.save()
-
-        self.assertEquals(f.id, 2)
-        foo = self.ParentReference.objects.select_related().get(id=2)
-
-        # ensure the referenced attribute resolves
-        foo.bar.foo
-
-        self.assertEquals(pyamf.encode(foo).getvalue(), '\x03\x00\x02id\x00'
-            '@\x00\x00\x00\x00\x00\x00\x00\x00\x04name\x02\x00\x03foo\x00'
-            '\x03bar\x03\x00\x02id\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x04na'
-            'me\x02\x00\x03bar\x00\x03foo\x07\x00\x00\x00\x00\t\x00\x00\t')
-
-
-def suite():
-    suite = unittest.TestSuite()
-
-    try:
-        import django
-    except ImportError:
-        return suite
-
-    test_cases = [
-        TypeMapTestCase,
-        ClassAliasTestCase,
-        ForeignKeyTestCase,
-        I18NTestCase,
-        PKTestCase,
-        ModelInheritanceTestCase,
-        FieldsTestCase,
-        ReferenceTestCase
-    ]
-
-    try:
-        import PIL
-    except:
-        pass
-    else:
-        test_cases.append(ImageTestCase)
-
-    for tc in test_cases:
-        suite.addTest(unittest.makeSuite(tc))
-
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')

Copied: pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py \
(from rev 2928, pyamf/trunk/pyamf/tests/adapters/test_django.py) \
                ===================================================================
--- pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py	         \
                (rev 0)
+++ pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py	2009-10-19 \
10:33:37 UTC (rev 2929) @@ -0,0 +1,845 @@
+# Copyright (c) 2007-2009 The PyAMF Project.
+# See LICENSE.txt for details.
+
+"""
+PyAMF Django adapter tests.
+
+@since: 0.3.1
+"""
+
+import unittest
+import sys
+import os
+import new
+import datetime
+
+import pyamf
+from pyamf.tests import util
+
+
+class ModelsBaseTestCase(unittest.TestCase):
+    def setUp(self):
+        self.old_env = os.environ.copy()
+        self.mods = sys.modules.copy()
+
+        if 'DJANGO_SETTINGS_MODULE' in os.environ.keys():
+            from django import conf
+            import copy
+
+            self.mod = copy.deepcopy(conf.settings)
+            mod = conf.settings
+            self.existing = True
+        else:
+            os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+            mod = new.module('settings')
+
+            sys.modules['settings'] = mod
+
+            self.existing = False
+
+        app = new.module('adapters')
+
+        app_models = new.module('adapters.models')
+        setattr(app, 'models', app_models)
+        setattr(app, '__file__', '')
+        setattr(app_models, '__file__', '')
+
+        sys.modules['adapters'] = app
+        sys.modules['adapters.models'] = app_models
+
+        self.app = app
+        self.models = app_models
+
+        setattr(mod, 'DATABASE_ENGINE', 'sqlite3')
+        setattr(mod, 'DATABASE_NAME', ':memory:')
+        setattr(mod, 'INSTALLED_APPS', ('adapters',))
+        setattr(mod, 'USE_I18N', False)
+
+        from pyamf.adapters import _django_db_models_base as models_adapter
+
+        self.adapter = models_adapter
+
+    def tearDown(self):
+        util.replace_dict(os.environ, self.old_env)
+        util.replace_dict(sys.modules, self.mods)
+
+        if self.existing:
+            from django import conf
+            conf.settings = self.mod
+
+    def resetDB(self):
+        from django.db import connection
+        import sys
+
+        old_stderr = sys.stderr
+        sys.stderr = util.NullFileDescriptor()
+
+        self.db_name = connection.creation.create_test_db(0, autoclobber=True)
+
+        sys.stderr = old_stderr
+
+
+class TypeMapTestCase(ModelsBaseTestCase):
+    def test_objects_all(self):
+        from django.db import models
+
+        class Spam(models.Model):
+            pass
+
+        self.resetDB()
+        encoder = pyamf.get_encoder(pyamf.AMF0)
+
+        encoder.writeElement(Spam.objects.all())
+        self.assertEquals(encoder.stream.getvalue(), '\n\x00\x00\x00\x00')
+
+        encoder = pyamf.get_encoder(pyamf.AMF3)
+        encoder.writeElement(Spam.objects.all())
+        self.assertEquals(encoder.stream.getvalue(), '\t\x01\x01')
+
+    def test_NOT_PROVIDED(self):
+        from django.db.models import fields
+
+        encoder = pyamf.get_encoder(pyamf.AMF0)
+
+        encoder.writeElement(fields.NOT_PROVIDED)
+        self.assertEquals(encoder.stream.getvalue(), '\x06')
+
+        encoder = pyamf.get_encoder(pyamf.AMF3)
+        encoder.writeElement(fields.NOT_PROVIDED)
+        self.assertEquals(encoder.stream.getvalue(), '\x00')
+
+
+class ClassAliasTestCase(ModelsBaseTestCase):
+    def test_time(self):
+        from django.db import models
+
+        class TestClass(models.Model):
+            t = models.TimeField()
+            d = models.DateField()
+            dt = models.DateTimeField()
+
+        x = TestClass()
+
+        x.t = datetime.time(12, 12, 12)
+        x.d = datetime.date(2008, 3, 12)
+        x.dt = datetime.datetime(2008, 3, 12, 12, 12, 12)
+
+        alias = self.adapter.DjangoClassAlias(TestClass, None)
+
+        sa, da = alias.getEncodableAttributes(x)
+
+        self.assertEquals(sa, {
+            'id': None,
+            'd': datetime.datetime(2008, 3, 12, 0, 0),
+            'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
+            't': datetime.datetime(1970, 1, 1, 12, 12, 12)
+        })
+
+        self.assertEquals(da, None)
+
+        y = TestClass()
+
+        alias.applyAttributes(y, {
+            'id': None,
+            'd': datetime.datetime(2008, 3, 12, 0, 0),
+            'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
+            't': datetime.datetime(1970, 1, 1, 12, 12, 12)
+        })
+
+        self.assertEquals(y.id, None)
+        self.assertEquals(y.d, datetime.date(2008, 3, 12))
+        self.assertEquals(y.dt, datetime.datetime(2008, 3, 12, 12, 12, 12))
+        self.assertEquals(y.t, datetime.time(12, 12, 12))
+
+        y = TestClass()
+
+        alias.applyAttributes(y, {
+            'id': None,
+            'd': None,
+            'dt': None,
+            't': None
+        })
+
+        self.assertEquals(y.id, None)
+        self.assertEquals(y.d, None)
+        self.assertEquals(y.dt, None)
+        self.assertEquals(y.t, None)
+
+    def test_undefined(self):
+        from django.db import models
+        from django.db.models import fields
+
+        class UndefinedClass(models.Model):
+            pass
+
+        alias = self.adapter.DjangoClassAlias(UndefinedClass, None)
+
+        x = UndefinedClass()
+
+        alias.applyAttributes(x, {
+            'id': pyamf.Undefined
+        })
+
+        self.assertEquals(x.id, fields.NOT_PROVIDED)
+
+        x.id = fields.NOT_PROVIDED
+
+        sa, da = alias.getEncodableAttributes(x)
+
+        self.assertEquals(da, None)
+        self.assertEquals(sa, {'id': pyamf.Undefined})
+
+    def test_non_field_prop(self):
+        from django.db import models
+
+        class Book(models.Model):
+            def _get_number_of_odd_pages(self):
+                return 234
+
+            # note the lack of a setter callable ..
+            numberOfOddPages = property(_get_number_of_odd_pages)
+
+        alias = self.adapter.DjangoClassAlias(Book, 'Book')
+
+        x = Book()
+
+        self.assertEquals(alias.getEncodableAttributes(x), (
+            {'id': None},
+            {'numberOfOddPages': 234}
+        ))
+
+        # now we test sending the numberOfOddPages attribute
+        alias.applyAttributes(x, {'numberOfOddPages': 24, 'id': None})
+
+        # test it hasn't been set
+        self.assertEquals(x.numberOfOddPages, 234)
+
+    def test_dynamic(self):
+        """
+        Test for dynamic property encoding.
+        """
+        from django.db import models
+
+        class Foo(models.Model):
+            pass
+
+        alias = self.adapter.DjangoClassAlias(Foo, 'Book')
+
+        x = Foo()
+        x.spam = 'eggs'
+
+        self.assertEquals(alias.getEncodableAttributes(x), (
+            {'id': None},
+            {'spam': 'eggs'}
+        ))
+
+        # now we test sending the numberOfOddPages attribute
+        alias.applyAttributes(x, {'spam': 'foo', 'id': None})
+
+        # test it has been set
+        self.assertEquals(x.spam, 'foo')
+
+
+class ForeignKeyTestCase(ModelsBaseTestCase):
+    def test_one_to_many(self):
+        from django.db import models
+
+        class Reporter(models.Model):
+            first_name = models.CharField(max_length=30)
+            last_name = models.CharField(max_length=30)
+            email = models.EmailField()
+
+            def __unicode__(self):
+                return u"%s %s" % (self.first_name, self.last_name)
+
+        class Article(models.Model):
+            headline = models.CharField(max_length=100)
+            pub_date = models.DateField()
+            reporter = models.ForeignKey(Reporter)
+
+            def __unicode__(self):
+                return self.headline
+
+        self.resetDB()
+
+        # initialise the db ..
+        r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
+        r.save()
+
+        r2 = Reporter(first_name='Paul', last_name='Jones', \
email='paul@example.com') +        r2.save()
+
+        a = Article(headline="This is a test", pub_date=datetime.date(2005, 7, 27), \
reporter=r) +        a.save()
+
+        self.assertEquals(a.id, 1)
+
+        del a
+
+        a = Article.objects.filter(pk=1)[0]
+
+        self.assertFalse('_reporter_cache' in a.__dict__)
+        a.reporter
+        self.assertTrue('_reporter_cache' in a.__dict__)
+
+        del a
+
+        a = Article.objects.filter(pk=1)[0]
+        alias = self.adapter.DjangoClassAlias(Article, defer=True)
+
+        self.assertFalse(hasattr(alias, 'fields'))
+        sa, da = alias.getEncodableAttributes(a)
+        self.assertEquals(sa, {
+            'headline': u'This is a test',
+            'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
+            'id': 1,
+        })
+        # note that the reporter attribute does not exist.
+        self.assertEquals(da, None)
+
+        self.assertFalse('_reporter_cache' in a.__dict__)
+        self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
+            '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
+            '\x01\x08\x01BpUYj@\x00\x00\x01')
+
+        del a
+
+        # now with select_related to pull in the reporter object
+        a = Article.objects.select_related().filter(pk=1)[0]
+
+        alias = self.adapter.DjangoClassAlias(Article, defer=True)
+
+        self.assertFalse(hasattr(alias, 'fields'))
+        self.assertEquals(alias.getEncodableAttributes(a), ({
+            'headline': u'This is a test',
+            'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
+            'id': 1,
+        },
+        {
+            'reporter': r,
+        }))
+
+        self.assertTrue('_reporter_cache' in a.__dict__)
+        self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
+            '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
+            '\x01\x08\x01BpUYj@\x00\x00\x11reporter\nK\x01\x0bemail\x15'
+            'first_name\x02\x13last_name\x06!john@example.com\x06\tJohn\x04'
+            '\x01\x06\x0bSmith\x01\x01')
+
+    def test_many_to_many(self):
+        from django.db import models
+
+        class Publication(models.Model):
+            title = models.CharField(max_length=30)
+
+            def __unicode__(self):
+                return self.title
+
+            class Meta:
+                ordering = ('title',)
+
+        class Article2(models.Model):
+            headline = models.CharField(max_length=100)
+            publications = models.ManyToManyField(Publication)
+
+            def __unicode__(self):
+                return self.headline
+
+            class Meta:
+                ordering = ('headline',)
+
+        self.resetDB()
+
+        # install some test data - taken from
+        # http://www.djangoproject.com/documentation/models/many_to_many/
+        p1 = Publication(id=None, title='The Python Journal')
+        p1.save()
+        p2 = Publication(id=None, title='Science News')
+        p2.save()
+        p3 = Publication(id=None, title='Science Weekly')
+        p3.save()
+
+        # Create an Article.
+        a1 = Article2(id=None, headline='Django lets you build Web apps easily')
+        a1.save()
+        self.assertEquals(a1.id, 1)
+
+        # Associate the Article with a Publication.
+        a1.publications.add(p1)
+
+        pub_alias = self.adapter.DjangoClassAlias(Publication, None)
+        art_alias = self.adapter.DjangoClassAlias(Article2, None)
+
+        test_publication = Publication.objects.filter(pk=1)[0]
+        test_article = Article2.objects.filter(pk=1)[0]
+
+        sa, da = pub_alias.getEncodableAttributes(test_publication)
+        self.assertEquals(sa, {'id': 1, 'title': u'The Python Journal'})
+        self.assertEquals(da, None)
+
+        sa, da = art_alias.getEncodableAttributes(test_article)
+        self.assertEquals(sa, {
+            'headline': u'Django lets you build Web apps easily',
+            'id': 1,
+            'publications': [p1]
+        })
+        self.assertEquals(da, None)
+
+        x = Article2()
+        art_alias.applyAttributes(x, {
+            'headline': u'Test',
+            'id': 1,
+            'publications': [p1]
+        })
+
+        self.assertEquals(x.headline, u'Test')
+        self.assertEquals(x.id, 1)
+
+        p = x.publications.all()
+
+        self.assertEquals(len(p), 1)
+        self.assertEquals(p[0], p1)
+
+    def test_nullable_foreign_keys(self):
+        from django.db import models
+
+        class FooBar(models.Model):
+            pass
+
+        class NullForeignKey(models.Model):
+            foobar = models.ForeignKey(FooBar, null=True)
+
+        class BlankForeignKey(models.Model):
+            foobar = models.ForeignKey(FooBar, blank=True)
+
+        self.resetDB()
+
+        x = FooBar()
+        x.save()
+
+        nfk_alias = self.adapter.DjangoClassAlias(NullForeignKey, None)
+        bfk_alias = self.adapter.DjangoClassAlias(BlankForeignKey, None)
+
+        nfk = NullForeignKey()
+
+        sa, da = nfk_alias.getEncodableAttributes(nfk)
+
+        self.assertEquals(sa, {'id': None})
+        self.assertEquals(da, None)
+
+    def test_static_relation(self):
+        """
+        @see: #693
+        """
+        from django.db import models
+        from pyamf import util
+
+        class Gak(models.Model):
+            pass
+
+        class Baz(models.Model):
+            gak = models.ForeignKey(Gak)
+
+            class __amf__:
+                static = ('gak',)
+
+        self.resetDB()
+
+        alias = self.adapter.DjangoClassAlias(Baz, **util.get_class_meta(Baz))
+
+        alias.compile()
+
+        self.assertTrue('gak' in alias.relations)
+        self.assertTrue('gak' in alias.decodable_properties)
+        self.assertTrue('gak' in alias.static_attrs)
+
+        x = Baz()
+
+        alias.getDecodableAttributes(x, {'id': None, 'gak': 'foo'})
+
+
+class I18NTestCase(ModelsBaseTestCase):
+    def test_encode(self):
+        from django.utils.translation import ugettext_lazy
+
+        self.assertEquals(pyamf.encode(ugettext_lazy('Hello')).getvalue(),
+            '\x02\x00\x05Hello')
+
+
+class PKTestCase(ModelsBaseTestCase):
+    """
+    See ticket #599 for this. Check to make sure that django pk fields
+    are set first
+    """
+
+    def test_behaviour(self):
+        from django.db import models
+
+        class Publication(models.Model):
+            title = models.CharField(max_length=30)
+
+            def __unicode__(self):
+                return self.title
+
+            class Meta:
+                ordering = ('title',)
+
+        class Article2(models.Model):
+            headline = models.CharField(max_length=100)
+            publications = models.ManyToManyField(Publication)
+
+            def __unicode__(self):
+                return self.headline
+
+            class Meta:
+                ordering = ('headline',)
+
+        self.resetDB()
+
+        p = Publication(id=None, title='The Python Journal')
+        a = Article2(id=None, headline='Django lets you build Web apps easily')
+
+        # Associate the Article with a Publication.
+        self.assertRaises(ValueError, lambda a, p: a.publications.add(p), a, p)
+
+        p.save()
+        a.save()
+
+        self.assertEquals(a.id, 2)
+
+        article_alias = self.adapter.DjangoClassAlias(Article2, None)
+        x = Article2()
+
+        article_alias.applyAttributes(x, {
+            'headline': 'Foo bar!',
+            'id': 2,
+            'publications': [p]
+        })
+
+    def test_none(self):
+        """
+        See #556. Make sure that PK fields with a value of 0 are actually set
+        to C{None}.
+        """
+        from django.db import models
+
+        class Foo(models.Model):
+            pass
+
+        self.resetDB()
+
+        alias = self.adapter.DjangoClassAlias(Foo, None)
+
+        x = Foo()
+
+        self.assertEquals(x.id, None)
+
+        alias.applyAttributes(x, {
+            'id': 0
+        })
+
+        self.assertEquals(x.id, None)
+
+    def test_no_pk(self):
+        """
+        Ensure that Models without a primary key are correctly serialized.
+        See #691.
+        """
+        from django.db import models
+
+        class NotSaved(models.Model):
+            name = models.CharField(max_length=100)
+
+        instances = [NotSaved(name="a"), NotSaved(name="b")]
+        encoded = pyamf.encode(instances, encoding=pyamf.AMF3).getvalue()
+        decoded = pyamf.get_decoder(pyamf.AMF3, encoded).readElement()
+        self.assertEquals(decoded[0]['name'], 'a')
+        self.assertEquals(decoded[1]['name'], 'b')
+
+
+class ModelInheritanceTestCase(ModelsBaseTestCase):
+    """
+    Tests for L{Django model \
inheritance<http://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance>}
 +    """
+
+    def test_abstract(self):
+        from django.db import models
+
+        class CommonInfo(models.Model):
+            name = models.CharField(max_length=100)
+            age = models.PositiveIntegerField()
+
+            class Meta:
+                abstract = True
+
+        class Student(CommonInfo):
+            home_group = models.CharField(max_length=5)
+
+        self.resetDB()
+
+        alias = self.adapter.DjangoClassAlias(Student)
+
+        x = Student()
+
+        sa, da = alias.getEncodableAttributes(x)
+
+        self.assertEquals(sa, {
+            'age': None,
+            'home_group': '',
+            'id': None,
+            'name': ''
+        })
+
+        self.assertEquals(da, None)
+
+    def test_concrete(self):
+        from django.db import models
+
+        class Place(models.Model):
+            name = models.CharField(max_length=50)
+            address = models.CharField(max_length=80)
+
+        class Restaurant(Place):
+            serves_hot_dogs = models.BooleanField()
+            serves_pizza = models.BooleanField()
+
+        self.resetDB()
+
+        alias = self.adapter.DjangoClassAlias(Place)
+        x = Place()
+
+        sa, da = alias.getEncodableAttributes(x)
+
+        self.assertEquals(sa, {
+            'id': None,
+            'name': '',
+            'address': ''
+        })
+
+        self.assertEquals(da, None)
+
+        alias = self.adapter.DjangoClassAlias(Restaurant)
+        x = Restaurant()
+
+        sa, da = alias.getEncodableAttributes(x)
+
+        self.assertEquals(sa, {
+            'id': None,
+            'name': '',
+            'address': '',
+            'serves_hot_dogs': False,
+            'serves_pizza': False
+        })
+
+        self.assertEquals(da, None)
+
+
+class MockFile(object):
+    """
+    mock for L{django.core.files.base.File}
+    """
+
+    def chunks(self):
+        return []
+
+    def __len__(self):
+        return 0
+
+    def read(self, n):
+        return ''
+
+
+class FieldsTestCase(ModelsBaseTestCase):
+    """
+    Tests for L{fields}
+    """
+
+    def tearDown(self):
+        ModelsBaseTestCase.tearDown(self)
+
+        try:
+            os.unlink(os.path.join(os.getcwd(), 'foo'))
+        except OSError:
+            raise
+            pass
+
+    def test_file(self):
+        from django.db import models
+
+        self.executed = False
+
+        def get_studio_watermark(*args, **kwargs):
+            self.executed = True
+
+            return 'foo'
+
+        class Image(models.Model):
+            file = models.FileField(upload_to=get_studio_watermark)
+            text = models.CharField(max_length=64)
+
+        self.resetDB()
+
+        alias = self.adapter.DjangoClassAlias(Image)
+
+        i = Image()
+        i.file.save('bar', MockFile())
+
+        i.save()
+
+        sa, da = alias.getEncodableAttributes(i)
+
+        self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo'})
+        self.assertEquals(da, None)
+        self.assertTrue(self.executed)
+
+        attrs = alias.getDecodableAttributes(i, sa)
+
+        self.assertEquals(attrs, {'text': ''})
+
+
+class ImageTestCase(ModelsBaseTestCase):
+    """
+    Tests for L{fields}
+    """
+
+    def test_image(self):
+        from django.db import models
+
+        self.executed = False
+
+        def get_studio_watermark(*args, **kwargs):
+            self.executed = True
+
+            return 'foo'
+
+        class Profile(models.Model):
+            file = models.ImageField(upload_to=get_studio_watermark)
+            text = models.CharField(max_length=64)
+
+        self.resetDB()
+
+        alias = self.adapter.DjangoClassAlias(Profile)
+
+        i = Profile()
+        i.file.save('bar', MockFile())
+
+        i.save()
+
+        sa, da = alias.getEncodableAttributes(i)
+
+        self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo_'})
+        self.assertEquals(da, None)
+        self.assertTrue(self.executed)
+
+        attrs = alias.getDecodableAttributes(i, sa)
+
+        self.assertEquals(attrs, {'text': ''})
+
+
+class ReferenceTestCase(ModelsBaseTestCase):
+    """
+    Test case to make sure that the same object from the database is encoded
+    by reference.
+    """
+
+    def setUp(self):
+        ModelsBaseTestCase.setUp(self)
+
+        from django.db import models
+
+        class ParentReference(models.Model):
+            name = models.CharField(max_length=100)
+            bar = models.ForeignKey('ChildReference', null=True)
+
+        class ChildReference(models.Model):
+            name = models.CharField(max_length=100)
+            foo = models.ForeignKey(ParentReference)
+
+        self.ParentReference = ParentReference
+        self.ChildReference = ChildReference
+
+        self.resetDB()
+
+    def tearDown(self):
+        ModelsBaseTestCase.tearDown(self)
+
+    def test_not_referenced(self):
+        """
+        Test to ensure that we observe the correct behaviour in the Django
+        ORM.
+        """
+        f = self.ParentReference()
+        f.name = 'foo'
+
+        b = self.ChildReference()
+        b.name = 'bar'
+
+        f.save()
+        b.foo = f
+        b.save()
+        f.bar = b
+        f.save()
+
+        self.assertEquals(f.id, 1)
+        foo = self.ParentReference.objects.select_related().get(id=1)
+
+        self.assertFalse(foo.bar.foo is foo)
+
+    def test_referenced_encode(self):
+        f = self.ParentReference()
+        f.name = 'foo'
+
+        b = self.ChildReference()
+        b.name = 'bar'
+
+        f.save()
+        b.foo = f
+        b.save()
+        f.bar = b
+        f.save()
+
+        self.assertEquals(f.id, 2)
+        foo = self.ParentReference.objects.select_related().get(id=2)
+
+        # ensure the referenced attribute resolves
+        foo.bar.foo
+
+        self.assertEquals(pyamf.encode(foo).getvalue(), '\x03\x00\x02id\x00'
+            '@\x00\x00\x00\x00\x00\x00\x00\x00\x04name\x02\x00\x03foo\x00'
+            '\x03bar\x03\x00\x02id\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x04na'
+            'me\x02\x00\x03bar\x00\x03foo\x07\x00\x00\x00\x00\t\x00\x00\t')
+
+
+def suite():
+    suite = unittest.TestSuite()
+
+    try:
+        import django
+    except ImportError:
+        return suite
+
+    test_cases = [
+        TypeMapTestCase,
+        ClassAliasTestCase,
+        ForeignKeyTestCase,
+        I18NTestCase,
+        PKTestCase,
+        ModelInheritanceTestCase,
+        FieldsTestCase,
+        ReferenceTestCase
+    ]
+
+    try:
+        import PIL
+    except:
+        pass
+    else:
+        test_cases.append(ImageTestCase)
+
+    for tc in test_cases:
+        suite.addTest(unittest.makeSuite(tc))
+
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')

_______________________________________________
PyAMF commits mailing list - commits@pyamf.org
http://lists.pyamf.org/mailman/listinfo/commits


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic