### py_interface -- A Python-implementation of an Erlang node ### ### $Id: erl_common.py,v 1.9 2002/05/28 22:09:24 tab Exp $ ### ### Copyright (C) 2002 Tomas Abrahamsson ### ### Author: Tomas Abrahamsson ### ### This file is part of the Py-Interface library ### ### This library is free software; you can redistribute it and/or ### modify it under the terms of the GNU Library General Public ### License as published by the Free Software Foundation; either ### version 2 of the License, or (at your option) any later version. ### ### This library is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ### Library General Public License for more details. ### ### You should have received a copy of the GNU Library General Public ### License along with this library; if not, write to the Free ### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ### erl_common.py -- common utility functions import os import string import socket def ReadInt1(s): """Convert first byte from a string to an unsigned 8-bit integer.""" return ord(s[0]) def ReadInt2(s): """Convert first two bytes from a string to an unsigned 16-bit integer.""" return (ord(s[0]) << 8) + \ (ord(s[1]) << 0) def ReadInt4(s): """Convert first four bytes from a string to an unsigned 32-bit integer. Returns integer | long In Python on a 32-bit machine, integers are signed and within the range -2^31 .. (2^31 - 1). If the 32-bit integer fits within this range, an integer is returned, otherwise a long is returned.""" l4 = (long(ord(s[0])) << 24) + \ (ord(s[1]) << 16) + \ (ord(s[2]) << 8) + \ (ord(s[3]) << 0) try: i4 = int(l4) return i4 except OverflowError: return l4 def PackInt1(i): """Converts an unsigned 8-bit integer/long into a string, 1 byte long.""" return chr(i & 255) def PackInt2(i): """Converts an unsigned 16-bit integer/long into a string, 2 byte long.""" return chr((i >> 8) & 255) + \ chr((i >> 0) & 255) def PackInt4(i): """Converts an unsigned 32-bit integer/long into a string, 4 byte long.""" return chr((i >> 24) & 255) + \ chr((i >> 16) & 255) + \ chr((i >> 8) & 255) + \ chr((i >> 0) & 255) def AlignNodeName(nodeName, useShortNodeNames=1): """Make sure the node name is a valid node name. NODE-NAME = string A node name, possibly containing an "@" USE-SHORT-NODE-NAMES = bool Whether to align using short node names or not. If not, then long node names are assumed. Returns: string Throws: nothing The returned node name: 1. If the NODE-NAME contains an `@', then NODE-NAME is returned unchanged 2. If the NODE-NAME does not contain an `@', then the returned node name is constructed as NODE-NAME + "@" + host-name, where host-name is either on short or long form, depending on USE-SHORT-NODE-NAMES. """ if useShortNodeNames: return AlignShortNodeName(nodeName) else: return AlignLongNodeName(nodeName) def AlignShortNodeName(nodeName): """Align a node, use short hostname if needed. See doc for AlignNodeName""" if "@" in nodeName: return nodeName fqdn = GetFullyQualifiedHostName() shortHostName = string.split(fqdn, ".")[0] return nodeName + "@" + shortHostName def AlignLongNodeName(nodeName): """Align a node, use long hostname if needed. See doc for AlignNodeName""" if "@" in nodeName: return nodeName fqdn = GetFullyQualifiedHostName() return nodeName + "@" + fqdn def GetFullyQualifiedHostName(name=""): """Get fully qualified domain name from name. An empty argument is interpreted as meaning the local host. First the hostname returned by gethostbyaddr() is checked, then possibly existing aliases. In case no FQDN is available, hostname is returned. """ try: # Try the builtin version if it exists. # It does in Python 2.0 return socket.getfqdn(name) except AttributeError, info: # This fallback code is provided for Python 1.5.2 and earlier # It is stolen with pride from Python 2.0 name = string.strip(name) if not name or name == '0.0.0.0': name = socket.gethostname() try: hostname, aliases, ipaddrs = socket.gethostbyaddr(name) except error: pass else: aliases.insert(0, hostname) for name in aliases: if '.' in name: break else: name = hostname return name def getenv(envName): """Read an environment variable. ENV-NAME = string The name of the environment variable Returns: "" | string The env-value, or "" if the env-name isn't defined Throws: nothing """ if os.environ.has_key(envName): return os.environ[envName] else: return "" def IndexSeq(seq): """Given a sequence, return a list of tuples (i, elem[i]). Example: IndexSeq(["a", "b", "c"]) ==> [(0, "a"), (1, "b"), (2, "c")].""" return map(None, range(len(seq)), seq) _logfilename = None def SetLogFile(fileName): """Setup a file name to log to FILE-NAME = string The name of a file to log to Returns: void Sets up a log file. """ global _logfilename def Log(str): """Log a string to the log file STR = string The string to log (without trailing new-line) Returns: void Throws: nothing Logs a string to the file set up by logfilename. The file is opened only during the logging. It is kept closed between calls to this function. """ global _logfilename if _logfilename != None: try: f = open(_logfilename, "w") f.write(str) f.write("\n") f.flush() f.close() except IOError, info: # Silently ignore pass _modulesToDebug = [] _debugAllModules = 0 _debugFileName = None def DebugToFile(fileName): """Setup a file name to print debugging messages to FILE-NAME = string The name of a file to log to Returns: void Throws: nothing Sets up a file to print debugging messages to. """ global _debugFileName _debugFileName = fileName def DebugOnAll(): """Turn on debugging for all modules No arguments Returns: void Throws: nothing Turns on debugging flag for all modules """ global _modulesToDebug, _debugAllModules _debugAllModules = 1 def DebugOn(moduleList): """Turn on debugging for selected modules MODULE-LIST = list(string) Returns: void Throws: nothing Turns on debugging flag for selected modules """ global _modulesToDebug, _debugAllModules _debugAllModules = 0 _modulesToDebug = moduleList def Debug(module, txt): """Print a debug text MODULE = string Name of the module that is printing the debug text TXT = string The text to print as debugging message Returns: void Throws: nothing Prints a debugging text. The text is printed if debugging for the module is turned on, see DebugOnAll and DebugOn. The debug message is printed to stdout, except if debugging has been set to go to a file, see DebugToFile. """ global _modulesToDebug, _debugAllModules if _debugAllModules or module in _modulesToDebug: _DebugEmitText("%s: %s" % (module, txt)) def DebugHex(module, txt, msg): """Print a debug text and a hexdump of a message MODULE = string Name of the module that is printing the debug text TXT = string The text to print as debugging message MSG = string The message to hexdump Returns: void Throws: nothing Prints a debugging text. The text is printed if debugging for the module is turned on, see DebugOnAll and DebugOn. The debug message is printed to stdout, except if debugging has been set to go to a file, see DebugToFile. """ hexMsg = HexDumpFormat(msg) _DebugEmitText("%s: %s\n%s" % (module, txt, hexMsg)) def _DebugEmitText(txt): global _debugFileName if _debugFileName == None: print txt else: try: f = open(_debugFileName, "w") f.write(txt) f.flush() f.close() except IOError, info: # Silently ignore pass def _HexDumpFormat(string): def dump_chars(addr, s): hexString = "" ascString = "" for i, c in map(None, range(len(s)), s): if i > 0: hexString = hexString + " " hexString = hexString + ("%02x" % ord(c)) if (c < " ") or ((ord(c) >= 128) and (ord(c) < 160)): ascString = ascString + "." else: ascString = ascString + c numFill = 16 - len(s) hexString = hexString + " " * numFill addrString = "%04x" % addr return addrString + ": " + hexString + " " + ascString remaining_chars = string; addr = 0 result = "" while len(remaining_chars) > 0: if len(remaining_chars) < 16: result = result + dump_chars(addr, remaining_chars) remaining_chars = "" else: result = result + dump_chars(addr, remaining_chars[:16]) remaining_chars = remaining_chars[16:] addr = addr + 16 return result class Callback: """This class provides a callback object. Here's how to use it: def MyFunction1(...): pass def MyFunction2(..., arg1, arg2, arg3): pass def MyFunction3(..., arg1, arg2, arg3, kw1=None, kw2=None): pass cb1 = Callback(MyFunction1) cb2 = Callback(MyFunction2, 1, 2, 3) cb3 = Callback(MyFunction3, 1, 2, 3, kw1=4, kw2=5) RegisterCallback(cb1) RegisterCallback(cb2) RegisterCallback(cb3) Any arguments that the invoker uses are tacked on before the first callback-argument, hence the `...' in the function definitions above. This example might clarify: def MyFunction(s1, s2, arg1, arg2): print "s1=%s, s2=%s, arg1=%s, arg2=%s" % (`s1`, `s2`, `arg1`, `arg2`) mycb = Callback(MyFunction, 3, 4) RegisterCallback(mycb) ... #someone calls: cb(1, 2) # This will cause MyFunction to be invoked as MyFuntion(1,2,3,4), # the arguments 1 and 2 tacked on to the front, # coming from the callback invoker, and arguments 3 and 4 # come from the creation of the callback object # Thus, the following will be printed: s1=1, s2=2, arg1=3, arg2=4 """ def __init__(self, callback, *optArgs, **namedArgs): self.callback = callback self.optArgs = optArgs self.namedArgs = namedArgs def __call__(self, *extraArgs): try: return apply(self.callback, extraArgs+self.optArgs, self.namedArgs) except KeyboardInterrupt: raise except: print "Error in VCallback %s" % self.__repr__() raise def __repr__(self): return "" % `self.callback` class VCallback: """This class provides a callback object. It is similar to the Callback (see the doc for this class), but is intended to be used in a situation where you have already collected the optional args and the keyword args. Here's an example of when to use this class instead of the Callback class: def DefineRegisterCallback(cbfn, *optArgs, *namedArgs): cb = VCallback(cbfn, optArgs namedArgs) RegisterCallback(cb) ... DefineRegisterCallback(MyFunction, 1, 2) """ def __init__(self, callback, optArgs, namedArgs): self.callback = callback self.optArgs = optArgs self.namedArgs = namedArgs def __call__(self, *extraArgs): try: return apply(self.callback, extraArgs+self.optArgs, self.namedArgs) except KeyboardInterrupt: raise except: print "Error in VCallback %s" % self.__repr__() print " extraArgs=%s" % `extraArgs` print " self.optArgs=%s" % `self.optArgs` print " self.namedArgs=%s" % `self.namedArgs` raise def __repr__(self): return "" % `self.callback`