PK 5Ft/__init__.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/__init__.py,v 1.49 2006-08-13 22:07:02 uogbuji Exp $ """ 4Suite: an open-source platform for XML and RDF processing. Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ __all__ = ['DEFAULT_ENCODING', 'MAX_PYTHON_RECURSION_DEPTH', '__version__', 'FtException', 'FtWarning', 'GetConfigVars', 'GetConfigVar', 'TranslateMessage'] # True/False did not appear until Python 2.2.1 import sys if sys.version < '2.2.1': raise ImportError('4Suite requires Python 2.2.1 or newer.') # PYTHONCASEOK is an environment variable that causes imports to # treat module names case-insensitively on certain file systems, # which is dangerous in 4Suite since we may have modules with the # same names (when compared case-insensitively) as stdlib modules. # We can't be sure which file system type is in use, so we can't # restrict this check to certain platforms. See PEP 235. import os if 'PYTHONCASEOK' in os.environ: raise ImportError('4Suite requires case-sensitive imports;' ' unset PYTHONCASEOK environment variable.') # Frozen executables do not properly initialize the codecs module as their # import hooks are not installed when it is loaded. See Python/pythonrun.c # in a Python source distribution for the relevant code in Py_Initialize(). if getattr(sys, 'frozen', False): import encodings # Get the encoding that the user is likely using. import locale try: # getpreferredencoding() new in Python 2.3+ encoding = locale.getpreferredencoding() except locale.Error: # Unable to set locale; use the current settings. encoding = locale.getpreferredencoding(False) except AttributeError: if sys.platform in ('win32', 'darwin', 'mac'): # On Windows, this will return the ANSI code page; # On Mac, this should return the system encoding. encoding = locale.getdefaultlocale()[1] elif hasattr(locale, 'CODESET'): # CODESET requires that nl_langinfo() also exists. encoding = locale.nl_langinfo(locale.CODESET) else: # fall back to parsing environment variables encoding = locale.getdefaultlocale()[1] DEFAULT_ENCODING = encoding or 'US-ASCII' #10,000 is the value from Python 1.5.2 MAX_PYTHON_RECURSION_DEPTH = 10000 class FtException(Exception): """ Base class for all 4Suite-specific exceptions """ #FIXME: make all exception classes use *args instead of argtuple def __init__(self, errorCode, messages, argtuple=(), **kwargs): """ errorCode = Numeric ID for the type of error. messages = Mapping of errorCodes to localized error message strings. argtuple or keyword args = Values for message string formatting. """ assert not (argtuple and kwargs) # we can use args or kwargs, not both self.message = messages[errorCode] % (kwargs or argtuple) self.errorCode = errorCode # Exception.__init__() will set self.args to the args passed to it Exception.__init__(self, self.message, (kwargs or argtuple)) def __getattr__(self, name): if name == 'params': return self.args[1] raise AttributeError(name) def __str__(self): return self.message def __repr__(self): return '%s: %s' % (self.__class__.__name__, self.message) class FtWarning(Warning): """ Base class for all 4Suite-specific warnings. """ pass # Install our warnings display hook for 4Suite warnings import warnings __stdlib__showwarning = warnings.showwarning def __ft__showwarning(message, category, filename, lineno, file=None): """ warnings.showwarning() replacement that word-wraps the message if file is a terminal, and doesn't add filename, line, stack info to FtWarnings. """ if issubclass(category, FtWarning): from Ft.Lib import Wrap, Terminal if file is None: file = sys.stderr terminal = Terminal.Terminal(file) message = "%s: %s\n" % (category.__name__, message) if terminal.isatty(): message = Wrap(message, terminal.columns()) terminal.writetty(message) terminal.flush() else: __stdlib__showwarning(message, category, filename, lineno, file) return warnings.showwarning = __ft__showwarning # Load the installation information module, only available from installed # 4Suite or during setup via dummy module. try: import __config__ except ImportError: from distutils.fancy_getopt import wrap_text msg = """ 4Suite is having trouble importing the modules it needs. This is usually because the current working directory, which happens to be %r at the moment, contains modules with names that are the same as modules that 4Suite is trying to import. For example, 4Suite cannot be invoked from the source code directory that contains the setup.py that was used to install 4Suite. Try changing the current working directory to a suitable location outside of the 4Suite source. If you continue to have trouble, please send a message to the 4Suite mailing list at 4suite@lists.fourthought.com, along with any information that might explain why you got this message. """ % os.getcwd() # Wrap the message to 78 characters preserving paragraphs lines = [] for chunk in msg.split('\n\n'): lines.extend(wrap_text(chunk, 78)) lines.append('') raise SystemExit('\n'.join(lines)) def GetConfigVars(*names): """ With no arguments, return a dictionary of all configuration variables relevant for the current installation. With arguments, return a list of values that result from looking up each argument in the configuration variable dictionary. The following are the currently defined variables and their meaning: NAME, FULLNAME, VERSION, URL - fields as given for call to setup() BINDIR - directory for user executables DATADIR - directory for read-only platform-independent data LIBDIR - directory for extra libraries LOCALEDIR - directory for message catalogs LOCALSTATEDIR - directory for modifiable host-specific data SYSCONFIDIR - directory for read-only host-specific data """ if names: vals = [] for name in names: vals.append(getattr(__config__, name, None)) return vals else: return vars(__config__) def GetConfigVar(name): """ Return the value of a single variable using the dictionary returned by 'get_config_vars()'. Equivalent to GetConfigVars().get(name) """ return getattr(__config__, name, None) __version__ = __config__.VERSION from Ft.Lib import Gettext if getattr(__config__, 'RESOURCEBUNDLE', False): bundle = __name__ else: bundle = None translation = Gettext.GetTranslation(__config__.NAME, __config__.LOCALEDIR, fallback=True, bundle=bundle) TranslateMessage = translation.gettext TranslateMessagePlural = translation.ngettext # This *MUST* be the last thing done so that the module namespace reflects # all of the above when using Python Eggs. try: import pkg_resources except ImportError: pass else: try: pkg_resources.get_distribution(__config__.NAME) except pkg_resources.DistributionNotFound: pass else: pkg_resources.declare_namespace(__name__) sys.modules[__name__].__dict__.update(globals()) PKYvP3G(Ft/Lib/DbUtil.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/DbUtil.py,v 1.5 2005-10-16 20:50:51 jkloth Exp $ """ Utilities for database connections Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ __all__ = ['EscapeQuotes'] try: from EscapeQuotesc import escape as EscapeQuotes except ImportError: def EscapeQuotes(qstr): """ Postgres uses single quotes for string marker, so put a backslash before single quotes for insertion into a database. Also escape backslashes. pre: qstr = string to be escaped post: return the string with all single quotes escaped """ if qstr is None: return u'' tmp = qstr.replace("\\","\\\\") tmp = tmp.replace("'", "\\'") return unicode(tmp) PK 5Y11Ft/Lib/Gettext.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Gettext.py,v 1.2 2006-08-13 22:44:33 jkloth Exp $ """ Internationalization and localization support. Copyright 2006 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import os import sys import gettext from Ft.Lib import ImportUtil __all__ = ['GetTranslation'] if sys.version < '2.3': def test(condition, true, false): """ Implements the C expression: condition ? true : false Required to correctly interpret plural forms. """ if condition: return true else: return false def c2py(plural): """Gets a C expression as used in PO files for plural forms and returns a Python lambda function that implements an equivalent expression. """ # Security check, allow only the "n" identifier from cStringIO import StringIO import token, tokenize tokens = tokenize.generate_tokens(StringIO(plural).readline) try: danger = [x for x in tokens if x[0] == token.NAME and x[1] != 'n'] except tokenize.TokenError: raise ValueError( 'plural forms expression error, maybe unbalanced parenthesis') else: if danger: raise ValueError('plural forms expression could be dangerous') # Replace some C operators by their Python equivalents plural = plural.replace('&&', ' and ') plural = plural.replace('||', ' or ') expr = re.compile(r'\!([^=])') plural = expr.sub(' not \\1', plural) # Regular expression and replacement function used to transform # "a?b:c" to "test(a,b,c)". expr = re.compile(r'(.*?)\?(.*?):(.*)') def repl(x): return "test(%s, %s, %s)" % (x.group(1), x.group(2), expr.sub(repl, x.group(3))) # Code to transform the plural expression, taking care of parentheses stack = [''] for c in plural: if c == '(': stack.append('') elif c == ')': if len(stack) == 1: # Actually, we never reach this code, because unbalanced # parentheses get caught in the security check at the # beginning. raise ValueError, 'unbalanced parenthesis in plural form' s = expr.sub(repl, stack.pop()) stack[-1] += '(%s)' % s else: stack[-1] += c plural = expr.sub(repl, stack.pop()) return eval('lambda n: int(%s)' % plural) if sys.version >= '2.4': NullTranslations = gettext.NullTranslations GNUTranslations = gettext.GNUTranslations else: import locale try: getpreferredencoding = locale.getpreferredencoding except AttributeError: import sys if sys.platform in ('win32', 'darwin', 'mac'): def getpreferredencoding(do_setlocale=True): import _locale return _locale._getdefaultlocale()[1] elif hasattr(locale, 'CODESET'): # Return the charset that the user is likely using, according # to the system configuration. def getpreferredencoding(do_setlocale=True): if do_setlocale: oldloc = locale.setlocale(locale.LC_CTYPE) locale.setlocale(locale.LC_CTYPE, "") result = locale.nl_langinfo(locale.CODESET) locale.setlocale(locale.LC_CTYPE, oldloc) return result else: return locale.nl_langinfo(locale.CODESET) else: # Fall back to parsing environment variables def getpreferredencoding(do_setlocale=True): return locale.getdefaultlocale()[1] class NullTranslations(gettext.NullTranslations): _output_charset = None if sys.version < '2.3': _fallback = None def add_fallback(self, fallback): if self._fallback: self._fallback.add_fallback(fallback) else: self._fallback = fallback def gettext(self, message): if self._fallback: return self._fallback.gettext(message) return message def ngettext(self, msgid1, msgid2, n): if self._fallback: return self._fallback.ngettext(msgid1, msgid2, n) if n == 1: return msgid1 else: return msgid2 def ugettext(self, message): if self._fallback: return self._fallback.ugettext(message) return unicode(message) def ungettext(self, msgid1, msgid2, n): if self._fallback: return self._fallback.ungettext(msgid1, msgid2, n) if n == 1: return unicode(msgid1) else: return unicode(msgid2) def lgettext(self, message): if self._fallback: return self._fallback.lgettext(message) return message def lngettext(self, msgid1, msgid2, n): if self._fallback: return self._fallback.lngettext(msgid1, msgid2, n) if n == 1: return msgid1 else: return msgid2 def output_charset(self): return self._output_charset def set_output_charset(self, charset): self._output_charset = charset class GNUTranslations(gettext.GNUTranslations, NullTranslations): _output_charset = None if sys.version < '2.3': def _parse(self, fp): gettext.GNUTranslations._parse(self, fp) # Get the plural selector function if 'plural-forms' in self._info: v = self._info['plural-forms'].split(';') plural = v[1].split('plural=')[1] self.plural = c2py(plural) else: # germanic plural by default self.plural = lambda n: int(n != 1) # Unconditionally convert both msgids and msgstrs to # Unicode using the character encoding specified in the # charset parameter of the Content-Type header. messages = self._catalog.iteritems() self._catalog = catalog = {} for msg, tmsg in messages: if '\x00' in msg: # Plural forms msgid1, msgid2 = msg.split('\x00') tmsg = tmsg.split('\x00') if self._charset: msgid1 = unicode(msgid1, self._charset) tmsg = [unicode(x, self._charset) for x in tmsg] for i in range(len(tmsg)): catalog[(msgid1, i)] = tmsg[i] else: if self._charset: msg = unicode(msg, self._charset) tmsg = unicode(tmsg, self._charset) catalog[msg] = tmsg return def gettext(self, message): missing = object() tmsg = self._catalog.get(message, missing) if tmsg is missing: if self._fallback: return self._fallback.gettext(message) return message # Encode the Unicode tmsg back to an 8-bit string, if possible if self._output_charset: return tmsg.encode(self._output_charset) elif self._charset: return tmsg.encode(self._charset) return tmsg def lgettext(self, message): missing = object() tmsg = self._catalog.get(message, missing) if tmsg is missing: if self._fallback: return self._fallback.lgettext(message) return message if self._output_charset: return tmsg.encode(self._output_charset) return tmsg.encode(locale.getpreferredencoding()) def ngettext(self, msgid1, msgid2, n): try: tmsg = self._catalog[(msgid1, self.plural(n))] if self._output_charset: return tmsg.encode(self._output_charset) elif self._charset: return tmsg.encode(self._charset) return tmsg except KeyError: if self._fallback: return self._fallback.ngettext(msgid1, msgid2, n) if n == 1: return msgid1 else: return msgid2 def lngettext(self, msgid1, msgid2, n): try: tmsg = self._catalog[(msgid1, self.plural(n))] if self._output_charset: return tmsg.encode(self._output_charset) return tmsg.encode(locale.getpreferredencoding()) except KeyError: if self._fallback: return self._fallback.lngettext(msgid1, msgid2, n) if n == 1: return msgid1 else: return msgid2 def ugettext(self, message): missing = object() tmsg = self._catalog.get(message, missing) if tmsg is missing: if self._fallback: return self._fallback.ugettext(message) return unicode(message) return tmsg def ungettext(self, msgid1, msgid2, n): try: tmsg = self._catalog[(msgid1, self.plural(n))] except KeyError: if self._fallback: return self._fallback.ungettext(msgid1, msgid2, n) if n == 1: tmsg = unicode(msgid1) else: tmsg = unicode(msgid2) return tmsg # Locate a .mo file using the gettext strategy def FindCatalogs(domain, localedir=None, languages=None): # Get some reasonable defaults for arguments that were not supplied if localedir is None: localedir = gettext._default_localedir if languages is None: languages = [] for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): val = os.environ.get(envar) if val: languages = val.split(':') break if 'C' not in languages: languages.append('C') # now normalize and expand the languages nelangs = [] for lang in languages: for nelang in gettext._expand_lang(lang): if nelang not in nelangs: nelangs.append(nelang) # select a language result = [] for lang in nelangs: if lang == 'C': break mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain) result.append(mofile) return result def GetTranslation(domain, localedir=None, languages=None, class_=None, fallback=False, codeset=None, bundle=None): if class_ is None: class_ = GNUTranslations result = None for mofile in FindCatalogs(domain, localedir, languages): try: if bundle: resource = ImportUtil.OsPathToResource(mofile) stream = ImportUtil.GetResourceStream(bundle, resource) else: stream = open(mofile, 'rb') except IOError: continue t = class_(stream) if codeset: t.set_output_charset(codeset) if result is None: result = t else: result.add_fallback(t) if result is None: if fallback: return NullTranslations() from errno import ENOENT raise IOError(ENOENT, 'No translation file found for domain', domain) return result PKlM 5x\\Ft/Lib/ImportUtil.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/ImportUtil.py,v 1.1 2006-08-11 15:43:24 jkloth Exp $ """ Utilites for working with Python PEP 302 import hooks. Copyright 2006 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ from __future__ import generators import os import sys import imp import time import types import cStringIO try: from zipimport import zipimporter except ImportError: # placeholder for Python 2.2 to simplify code paths class zipimporter(object): pass __all__ = [ # Module Utilities 'FindLoader', 'FindImporter', 'GetImporter', 'IterModules', 'GetLastModified', 'GetSearchPath', # Resource Utilities 'OsPathToResource', 'NormalizeResource', 'GetResourceFilename', 'GetResourceString', 'GetResourceStream', 'GetResourceLastModified', ] # Indicate that the use of "special" names is handled in a "zip-safe" way. __zipsafe__ = True IMP_SEARCH_ORDER = [ desc[0] for desc in imp.get_suffixes() ] # ZIP imports always search for .pyc AND .pyo, but reverse their order # depending on the optimzation flag (-O). ZIP_SEARCH_ORDER = [ '.py', '.pyc', '.pyo'] if not __debug__: ZIP_SEARCH_ORDER.remove('.pyc') ZIP_SEARCH_ORDER.append('.pyc') try: # New in Python 2.5 (or setuptools 0.7+) from pkgutil import ImpImporter, ImpLoader, iter_importers, get_loader, \ find_loader, iter_modules, get_importer except ImportError: import marshal, inspect, re MODULE_TYPE_INFO = {} for suffix, mode, module_type in imp.get_suffixes(): MODULE_TYPE_INFO[module_type] = (suffix, mode) class ImpImporter: """PEP 302 Importer that wraps Python's "classic" import algorithm ImpImporter(dirname) produces a PEP 302 importer that searches that directory. ImpImporter(None) produces a PEP 302 importer that searches the current sys.path, plus any modules that are frozen or built-in. Note that ImpImporter does not currently support being used by placement on sys.meta_path. """ def __init__(self, path=None): if path is not None and not os.path.isdir(path): raise ImportError("not a directory") self.path = path def __repr__(self): return "" % self.path def find_module(self, fullname, path=None): # Note: we ignore 'path' argument since it is only used via # meta_path subname = fullname.split(".")[-1] if subname != fullname and self.path is None: return None if self.path is None: path = None else: path = [os.path.realpath(self.path)] try: file, filename, etc = imp.find_module(subname, path) except ImportError: return None return ImpLoader(fullname, file, filename, etc) def iter_modules(self, prefix=''): if self.path is None or not os.path.isdir(self.path): return yielded = {} filenames = os.listdir(self.path) filenames.sort() # handle packages before same-named modules for fn in filenames: modname = inspect.getmodulename(fn) if modname == '__init__' or modname in yielded: continue path = os.path.join(self.path, fn) ispkg = False if not modname and os.path.isdir(path) and '.' not in fn: modname = fn for fn in os.listdir(path): subname = inspect.getmodulename(fn) if subname == '__init__': ispkg = True break else: continue # not a package if modname and '.' not in modname: yielded[modname] = 1 yield prefix + modname, ispkg return class ImpLoader: """PEP 302 Loader that wraps Python's "classic" import algorithm """ code = None source = None def __init__(self, fullname, file, filename, etc): self.file = file self.filename = filename self.fullname = fullname self.etc = etc def load_module(self, fullname): if fullname in sys.modules: return sys.modules[fullname] self._reopen() try: mod = imp.load_module(fullname, self.file, self.filename, self.etc) finally: if self.file: self.file.close() # Note: we don't set __loader__ because we want the module to # look normal; i.e. this is just a wrapper for standard import # machinery return mod def get_data(self, pathname): f = open(pathname, 'rb') try: data = f.read() finally: f.close() return data def _reopen(self): if self.file and self.file.closed: suffix, mode, module_type = self.etc self.file = open(self.filename, mode) return def _fix_name(self, fullname): if fullname is None: fullname = self.fullname elif fullname != self.fullname: raise ImportError("Loader for module %s cannot handle " "module %s" % (self.fullname, fullname)) return fullname def _get_package_loader(self): return ImpImporter(self.filename).find_module('__init__') def is_package(self, fullname): fullname = self._fix_name(fullname) return self.etc[2] == imp.PKG_DIRECTORY def get_code(self, fullname=None): fullname = self._fix_name(fullname) if self.code is None: module_type = self.etc[2] if module_type == imp.PY_SOURCE: source = self.get_source(fullname) self.code = compile(source, self.filename, 'exec') elif module_type == imp.PY_COMPILED: self._reopen() try: magic = self.file.read(4) if magic == imp.get_magic(): timestamp = self.file.read(4) self.code = marshal.load(self.file) finally: self.file.close() elif module_type == imp.PKG_DIRECTORY: self.code = self._get_package_loader().get_code() return self.code def get_source(self, fullname=None): fullname = self._fix_name(fullname) if self.source is None: module_type = self.etc[2] if module_type == imp.PY_SOURCE: self._reopen() try: self.source = self.file.read() finally: self.file.close() elif module_type == imp.PY_COMPILED: suffix, mode = MODULE_TYPE_INFO[imp.PY_COMPILED] filename = os.path.splitext(self.filename)[0] + suffix if os.path.exists(filename): f = open(filename, mode) try: self.source = f.read() finally: f.close() elif module_type == imp.PKG_DIRECTORY: self.source = self._get_package_loader().get_source() return self.source def get_filename(self, fullname=None): fullname = self._fix_name(fullname) module_type = self.etc[2] if module_type == imp.PKG_DIRECTORY: return self._get_package_loader().get_filename() elif module_type in MODULE_TYPE_INFO: return self.filename return None def get_importer(path_item): """Retrieve a PEP 302 importer for the given path item """ if sys.version < '2.3': importer = None else: try: importer = sys.path_importer_cache[path_item] except KeyError: for path_hook in sys.path_hooks: try: importer = path_hook(path_item) break except ImportError: pass else: importer = None sys.path_importer_cache.setdefault(path_item, importer) # The boolean values are used for caching valid and invalid # file paths for the built-in import machinery if importer in (None, True, False): try: importer = ImpImporter(path_item) except ImportError: importer = None return importer def iter_importers(fullname=''): if '.' in fullname: # Get the containing package's __path__ pkg = '.'.join(fullname.split('.')[:-1]) if pkg not in sys.modules: __import__(pkg) path = sys.modules[pkg].__path__ else: # sys.meta_path is available in Python 2.3+ for importer in getattr(sys, 'meta_path', []): yield importer path = sys.path for item in path: yield get_importer(item) if '.' not in fullname: yield ImpImporter() def get_loader(module_or_name): """Get a PEP 302 "loader" object for module_or_name If the module or package is accessible via the normal import mechanism, a wrapper around the relevant part of that machinery is returned. Returns None if the module cannot be found or imported. If the named module is not already imported, its containing package (if any) is imported, in order to establish the package __path__. """ if module_or_name in sys.modules: module_or_name = sys.modules[module_or_name] if isinstance(module_or_name, types.ModuleType): module = module_or_name loader = getattr(module, '__loader__', None) if loader is not None: return loader fullname = module.__name__ else: fullname = module_or_name return find_loader(fullname) def find_loader(fullname): """Find a PEP 302 "loader" object for fullname If fullname contains dots, path must be the containing package's __path__. Returns None if the module cannot be found or imported. """ for importer in iter_importers(fullname): loader = importer.find_module(fullname) if loader is not None: return loader return None def iter_zipimport_modules(importer, prefix): # make the path components regex safe sep = os.sep.replace('\\', '\\\\') path = prefix.replace(os.sep, sep) # using "non-greedy" matching in case a suffix is not just an # extension (like module.so for dlopen imports) modname = '[a-zA-Z_][a-zA-Z0-9_]*?' pkginit = sep + '__init__' suffix = '|'.join([ desc[0] for desc in imp.get_suffixes() ]) suffix = suffix.replace('.', '\\.') pattern = '^%s(%s)(%s)?(%s)$' % (path, modname, pkginit, suffix) submodule_match = re.compile(pattern).match yielded = {} dirlist = list(importer._files) dirlist.sort() for fn in dirlist: match = submodule_match(fn) if match is not None: modname, pkginit, suffix = match.groups() if pkginit: ispkg = True elif modname == '__init__': continue else: ispkg = False if modname not in yielded: yielded[modname] = True yield modname, ispkg return def iter_modules(path=None, prefix=''): """Yield submodule names+loaders for path or sys.path""" if path is None: importers = iter_importers() else: importers = map(get_importer, path) yielded = {} for importer in importers: if hasattr(importer, 'iter_modules'): modules = importer.iter_modules(prefix) elif isinstance(importer, zipimporter): modules = iter_zipimport_modules(importer, prefix) else: modules = [] for name, ispkg in modules: if name not in yielded: yielded[name] = 1 yield importer, name, ispkg return try: from pkg_resources import get_provider, resource_filename except ImportError: class DefaultProvider: """Resource provider for "classic" loaders""" def __init__(self, module): self.loader = getattr(module, '__loader__', None) self.module_path = os.path.dirname(module.__file__) def get_resource_filename(self, manager, resource_name): return self._fn(self.module_path, resource_name) def get_resource_stream(self, manager, resource_name): return open(self._fn(self.module_path, resource_name), 'rb') def get_resource_string(self, manager, resource_name): stream = self.get_resource_stream(manager, resource_name) try: return stream.read() finally: stream.close() def has_resource(self, resource_name): return self._has(self._fn(self.module_path, resource_name)) def resource_isdir(self, resource_name): return self._isdir(self._fn(self.module_path, resource_name)) def resource_listdir(self, resource_name): return self._listdir(self._fn(self.module_path, resource_name)) def _fn(self, base, resource_name): return os.path.join(base, *resource_name.split('/')) def _has(self, pathname): return os.path.exists(pathname) def _isdir(self, pathname): return os.path.isdir(pathname) def _listdir(self, pathname): return os.listdir(pathname) class ZipProvider(DefaultProvider): """Resource provider for ZIP loaders""" _dirindex = None def __init__(self, module): DefaultProvider.__init__(self, module) self.zipinfo = self.loader._files self.zip_pre = self.loader.archive + os.sep def get_resource_filename(self, manager, resource_name): raise NotImplementedError("not supported by ZIP loaders") def get_resource_stream(self, manager, resource_name): data = self.get_resource_string(manager, resource_name) return cStringIO.StringIO(data) def get_resource_string(self, manager, resource_name): pathname = self._fn(self.module_path, resource_name) return self.loader.get_data(pathname) def _zipinfo_name(self, pathname): # Convert a virtual filename (full path to file) into a zipfile # subpath usable with the zipimport directory cache for our # target archive. if pathname.startswith(self.zip_pre): return pathname[len(self.zip_pre):] raise ValueError("%s not in %s" % (pathname, self.zip_pre)) def _build_index(self): self._dirindex = index = {} for path in self.zipinfo: parts = path.split(os.sep) while parts: parent = os.sep.join(parts[:-1]) if parent in index: index[parent].append(parts[-1]) break else: index[parent] = [parts.pop()] return index def _has(self, pathname): arcname = self._zipinfo_name(fspath) return (arcname in self.zipinfo or arcname in (self._dirindex or self._build_index())) def _isdir(self, pathname): arcname = self._zipinfo_name(pathname) return arcname in (self._dirindex or self._build_index()) def _listdir(self, pathname): arcname = self._zipinfo_name(pathname) if arcname in (self._dirindex or self._build_index()): return self._dirindex[arcname][:] return [] def get_provider(fullname): if fullname not in sys.modules: __import__(fullname) module = sys.modules[fullname] loader = getattr(module, '__loader__', None) if loader is None: provider = DefaultProvider(module) elif isinstance(loader, zipimporter): provider = ZipProvider(module) else: raise NotImplementedError('unsupported loader type: %s' % loader) return provider _resource_manager = None else: # pkg_resources (aka setuptools) installed.; the resource_filename # top-level name is actually the bound method of the global # ResourceManager (at least that is what the PkgResources docs say). _resource_manager = resource_filename.im_self del resource_filename GetImporter = get_importer FindLoader = find_loader IterModules = iter_modules def FindImporter(fullname): """Find a PEP 302 "loader" object for fullname If fullname contains dots, path must be the containing package's __path__. Returns None if the module cannot be found or imported. """ for importer in iter_importers(fullname): if importer.find_module(fullname) is not None: return importer return None def GetLastModified(fullname): """ Returns the last modified timestamp for the given module. """ loader = get_loader(fullname) if hasattr(loader, 'get_filename'): suffixes = IMP_SEARCH_ORDER elif isinstance(loader, zipimporter): suffixes = ZIP_SEARCH_ORDER else: raise NotImplementedError("unsupported loader %s" % laoder) barename = os.sep + fullname.replace('.', os.sep) if loader.is_package(fullname): barename += os.sep + '__init__' for suffix in suffixes: pathname = barename + suffix try: timestamp = GetResourceLastModified(fullname, pathname) except EnvironmentError: timestamp = 0 else: break return timestamp def GetSearchPath(fullname): loader = get_loader(fullname) if loader.is_package(fullname): if fullname in sys.modules: package = sys.modules[fullname] else: package = loader.load_module(fullname) return package.__path__ return None # -- Resource Handling ------------------------------------------------ def OsPathToResource(pathname): components = [] for component in pathname.split(os.sep): if component == '..': del components[-1:] elif component not in ('', '.'): components.append(component) resource = '/'.join(components) if pathname.startswith(os.sep): resource = '/' + resource return resource def NormalizeResource(package, resource): # normalize the resource pathname # Note, posixpath is not used as it doesn't remove leading '..'s components = [] for component in resource.split('/'): if component == '..': del components[-1:] elif component not in ('', '.'): components.append(component) absolute = resource.startswith('/') resource = '/'.join(components) provider = get_provider(package) if absolute: # Find the provider for the distribution directory module_path = provider.module_path packages = package.split('.') if not get_loader(package).is_package(package): del packages[-1] for module in packages: module_path = os.path.dirname(module_path) provider.module_path = module_path return (provider, resource) def GetResourceFilename(package, resource): """Returns a true filesystem name for the specified resource. """ provider, resource = NormalizeResource(package, resource) return provider.get_resource_filename(_resource_manager, resource) def GetResourceString(package, resource): """Return a string containing the contents of the specified resource. If the pathname is absolute it is retrieved starting at the path of the importer for 'fullname'. Otherwise, it is retrieved relative to the module within the loader. """ provider, resource = NormalizeResource(package, resource) return provider.get_resource_string(_resource_manager, resource) def GetResourceStream(package, resource): """Return a readable stream for specified resource""" provider, resource = NormalizeResource(package, resource) return provider.get_resource_stream(_resource_manager, resource) def GetResourceLastModified(package, resource): """Return a timestamp indicating the last-modified time of the specified resource. Raises IOError is the pathname cannot be found from the loader for 'fullname'. """ provider, resource = NormalizeResource(package, resource) if isinstance(provider.loader, zipimporter): if not resource: # it is the archive itself timestamp = os.stat(provider.module_path).st_mtime else: filename = provider._fn(provider.module_path, resource) zipinfo_name = provider._zipinfo_name(filename) try: dostime, dosdate = provider.zipinfo[zipinfo_name][5:7] except: import errno errorcode = errno.ENOENT raise IOError(errorcode, os.strerror(errorcode), zipinfo_name) timestamp = time.mktime(( ((dosdate >> 9) & 0x7f) + 1980, # tm_year ((dosdate >> 5) & 0x0f) - 1, # tm_mon ((dosdate >> 0) & 0x1f), # tm_mday ((dostime >> 11) & 0x1f), # tm_hour ((dostime >> 5) & 0x3f), # tm_min ((dostime >> 0) & 0x1f) * 2, # tm_secs 0, 0, -1)) else: filename = provider.get_resource_filename(_resource_manager, resource) timestamp = os.stat(filename).st_mtime return timestamp PK5p*LL Ft/Lib/Iri.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Iri.py,v 1.4.4.1 2006-08-23 06:46:34 jkloth Exp $ """ Classes and functions related to IRI processing Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import sys def IriToUri(iri, convertHost=False): r""" Converts an IRI or IRI reference to a URI or URI reference, implementing sec. 3.1 of draft-duerst-iri-10. The convertHost flag indicates whether to perform conversion of the ireg-name (host) component of the IRI to an RFC 2396-compatible URI reg-name (IDNA encoded), e.g. IriToUri(u'http://r\xe9sum\xe9.example.org/', convertHost=False) => u'http://r%C3%A9sum%C3%A9.example.org/' IriToUri(u'http://r\xe9sum\xe9.example.org/', convertHost=True) => u'http://xn--rsum-bpad.example.org/' Ordinarily, the IRI should be given as a unicode string. If the IRI is instead given as a byte string, then it will be assumed to be UTF-8 encoded, will be decoded accordingly, and as per the requirements of the conversion algorithm, will NOT be normalized. """ if not isinstance(iri, str): iri = NfcNormalize(iri) if convertHost and sys.version_info[0:2] >= (2,3): # first we have to get the host from Ft.Lib.Uri import SplitUriRef, UnsplitUriRef (scheme, auth, path, query, frag) = SplitUriRef(iri) if auth and auth.find('@') > -1: userinfo, hostport = auth.split('@') else: userinfo = None hostport = auth if hostport and hostport.find(':') > -1: host, port = hostport.split(':') else: host = hostport port = None if host: host = ConvertIregName(host) auth = '' if userinfo: auth += userinfo + '@' auth += host if port: auth += ':' + port iri = UnsplitUriRef((scheme, auth, path, query, frag)) res = u'' pos = 0 surrogate = None for c in iri: cp = ord(c) if cp > 128: if cp < 160: # FIXME: i18n raise ValueError("Illegal character at position %d (0-based) of IRI %r" % (pos, iri)) # 'for c in iri' may give us surrogate pairs elif cp > 55295: if cp < 56320: # d800-dbff surrogate = c continue elif cp < 57344: # dc00-dfff if surrogate is None: raise ValueError("Illegal surrogate pair in %r" % iri) c = surrogate + c else: raise ValueError("Illegal surrogate pair in %r" % iri) surrogate = None for octet in c.encode('utf-8'): res += u'%%%02X' % ord(octet) else: res += c pos += 1 return res def NfcNormalize(iri): """ On Python 2.3 and higher, normalizes the given unicode string according to Unicode Normalization Form C (NFC), so that it can be used as an IRI or IRI reference. """ try: from unicodedata import normalize iri = normalize('NFC', iri) except ImportError: pass return iri def ConvertIregName(iregname): """ On Python 2.3 and higher, converts the given ireg-name component of an IRI to a string suitable for use as a URI reg-name in pre- rfc2396bis schemes and resolvers. Returns the ireg-name unmodified on Python 2.2. """ try: # I have not yet verified that the default IDNA encoding # matches the algorithm required by the IRI spec, but it # does work on the one simple example in the spec. iregname = iregname.encode('idna') except: pass return iregname PKf2x~Ft/Lib/MessageSource.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/MessageSource.py,v 1.14 2005-03-06 06:39:32 mbrown Exp $ """ Messages for Ft.Lib Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ from Ft import TranslateMessage as _ from Ft.Lib import UriException # %r preferred for reporting URIs because the URI refs can be empty # strings or, if invalid, could contain characters unsafe for the error # message stream. # URI = { UriException.INVALID_BASE_URI: _("Invalid base URI: %(base)r cannot be used to resolve reference %(ref)r"), UriException.RELATIVE_BASE_URI: _("Invalid base URI: %(base)r cannot be used to resolve reference %(ref)r;" " the base URI must be absolute, not relative."), UriException.NON_FILE_URI: _("Only a 'file' URI can be converted to an OS-specific path; URI given was %r"), UriException.UNIX_REMOTE_HOST_FILE_URI: _("A URI containing a remote host name cannot be converted to a path on posix;" " URI given was %r"), UriException.RESOURCE_ERROR: _("Error retrieving resource %(loc)r: %(msg)s"), UriException.UNSUPPORTED_PLATFORM: _("Platform %r not supported by URI function %s"), UriException.SCHEME_REQUIRED: _("Scheme-based resolution requires a URI with a scheme; " "neither the base URI %(base)r nor the reference %(ref)r have one."), UriException.INVALID_PUBLIC_ID_URN: _("A public ID cannot be derived from URN %(urn)r" " because it does not conform to RFC 3151."), UriException.UNSUPPORTED_SCHEME: _("The URI scheme %(scheme)s is not supported by resolver %(resolver)s"), UriException.IDNA_UNSUPPORTED: _("The URI ref %(uri)r cannot be made urllib-safe on this version of Python (IDNA encoding unsupported)."), } PKu`2.fpFt/Lib/ObjectPrint.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/ObjectPrint.py,v 1.2 2003-01-18 18:03:42 mbrown Exp $ """ Pretty-printing of objects Copyright 2003 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import pprint as _pprint import types as _types def _inst_to_dict(inst): dict = vars(inst).copy() for (key, value) in dict.items(): if type(value) is _types.InstanceType: dict[key] = _inst_to_dict(value) return dict def pprint(object): if type(object) is _types.InstanceType: object = _inst_to_dict(object) _pprint.pprint(object) PK<2vFt/Lib/ProgressIndicator.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/ProgressIndicator.py,v 1.8 2005-04-18 23:17:57 jkloth Exp $ """ Progress indicator Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import sys, time, os from Ft.Lib import Terminal class ProgressIndicator: """ A progress indicator intended for terminal output (relies on ^H). Indicator style, given as constructor argument, can be 0: percentage; 1: bar; or 2: both. Default is 0. If using styles 1 or 2, an optional width argument for the bar portion can also be given (default 60). Example usage: # First emit whatever prefaces the indicator, if desired print " status:", sys.__stdout__.flush() # Create a new indicator p = ProgressIndicator(2) p.newIndicator() # With each iteration through a task, or as often as you want, # call updateProgress(), passing 2 numbers: amount completed, # and total amount to do. limit = 300000 for i in range(limit): p.updateProgress(i, limit) print """ if os.name == 'nt' or os.name == 'dos': # dark/light shades... thanks Oleg Broytmann. # OK for all MS-DOS codepages except 864 (Arabic) & 874 (Thai) # and python doesn't seem to mind writing the non-ASCII chars. _hashchar = '\xb2' _blankchar = '\xb0' elif (os.environ.get('LANG', '').endswith('.UTF-8') and os.environ.get('TERM') == "xterm"): # UTF-8 enabled terminal # 25% => x2591 # 50% => x2592 # 75% => x2593 # 100% => x2588 _hashchar = u'\u2588'.encode('UTF-8') _blankchar = u'\u2591'.encode('UTF-8') else: _hashchar = '*' _blankchar = ' ' _current = 0 _total = 0 def __init__(self, prefix, stream=sys.stdout): if type(prefix) == type(u''): # avoid possible UnicodeError during output self.prefix = prefix.encode('ascii','replace') else: self.prefix = prefix self._tty = Terminal.Terminal(stream) self._writetty = self._tty.writetty self._flushtty = self._tty.flush return def newIndicator(self, total): """ Start a new indicator at 00%. Optional style and width arguments are same as constructor. """ self._current = 0 self._total = total self._showProgress() return def _erase(self): self._writetty("\r") def message(self,message): # left-justify message within column width, padding or trimming as # required. columns = self._tty.columns() message = '%-*.*s' % (columns, columns, message) self._erase() self._writetty(message + '\n'); self._showProgress() def updateProgress(self, cur): """ Update an existing indicator to reflect given progress. Arguments are amount completed so far, and total to do. For example, if 4 out of 30 have been completed, call updateProgress(4,30). """ self._current = cur self._erase() self._showProgress() return def _showProgress(self): barwidth = self._tty.columns() - 3 - len(self.prefix) - 4 hashwidth = int(float(self._current+1)/self._total * (barwidth - 2)) pct = int((self._current+1)*100/self._total) self._writetty("%s |%s%s %s%%" % ( self.prefix, self._hashchar * hashwidth, self._blankchar * (barwidth - hashwidth - 2) + "|", " " * (pct < 100) + "%02d" % pct)) self._flushtty() return class AutoProgressIndicator(ProgressIndicator): def __init__(self, prefix, total, step=1, stream=sys.stdout): ProgressIndicator.__init__(self, prefix, stream) self.newIndicator(total) self._step = 1 return def advance(self): self.updateProgress(self._current + self._step) return PK-4@vB-B-Ft/Lib/Random.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Random.py,v 1.8 2006-01-13 06:12:55 mbrown Exp $ """ Thread-safe random number generation Random number generation capabilities, speed, and thread safety in stdlib vary from version to version of Python. In addition, attempts to use an OS-specific random number source can result in unexpected exceptions being raised. Also, a bug in Python 2.3.0 can lead to a reduction in entropy, and a bug in Python 2.4.0 and 2.4.1 can result in exceptions related to open filehandles on some multithreaded Posix platforms. This module works around as many of these issues as it can by defining random number generator classes that can be used safely by multiple threads, using the best random number sources available. They support all versions of Python from 2.1 up, and fall back on more reliable generators when exception conditions occur. In addition, convenience functions equivalent to random.random() and os.urandom() are exposed. Copyright 2006 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ __all__ = ['urandom', 'FtRandom', 'FtSystemRandom', 'DEFAULT_RNG', 'Random', 'GetRandomBytes'] import random, threading, os, sys from sys import version_info py230 = version_info[0:3] == (2, 3, 0) py23up = version_info[0:2] > (2, 2) py24up = version_info[0:2] > (2, 3) py242up = version_info[0:3] > (2, 4, 1) posix = os.name == 'posix' win32 = sys.platform == 'win32' _lock = threading.Lock() #============================================================================= # Thread-safe implementation of os.urandom() # (still raises NotImplementedError when no OS-specific random number source) # if win32 and py24up: urandom = os.urandom elif posix: if py242up: urandom = os.urandom else: # Python 2.4.2's os.urandom() def urandom(n): """urandom(n) -> str Return a string of n random bytes suitable for cryptographic use. """ try: _urandomfd = os.open("/dev/urandom", os.O_RDONLY) except: raise NotImplementedError("/dev/urandom (or equivalent) not found") bytes = "" while len(bytes) < n: bytes += os.read(_urandomfd, n - len(bytes)) os.close(_urandomfd) return bytes if hasattr(random, '_urandom'): random._urandom = urandom else: def urandom(n): """urandom(n) -> str Return a string of n random bytes suitable for cryptographic use. """ raise NotImplementedError("There is no OS-specific random number source.") #============================================================================= # FtRandom: a non-crypto-safe PRNG (Mersenne Twister or Wichmann-Hill, made # thread-safe). By default, seeded from an OS-specific random number source, # if available. # if posix and not py24up: # posix py2.3 down: use urandom if possible from binascii import hexlify def _best_seed(self, a=None): """Initialize internal state from hashable object. None or no argument seeds from current time or from an operating system specific randomness source if available. If a is not None or an int or long, hash(a) is used instead. """ if a is None: try: a = long(hexlify(urandom(16)), 16) except NotImplementedError: # posix py2.3.0: use system clock, but avoid buggy stdlib if py230: import time a = long(time.time() * 256) super(FtRandom, self).seed(a) elif py230: # win32 py2.3.0: use system clock, but avoid buggy stdlib def _best_seed(self, a=None): import time a = long(time.time() * 256) super(FtRandom, self).seed(a) else: # posix or win32 py2.4 up: urandom if possible, fall back on system clock # win32 py2.3 down: system clock only _best_seed = random.Random.seed # random.Random.gauss() is not thread-safe def _gauss(self, *args, **kwargs): """Gaussian distribution. mu is the mean, and sigma is the standard deviation. Thread-safe. """ _lock.acquire() rv = super(self.__class__, self).gauss(*args, **kwargs) _lock.release() return rv if py23up: # Mersenne Twister, already thread-safe _random = random.Random.random def _getrandbytes(self, k): """getrandbytes(k) -> x. Returns k random bytes as a str.""" bytes = "" while len(bytes) < k: n = super(FtRandom, self).random() bytes += chr(int(n * 256)) return bytes else: # Wichmann-Hill, made thread-safe def _random(self): """Get the next random number in the range [0.0, 1.0).""" _lock.acquire() n = super(FtRandom, self).random() _lock.release() return n def _getrandbytes(self, k): """getrandbytes(k) -> x. Returns k random bytes as a str.""" bytes = "" _lock.acquire() while len(bytes) < k: n = super(FtRandom, self).random() bytes += chr(int(n * 256)) _lock.release() return bytes if py24up: _getrandbits = random.Random.getrandbits else: # This is the py2.4 implementation from binascii import hexlify def _getrandbits(self, k): """getrandbits(k) -> x. Generates a long int with k random bits.""" if k <= 0: raise ValueError('number of bits must be greater than zero') if k != int(k): raise TypeError('number of bits should be an integer') bytes = (k + 7) // 8 # bits / 8 and rounded up x = long(hexlify(self.getrandbytes(bytes)), 16) return x >> (bytes * 8 - k) # trim excess bits class FtRandom(random.Random, object): """ The best available OS-agnostic PRNG, thread-safe. Implements getrandbits() in all versions of Python. Also adds getrandbytes(), which returns a str of bytes. """ seed = _best_seed gauss = _gauss random = _random getrandbits = _getrandbits getrandbytes = _getrandbytes def __init__(self, *args, **kwargs): return super(FtRandom, self).__init__(*args, **kwargs) #============================================================================= # FtSystemRandom: a PRNG that uses an OS-specific random number source, if # available, falling back on an instance of FtRandom. It is as crypto-safe as # the OS-specific random number source, when such a source is available. # Calls to seed() and jumpahead() only affect the fallback FtRandom instance. # if win32 and not py24up: # don't bother trying OS-specific sources on win32 before py2.4 def _random(self): """Get the next random number in the range [0.0, 1.0).""" return self._fallback_prng.random() def _getrandbits(self, k): """getrandbits(k) -> x. Generates a long int with k random bits.""" return self._fallback_prng.getrandbits(k) def _getrandbytes(self, k): """getrandbytes(k) -> x. Returns k random bytes as a str.""" return self._fallback_prng.getrandbytes(k) else: # Functions that read random numbers from OS-specific sources # Use random() and getrandbits() from random.SystemRandom. # We've already replaced random._urandom with our urandom, so it's OK. try: # py2.4 up... from random import SystemRandom as _SystemRandom _sr_random = _SystemRandom.random.im_func _sr_getrandbits = _SystemRandom.getrandbits.im_func except ImportError: # py2.3 down, posix (since we tested for win32 above)... # These are based on the py2.4 implementation. from binascii import hexlify _BPF = 53 # Number of bits in a float _RECIP_BPF = 2**-_BPF def _sr_random(self): """Get the next random number in the range [0.0, 1.0).""" return (long(hexlify(urandom(7)), 16) >> 3) * _RECIP_BPF def _sr_getrandbits(self, k): """getrandbits(k) -> x. Generates a long int with k random bits.""" if k <= 0: raise ValueError('number of bits must be greater than zero') if k != int(k): raise TypeError('number of bits should be an integer') bytes = (k + 7) // 8 # bits / 8 and rounded up x = long(hexlify(urandom(bytes)), 16) return x >> (bytes * 8 - k) # trim excess bits # Wrapper functions that try OS-specific sources first, then fall back def _random(self): """Get the next random number in the range [0.0, 1.0).""" try: return _sr_random(self) except NotImplementedError: return self._fallback_prng.random() def _getrandbits(self, *args, **kwargs): """getrandbits(k) -> x. Generates a long int with k random bits.""" try: return _sr_getrandbits(self, *args, **kwargs) except NotImplementedError: return self._fallback_prng.getrandbits(*args, **kwargs) def _getrandbytes(self, k): """getrandbytes(k) -> x. Returns k random bytes as a str.""" try: return urandom(k) except NotImplementedError: return self._fallback_prng.getrandbytes(k) class FtSystemRandom(FtRandom): """ A PRNG that uses an OS-specific random number source, if available, falling back on an instance of FtRandom. Calls to seed(), jumpahead(), getstate() and setstate() only affect the fallback FtRandom instance. Implements getrandbits() in all versions of Python. Also adds getrandbytes(), which returns a str of bytes. """ random = _random getrandbits = _getrandbits getrandbytes = _getrandbytes def __init__(self, *args, **kwargs): self._fallback_prng = FtRandom() return super(FtSystemRandom, self).__init__(*args, **kwargs) def seed(self, *args, **kwargs): """Seed the fallback PRNG (an instance of FtRandom)""" return self._fallback_prng.seed(*args, **kwargs) def jumpahead(self, *args, **kwargs): """Make the fallback PRNG (an instance of FtRandom) jump ahead""" return self._fallback_prng.jumpahead(*args, **kwargs) def getstate(self): """Return internal state; can be passed to setstate() later.""" return self._fallback_prng.getstate() def setstate(self, state): """Restore internal state from object returned by getstate().""" self._fallback_prng.setstate(state) return #============================================================================= # convenience functions # DEFAULT_RNG = FtSystemRandom() def Random(): """Returns a random float, n, where 0 <= n < 1""" return DEFAULT_RNG.random() def GetRandomBytes(numBytes): """ Returns a string of random bytes from the best RNG available. Equivalent to os.urandom(), but failsafe. """ return DEFAULT_RNG.getrandbytes(numBytes) PK:2ICeeFt/Lib/Regex.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Regex.py,v 1.1 2005-01-27 04:47:36 uogbuji Exp $ """ Tools to manage the many different flavors of regex Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import re #e.g. u"(foo){5,}" -> u"(foo){5}(foo)*" MIN_LENGTH_SEQ_PAT = re.compile(u"(\\(.+\\))\\{([0-9]+),\\}") def W3cRegexToPyRegex(w3cregex): """ Convert W3C regex to Python regex e.g.: >>> from Ft.Lib.Regex import W3cRegexToPyRegex >>> print repr(W3cRegexToPyRegex(u"(foo){5,}")) u'((foo)){5}(foo)*' """ #Input format: W3C regex ( http://www.w3.org/TR/xmlschema-2/#dt-regex ) #Output format: Python regex ( http://docs.python.org/lib/re-syntax.html ) regex = MIN_LENGTH_SEQ_PAT.subn(lambda m: u"("+m.group(1)+u")"+u"{"+m.group(2)+u"}"+m.group(1)+u"*", w3cregex)[0] #FIXME: A lot more work on character classes and the like return regex PK %2R_{Ft/Lib/Resolvers.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Resolvers.py,v 1.3 2005-01-05 10:04:22 mbrown Exp $ """ Specialized and useful URI resolvers Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import os, sys, cStringIO from Ft.Lib import Uri class SchemeRegistryResolver(Uri.FtUriResolver): """ A type of resolver that allows developers to register different callable objects to handle different URI schemes. The default action if there is nothing registered for the scheme will be to fall back to UriResolverBase behavior *unless* you have in the mapping a special scheme None. The callable object that is the value on that key will then be used as the default for all unknown schemes. The expected function signature for scheme call-backs matches UriResolverBase.resolve, without the instance argument: resolve(uri, base=None) Reminder: Since this does not include self, if you are registering a method, use the method instance (i.e. myresolver().handler rather than myresolver.handler) You can manipulate the mapping directly using the "handlers" attribute. """ def __init__(self, handlers=None): """ handlers - a Python dictionary with scheme names as keys (e.g. "http") and callable objects as values """ Uri.UriResolverBase.__init__(self) self.handlers = handlers or {} return def resolve(self, uri, base=None): scheme = Uri.GetScheme(uri) if not scheme: if base: scheme = Uri.GetScheme(base) if not scheme: #Another option is to fall back to Base class behavior raise Uri.UriException(Uri.UriException.SCHEME_REQUIRED, base=base, ref=uri) func = self.handlers.get(scheme) if not func: func = self.handlers.get(None) if not func: return Uri.UriResolverBase.resolve(self, uri, base) return func(uri, base) #Legacy import support. Uri.SchemeRegistryResolver = SchemeRegistryResolver class FacadeResolver(Uri.FtUriResolver): """ A type of resolver that can be used to create a facade or cache of resources by keeping a dictionary of URI to result mappings. When a URI is provided for resolution, the mapping is first checked, and a stream is constructed by wrapping the mapping value string. If no match is found in the mapping, fall back to the standard resolver logic. You can manipulate the mapping directly using the "cache" attribute. """ def __init__(self, cache=None, observer=None): """ cache - a dictionary with mapings from URI to value (as an object to be converted to a UTF-8 encoded string) observer - callable object invoked on each resolution request """ Uri.UriResolverBase.__init__(self) self.cache = cache or {} self.observer = observer return def resolve(self, uri, base=None): self.observer(uri, base) #Does not factor in base. Should it? if uri in self.cache: cachedval = self.cache[uri] if isinstance(cachedval, unicode): return cStringIO.StringIO(cachedval.encode('utf-8')) else: return cStringIO.StringIO(str(cachedval)) return Uri.UriResolverBase.resolve(self, uri, base) PKюA4_++Ft/Lib/Terminal.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Terminal.py,v 1.6 2006-02-01 23:54:35 jkloth Exp $ """ Provides some of the information from the terminfo database. Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import os, re, sys from Ft.Lib.Terminfo import TERMTYPES as _ANSITERMS from Ft.Lib.Terminfo import DEFAULT_LINES as _LINES from Ft.Lib.Terminfo import DEFAULT_COLUMNS as _COLUMNS if sys.platform == 'win32': import msvcrt from Ft.Lib import _win32con elif os.name == 'posix': _HAVE_TIOCGWINSZ = False try: import fcntl, termios, struct except ImportError: pass else: _HAVE_TIOCGWINSZ = hasattr(termios, 'TIOCGWINSZ') # ISO 6429 color sequences are composed of sequences of numbers # separated by semicolons. The most common codes are: # # 0 to restore default color # 1 for brighter colors # 4 for underlined text # 5 for flashing text # 30 for black foreground # 31 for red foreground # 32 for green foreground # 33 for yellow (or brown) foreground # 34 for blue foreground # 35 for purple foreground # 36 for cyan foreground # 37 for white (or gray) foreground # 40 for black background # 41 for red background # 42 for green background # 43 for yellow (or brown) background # 44 for blue background # 45 for purple background # 46 for cyan background # 47 for white (or gray) background class AnsiEscapes: class Colors: DEFAULT = '\033[0m' BOLD = '\033[1m' FOREGROUND_BLACK = '\033[30m' FOREGROUND_MAROON = '\033[31m' FOREGROUND_GREEN = '\033[32m' FOREGROUND_BROWN = FOREGROUND_OLIVE = '\033[33m' FOREGROUND_NAVY = '\033[34m' FOREGROUND_PURPLE = '\033[35m' FOREGROUND_TEAL = '\033[36m' FOREGROUND_SILVER = '\033[37m' FOREGROUND_GRAY = '\033[1;30m' FOREGROUND_RED = '\033[1;31m' FOREGROUND_LIME = '\033[1;32m' FOREGROUND_YELLOW = '\033[1;33m' FOREGROUND_BLUE = '\033[1;34m' FOREGROUND_MAGENTA = FOREGROUND_FUCHSIA = '\033[1;35m' FOREGROUND_CYAN = FOREGROUND_AQUA = '\033[1;36m' FOREGROUND_WHITE = '\033[1;37m' BACKGROUND_BLACK = '\033[40m' BACKGROUND_MAROON = '\033[41m' BACKGROUND_GREEN = '\033[42m' BACKGROUND_BROWN = BACKGROUND_OLIVE = '\033[43m' BACKGROUND_NAVY = '\033[44m' BACKGROUND_PURPLE = '\033[45m' BACKGROUND_TEAL = '\033[46m' BACKGROUND_SILVER = '\033[47m' # Methods/members a Terminal instance should expose from its underly stream. _file_methods = ('flush', 'write', 'read', 'isatty', 'encoding') class Terminal: def __init__(self, stream, keepAnsiEscapes=True): self._stream = stream for name in _file_methods: method = getattr(stream, name, None) if method is not None: setattr(self, name, method) if self.isatty(): if sys.platform == 'win32': self._init_win32(stream, keepAnsiEscapes) elif os.name == 'posix' and os.environ.get('TERM') in _ANSITERMS: self._init_posix(stream, keepAnsiEscapes) return def _init_win32(self, stream, keepAnsiEscapes): try: self._handle = h = msvcrt.get_osfhandle(stream.fileno()) except IOError: pass else: if keepAnsiEscapes: self._write_escape = self._escape_win32 self._default_attribute = \ _win32con.GetConsoleScreenBufferInfo(self._handle)[2] self.size = self._size_win32 return def _init_posix(self, stream, keepAnsiEscapes): if keepAnsiEscapes: # stream handles ANSI escapes natively self.writetty = stream.write if _HAVE_TIOCGWINSZ: self.size = self._size_termios return def lines(self): return self.size()[0] def columns(self): return self.size()[1] def size(self): return (_LINES, _COLUMNS) # noop method for underlying streams which do not implement it def flush(self): return # noop method for underlying streams which do not implement it def write(self, str): return # noop method for underlying streams which do not implement it def read(self, size=-1): return '' # noop method for underlying streams which do not implement it def isatty(self): return False def close(self): # don't attempt to close a tty streams if self.isatty(): return # ignore any errors closing the underlying stream try: self._stream.close() except: pass return # ANSI Set Display Mode: ESC[#;...;#m _ansi_sdm = re.compile('\033\\[([0-9]+)(?:;([0-9]+))*m') def writetty(self, bytes): start = 0 match = self._ansi_sdm.search(bytes) while match is not None: # write everything up to the escape sequence self._stream.write(bytes[start:match.start()]) # process the color codes self._write_escape(match.groups()) # skip over the escape sequence start = match.end() # find the next sequence match = self._ansi_sdm.search(bytes, start) # write the remainder self._stream.write(bytes[start:]) return def _write_escape(self, codes): """ Escape function for handling ANSI Set Display Mode. Default behavior is to simply ignore the call (e.g. nothing is added to the output). """ return # -- Terminal specific functions ----------------------------------- def _size_termios(self): ws = struct.pack("HHHH", 0, 0, 0, 0) ws = fcntl.ioctl(self._stream.fileno(), termios.TIOCGWINSZ, ws) lines, columns, x, y = struct.unpack("HHHH", ws) return (lines, columns) def _escape_win32(self, codes): """Translates the ANSI color codes into the Win32 API equivalents.""" # get the current text attributes for the stream size, cursor, attributes, window = \ _win32con.GetConsoleScreenBufferInfo(self._handle) for code in map(int, filter(None, codes)): if code == 0: # normal # the default attribute attributes = self._default_attribute elif code == 1: # bold # bold only applies to the foreground color attributes |= _win32con.FOREGROUND_INTENSITY elif code == 30: # black attributes &= _win32con.BACKGROUND elif code == 31: # red attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= _win32con.FOREGROUND_RED elif code == 32: # green attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= _win32con.FOREGROUND_GREEN elif code == 33: # brown (bold: yellow) attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= (_win32con.FOREGROUND_RED | _win32con.FOREGROUND_GREEN) elif code == 34: # blue attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= _win32con.FOREGROUND_BLUE elif code == 35: # purple (bold: magenta) attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= (_win32con.FOREGROUND_RED | _win32con.FOREGROUND_BLUE) elif code == 36: # cyan attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= (_win32con.FOREGROUND_BLUE | _win32con.FOREGROUND_GREEN) elif code == 37: # gray (bold: white) attributes &= (_win32con.FOREGROUND_INTENSITY | _win32con.BACKGROUND) attributes |= (_win32con.FOREGROUND_RED | _win32con.FOREGROUND_GREEN | _win32con.FOREGROUND_BLUE) elif code == 40: # black attributes &= _win32con.FOREGROUND elif code == 41: # red attributes &= _win32con.FOREGROUND attributes |= _win32con.BACKGROUND_RED elif code == 42: # green attributes &= _win32con.FOREGROUND attributes |= _win32con.BACKGROUND_GREEN elif code == 43: # brown attributes &= _win32con.FOREGROUND attributes |= (_win32con.BACKGROUND_RED | _win32con.BACKGROUND_GREEN) elif code == 44: # blue attributes &= _win32con.FOREGROUND attributes |= _win32con.BACKGROUND_BLUE elif code == 45: # purple attributes &= _win32con.FOREGROUND attributes |= (_win32con.BACKGROUND_RED | _win32con.BACKGROUND_BLUE) elif code == 46: # cyan attributes &= _win32con.FOREGROUND attributes |= (_win32con.BACKGROUND_BLUE | _win32con.BACKGROUND_GREEN) elif code == 47: # gray attributes &= _win32con.FOREGROUND attributes |= (_win32con.BACKGROUND_RED | _win32con.BACKGROUND_GREEN | _win32con.BACKGROUND_BLUE) _win32con.SetConsoleTextAttribute(self._handle, attributes) return def _size_win32(self): size, cursor, attributes, window = \ _win32con.GetConsoleScreenBufferInfo(self._handle) left, top, right, bottom = window # use the buffer size for the column width as Windows wraps text # there instead of at the displayed window size columns, lines = size return (bottom - top, columns - 1) PK o3Zy铛Ft/Lib/Terminfo.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Terminfo.py,v 1.6 2005-11-15 07:36:24 mbrown Exp $ """ Provides some of the information from the terminfo database. Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import sys, os TERMTYPES = ['linux', 'console', 'con132x25', 'con132x30', 'con132x43', 'con132x60', 'con80x25', 'con80x28', 'con80x30', 'con80x43', 'con80x50', 'con80x60', 'xterm', 'xterm-color', 'color-xterm', 'vt100', 'vt100-color', 'rxvt', 'ansi', 'Eterm', 'putty', 'vt220-color', 'cygwin' ] # Default sizes should fit on all displays. DEFAULT_LINES = 24 DEFAULT_COLUMNS = 80 def GetLines(stream=sys.stdout): lines = DEFAULT_LINES if hasattr(stream, 'isatty') and stream.isatty() \ and os.environ.get('TERM') in TERMTYPES: try: import fcntl, termios, struct except ImportError: pass else: if hasattr(termios, 'TIOCGWINSZ'): ws = struct.pack("HHHH", 0, 0, 0, 0) ws = fcntl.ioctl(stream.fileno(), termios.TIOCGWINSZ, ws) lines, columns, x, y = struct.unpack("HHHH", ws) return lines def GetColumns(stream=sys.stdout): columns = DEFAULT_COLUMNS if stream.isatty() and os.environ.get('TERM') in TERMTYPES: try: import fcntl, termios, struct except ImportError: pass else: if hasattr(termios, 'TIOCGWINSZ'): ws = struct.pack("HHHH", 0, 0, 0, 0) ws = fcntl.ioctl(stream.fileno(), termios.TIOCGWINSZ, ws) lines, columns, x, y = struct.unpack("HHHH", ws) return columns PK200Ft/Lib/Time.py######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Time.py,v 1.21 2005-04-06 23:36:48 jkloth Exp $ """ Date and time related functionality for use within 4Suite only. This module is experimental and may not be staying in 4Suite for long; application developers should avoid forming dependencies on it. Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ import re, time, calendar, rfc822 _month_days = ( (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), ) def DayOfYearFromYMD(year, month, day): """ Calculates the Julian day (day of year, between 1 and 366), for the given date. This function is accurate for dates back to 01 Jan 0004 (that's 4 A.D.), when the Julian calendar stabilized. """ days_table = _month_days[calendar.isleap(year)] days = 0 for m in range(1, month): days += days_table[m] return days + day def WeekdayFromYMD(year, month, day): """ Calculates the day of week (0=Mon, 6=Sun) for the given date. This function is accurate for dates on/after Friday, 15 Oct 1582, when the Gregorian reform took effect, although it should be noted that some nations didn't adopt the Gregorian calendar until as late as the 20th century, so dates that were referenced before then would have fallen on a different day of the week, at the time. """ # An alternate way of calculating weekdays is to let the python time # module do it for you, like this: # # time.localtime() on Windows won't take negative arguments! # min_year = time.localtime(0)[0] + 1 # max_year = time.localtime(sys.maxint)[0] - 1 # if self._localYear > min_year and self._localYear < max_year: # secsSinceEpoch = time.mktime(self.asPythonTimeTuple(local=1)) # self._utcWeekday = time.gmtime(secsSinceEpoch)[6] # self._localWeekday = time.localtime(secsSinceEpoch)[6] # # However, this can only be used with dates that time.localtime() can # handle, which varies from system to system and is likely to be within # the bounds of 1970 and 2037. # We use a convenient epoch of 2001-01-01: it was a Monday if year == 2001: days_from_epoch = 0 else: years = range(2001, year, cmp(year, 2001)) leap_days = len(filter(calendar.isleap, years)) days_from_epoch = len(years) * 365 + leap_days if year < 2001: # add the days after this date in the year day_of_year = DayOfYearFromYMD(year, month, day) days_from_epoch += (365 + calendar.isleap(year) - day_of_year) # this is the total number of days before the 20010101 epoch days_from_epoch *= -1 else: # this is the total number of days after the 20010101 epoch days_from_epoch += (DayOfYearFromYMD(year, month, day) - 1) return days_from_epoch % 7 class DT: """ A class that contains the data needed to represent a single point in time using many different date and time formats. Its constructor requires a UTC (GMT) date and time (year, month (0-11), day (0-31), hour (0-23), minute (0-59), second (0-59), millisecond (0-999), plus some information to help express this time in local terms: a local time zone name, or if that's not available, an hour offset of the local time from GMT (-11 to 14, typically), a minute offset of the local time from GMT (0 or 30, usually), and an optional flag indicating Daylight Savings Time, to help determine the time zone name. """ def __init__(self, year, month, day, hour, #In GMT minute, #in GMT second, milliSecond, daylightSavings, #1 means yes tzName, tzHourOffset, tzMinuteOffset): tzMinuteOffset = int(tzMinuteOffset) tzHourOffset = int(tzHourOffset) milliSecond = float(milliSecond) second = int(second) minute = int(minute) hour = int(hour) day = int(day) month = int(month) year = int(year) daylightSavings = not not daylightSavings #This is purely to help look up the tzName if tzName: self._tzName = tzName else: k = float(tzHourOffset) + float(tzMinuteOffset)/60.0 d = self.tzNameTable.get(k) if d: if daylightSavings: n = d[3] if not n: n = d[2] else: n = d[2] if not n: n = d[0] self._tzName = n else: self._tzName = '' #Normalize the milli-seconds between 0 and 999 secondShift = 0 while milliSecond < 0: secondShift -=1 milliSecond += 1000 while milliSecond > 999: secondShift +=1 milliSecond -=1000 self._milliSecond = milliSecond #Normalize the seconds between 0 and 59 second += secondShift minuteShift = 0 while second < 0: minuteShift -=1 second += 60 while second > 59: minuteShift +=1 second -=60 self._second = second #Normalize Minute between 0 and 59 utcHourShift, self._utcMinute = self.__normalizeMinute(minute + minuteShift) localHourShift, self._localMinute = self.__normalizeMinute(minute + minuteShift + tzMinuteOffset) #Normalize Hour between 0 and 23 utcDayShift, self._utcHour = self.__normalizeHour(hour + utcHourShift) localDayShift, self._localHour = self.__normalizeHour(hour + localHourShift + tzHourOffset) #Normalize Date as one between 0 and max day of month self._utcYear, self._utcMonth, self._utcDay = self.__normalizeDate(day + utcDayShift, month, year) self._localYear, self._localMonth, self._localDay = self.__normalizeDate(day + localDayShift, month, year) self._tzHourOffset = tzHourOffset self._tzMinuteOffset = tzMinuteOffset #Set Day In Year (Julian day) self._utcDayOfYear = DayOfYearFromYMD(self._utcYear, self._utcMonth, self._utcDay) self._localDayOfYear = DayOfYearFromYMD(self._localYear, self._localMonth, self._localDay) #Set Weekday self._utcWeekday = WeekdayFromYMD(self._utcYear, self._utcMonth, self._utcDay) self._localWeekday = WeekdayFromYMD(self._localYear, self._localMonth, self._localDay) #Lastly, set this for XPath self.stringValue = self.asISO8601DateTime(local=1) def asISO8601DateTime(self, local=0): """ Represents this DT object as an ISO 8601 date-time string, using UTC time like '2001-01-01T00:00:00Z' if local=0, or local time with UTC offset like '2000-12-31T17:00:00-07:00' if local=1. """ return "%s%s" % (self.asISO8601Date(local), self.asISO8601Time(local)) def asISO8601Date(self, local=0): """ Represents this DT object as an ISO 8601 date-time string, like '2001-01-01' if local=0, or '2000-12-31' if local=1. The local date may vary from UTC date depending on the time of day that is stored in the object. """ if local: y = self._localYear m = self._localMonth d = self._localDay else: y = self._utcYear m = self._utcMonth d = self._utcDay return "%d-%02d-%02d" % (y, m, d) def asISO8601Time(self, local=0): """ Represents this DT object as an ISO 8601 time string, using UTC time like 'T00:00:00Z' if local=0, or local time with UTC offset like 'T17:00:00-07:00' if local=1 """ if local: h = self._localHour m = self._localMinute useTz = 1 else: h = self._utcHour m = self._utcMinute useTz = 0 s = self._second ms = self._milliSecond * 100 rt = "T%02d:%02d:%02d" % (h, m, s) if ms: t = "%d" % ms if len(t) > 3: t = t[:3] while(t and t[-1] == '0'): t = t[:-1] rt += "," + t if not useTz or (not self._tzHourOffset and not self._tzMinuteOffset): rt += "Z" else: if self._tzHourOffset < 0: sign = "-" tzh = -1 * self._tzHourOffset else: sign = "+" tzh = self._tzHourOffset rt += ("%s%02d:%02d" % (sign, tzh, self._tzMinuteOffset)) return rt def asRFC822DateTime(self, local=0): """ Represents this DT object as an RFC 1123 (which updated RFC 822) date string, using UTC time like 'Mon, 01 Jan 2001 00:00:00 GMT' if local=0, or local time with time zone indicator or offset like 'Sun, 31 Dec 2000 17:00:00 MDT' if local=1. Although RFC 822 allows the weekday to be optional, it is always included in the returned string. """ if local: wday = self.abbreviatedWeekdayNameTable[self._localWeekday] mon = self.abbreviatedMonthNameTable[self._localMonth] day = self._localDay year = self._localYear hour = self._localHour minute = self._localMinute # RFC 822 only allows certain timezone names if self._tzName and self._tzName in ['GMT', 'EST', 'EDT', 'CST', 'CDT', 'MST', 'MDT', 'PST', 'PDT']: tz = self._tzName else: tz = '%+03d%02d' % (self._tzHourOffset, self._tzMinuteOffset) else: wday = self.abbreviatedWeekdayNameTable[self._utcWeekday] mon = self.abbreviatedMonthNameTable[self._utcMonth] day = self._utcDay year = self._utcYear hour = self._utcHour minute = self._utcMinute tz = "GMT" # "Thu, 04 Jan 2001 09:15:39 MDT" # RFC 1123 changed the RFC 822 format to use 4-digit years return "%s, %02d %s %d %02d:%02d:%02d %s" % (wday, day, mon, year, hour, minute, self._second, tz) def asPythonTime(self, local=0): """ Returns the stored date and time as a float indicating the number of seconds since the local machine's epoch. """ return time.mktime(self.asPythonTimeTuple(local)) def asPythonTimeTuple(self, local=0): """ Returns the stored date and time as a Python time tuple, as documented in the time module. If the tuple is going to be passed to a function that expects the local time, set local=1. The Daylight Savings flag is always -1, which means unknown, and may or may not have ramifications. """ if local: return (self._localYear, self._localMonth, self._localDay, self._localHour, self._localMinute, self._second, self._localWeekday, self._localDayOfYear, -1) else: return (self._utcYear, self._utcMonth, self._utcDay, self._utcHour, self._utcMinute, self._second, self._utcWeekday, self._utcDayOfYear, -1) def year(self, local=0): """ Returns the year component of the stored date and time as an int like 2001. """ if local: return self._localYear return self._utcYear def month(self, local=0): """ Returns the month component of the stored date and time as an int in the range 0-11. """ if local: return self._localMonth return self._utcMonth def monthName(self, local=0): """ Returns the month component of the stored date and time as a string like 'January'. """ if local: return self.monthNameTable[self._localMonth] return self.monthNameTable[self._utcMonth] def abbreviatedMonthName(self, local=0): """ Returns the month component of the stored date and time as a string like 'Jan'. """ if local: return self.abbreviatedMonthNameTable[self._localMonth] return self.abbreviatedMonthNameTable[self._utcMonth] def day(self, local=0): """ Returns the day component of the stored date and time as an integer in the range 1-31. """ if local: return self._localDay return self._utcDay def dayOfYear(self, local=0): """ Returns the day of year component of the stored date and time as an int in the range 1-366. """ if local: return self._localDayOfYear return self._utcDayOfYear def dayOfWeek(self, local=0): """ Returns the day of week component of the stored date and time as an int in the range 0-6 (0=Monday). """ if local: return self._localWeekday return self._utcWeekday def hour(self, local=0): """ Returns the hour component of the stored date and time as an int in the range 0-23. """ if local: return self._localHour return self._utcHour def minute(self, local=0): """ Returns the minute component of the stored date and time as an int in the range 0-59. """ if local: return self._localMinute return self._utcMinute def second(self): """ Returns the second component of the stored date and time as an int in the range 0-59. """ return self._second def milliSecond(self): """ Returns the millisecond component of the stored date and time as an int in the range 0-999. """ return self._milliSecond def tzName(self): """ Returns the local time's time zone name component of the stored date and time as a string like 'MST'. """ return self._tzName def tzHourOffset(self): """ Returns the local time's hour offset from GMT component of the stored date and time as an int, typically in the range -12 to 14. """ return self._tzHourOffset def tzMinuteOffset(self): """ Returns the local time's minute offset from GMT component of the stored date and time as an int in the range 0-59. """ return self._tzMinuteOffset def __normalizeMinute(self, minute): hourShift = 0 while minute < 0: hourShift -=1 minute += 60 while minute > 59: hourShift +=1 minute -= 60 return hourShift, minute def __normalizeHour(self, hour): dayShift = 0 while hour < 0: dayShift -=1 hour += 24 while hour > 23: dayShift +=1 hour -= 24 return dayShift, hour def __normalizeDate(self, day, month, year): # Returns a valid year, month and day, given a day value that is out # of the acceptable range. This is needed so that the correct local # date can be determined after adding the local time offset to the # UTC time. The time difference may result in the day being shifted, # for example Jan 1 may become Jan 0, which needs to be normalized # to Dec 31 of the preceding year. This function may also be used to # convert a Julian day (1-366) for the given year to a proper year, # month and day, if the month is initially set to 1. while (month < 1 or month > 12 or day < 1 or day > _month_days[calendar.isleap(year)][month] ): if month < 1: year -= 1 month += 12 elif month > 12: year += 1 month -= 12 elif day < 1: month -= 1 if month == 0: #Special case day += 31 else: day += _month_days[calendar.isleap(year)][month] elif day > _month_days[calendar.isleap(year)][month]: day -= _month_days[calendar.isleap(year)][month] month += 1 return year, month, day #Pythonic Interface __str__ = asISO8601DateTime def __cmp__(self, other): if isinstance(other, (str, unicode)): return cmp(self.asISO8601DateTime(), other) elif isinstance(other, (int, float)): return cmp(self.asPythonTime(), other) elif not isinstance(other, DT): raise TypeError("Cannot Compare DT with %s" % repr(other)) #Compare two instances #For now, compare our strings return cmp(self.asISO8601DateTime(), other.asISO8601DateTime()) def __hash__(self): return id(self) #For internal lookups abbreviatedMonthNameTable = ('ERR', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') monthNameTable = ('ERROR', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December') weekdayNameTable = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday') abbreviatedWeekdayNameTable = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun') # keyed by offset; # values are (GMT TZ, military TZ, most likely civ TZ, # most likely civ TZ if on summer/daylight savings time) tzNameTable = { +0 : ("GMT", "Zulu", "GMT", "BST"), +1 : ("GMT+1", "Alpha", "CET", "MEST"), +2 : ("GMT+2", "Bravo", "EET", ""), +3 : ("GMT+3", "Charlie", "BT", ""), +3.5 : ("GMT+3:30", "", "", ""), +4 : ("GMT+4", "Delta", "", ""), +4.5 : ("GMT+4:30", "", "", ""), +5 : ("GMT+5", "Echo", "", ""), +5.5 : ("GMT+5:30", "", "", ""), +6 : ("GMT+6", "Foxtrot", "", ""), +6.5 : ("GMT+6:30", "", "", ""), +7 : ("GMT+7", "Golf", "WAST", ""), +8 : ("GMT+8", "Hotel", "CCT", ""), +9 : ("GMT+9", "India", "JST", ""), +9.5 : ("GMT+9:30", "", "Australia Central Time", ""), +10 : ("GMT+10", "Kilo", "GST", ""), +10.5 : ("GMT+10:30", "", "", ""), +11 : ("GMT+11", "Lima", "", ""), +11.5 : ("GMT+11:30", "", "", ""), +12 : ("GMT+12", "Mike", "NZST", ""), +13 : ("GMT+13", "", "", ""), +14 : ("GMT+14", "", "", ""), -1 : ("GMT-1", "November", "WAT", ""), -2 : ("GMT-2", "Oscar", "AT", ""), -3 : ("GMT-3", "Papa", "", "ADT"), -3.5 : ("GMT-3", "", "", ""), -4 : ("GMT-4", "Quebec", "AST", "EDT"), -5 : ("GMT-5", "Romeo", "EST", "CDT"), -6 : ("GMT-6", "Sierra", "CST", "MDT"), -7 : ("GMT-7", "Tango", "MST", "PDT"), -8 : ("GMT-8", "Uniform", "PST", ""), -8.5 : ("GMT-8:30", "", "", "YDT"), -9 : ("GMT-9", "Victor", "YST", ""), -9.5 : ("GMT-9:30", "", "", "HDT"), -10 : ("GMT-10", "Whiskey", "AHST", ""), -11 : ("GMT-11", "XRay", "NT", ""), -12 : ("GMT-11", "Yankee", "IDLW", ""), } CENTURY="(?P[0-9]{2,2})" YEAR="(?P[0-9]{2,2})" MONTH="(?P[0-9]{2,2})" DAY="(?P[0-9]{2,2})" BASIC_DATE="%s?%s%s%s" % (CENTURY, YEAR, MONTH, DAY) EXTENDED_DATE="%s?%s-%s-%s" % (CENTURY, YEAR, MONTH, DAY) YEAR_AND_MONTH_DATE="(-|%s)%s-%s" % (CENTURY, YEAR, MONTH) YEAR_AND_MONTH_DATE_EXTENDED="-%s%s" % (YEAR, MONTH) YEAR_ONLY_DATE="(-|%s)%s" % (CENTURY, YEAR) CENTURY_ONLY_DATE=CENTURY DAY_OF_MONTH="--%s(?:-?%s)?" % (MONTH, DAY) DAY_ONLY_DATE="---%s" % (DAY) #build the list of calendar date expressions cd_expressions = [BASIC_DATE, EXTENDED_DATE, YEAR_AND_MONTH_DATE, YEAR_AND_MONTH_DATE_EXTENDED, YEAR_ONLY_DATE, CENTURY_ONLY_DATE, DAY_OF_MONTH, DAY_ONLY_DATE] cd_expressions = map(lambda x:"(?P%s)" % x, cd_expressions) ORDINAL_DAY="(?P[0-9]{3,3})" ORDINAL_DATE="(?P%s?%s-?%s)" % (CENTURY, YEAR, ORDINAL_DAY) ORDINAL_DATE_ONLY="(?P-%s)" % (ORDINAL_DAY) od_expressions = [ORDINAL_DATE, ORDINAL_DATE_ONLY] WEEK="(?P[0-9][0-9])" WEEK_DAY="(?P[1-7])" BASIC_WEEK_DATE="%s?%sW%s%s?" %(CENTURY, YEAR, WEEK, WEEK_DAY) EXTENDED_WEEK_DATE="%s?%s-W%s(?:-%s)?" %(CENTURY, YEAR, WEEK, WEEK_DAY) WEEK_IN_DECADE="-(?P[0-9])W%s%s" % (WEEK, WEEK_DAY) WEEK_IN_DECADE_EXTENDED="-(?P[0-9])-W%s-%s" % (WEEK, WEEK_DAY) WEEK_AND_DAY_BASIC="-W%s(?:-?%s)?"%(WEEK, WEEK_DAY) WEEKDAY_ONLY="-W?-%s" % (WEEK_DAY) #build the list of week date expressions wd_expressions=[BASIC_WEEK_DATE, EXTENDED_WEEK_DATE, WEEK_IN_DECADE, WEEK_IN_DECADE_EXTENDED, WEEK_AND_DAY_BASIC, WEEKDAY_ONLY] wd_expressions = map(lambda x:"(?P%s)" % x, wd_expressions) #Build the list of date expressions date_expressions = map(lambda x:"(?P%s)" % x, cd_expressions+od_expressions+wd_expressions) HOUR="(?P(?:0[0-9])|(?:1[0-9])|(?:2[0-4]))" MINUTE="(?P(?:[0-5][0-9])|(?:60))" SECOND="(?P(?:[0-5][0-9])|(?:60))" DECIMAL_SEPARATOR="(?:\.|,)" DECIMAL_VALUE="(?P[0-9]*)" BASIC_TIME_FORMAT="(?:%s%s%s(?:%s%s)?)" % (HOUR, MINUTE, SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE) EXTENDED_TIME_FORMAT="(?:%s:%s:%s(?:%s%s)?)" % (HOUR, MINUTE, SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE) HOUR_MINUTE_TIME="(?:%s:?%s(?:%s%s)?)" % (HOUR, MINUTE, DECIMAL_SEPARATOR, DECIMAL_VALUE) HOUR_TIME="(?:%s(?:%s%s)?)" % (HOUR, DECIMAL_SEPARATOR, DECIMAL_VALUE) MINUTE_SECOND_TIME="(?:-%s:?%s(?:%s%s)?)" % (MINUTE, SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE) MINUTE_TIME="(?:-%s(?:%s%s)?)" % (MINUTE, DECIMAL_SEPARATOR, DECIMAL_VALUE) SECOND_TIME="(?P--%s(?:%s%s)?)" % (SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE) #build the basic time expressions bt_expressions = [BASIC_TIME_FORMAT, EXTENDED_TIME_FORMAT, HOUR_MINUTE_TIME, HOUR_TIME, MINUTE_SECOND_TIME, MINUTE_TIME, SECOND_TIME] bt_expressions = map(lambda x:"(?P