Back to index

plone3  3.1.7
utils.py
Go to the documentation of this file.
00001 import sys
00002 import os
00003 import socket
00004 from random import random
00005 from time import time
00006 from inspect import getargs, getmro
00007 from md5 import md5
00008 from types import ClassType, MethodType
00009 from zope.i18nmessageid import Message
00010 from UserDict import UserDict as BaseDict
00011 
00012 from AccessControl import ClassSecurityInfo
00013 from AccessControl.SecurityInfo import ACCESS_PUBLIC
00014 
00015 from Acquisition import aq_base
00016 from ExtensionClass import ExtensionClass
00017 from Globals import InitializeClass
00018 from Products.CMFCore.utils import getToolByName
00019 from Products.Archetypes.debug import log
00020 from Products.Archetypes.debug import deprecated
00021 from Products.Archetypes.config import DEBUG_SECURITY
00022 from Products.statusmessages.interfaces import IStatusMessage
00023 
00024 # BBB, this can be removed once we do not support PTS anymore
00025 from Products.PageTemplates.GlobalTranslationService \
00026      import getGlobalTranslationService as getGTS
00027 
00028 from Interface.bridge import createZope3Bridge
00029 from Products.Five.fiveconfigure import createZope2Bridge
00030 def makeBridgeMaker(func):
00031     def makeBridge(*args):
00032         module=args[0]
00033         ifaces = args[1:]
00034         for iface in ifaces:
00035             func(iface, module, iface.__name__)
00036     return makeBridge
00037 
00038 makeZ2Bridges=makeBridgeMaker(createZope2Bridge)
00039 makeZ3Bridges=makeBridgeMaker(createZope3Bridge)
00040 
00041 try:
00042     _v_network = str(socket.gethostbyname(socket.gethostname()))
00043 except:
00044     _v_network = str(random() * 100000000000000000L)
00045 
00046 def make_uuid(*args):
00047     t = str(time() * 1000L)
00048     r = str(random()*100000000000000000L)
00049     data = t +' '+ r +' '+ _v_network +' '+ str(args)
00050     uid = md5(data).hexdigest()
00051     return uid
00052 
00053 # linux kernel uid generator. It's a little bit slower but a little bit better
00054 KERNEL_UUID = '/proc/sys/kernel/random/uuid'
00055 
00056 if os.path.isfile(KERNEL_UUID):
00057     HAS_KERNEL_UUID = True
00058     def uuid_gen():
00059         fp = open(KERNEL_UUID, 'r')
00060         while 1:
00061             uid = fp.read()[:-1]
00062             fp.seek(0)
00063             yield uid
00064     uid_gen = uuid_gen()
00065 
00066     def kernel_make_uuid(*args):
00067         return uid_gen.next()
00068 else:
00069     HAS_KERNEL_UUID = False
00070     kernel_make_uuid = make_uuid
00071 
00072 
00073 def fixSchema(schema):
00074     """Fix persisted schema from AT < 1.3 (UserDict-based)
00075     to work with the new fixed order schema."""
00076     from Products.Archetypes.Schema import Schemata
00077     if not hasattr(aq_base(schema), '_fields'):
00078         fields = schema.data.values()
00079         Schemata.__init__(schema, fields)
00080         del schema.data
00081     return schema
00082 
00083 _marker = []
00084 
00085 def mapply(method, *args, **kw):
00086     """ Inspect function and apply positional and keyword arguments as possible.
00087 
00088     Add more examples.
00089 
00090     >>> def f(a, b, c=2, d=3):
00091     ...     print a, b, c, d
00092 
00093     >>> mapply(f, *(1, 2), **{'d':4})
00094     1 2 2 4
00095 
00096     >>> mapply(f, *(1, 2), **{'c':3})
00097     1 2 3 3
00098 
00099     >>> mapply(f, *(1, 2), **{'j':3})
00100     1 2 2 3
00101 
00102     >>> def f(a, b):
00103     ...     print a, b
00104 
00105     >>> mapply(f, *(1, 2), **{'j':3})
00106     1 2
00107 
00108     >>> def f(a, b=2):
00109     ...     print a, b
00110 
00111     >>> mapply(f, *(1,), **{'j':3})
00112     1 2
00113 
00114     >>> mapply(f, *(1,), **{'j':3})
00115     1 2
00116 
00117     TODO Should raise an exception 'Multiple values for argument' here.
00118 
00119     >>> mapply(f, *(1,), **{'a':3})
00120     1 2
00121 
00122     >>> mapply(f, *(1,), **{'b':3})
00123     1 3
00124 
00125     >>> def f(a=1, b=2):
00126     ...     print a, b
00127 
00128     >>> mapply(f, *(), **{'b':3})
00129     1 3
00130 
00131     >>> mapply(f, *(), **{'a':3})
00132     3 2
00133     """
00134     m = method
00135     if hasattr(m, 'im_func'):
00136         m = m.im_func
00137     code = m.func_code
00138     fn_args = getargs(code)
00139     call_args = list(args)
00140     if fn_args[1] is not None and fn_args[2] is not None:
00141         return method(*args, **kw)
00142     if fn_args[1] is None:
00143         if len(call_args) > len(fn_args[0]):
00144             call_args = call_args[:len(fn_args[0])]
00145     nkw = {}
00146     if len(call_args) < len(fn_args[0]):
00147         for arg in fn_args[0][len(call_args):]:
00148             value = kw.get(arg, _marker)
00149             if value is not _marker:
00150                 nkw[arg] = value
00151                 del kw[arg]
00152     largs = len(call_args) + len(nkw.keys())
00153     if largs < len(fn_args[0]):
00154         for arg in fn_args[0][largs:]:
00155             value = kw.get(arg, _marker)
00156             if value is not _marker:
00157                 call_args.append(value)
00158                 del kw[arg]
00159     if fn_args[2] is not None:
00160         return method(*call_args, **kw)
00161     if fn_args[0]:
00162         return method(*call_args, **nkw)
00163     return method()
00164 
00165 def className(klass):
00166     if type(klass) not in [ClassType, ExtensionClass]:
00167         klass = klass.__class__
00168     return "%s.%s" % (klass.__module__, klass.__name__)
00169 
00170 def productDir():
00171     module = sys.modules[__name__]
00172     return os.path.dirname(module.__file__)
00173 
00174 def pathFor(path=None, file=None):
00175     base = productDir()
00176     if path:
00177         base = os.path.join(base, path)
00178     if file:
00179         base = os.path.join(base, file)
00180 
00181     return base
00182 
00183 def capitalize(string):
00184     if string[0].islower():
00185         string = string[0].upper() + string[1:]
00186     return string
00187 
00188 def findDict(listofDicts, key, value):
00189     #Look at a list of dicts for one where key == value
00190     for d in listofDicts:
00191         if d.has_key(key):
00192             if d[key] == value:
00193                 return d
00194     return None
00195 
00196 
00197 def basename(path):
00198     return path[max(path.rfind('\\'), path.rfind('/'))+1:]
00199 
00200 def unique(s):
00201     """Return a list of the elements in s, but without duplicates.
00202 
00203     For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
00204     unique("abcabc") some permutation of ["a", "b", "c"], and
00205     unique(([1, 2], [2, 3], [1, 2])) some permutation of
00206     [[2, 3], [1, 2]].
00207 
00208     For best speed, all sequence elements should be hashable.  Then
00209     unique() will usually work in linear time.
00210 
00211     If not possible, the sequence elements should enjoy a total
00212     ordering, and if list(s).sort() doesn't raise TypeError it's
00213     assumed that they do enjoy a total ordering.  Then unique() will
00214     usually work in O(N*log2(N)) time.
00215 
00216     If that's not possible either, the sequence elements must support
00217     equality-testing.  Then unique() will usually work in quadratic
00218     time.
00219     """
00220     # taken from ASPN Python Cookbook,
00221     # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
00222 
00223     n = len(s)
00224     if n == 0:
00225         return []
00226 
00227     # Try using a dict first, as that's the fastest and will usually
00228     # work.  If it doesn't work, it will usually fail quickly, so it
00229     # usually doesn't cost much to *try* it.  It requires that all the
00230     # sequence elements be hashable, and support equality comparison.
00231     u = {}
00232     try:
00233         for x in s:
00234             u[x] = 1
00235     except TypeError:
00236         del u  # move on to the next method
00237     else:
00238         return u.keys()
00239 
00240     # We can't hash all the elements.  Second fastest is to sort,
00241     # which brings the equal elements together; then duplicates are
00242     # easy to weed out in a single pass.
00243     # NOTE:  Python's list.sort() was designed to be efficient in the
00244     # presence of many duplicate elements.  This isn't true of all
00245     # sort functions in all languages or libraries, so this approach
00246     # is more effective in Python than it may be elsewhere.
00247     try:
00248         t = list(s)
00249         t.sort()
00250     except TypeError:
00251         del t  # move on to the next method
00252     else:
00253         assert n > 0
00254         last = t[0]
00255         lasti = i = 1
00256         while i < n:
00257             if t[i] != last:
00258                 t[lasti] = last = t[i]
00259                 lasti += 1
00260             i += 1
00261         return t[:lasti]
00262 
00263     # Brute force is all that's left.
00264     u = []
00265     for x in s:
00266         if x not in u:
00267             u.append(x)
00268     return u
00269 
00270 
00271 
00272 class DisplayList:
00273     """Static display lists, can look up on
00274     either side of the dict, and get them in sorted order
00275 
00276     NOTE: Both keys and values *must* contain unique entries! You can have
00277     two times the same value. This is a "feature" not a bug. DisplayLists
00278     are meant to be used as a list inside html form entry like a drop down.
00279 
00280     >>> dl = DisplayList()
00281 
00282     Add some keys
00283     >>> dl.add('foo', 'bar')
00284     >>> dl.add('egg', 'spam')
00285 
00286     Assert some values
00287     >>> dl.index
00288     2
00289     >>> dl.keys()
00290     ['foo', 'egg']
00291     >>> dl.values()
00292     ['bar', 'spam']
00293     >>> dl.items()
00294     (('foo', 'bar'), ('egg', 'spam'))
00295 
00296     You can't use e.g. objects as keys or values
00297     >>> dl.add(object(), 'error')
00298     Traceback (most recent call last):
00299     TypeError: DisplayList keys must be strings or ints, got <type 'object'>
00300 
00301     >>> dl.add('error', object())
00302     Traceback (most recent call last):
00303     TypeError: DisplayList values must be strings or ints, got <type 'object'>
00304 
00305     GOTCHA
00306     Adding a value a second time does overwrite the key, too!
00307     >>> dl.add('fobar' ,'spam')
00308     >>> dl.keys()
00309     ['foo', 'fobar']
00310 
00311     >>> dl.items()
00312     (('foo', 'bar'), ('fobar', 'spam'))
00313 
00314     Install warning hook for the next tests since they will raise a warning
00315     and I don't want to spoil the logs.
00316     >>> from Testing.ZopeTestCase import WarningsHook
00317     >>> w = WarningsHook(); w.install()
00318 
00319     Using ints as DisplayList keys works but will raise an deprecation warning
00320     You should use IntDisplayList for int keys
00321 
00322     >>> idl = DisplayList()
00323     >>> idl.add(1, 'number one')
00324     >>> idl.add(2, 'just the second')
00325 
00326     >>> idl.items()
00327     ((1, 'number one'), (2, 'just the second'))
00328 
00329     Remove warning hook
00330     >>> w.uninstall(); del w
00331     """
00332 
00333     security = ClassSecurityInfo()
00334     security.setDefaultAccess('allow')
00335 
00336     def __init__(self, data=None):
00337         self._keys = {}
00338         self._i18n_msgids = {}
00339         self._values = {}
00340         self._itor   = []
00341         self.index = 0
00342         if data:
00343             self.fromList(data)
00344 
00345     def __repr__(self):
00346         return '<DisplayList %s at %s>' % (self[:], id(self))
00347 
00348     def __str__(self):
00349         return str(self[:])
00350 
00351     def __call__(self):
00352         return self
00353 
00354     def fromList(self, lst):
00355         for item in lst:
00356             if isinstance(item, list):
00357                 item = tuple(item)
00358             self.add(*item)
00359 
00360     def __len__(self):
00361         return self.index
00362 
00363     def __add__(self, other):
00364         a = tuple(self.items())
00365         if hasattr(other, 'items'):
00366             b = other.items()
00367         else: #assume a seq
00368             b = tuple(zip(other, other))
00369 
00370         msgids = self._i18n_msgids
00371         msgids.update(getattr(other, '_i18n_msgids', {}))
00372 
00373         v = DisplayList(a + b)
00374         v._i18n_msgids = msgids
00375         return v
00376 
00377     def index_sort(self, a, b):
00378         return  a[0] - b[0]
00379 
00380     def add(self, key, value, msgid=None):
00381         if isinstance(key, int):
00382             deprecated('Using ints as DisplayList keys is deprecated (add)')
00383         if not isinstance(key, basestring) and not isinstance(key, int):
00384             raise TypeError('DisplayList keys must be strings or ints, got %s' %
00385                             type(key))
00386         if not isinstance(value, basestring) and not isinstance(value, int):
00387             raise TypeError('DisplayList values must be strings or ints, got %s' %
00388                             type(value))
00389         if msgid is not None:
00390             deprecated('Using explicit msgids for DisplayLists is deprecated. '
00391                         'Store Zope3 Messages as values directly.')
00392             if not isinstance(msgid, basestring):
00393                 raise TypeError('DisplayList msg ids must be strings, got %s' %
00394                                 type(msgid))
00395         self.index +=1
00396         k = (self.index, key)
00397         v = (self.index, value)
00398 
00399         self._keys[key] = v
00400         self._values[value] = k
00401         self._itor.append(key)
00402         if msgid is not None:
00403             self._i18n_msgids[key] = msgid
00404 
00405     def getKey(self, value, default=None):
00406         """get key"""
00407         v = self._values.get(value, None)
00408         if v: return v[1]
00409         for k, v in self._values.items():
00410             if repr(value) == repr(k):
00411                 return v[1]
00412         return default
00413 
00414     def getValue(self, key, default=None):
00415         "get value"
00416         if isinstance(key, int):
00417             deprecated('Using ints as DisplayList keys is deprecated (getValue)')
00418         if not isinstance(key, basestring) and not isinstance(key, int):
00419             raise TypeError('DisplayList keys must be strings or ints, got %s' %
00420                             type(key))
00421         v = self._keys.get(key, None)
00422         if v: return v[1]
00423         for k, v in self._keys.items():
00424             if repr(key) == repr(k):
00425                 return v[1]
00426         return default
00427 
00428     def getMsgId(self, key):
00429         "get i18n msgid"
00430         deprecated('DisplayList getMsgId is deprecated. Store Zope3 Messages '
00431                    'as values instead.')
00432         if isinstance(key, int):
00433             deprecated('Using ints as DisplayList keys is deprecated (msgid)')
00434         if not isinstance(key, basestring) and not isinstance(key, int):
00435             raise TypeError('DisplayList keys must be strings or ints, got %s' %
00436                             type(key))
00437         if self._i18n_msgids.has_key(key):
00438             return self._i18n_msgids[key]
00439         else:
00440             return self._keys[key][1]
00441 
00442     def keys(self):
00443         "keys"
00444         kl = self._values.values()
00445         kl.sort(self.index_sort)
00446         return [k[1] for k in kl]
00447 
00448     def values(self):
00449         "values"
00450         vl = self._keys.values()
00451         vl.sort(self.index_sort)
00452         return [v[1] for v in vl]
00453 
00454     def items(self):
00455         """items"""
00456         keys = self.keys()
00457         return tuple([(key, self.getValue(key)) for key in keys])
00458 
00459     def sortedByValue(self):
00460         """return a new display list sorted by value"""
00461         def _cmp(a, b):
00462             return cmp(a[1], b[1])
00463         values = list(self.items())
00464         values.sort(_cmp)
00465         return DisplayList(values)
00466 
00467     def sortedByKey(self):
00468         """return a new display list sorted by key"""
00469         def _cmp(a, b):
00470             return cmp(a[0], b[0])
00471         values = list(self.items())
00472         values.sort(_cmp)
00473         return DisplayList(values)
00474 
00475     def __cmp__(self, dest):
00476         if not isinstance(dest, DisplayList):
00477             raise TypeError, 'Cant compare DisplayList to %s' % (type(dest))
00478 
00479         return cmp(self.sortedByKey()[:], dest.sortedByKey()[:])
00480 
00481     def __getitem__(self, key):
00482         #Ok, this is going to pass a number
00483         #which is index but not easy to get at
00484         #with the data-struct, fix when we get real
00485         #itor/generators
00486         return self._itor[key]
00487 
00488     def __getslice__(self,i1,i2):
00489         r=[]
00490         for i in xrange(i1,i2):
00491             try: r.append((self._itor[i], self.getValue(self._itor[i]),))
00492             except IndexError: return r
00493         return DisplayList(r)
00494 
00495     slice=__getslice__
00496 
00497 InitializeClass(DisplayList)
00498 
00499 class IntDisplayList(DisplayList):
00500     """Static display lists for integer keys, can look up on
00501     either side of the dict, and get them in sorted order
00502 
00503     The IntDisplayList can be used with integer values only. You should use it
00504     in favor of a DisplayList if you want to use ints as keys. The support for
00505     ints as keys for the ordinary DisplayList will be dropped in the next
00506     release.
00507 
00508     NOTE: Both keys and values *must* contain unique entries! You can have
00509     two times the same value. This is a "feature" not a bug. DisplayLists
00510     are meant to be used as a list inside html form entry like a drop down.
00511 
00512     >>> idl = IntDisplayList()
00513 
00514     Add some keys
00515     >>> idl.add(1, 'number one')
00516     >>> idl.add(2, 'just the second')
00517 
00518     Assert some values
00519     >>> idl.index
00520     2
00521     >>> idl.keys()
00522     [1, 2]
00523     >>> idl.values()
00524     ['number one', 'just the second']
00525     >>> idl.items()
00526     ((1, 'number one'), (2, 'just the second'))
00527 
00528     You can use only ints as keys
00529     >>> idl.add(object(), 'error')
00530     Traceback (most recent call last):
00531     TypeError: DisplayList keys must be ints, got <type 'object'>
00532 
00533     >>> idl.add(42, object())
00534     Traceback (most recent call last):
00535     TypeError: DisplayList values must be strings or ints, got <type 'object'>
00536 
00537     >>> idl.add('stringkey', 'error')
00538     Traceback (most recent call last):
00539     TypeError: DisplayList keys must be ints, got <type 'str'>
00540 
00541     >>> idl.add(u'unicodekey', 'error')
00542     Traceback (most recent call last):
00543     TypeError: DisplayList keys must be ints, got <type 'unicode'>
00544 
00545     GOTCHA
00546     Adding a value a second time does overwrite the key, too!
00547     >>> idl.add(3 , 'just the second')
00548     >>> idl.keys()
00549     [1, 3]
00550     >>> idl.items()
00551     ((1, 'number one'), (3, 'just the second'))
00552 
00553     It is possible to get the value also by a stringified int
00554     >>> idl.getValue("1")
00555     'number one'
00556     >>> idl.getValue(u"1")
00557     'number one'
00558     """
00559 
00560     security = ClassSecurityInfo()
00561     security.setDefaultAccess('allow')
00562 
00563     def add(self, key, value, msgid=None):
00564         if not isinstance(key, int):
00565             raise TypeError('DisplayList keys must be ints, got %s' %
00566                             type(key))
00567         if not isinstance(value, basestring) and not isinstance(value, int):
00568             raise TypeError('DisplayList values must be strings or ints, got %s' %
00569                             type(value))
00570         if msgid is not None:
00571             deprecated('Using explicit msgids for IntDisplayLists is deprecated. '
00572                         'Store Zope3 Messages as values directly.')
00573             if not isinstance(msgid, basestring):
00574                 raise TypeError('DisplayList msg ids must be strings, got %s' %
00575                                 type(msgid))
00576         self.index +=1
00577         k = (self.index, key)
00578         v = (self.index, value)
00579 
00580         self._keys[key] = v
00581         self._values[value] = k
00582         self._itor.append(key)
00583         if msgid is not None:
00584             self._i18n_msgids[key] = msgid
00585 
00586     def getValue(self, key, default=None):
00587         """get value"""
00588         if isinstance(key, basestring):
00589             key = int(key)
00590         elif isinstance(key, int):
00591             pass
00592         else:
00593             raise TypeError("Key must be string or int")
00594         v = self._keys.get(key, None)
00595         if v: return v[1]
00596         for k, v in self._keys.items():
00597             if repr(key) == repr(k):
00598                 return v[1]
00599         return default
00600 
00601     def getMsgId(self, key):
00602         "get i18n msgid"
00603         deprecated('IntDisplayList getMsgId is deprecated. Store Zope3 Messages'
00604                    ' as values instead.')
00605         if isinstance(key, basestring):
00606             key = int(key)
00607         elif isinstance(key, int):
00608             pass
00609         else:
00610             raise TypeError("Key must be string or int")
00611         if self._i18n_msgids.has_key(key):
00612             return self._i18n_msgids[key]
00613         else:
00614             return self._keys[key][1]
00615 
00616 class Vocabulary(DisplayList):
00617     """
00618     Wrap DisplayList class and add internationalisation
00619     """
00620 
00621     security = ClassSecurityInfo()
00622     security.setDefaultAccess('allow')
00623 
00624     def __init__(self, display_list, instance, i18n_domain):
00625         self._keys = display_list._keys
00626         self._i18n_msgids = display_list._i18n_msgids
00627         self._values = display_list._values
00628         self._itor   = display_list._itor
00629         self.index = display_list.index
00630         self._instance = instance
00631         self._i18n_domain = i18n_domain
00632 
00633     def getValue(self, key, default=None):
00634         """
00635         Get i18n value
00636         """
00637         if isinstance(key, int):
00638             deprecated('Using ints as DisplayList keys is deprecated (getValue)')
00639         if not isinstance(key, basestring) and not isinstance(key, int):
00640             raise TypeError('DisplayList keys must be strings or ints, got %s' %
00641                             type(key))
00642         v = self._keys.get(key, None)
00643         value = default
00644         if v:
00645             value = v[1]
00646         else:
00647             for k, v in self._keys.items():
00648                 if repr(key) == repr(k):
00649                     value = v[1]
00650                     break
00651 
00652         if self._i18n_domain and self._instance:
00653             msg = self._i18n_msgids.get(key, None) or value
00654 
00655             if isinstance(msg, Message):
00656                 return msg
00657 
00658             if not msg:
00659                 return ''
00660 
00661             return getGTS().translate(self._i18n_domain, msg,
00662                                       context=self._instance, default=value)
00663         else:
00664             return value
00665 
00666 InitializeClass(Vocabulary)
00667 
00668 class OrderedDict(BaseDict):
00669     """A wrapper around dictionary objects that provides an ordering for
00670        keys() and items()."""
00671 
00672     security = ClassSecurityInfo()
00673     security.setDefaultAccess('allow')
00674 
00675     def __init__(self, dict=None): 
00676         self._keys = []     
00677         BaseDict.__init__(self, dict)     
00678         if dict is not None:       
00679             self._keys = self.data.keys()
00680 
00681     def __setitem__(self, key, item):
00682         if not self.data.has_key(key):
00683             self._keys.append(key)
00684         return BaseDict.__setitem__(self, key, item)
00685 
00686     def __delitem__(self, key):
00687         BaseDict.__delitem__(self, key)
00688         self._keys.remove(key)
00689 
00690     def clear(self):
00691         BaseDict.clear(self)
00692         self._keys = []
00693 
00694     def keys(self):
00695         return self._keys
00696 
00697     def items(self):
00698         return [(k, self.get(k)) for k in self._keys]
00699 
00700     def reverse(self):
00701         items = list(self.items())
00702         items.reverse()
00703         return items
00704 
00705     def values(self):
00706         return [self.get(k) for k in self._keys]
00707 
00708     def update(self, dict):
00709         for k in dict.keys():
00710             if not self.data.has_key(k):
00711                 self._keys.append(k)
00712         return BaseDict.update(self, dict)
00713 
00714     def copy(self):
00715         if self.__class__ is OrderedDict:
00716             c = OrderedDict()
00717             for k, v in self.items():
00718                 c[k] = v
00719             return c
00720         import copy
00721         c = copy.copy(self)
00722         return c
00723 
00724     def setdefault(self, key, failobj=None):
00725         if not self.data.has_key(key):
00726             self._keys.append(key)
00727         return BaseDict.setdefault(self, key, failobj)
00728 
00729     def popitem(self):
00730         if not self.data:
00731             raise KeyError, 'dictionary is empty'
00732         k = self._keys.pop()
00733         v = self.data.get(k)
00734         del self.data[k]
00735         return (k, v)
00736 
00737     def pop(self, key):
00738         v = self.data.pop(key) # will raise KeyError if needed
00739         self._keys.remove(key)
00740         return v
00741 
00742 InitializeClass(OrderedDict)
00743 
00744 
00745 def getRelPath(self, ppath):
00746     """take something with context (self) and a physical path as a
00747     tuple, return the relative path for the portal"""
00748     urlTool = getToolByName(self, 'portal_url')
00749     portal_path = urlTool.getPortalObject().getPhysicalPath()
00750     ppath = ppath[len(portal_path):]
00751     return ppath
00752 
00753 def getRelURL(self, ppath):
00754     return '/'.join(getRelPath(self, ppath))
00755 
00756 def getPkgInfo(product):
00757     """Get the __pkginfo__ from a product
00758 
00759     chdir before importing the product
00760     """
00761     prd_home = product.__path__[0]
00762     cur_dir = os.path.abspath(os.curdir)
00763     os.chdir(prd_home)
00764     pkg = __import__('%s.__pkginfo__' % product.__name__, product, product,
00765                       ['__pkginfo__'])
00766     os.chdir(cur_dir)
00767     return pkg
00768 
00769 def shasattr(obj, attr, acquire=False):
00770     """Safe has attribute method
00771 
00772     * It's acquisition safe by default because it's removing the acquisition
00773       wrapper before trying to test for the attribute.
00774 
00775     * It's not using hasattr which might swallow a ZODB ConflictError (actually
00776       the implementation of hasattr is swallowing all exceptions). Instead of
00777       using hasattr it's comparing the output of getattr with a special marker
00778       object.
00779 
00780     TODO the getattr() trick can be removed when Python's hasattr() is fixed to
00781     catch only AttributeErrors.
00782 
00783     Quoting Shane Hathaway:
00784 
00785     That said, I was surprised to discover that Python 2.3 implements hasattr
00786     this way (from bltinmodule.c):
00787 
00788             v = PyObject_GetAttr(v, name);
00789             if (v == NULL) {
00790                     PyErr_Clear();
00791                     Py_INCREF(Py_False);
00792                     return Py_False;
00793             }
00794         Py_DECREF(v);
00795         Py_INCREF(Py_True);
00796         return Py_True;
00797 
00798     It should not swallow all errors, especially now that descriptors make
00799     computed attributes quite common.  getattr() only recently started catching
00800     only AttributeErrors, but apparently hasattr is lagging behind.  I suggest
00801     the consistency between getattr and hasattr should be fixed in Python, not
00802     Zope.
00803 
00804     Shane
00805     """
00806     if not acquire:
00807         obj = aq_base(obj)
00808     return getattr(obj, attr, _marker) is not _marker
00809 
00810 
00811 WRAPPER = '__at_is_wrapper_method__'
00812 ORIG_NAME = '__at_original_method_name__'
00813 def isWrapperMethod(meth):
00814     return getattr(meth, WRAPPER, False)
00815 
00816 def call_original(self, __name__, __pattern__, *args, **kw):
00817     return getattr(self, __pattern__ % __name__)(*args, **kw)
00818 
00819 def wrap_method(klass, name, method, pattern='__at_wrapped_%s__'):
00820     old_method = getattr(klass, name)
00821     if isWrapperMethod(old_method):
00822         log('Already wrapped method %s.%s. Skipping.' % (klass.__name__, name))
00823         return
00824     new_name = pattern % name
00825     setattr(klass, new_name, old_method)
00826     setattr(method, ORIG_NAME, new_name)
00827     setattr(method, WRAPPER, True)
00828     setattr(klass, name, method)
00829 
00830 def unwrap_method(klass, name):
00831     old_method = getattr(klass, name)
00832     if not isWrapperMethod(old_method):
00833         raise ValueError, ('Non-wrapped method %s.%s' % (klass.__name__, name))
00834     orig_name = getattr(old_method, ORIG_NAME)
00835     new_method = getattr(klass, orig_name)
00836     delattr(klass, orig_name)
00837     setattr(klass, name, new_method)
00838 
00839 
00840 def _get_position_after(label, options):
00841     position = 0
00842     for item in options:
00843         if item['label'] != label:
00844             continue
00845         position += 1
00846     return position
00847 
00848 def insert_zmi_tab_before(label, new_option, options):
00849     _options = list(options)
00850     position = _get_position_after(label, options)
00851     _options.insert(position-1, new_option)
00852     return tuple(_options)
00853 
00854 def insert_zmi_tab_after(label, new_option, options):
00855     _options = list(options)
00856     position = _get_position_after(label, options)
00857     _options.insert(position, new_option)
00858     return tuple(_options)
00859 
00860 def _getSecurity(klass, create=True):
00861     # a Zope 2 class can contain some attribute that is an instance
00862     # of ClassSecurityInfo. Zope 2 scans through things looking for
00863     # an attribute that has the name __security_info__ first
00864     info = vars(klass)
00865     security = None
00866     for k, v in info.items():
00867         if hasattr(v, '__security_info__'):
00868             security = v
00869             break
00870     # Didn't found a ClassSecurityInfo object
00871     if security is None:
00872         if not create:
00873             return None
00874         # we stuff the name ourselves as __security__, not security, as this
00875         # could theoretically lead to name clashes, and doesn't matter for
00876         # zope 2 anyway.
00877         security = ClassSecurityInfo()
00878         setattr(klass, '__security__', security)
00879         if DEBUG_SECURITY:
00880             print '%s has no ClassSecurityObject' % klass.__name__
00881     return security
00882 
00883 def mergeSecurity(klass):
00884     # This method looks into all the base classes and tries to
00885     # merge the security declarations into the current class.
00886     # Not needed in normal circumstances, but useful for debugging.
00887     bases = list(getmro(klass))
00888     bases.reverse()
00889     security = _getSecurity(klass)
00890     for base in bases[:-1]:
00891         s = _getSecurity(base, create=False)
00892         if s is not None:
00893             if DEBUG_SECURITY:
00894                 print base, s.names, s.roles
00895             # Apply security from the base classes to this one
00896             s.apply(klass)
00897             continue
00898         cdict = vars(base)
00899         b_perms = cdict.get('__ac_permissions__', ())
00900         if b_perms and DEBUG_SECURITY:
00901             print base, b_perms
00902         for item in b_perms:
00903             permission_name = item[0]
00904             security._setaccess(item[1], permission_name)
00905             if len(item) > 2:
00906                 security.setPermissionDefault(permission_name, item[2])
00907         roles = [(k, v) for k, v in cdict.items() if k.endswith('__roles__')]
00908         for k, v in roles:
00909             name = k[:-9]
00910             security.names[name] = v
00911 
00912 def setSecurity(klass, defaultAccess=None, objectPermission=None):
00913     """Set security of classes
00914 
00915     * Adds ClassSecurityInfo if necessary
00916     * Sets default access ('deny' or 'allow')
00917     * Sets permission of objects
00918     """
00919     security = _getSecurity(klass)
00920     if defaultAccess:
00921         security.setDefaultAccess(defaultAccess)
00922     if objectPermission:
00923         if objectPermission == 'public':
00924             security.declareObjectPublic()
00925         elif objectPermission == 'private':
00926             security.declareObjectPrivate()
00927         else:
00928             security.declareObjectProtected(objectPermission)
00929 
00930     InitializeClass(klass)
00931 
00932     if DEBUG_SECURITY:
00933         if getattr(klass, '__allow_access_to_unprotected_subobjects__', False):
00934             print '%s: Unprotected access is allowed: %s' % (
00935                   klass.__name__, klass.__allow_access_to_unprotected_subobjects__)
00936         for name in klass.__dict__.keys():
00937             method = getattr(klass, name)
00938             if name.startswith('_') or type(method) != MethodType:
00939                 continue
00940             if not security.names.has_key(name):
00941                 print '%s.%s has no security' % (klass.__name__, name)
00942             elif security.names.get(name) is ACCESS_PUBLIC:
00943                 print '%s.%s is public' % (klass.__name__, name)
00944 
00945 def contentDispositionHeader(disposition, charset='utf-8', language=None, **kw):
00946     """Return a properly quoted disposition header
00947 
00948     Originally from CMFManagedFile/content.py.
00949     charset default changed to utf-8 for consistency with the rest of Archetypes.
00950     """
00951 
00952     from email.Message import Message as emailMessage
00953 
00954     for key, value in kw.items():
00955         # stringify the value
00956         if isinstance(value, unicode):
00957             value = value.encode(charset)
00958         else:
00959             value = str(value)
00960             # raise an error if the charset doesn't match
00961             unicode(value, charset, 'strict')
00962         # if any value contains 8-bit chars, make it an
00963         # encoding 3-tuple for special treatment by
00964         # Message.add_header() (actually _formatparam())
00965         try:
00966             unicode(value, 'us-ascii', 'strict')
00967         except UnicodeDecodeError:
00968             value = (charset, language, value)
00969 
00970     m = emailMessage()
00971     m.add_header('content-disposition', disposition, **kw)
00972     return m['content-disposition']
00973 
00974 def addStatusMessage(request, message, type='info'):
00975     """Add a status message to the request.
00976     """
00977     IStatusMessage(request).addStatusMessage(message, type=type)