Commit c527f901 authored by Guido van Rossum's avatar Guido van Rossum

Guess... :-)

parent 75b801d4
"""Configuration file parser.
A setup file consists of sections, lead by a "[section]" header,
and followed by "name: value" entries, with continuations and such in
the style of rfc822.
The option values can contain format strings which refer to other
values in the same section, or values in a special [DEFAULT] section.
For example:
something: %(dir)s/whatever
would resolve the "%(dir)s" to the value of dir. All reference
expansions are done late, on demand.
Intrinsic defaults can be specified by passing them into the
ConfigParser constructor as a dictionary.
class:
ConfigParser -- responsible for for parsing a list of
configuration files, and managing the parsed database.
methods:
__init__(defaults=None) -- create the parser and specify a
dictionary of intrinsic defaults. The
keys must be strings, the values must
be appropriate for %()s string
interpolation. Note that `name' is
always an intrinsic default; it's value
is the section's name.
sections() -- return all the configuration section names, sans DEFAULT
options(section) -- return list of configuration options for the named
section
read(*filenames) -- read and parse the list of named configuration files
get(section, option, raw=0) -- return a string value for the named
option. All % interpolations are
expanded in the return values, based on
the defaults passed into the constructor
and the DEFAULT section.
getint(section, options) -- like get(), but convert value to an integer
getfloat(section, options) -- like get(), but convert value to a float
getboolean(section, options) -- like get(), but convert value to
a boolean (currently defined as 0
or 1, only)
"""
import sys
import string
import regex
from types import ListType
SECTHEAD_RE = "^\[\([-A-Za-z0-9]*\)\][" + string.whitespace + "]*$"
secthead_cre = regex.compile(SECTHEAD_RE)
OPTION_RE = "^\([-A-Za-z0-9.]+\)\(:\|[" + string.whitespace + "]*=\)\(.*\)$"
option_cre = regex.compile(OPTION_RE)
DEFAULTSECT = "DEFAULT"
# exception classes
class Error:
def __init__(self, msg=''):
self.__msg = msg
def __repr__(self):
return self.__msg
class NoSectionError(Error):
def __init__(self, section):
Error.__init__(self, 'No section: %s' % section)
self.section = section
class DuplicateSectionError(Error):
def __init__(self, section):
Error.__init__(self, "Section %s already exists" % section)
self.section = section
class NoOptionError(Error):
def __init__(self, option, section):
Error.__init__(self, "No option `%s' in section: %s" %
(option, section))
self.option = option
self.section = section
class InterpolationError(Error):
def __init__(self, reference, option, section):
Error.__init__(self,
"Bad value substitution: sect `%s', opt `%s', ref `%s'"
% (section, option, reference))
self.reference = reference
self.option = option
self.section = section
class ConfigParser:
def __init__(self, defaults=None):
self.__sections = {}
if defaults is None:
self.__defaults = {}
else:
self.__defaults = defaults
def defaults(self):
return self.__defaults
def sections(self):
"""Return a list of section names, excluding [DEFAULT]"""
# self.__sections will never have [DEFAULT] in it
return self.__sections.keys()
def add_section(self, section):
"""Create a new section in the configuration.
Raise DuplicateSectionError if a section by the specified name
already exists.
"""
if self.__sections.has_key(section):
raise DuplicateSectionError(section)
self.__sections[section] = {}
def has_section(self, section):
"""Indicate whether the named section is present in the configuration.
The DEFAULT section is not acknowledged.
"""
return self.__sections.has_key(section)
def options(self, section):
try:
opts = self.__sections[section].copy()
except KeyError:
raise NoSectionError(section)
opts.update(self.__defaults)
return opts.keys()
def read(self, filenames):
"""Read and parse a list of filenames."""
if type(filenames) is type(''):
filenames = [filenames]
for file in filenames:
try:
fp = open(file, 'r')
self.__read(fp)
except IOError:
pass
def get(self, section, option, raw=0):
"""Get an option value for a given section.
All % interpolations are expanded in the return values, based
on the defaults passed into the constructor.
The section DEFAULT is special.
"""
try:
d = self.__sections[section].copy()
except KeyError:
if section == DEFAULTSECT:
d = {}
else:
raise NoSectionError(section)
d.update(self.__defaults)
option = string.lower(option)
try:
rawval = d[option]
except KeyError:
raise NoOptionError(option, section)
# do the string interpolation
if raw:
return rawval
try:
return rawval % d
except KeyError, key:
raise InterpolationError(key, option, section)
def __get(self, section, conv, option):
return conv(self.get(section, option))
def getint(self, section, option):
return self.__get(section, string.atoi, option)
def getfloat(self, section, option):
return self.__get(section, string.atof, option)
def getboolean(self, section, option):
v = self.get(section, option)
val = string.atoi(v)
if val not in (0, 1):
raise ValueError, 'Not a boolean: %s' % v
return val
def __read(self, fp):
"""Parse a sectioned setup file.
The sections in setup file contains a title line at the top,
indicated by a name in square brackets (`[]'), plus key/value
options lines, indicated by `name: value' format lines.
Continuation are represented by an embedded newline then
leading whitespace. Blank lines, lines beginning with a '#',
and just about everything else is ignored.
"""
cursect = None # None, or a dictionary
optname = None
lineno = 0
while 1:
line = fp.readline()
if not line:
break
lineno = lineno + 1
# comment or blank line?
if string.strip(line) == '' or line[0] in '#;':
continue
if string.lower(string.split(line)[0]) == 'rem' \
and line[0] == "r": # no leading whitespace
continue
# continuation line?
if line[0] in ' \t' and cursect <> None and optname:
value = string.strip(line)
if value:
cursect = cursect[optname] + '\n ' + value
# a section header?
elif secthead_cre.match(line) >= 0:
sectname = secthead_cre.group(1)
if self.__sections.has_key(sectname):
cursect = self.__sections[sectname]
elif sectname == DEFAULTSECT:
cursect = self.__defaults
else:
cursect = {'name': sectname}
self.__sections[sectname] = cursect
# So sections can't start with a continuation line.
optname = None
# an option line?
elif option_cre.match(line) >= 0:
optname, optval = option_cre.group(1, 3)
optname = string.lower(optname)
optval = string.strip(optval)
# allow empty values
if optval == '""':
optval = ''
cursect[optname] = optval
# an error
else:
print 'Error in %s at %d: %s', (fp.name, lineno, `line`)
...@@ -177,7 +177,7 @@ class _posixfile_: ...@@ -177,7 +177,7 @@ class _posixfile_:
# Hack by davem@magnet.com to get locking to go on freebsd; # Hack by davem@magnet.com to get locking to go on freebsd;
# additions for AIX by Vladimir.Marangozov@imag.fr # additions for AIX by Vladimir.Marangozov@imag.fr
import sys, os import sys, os
if sys.platform == 'freebsd2': if sys.platform in ('netbsd1', 'freebsd2', 'freebsd3'):
flock = struct.pack('lxxxxlxxxxlhh', \ flock = struct.pack('lxxxxlxxxxlhh', \
l_start, l_len, os.getpid(), l_type, l_whence) l_start, l_len, os.getpid(), l_type, l_whence)
elif sys.platform in ['aix3', 'aix4']: elif sys.platform in ['aix3', 'aix4']:
...@@ -190,7 +190,7 @@ class _posixfile_: ...@@ -190,7 +190,7 @@ class _posixfile_:
flock = fcntl.fcntl(self._file_.fileno(), cmd, flock) flock = fcntl.fcntl(self._file_.fileno(), cmd, flock)
if '?' in how: if '?' in how:
if sys.platform == 'freebsd2': if sys.platform in ('netbsd1', 'freebsd2', 'freebsd3'):
l_start, l_len, l_pid, l_type, l_whence = \ l_start, l_len, l_pid, l_type, l_whence = \
struct.unpack('lxxxxlxxxxlhh', flock) struct.unpack('lxxxxlxxxxlhh', flock)
elif sys.platform in ['aix3', 'aix4']: elif sys.platform in ['aix3', 'aix4']:
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
# on other systems (e.g. Mac, Windows), os.path provides the same # on other systems (e.g. Mac, Windows), os.path provides the same
# operations in a manner specific to that platform, and is an alias # operations in a manner specific to that platform, and is an alias
# to another module (e.g. macpath, ntpath). # to another module (e.g. macpath, ntpath).
"""Common pathname manipulations, Posix version.
Instead of importing this module
directly, import os and refer to this module as os.path.
"""
import os import os
import stat import stat
...@@ -16,6 +20,7 @@ import stat ...@@ -16,6 +20,7 @@ import stat
# (another function should be defined to do that). # (another function should be defined to do that).
def normcase(s): def normcase(s):
"""Normalize case of pathname. Has no effect under Posix"""
return s return s
...@@ -23,6 +28,7 @@ def normcase(s): ...@@ -23,6 +28,7 @@ def normcase(s):
# Trivial in Posix, harder on the Mac or MS-DOS. # Trivial in Posix, harder on the Mac or MS-DOS.
def isabs(s): def isabs(s):
"""Test whether a path is absolute"""
return s[:1] == '/' return s[:1] == '/'
...@@ -31,6 +37,7 @@ def isabs(s): ...@@ -31,6 +37,7 @@ def isabs(s):
# Insert a '/' unless the first part is empty or already ends in '/'. # Insert a '/' unless the first part is empty or already ends in '/'.
def join(a, *p): def join(a, *p):
"""Join two or more pathname components, inserting '/' as needed"""
path = a path = a
for b in p: for b in p:
if b[:1] == '/': if b[:1] == '/':
...@@ -48,6 +55,8 @@ def join(a, *p): ...@@ -48,6 +55,8 @@ def join(a, *p):
# Trailing '/'es are stripped from head unless it is the root. # Trailing '/'es are stripped from head unless it is the root.
def split(p): def split(p):
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is
everything after the final slash. Either part may be empty"""
import string import string
i = string.rfind(p, '/') + 1 i = string.rfind(p, '/') + 1
head, tail = p[:i], p[i:] head, tail = p[:i], p[i:]
...@@ -63,6 +72,8 @@ def split(p): ...@@ -63,6 +72,8 @@ def split(p):
# It is always true that root + ext == p. # It is always true that root + ext == p.
def splitext(p): def splitext(p):
"""Split the extension from a pathname. Extension is everything from the
last dot to the end. Returns "(root, ext)", either part may be empty"""
root, ext = '', '' root, ext = '', ''
for c in p: for c in p:
if c == '/': if c == '/':
...@@ -83,24 +94,29 @@ def splitext(p): ...@@ -83,24 +94,29 @@ def splitext(p):
# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
def splitdrive(p): def splitdrive(p):
"""Split a pathname into drive and path. On Posix, drive is always
empty"""
return '', p return '', p
# Return the tail (basename) part of a path. # Return the tail (basename) part of a path.
def basename(p): def basename(p):
"""Returns the final component of a pathname"""
return split(p)[1] return split(p)[1]
# Return the head (dirname) part of a path. # Return the head (dirname) part of a path.
def dirname(p): def dirname(p):
"""Returns the directory component of a pathname"""
return split(p)[0] return split(p)[0]
# Return the longest prefix of all list elements. # Return the longest prefix of all list elements.
def commonprefix(m): def commonprefix(m):
"Given a list of pathnames, returns the longest common leading component"
if not m: return '' if not m: return ''
prefix = m[0] prefix = m[0]
for item in m: for item in m:
...@@ -116,6 +132,7 @@ def commonprefix(m): ...@@ -116,6 +132,7 @@ def commonprefix(m):
# This will always return false on systems where os.lstat doesn't exist. # This will always return false on systems where os.lstat doesn't exist.
def islink(path): def islink(path):
"""Test whether a path is a symbolic link"""
try: try:
st = os.lstat(path) st = os.lstat(path)
except (os.error, AttributeError): except (os.error, AttributeError):
...@@ -127,6 +144,7 @@ def islink(path): ...@@ -127,6 +144,7 @@ def islink(path):
# This is false for dangling symbolic links. # This is false for dangling symbolic links.
def exists(path): def exists(path):
"""Test whether a path exists. Returns false for broken symbolic links"""
try: try:
st = os.stat(path) st = os.stat(path)
except os.error: except os.error:
...@@ -139,6 +157,7 @@ def exists(path): ...@@ -139,6 +157,7 @@ def exists(path):
# for the same path. # for the same path.
def isdir(path): def isdir(path):
"""Test whether a path is a directory"""
try: try:
st = os.stat(path) st = os.stat(path)
except os.error: except os.error:
...@@ -151,6 +170,7 @@ def isdir(path): ...@@ -151,6 +170,7 @@ def isdir(path):
# for the same path. # for the same path.
def isfile(path): def isfile(path):
"""Test whether a path is a regular file"""
try: try:
st = os.stat(path) st = os.stat(path)
except os.error: except os.error:
...@@ -161,6 +181,7 @@ def isfile(path): ...@@ -161,6 +181,7 @@ def isfile(path):
# Are two filenames really pointing to the same file? # Are two filenames really pointing to the same file?
def samefile(f1, f2): def samefile(f1, f2):
"""Test whether two pathnames reference the same actual file"""
s1 = os.stat(f1) s1 = os.stat(f1)
s2 = os.stat(f2) s2 = os.stat(f2)
return samestat(s1, s2) return samestat(s1, s2)
...@@ -170,6 +191,7 @@ def samefile(f1, f2): ...@@ -170,6 +191,7 @@ def samefile(f1, f2):
# (Not necessarily the same file descriptor!) # (Not necessarily the same file descriptor!)
def sameopenfile(fp1, fp2): def sameopenfile(fp1, fp2):
"""Test whether two open file objects reference the same file"""
s1 = os.fstat(fp1) s1 = os.fstat(fp1)
s2 = os.fstat(fp2) s2 = os.fstat(fp2)
return samestat(s1, s2) return samestat(s1, s2)
...@@ -179,6 +201,7 @@ def sameopenfile(fp1, fp2): ...@@ -179,6 +201,7 @@ def sameopenfile(fp1, fp2):
# describing the same file? # describing the same file?
def samestat(s1, s2): def samestat(s1, s2):
"""Test whether two stat buffers reference the same file"""
return s1[stat.ST_INO] == s2[stat.ST_INO] and \ return s1[stat.ST_INO] == s2[stat.ST_INO] and \
s1[stat.ST_DEV] == s2[stat.ST_DEV] s1[stat.ST_DEV] == s2[stat.ST_DEV]
...@@ -187,6 +210,7 @@ def samestat(s1, s2): ...@@ -187,6 +210,7 @@ def samestat(s1, s2):
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?) # (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
def ismount(path): def ismount(path):
"""Test whether a path is a mount point"""
try: try:
s1 = os.stat(path) s1 = os.stat(path)
s2 = os.stat(join(path, '..')) s2 = os.stat(join(path, '..'))
...@@ -207,11 +231,15 @@ def ismount(path): ...@@ -207,11 +231,15 @@ def ismount(path):
# For each directory under top (including top itself, but excluding # For each directory under top (including top itself, but excluding
# '.' and '..'), func(arg, dirname, filenames) is called, where # '.' and '..'), func(arg, dirname, filenames) is called, where
# dirname is the name of the directory and filenames is the list # dirname is the name of the directory and filenames is the list
# files files (and subdirectories etc.) in the directory. # of files (and subdirectories etc.) in the directory.
# The func may modify the filenames list, to implement a filter, # The func may modify the filenames list, to implement a filter,
# or to impose a different order of visiting. # or to impose a different order of visiting.
def walk(top, func, arg): def walk(top, func, arg):
"""walk(top,func,args) calls func(arg, d, files) for each directory "d"
in the tree rooted at "top" (including "top" itself). "files" is a list
of all the files and subdirs in directory "d".
"""
try: try:
names = os.listdir(top) names = os.listdir(top)
except os.error: except os.error:
...@@ -235,6 +263,8 @@ def walk(top, func, arg): ...@@ -235,6 +263,8 @@ def walk(top, func, arg):
# variable expansion.) # variable expansion.)
def expanduser(path): def expanduser(path):
"""Expand ~ and ~user constructions. If user or $HOME is unknown,
do nothing"""
if path[:1] <> '~': if path[:1] <> '~':
return path return path
i, n = 1, len(path) i, n = 1, len(path)
...@@ -262,6 +292,8 @@ def expanduser(path): ...@@ -262,6 +292,8 @@ def expanduser(path):
_varprog = None _varprog = None
def expandvars(path): def expandvars(path):
"""Expand shell variables of form $var and ${var}. Unknown variables
are left unchanged"""
global _varprog global _varprog
if '$' not in path: if '$' not in path:
return path return path
...@@ -292,6 +324,7 @@ def expandvars(path): ...@@ -292,6 +324,7 @@ def expandvars(path):
# if it contains symbolic links! # if it contains symbolic links!
def normpath(path): def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
import string import string
# Treat initial slashes specially # Treat initial slashes specially
slashes = '' slashes = ''
...@@ -303,8 +336,7 @@ def normpath(path): ...@@ -303,8 +336,7 @@ def normpath(path):
while i < len(comps): while i < len(comps):
if comps[i] == '.': if comps[i] == '.':
del comps[i] del comps[i]
elif comps[i] == '..' and i > 0 and \ elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
comps[i-1] not in ('', '..'):
del comps[i-1:i+1] del comps[i-1:i+1]
i = i-1 i = i-1
elif comps[i] == '' and i > 0 and comps[i-1] <> '': elif comps[i] == '' and i > 0 and comps[i-1] <> '':
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import struct import struct
import fcntl import fcntl
import FCNTL import FCNTL
import os import os, sys
from test_support import verbose from test_support import verbose
filename = '/tmp/delete-me' filename = '/tmp/delete-me'
...@@ -16,7 +16,12 @@ rv = fcntl.fcntl(f.fileno(), FCNTL.F_SETFL, os.O_NONBLOCK) ...@@ -16,7 +16,12 @@ rv = fcntl.fcntl(f.fileno(), FCNTL.F_SETFL, os.O_NONBLOCK)
if verbose: if verbose:
print 'Status from fnctl with O_NONBLOCK: ', rv print 'Status from fnctl with O_NONBLOCK: ', rv
lockdata = struct.pack('hhllhh', FCNTL.F_WRLCK, 0, 0, 0, 0, 0) if sys.platform in ('netbsd1', 'freebsd2', 'freebsd3'):
lockdata = struct.pack('lxxxxlxxxxlhh', 0, 0, 0, FCNTL.F_WRLCK, 0)
elif sys.platform in ['aix3', 'aix4']:
lockdata = struct.pack('hhlllii', FCNTL.F_WRLCK, 0, 0, 0, 0, 0, 0)
else:
lockdata = struct.pack('hhllhh', FCNTL.F_WRLCK, 0, 0, 0, 0, 0)
if verbose: if verbose:
print 'struct.pack: ', `lockdata` print 'struct.pack: ', `lockdata`
......
...@@ -45,6 +45,12 @@ test('split', 'this is the split function', ...@@ -45,6 +45,12 @@ test('split', 'this is the split function',
['this', 'is', 'the', 'split', 'function']) ['this', 'is', 'the', 'split', 'function'])
test('split', 'a|b|c|d', ['a', 'b', 'c', 'd'], '|') test('split', 'a|b|c|d', ['a', 'b', 'c', 'd'], '|')
test('split', 'a|b|c|d', ['a', 'b', 'c|d'], '|', 2) test('split', 'a|b|c|d', ['a', 'b', 'c|d'], '|', 2)
test('split', 'a b c d', ['a', 'b c d'], None, 1)
test('split', 'a b c d', ['a', 'b', 'c d'], None, 2)
test('split', 'a b c d', ['a', 'b', 'c', 'd'], None, 3)
test('split', 'a b c d', ['a', 'b', 'c', 'd'], None, 4)
test('split', 'a b c d', ['a', 'b', 'c', 'd'], None, 0)
test('split', 'a b c d', ['a', 'b', 'c d'], None, 2)
# join now works with any sequence type # join now works with any sequence type
class Sequence: class Sequence:
...@@ -67,6 +73,15 @@ test('rstrip', ' hello ', ' hello') ...@@ -67,6 +73,15 @@ test('rstrip', ' hello ', ' hello')
test('swapcase', 'HeLLo cOmpUteRs', 'hEllO CoMPuTErS') test('swapcase', 'HeLLo cOmpUteRs', 'hEllO CoMPuTErS')
test('translate', 'xyzabcdef', 'xyzxyz', transtable, 'def') test('translate', 'xyzabcdef', 'xyzxyz', transtable, 'def')
test('replace', 'one!two!three!', 'one@two!three!', '!', '@', 1)
test('replace', 'one!two!three!', 'one@two@three!', '!', '@', 2)
test('replace', 'one!two!three!', 'one@two@three@', '!', '@', 3)
test('replace', 'one!two!three!', 'one@two@three@', '!', '@', 4)
test('replace', 'one!two!three!', 'one@two@three@', '!', '@', 0)
test('replace', 'one!two!three!', 'one@two@three@', '!', '@')
test('replace', 'one!two!three!', 'one!two!three!', 'x', '@')
test('replace', 'one!two!three!', 'one!two!three!', 'x', '@', 2)
strop.whitespace strop.whitespace
strop.lowercase strop.lowercase
strop.uppercase strop.uppercase
...@@ -8,7 +8,7 @@ if time.ctime(t) <> time.asctime(time.localtime(t)): ...@@ -8,7 +8,7 @@ if time.ctime(t) <> time.asctime(time.localtime(t)):
print 'time.ctime(t) <> time.asctime(time.localtime(t))' print 'time.ctime(t) <> time.asctime(time.localtime(t))'
time.daylight time.daylight
if int(time.mktime(time.localtime(t))) <> int(t): if long(time.mktime(time.localtime(t))) <> long(t):
print 'time.mktime(time.localtime(t)) <> t' print 'time.mktime(time.localtime(t)) <> t'
time.sleep(1.2) time.sleep(1.2)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment