Source code for gavo.formal.util

import re
from zope.interface import implementer
from twisted.web import template
from . import iformal


_IDENTIFIER_REGEX = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')


[docs]def titleFromName(name): def _(): it = iter(name) last = None while 1: try: ch = next(it) except StopIteration: return if ch == '_': if last != '_': yield ' ' elif last in (None,'_'): yield ch.upper() elif ch.isupper() and not last.isupper(): yield ' ' yield ch.upper() else: yield ch last = ch return ''.join(_())
[docs]def keytocssid(fieldKey, *extras): return render_cssid(fieldKey, *extras)
[docs]def render_cssid(fieldKey, *extras): """ Render the CSS id for the form field's key. """ l = [template.slot('formName'), '-', '-'.join(fieldKey.split('.'))] for extra in extras: l.append('-') l.append(str(extra)) return l
[docs]def validIdentifier(name): """ Test that name is a valid Python identifier. """ return _IDENTIFIER_REGEX.match(name) is not None
[docs]@implementer( iformal.IKey, iformal.ILabel ) class SequenceKeyLabelAdapter(object): def __init__(self, original): self.original = original
[docs] def key(self): return str(self.original[0])
[docs] def label(self): return str(self.original[1])
[docs]@implementer(iformal.ILabel) class StringableLabelAdapter(object): def __init__(self, original): self.original = str(original)
[docs] def label(self): return self.original
# This is a duplication of gavo.utils.misctricks; I don't want either # of the two depend on each other.
[docs]class CaseSemisensitiveDict(dict): """A dictionary allowing case-insensitive access to its content. This is used for DAL renderers which, unfortunately, are supposed to be case insensitive. Since case insensitivity is at least undesirable for service-specific keys, we go a semi-insenstitve approach here: First, we try literal matches, if that does not work, we try matching against an all-uppercase version. Name clashes resulting from different names being mapped to the same normalized version are handled in some random way. Don't do this. And don't rely on case normalization if at all possible. Only strings are allowed as keys here. This class is not concerned with the values. >>> d = CaseSemisensitiveDict({"a": 1, "A": 2, "b": 3}) >>> d["a"], d["A"], d["b"], d["B"] (1, 2, 3, 3) >>> d["B"] = 9; d["b"], d["B"] (3, 9) >>> del d["b"]; d["b"], d["B"] (9, 9) >>> "B" in d, "b" in d, "u" in d (True, True, False) >>> d.pop("a"), list(d.keys()) (1, ['A', 'B']) """ def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) self._normCasedCache = None def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: pass # try again with normalized case. return self._normCased[key.upper()] def __setitem__(self, key, value): self._normCasedCache = None dict.__setitem__(self, key, value) def __contains__(self, key): return dict.__contains__(self, key) or key.upper() in self._normCased
[docs] def get(self, key, default=None): try: return self[key] except KeyError: return default
[docs] def pop(self, key, default=KeyError): try: return dict.pop(self, key) except KeyError: pass # try again with normalized case. try: return self._normCased.pop(key.upper()) except KeyError: if default is not KeyError: return default raise
@property def _normCased(self): if self._normCasedCache is None: self._normCasedCache = dict((k.upper(), v) for k, v in self.items()) return self._normCasedCache
[docs] @classmethod def fromDict(cls, aDict): if isinstance(aDict, CaseSemisensitiveDict): return aDict else: return cls(aDict)