Fair enough.

I'd recommend that you update the documentation of db.Model to point out that 
"the Model class is not designed for multiple inheritance"
if it doesn't already say that somewhere I didn't see.

My workaround right now is to have my Model create a helper object and use 
manual delegation patterns to get the calls done.  It's kind of gross.  Is 
there a pythony way that would allow my model to present itself as a tzinfo, 
without using multiple inheritance?  I don't mind having the delegate inside my 
model, but I'd rather not have to have all my callers get the delegate through 
an extra call (or, in principle, even know about the delegate).

For example, my code used to read:

  def localDate(self):
    return self.date.replace(tzinfo=UTC()).astimezone(self.town.region)

and now it would have to read:

  def localDate(self):
    return 
self.date.replace(tzinfo=UTC()).astimezone(self.town.region.create_tz_helper())

If that's the only way to do it, I'll find a way to make that less annoying 
(perhaps create the helper at init time, and stick it in an attribute), but I'd 
rather not have to change all the callers that way.


-Joshua

On May 4, 2011, at 2:37 PM, Guido van Rossum wrote:

> On May 4, 11:17 am, Joshua Smith <joshuaesm...@charter.net> wrote:
>> I've worked around the problem by moving the tzinfo behavior into a helper 
>> object and delegating all the calls, but it's not as tidy as this original 
>> solution, so I'd still appreciate some advice:
>> 
>> 1) What did I do wrong?  Is there some fundamental design patten in python 
>> that says you have to do a certain thing when you subclass in order to 
>> expose an interface?  What is that thing I didn't do?
> 
> In general the Model class is not designed for multiple inheritance.
> In this case you were multiply inheriting from a built-in type
> (tzinfo) and there are various CPython restrictions on m.i. from
> those; the error message you got came from the machinery related to
> that. What we did was actually make Model *more* correct around some
> other m.i. scenarios, but unfortunately this broke your code that
> until now had been luckily avoiding the landmines.
> 
> The best solution (as you have found) is definitely to refactor your
> code and avoid m.i.; alternatives that happen to work (but run similar
> risks in the future) include swapping the inheritance order or adding
> a dummy __new__ to your class:
> 
>    def __new__(cls, *a, **k):
>        return object.__new__(cls)
> 
> However I cannot promise that this really works correctly nor that it
> will keep working in the future -- one of the downsides of using such
> a dynamic language as Python is that occasionally things happen to
> work that weren't intended to work...
> 
>> 2) Why is this code OK in development, but not in production?  The code 
>> that's throwing the exception (that call to __new__ in __init__.py) doesn't 
>> even exist in the development python code.
> 
> We're working on the 1.5.0 release. As Marzia explained last release
> (http://groups.google.com/group/google-appengine-python/browse_thread/
> thread/a3664fc8171ebaa8/c1228f138c8069d6) we can't push all components
> simultaneously so we typically upgrade the production runtime ahead of
> the SDK. (Do note that the 1.5.0 prerelease SDK does have this code.
> It is not perfect however. 
> http://code.google.com/p/googleappengine/downloads/list)
> 
>> 3) What's up with this changing during "system maintenance" and not as part 
>> of an SDK update?  I can't be the only one who's code unexpectedly stopped 
>> working.
> 
> It had nothing to do with the system maintenance; the 1.5.0 push to
> production was unrelated.
> 
> Again, sorry for your problems and glad you found a fix already!
> 
> --Guido van Rossum
> 
>> -Joshua
>> 
>> On May 4, 2011, at 10:10 AM, Joshua Smith wrote:
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>>> I have some code that has been working for a really long time, that 
>>> suddenly started throwing an exception.
>> 
>>> In my app, I have to keep some different time zones around, so I have a 
>>> Model that carries the interesting stuff, and it acts as a tzinfo for 
>>> convenience.
>> 
>>> Like this:
>> 
>>> class RegionModel(db.Model, datetime.tzinfo):
>>>   name = db.StringProperty()
>>>   gmtOffset = db.IntegerProperty()
>>>   tzName = db.StringProperty()
>>>   tzDstName = db.StringProperty()
>> 
>>>   def utcoffset(self, dt):
>>>     return datetime.timedelta(hours=self.gmtOffset) + self.dst(dt)
>> 
>>> etc.
>> 
>>> This has been working fine, but starting at 5/3 at 15:50, I'm getting this 
>>> exception:
>> 
>>> 2011-05-03 15:50:27.934
>>> datetime.tzinfo.__new__(RegionModel) is not safe, use object.__new__()
>>> Traceback (most recent call last):
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/ext/webapp/__i 
>>> nit__.py", line 698, in __call__
>>>     handler.get(*groups)
>>>   File "/base/data/home/apps/towngovernment/37.349853758361595417/main.py", 
>>> line 1691, in get
>>>     default = board.anyTown().nextLikelyMeetingTime()
>>>   File "/base/data/home/apps/towngovernment/37.349853758361595417/main.py", 
>>> line 248, in nextLikelyMeetingTime
>>>     t = self.nextLegalMeetingTime()
>>>   File "/base/data/home/apps/towngovernment/37.349853758361595417/main.py", 
>>> line 242, in nextLegalMeetingTime
>>>     now = self.region.currentLocalTime()
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init_ 
>>> _.py", line 3491, in __get__
>>>     instance = get(reference_id)
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init_ 
>>> _.py", line 1471, in get
>>>     return get_async(keys, **kwargs).get_result()
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/data 
>>> store_rpc.py", line 644, in get_result
>>>     results = self.__extra_hook(results)
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore. 
>>> py", line 488, in local_extra_hook
>>>     return extra_hook(result)
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init_ 
>>> _.py", line 1441, in extra_hook
>>>     model = cls1.from_entity(entity)
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init_ 
>>> _.py", line 1370, in from_entity
>>>     instance = cls(None, _from_entity=True, **entity_values)
>>>   File 
>>> "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init_ 
>>> _.py", line 767, in __new__
>>>     return super(Model, cls).__new__(cls)
>>> TypeError: datetime.tzinfo.__new__(RegionModel) is not safe, use 
>>> object.__new__()
>> 
>>> It appears that I suddenly need to start defining __init__ or __new__ or 
>>> something, but I'm out of my depth to know how to fix this.  Also, 
>>> everything still works just fine in the development environment.  It's only 
>>> broken in production.
>> 
>>> Can one of you python gurus please tell me how to override __init__ or 
>>> __new__ or whatever so that I don't break the db.Model stuff, and it allows 
>>> me to extend tzinfo with this class as I have been doing?
>> 
>>> This is urgent!
>> 
>>> Thanks.
>> 
>>> -Joshua
>> 
>>> --
>>> You received this message because you are subscribed to the Google Groups 
>>> "Google App Engine" group.
>>> To post to this group, send email to google-appengine@googlegroups.com.
>>> To unsubscribe from this group, send email to 
>>> google-appengine+unsubscr...@googlegroups.com.
>>> For more options, visit this group 
>>> athttp://groups.google.com/group/google-appengine?hl=en.
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Google App Engine" group.
> To post to this group, send email to google-appengine@googlegroups.com.
> To unsubscribe from this group, send email to 
> google-appengine+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/google-appengine?hl=en.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine" group.
To post to this group, send email to google-appengine@googlegroups.com.
To unsubscribe from this group, send email to 
google-appengine+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/google-appengine?hl=en.

Reply via email to