### 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 <tab@lysator.liu.se>
###
### 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 "<Callback to %s>" % `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 "<VCallback to %s>" % `self.callback`
syntax highlighted by Code2HTML, v. 0.9.1