import os, sys, types
from UserDict import UserDict
class WillNotRunError(Exception): pass
class PropertiesObject(UserDict):
"""A Properties Object.
A PropertiesObject represents, in a dictionary-like fashion, the values
found in a Properties.py file. That file is always included with a Webware
component to advertise its name, version, status, etc. Note that a Webware
component is a Python package that follows additional conventions.
Also, the top level Webware directory contains a Properties.py.
Component properties are often used for:
* generation of documentation
* runtime examination of components, especially prior to loading
PropertiesObject provides additional keys:
* filename - the filename from which the properties were read
* versionString - a nicely printable string of the version
* requiredPyVersionString - like versionString,
but for requiredPyVersion instead
* willRun - 1 if the component will run.
So far that means having the right Python version.
* willNotRunReason - defined only if willRun is 0,
contains a readable error message
Using a PropertiesObject is better than investigating the Properties.py
file directly, because the rules for determining derived keys and any
future convenience methods will all be provided here.
Usage example:
from MiscUtils.PropertiesObject import PropertiesObject
props = PropertiesObject(filename)
for item in props.items():
print '%s: %s' % item
Note: We don't normally suffix a class name with "Object" as we have
with this class, however, the name Properties.py is already used in
our containing package and all other packages.
"""
## Init and reading ##
def __init__(self, filename=None):
UserDict.__init__(self)
if filename:
self.readFileNamed(filename)
def loadValues(self, dict):
self.update(dict)
self.cleanPrivateItems()
def readFileNamed(self, filename):
self['filename'] = filename
results = {}
exec open(filename) in results
# @@ 2001-01-20 ce: try "...in self"
self.update(results)
self.cleanPrivateItems()
self.createDerivedItems()
## Self utility ##
def cleanPrivateItems(self):
"""Remove items whose keys start with a double underscore, such as __builtins__."""
for key in self.keys():
if key[:2] == '__':
del self[key]
def createDerivedItems(self):
self.createVersionString()
self.createRequiredPyVersionString()
self.createWillRun()
def _versionString(self, version):
"""Return the version number as a string.
For a sequence containing version information such as (2, 0, 0, 'pre'),
this returns a printable string such as '2.0pre'.
The micro version number is only excluded from the string if it is zero.
"""
ver = map(str, version)
numbers, rest = ver[:ver[2] == '0' and 2 or 3], ver[3:]
return '.'.join(numbers) + '-'.join(rest)
def createVersionString(self):
self['versionString'] = self._versionString(self['version'])
def createRequiredPyVersionString(self):
self['requiredPyVersionString'] = self._versionString(self['requiredPyVersion'])
def createWillRun(self):
self['willRun'] = 0
try:
# Invoke each of the checkFoo() methods
for key in self.willRunKeys():
methodName = 'check' + key[0].upper() + key[1:]
method = getattr(self, methodName)
method()
except WillNotRunError, msg:
self['willNotRunReason'] = msg
return
self['willRun'] = 1 # we passed all the tests
def willRunKeys(self):
"""Return keys to be examined before running the component.
This returns a list of all keys whose values should be examined in
order to determine if the component will run. Used by createWillRun().
"""
return ['requiredPyVersion', 'requiredOpSys', 'deniedOpSys', 'willRunFunc']
def checkRequiredPyVersion(self):
pyVer = getattr(sys, 'version_info', None)
if not pyVer:
# Prior 2.0 there was no version_info
# So we parse it out of .version which is a string
pyVer = sys.version.split(' ', 1)[0].split('.')
pyVer = map(int, pyVer)
if tuple(pyVer) < tuple(self['requiredPyVersion']):
raise WillNotRunError, 'Required Python ver is %s, but actual ver is %s.' % (
'.'.join(map(str, self['requiredPyVersion'])), '.'.join(map(str, pyVer)))
def checkRequiredOpSys(self):
requiredOpSys = self.get('requiredOpSys', None)
if requiredOpSys:
# We accept a string or list of strings
if type(requiredOpSys) is types.StringType:
requiredOpSys = [requiredOpSys]
if not os.name in requiredOpSys:
raise WillNotRunError, 'Required op sys is %s, but actual op sys is %s.' % (
'/'.join(requiredOpSys), os.name)
def checkDeniedOpSys(self):
deniedOpSys = self.get('deniedOpSys', None)
if deniedOpSys:
# We accept a string or list of strings
if type(deniedOpSys) is types.StringType:
deniedOpSys = [deniedOpSys]
if os.name in deniedOpSys:
raise WillNotRunError, 'Will not run on op sys %s and actual op sys is %s.' % (
'/'.join(deniedOpSys), os.name)
def checkRequiredSoftware(self):
"""Not implemented. No op right now."""
# Check required software
# @@ 2001-01-24 ce: TBD
# Issues include:
# - order of dependencies
# - circular dependencies
# - examining Properties and willRun of dependencies
reqSoft = self.get('requiredSoftware', None)
if reqSoft:
for soft in reqSoft:
# type, name, version
pass
def checkWillRunFunc(self):
willRunFunc = self.get('willRunFunc', None)
if willRunFunc:
whyNotMsg = willRunFunc()
if whyNotMsg:
raise WillNotRunError, whyNotMsg
|