clean up
This commit is contained in:
parent
aadf6d6914
commit
3fe0c9eaa8
@ -1,656 +0,0 @@
|
||||
"""A collection of string operations (most are no longer used).
|
||||
|
||||
Warning: most of the code you see here isn't normally used nowadays.
|
||||
Beginning with Python 1.6, many of these functions are implemented as
|
||||
methods on the standard string object. They used to be implemented by
|
||||
a built-in module called strop, but strop is now obsolete itself.
|
||||
|
||||
Public module variables:
|
||||
|
||||
whitespace -- a string containing all characters considered whitespace
|
||||
lowercase -- a string containing all characters considered lowercase letters
|
||||
uppercase -- a string containing all characters considered uppercase letters
|
||||
letters -- a string containing all characters considered letters
|
||||
digits -- a string containing all characters considered decimal digits
|
||||
hexdigits -- a string containing all characters considered hexadecimal digits
|
||||
octdigits -- a string containing all characters considered octal digits
|
||||
punctuation -- a string containing all characters considered punctuation
|
||||
printable -- a string containing all characters considered printable
|
||||
|
||||
"""
|
||||
|
||||
# Some strings for ctype-style character classification
|
||||
whitespace = ' \t\n\r\v\f'
|
||||
lowercase = 'abcdefghijklmnopqrstuvwxyz'
|
||||
uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
letters = lowercase + uppercase
|
||||
ascii_lowercase = lowercase
|
||||
ascii_uppercase = uppercase
|
||||
ascii_letters = ascii_lowercase + ascii_uppercase
|
||||
digits = '0123456789'
|
||||
hexdigits = digits + 'abcdef' + 'ABCDEF'
|
||||
octdigits = '01234567'
|
||||
punctuation = """!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
|
||||
printable = digits + letters + punctuation + whitespace
|
||||
|
||||
# Case conversion helpers
|
||||
# Use str to convert Unicode literal in case of -U
|
||||
l = map(chr, xrange(256))
|
||||
_idmap = str('').join(l)
|
||||
del l
|
||||
|
||||
# Functions which aren't available as string methods.
|
||||
|
||||
# Capitalize the words in a string, e.g. " aBc dEf " -> "Abc Def".
|
||||
def capwords(s, sep=None):
|
||||
"""capwords(s [,sep]) -> string
|
||||
|
||||
Split the argument into words using split, capitalize each
|
||||
word using capitalize, and join the capitalized words using
|
||||
join. If the optional second argument sep is absent or None,
|
||||
runs of whitespace characters are replaced by a single space
|
||||
and leading and trailing whitespace are removed, otherwise
|
||||
sep is used to split and join the words.
|
||||
|
||||
"""
|
||||
return (sep or ' ').join(x.capitalize() for x in s.split(sep))
|
||||
|
||||
|
||||
# Construct a translation string
|
||||
_idmapL = None
|
||||
def maketrans(fromstr, tostr):
|
||||
"""maketrans(frm, to) -> string
|
||||
|
||||
Return a translation table (a string of 256 bytes long)
|
||||
suitable for use in string.translate. The strings frm and to
|
||||
must be of the same length.
|
||||
|
||||
"""
|
||||
if len(fromstr) != len(tostr):
|
||||
raise ValueError, "maketrans arguments must have same length"
|
||||
global _idmapL
|
||||
if not _idmapL:
|
||||
_idmapL = list(_idmap)
|
||||
L = _idmapL[:]
|
||||
fromstr = map(ord, fromstr)
|
||||
for i in range(len(fromstr)):
|
||||
L[fromstr[i]] = tostr[i]
|
||||
return ''.join(L)
|
||||
|
||||
|
||||
|
||||
####################################################################
|
||||
import re as _re
|
||||
|
||||
class _multimap:
|
||||
"""Helper class for combining multiple mappings.
|
||||
|
||||
Used by .{safe_,}substitute() to combine the mapping and keyword
|
||||
arguments.
|
||||
"""
|
||||
def __init__(self, primary, secondary):
|
||||
self._primary = primary
|
||||
self._secondary = secondary
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return self._primary[key]
|
||||
except KeyError:
|
||||
return self._secondary[key]
|
||||
|
||||
|
||||
class _TemplateMetaclass(type):
|
||||
pattern = r"""
|
||||
%(delim)s(?:
|
||||
(?P<escaped>%(delim)s) | # Escape sequence of two delimiters
|
||||
(?P<named>%(id)s) | # delimiter and a Python identifier
|
||||
{(?P<braced>%(id)s)} | # delimiter and a braced identifier
|
||||
(?P<invalid>) # Other ill-formed delimiter exprs
|
||||
)
|
||||
"""
|
||||
|
||||
def __init__(cls, name, bases, dct):
|
||||
super(_TemplateMetaclass, cls).__init__(name, bases, dct)
|
||||
if 'pattern' in dct:
|
||||
pattern = cls.pattern
|
||||
else:
|
||||
pattern = _TemplateMetaclass.pattern % {
|
||||
'delim' : _re.escape(cls.delimiter),
|
||||
'id' : cls.idpattern,
|
||||
}
|
||||
cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE)
|
||||
|
||||
|
||||
class Template:
|
||||
"""A string class for supporting $-substitutions."""
|
||||
__metaclass__ = _TemplateMetaclass
|
||||
|
||||
delimiter = '$'
|
||||
idpattern = r'[_a-z][_a-z0-9]*'
|
||||
|
||||
def __init__(self, template):
|
||||
self.template = template
|
||||
|
||||
# Search for $$, $identifier, ${identifier}, and any bare $'s
|
||||
|
||||
def _invalid(self, mo):
|
||||
i = mo.start('invalid')
|
||||
lines = self.template[:i].splitlines(True)
|
||||
if not lines:
|
||||
colno = 1
|
||||
lineno = 1
|
||||
else:
|
||||
colno = i - len(''.join(lines[:-1]))
|
||||
lineno = len(lines)
|
||||
raise ValueError('Invalid placeholder in string: line %d, col %d' %
|
||||
(lineno, colno))
|
||||
|
||||
def substitute(*args, **kws):
|
||||
if not args:
|
||||
raise TypeError("descriptor 'substitute' of 'Template' object "
|
||||
"needs an argument")
|
||||
self, args = args[0], args[1:] # allow the "self" keyword be passed
|
||||
if len(args) > 1:
|
||||
raise TypeError('Too many positional arguments')
|
||||
if not args:
|
||||
mapping = kws
|
||||
elif kws:
|
||||
mapping = _multimap(kws, args[0])
|
||||
else:
|
||||
mapping = args[0]
|
||||
# Helper function for .sub()
|
||||
def convert(mo):
|
||||
# Check the most common path first.
|
||||
named = mo.group('named') or mo.group('braced')
|
||||
if named is not None:
|
||||
val = mapping[named]
|
||||
# We use this idiom instead of str() because the latter will
|
||||
# fail if val is a Unicode containing non-ASCII characters.
|
||||
return '%s' % (val,)
|
||||
if mo.group('escaped') is not None:
|
||||
return self.delimiter
|
||||
if mo.group('invalid') is not None:
|
||||
self._invalid(mo)
|
||||
raise ValueError('Unrecognized named group in pattern',
|
||||
self.pattern)
|
||||
return self.pattern.sub(convert, self.template)
|
||||
|
||||
def safe_substitute(*args, **kws):
|
||||
if not args:
|
||||
raise TypeError("descriptor 'safe_substitute' of 'Template' object "
|
||||
"needs an argument")
|
||||
self, args = args[0], args[1:] # allow the "self" keyword be passed
|
||||
if len(args) > 1:
|
||||
raise TypeError('Too many positional arguments')
|
||||
if not args:
|
||||
mapping = kws
|
||||
elif kws:
|
||||
mapping = _multimap(kws, args[0])
|
||||
else:
|
||||
mapping = args[0]
|
||||
# Helper function for .sub()
|
||||
def convert(mo):
|
||||
named = mo.group('named') or mo.group('braced')
|
||||
if named is not None:
|
||||
try:
|
||||
# We use this idiom instead of str() because the latter
|
||||
# will fail if val is a Unicode containing non-ASCII
|
||||
return '%s' % (mapping[named],)
|
||||
except KeyError:
|
||||
return mo.group()
|
||||
if mo.group('escaped') is not None:
|
||||
return self.delimiter
|
||||
if mo.group('invalid') is not None:
|
||||
return mo.group()
|
||||
raise ValueError('Unrecognized named group in pattern',
|
||||
self.pattern)
|
||||
return self.pattern.sub(convert, self.template)
|
||||
|
||||
|
||||
|
||||
####################################################################
|
||||
# NOTE: Everything below here is deprecated. Use string methods instead.
|
||||
# This stuff will go away in Python 3.0.
|
||||
|
||||
# Backward compatible names for exceptions
|
||||
index_error = ValueError
|
||||
atoi_error = ValueError
|
||||
atof_error = ValueError
|
||||
atol_error = ValueError
|
||||
|
||||
# convert UPPER CASE letters to lower case
|
||||
def lower(s):
|
||||
"""lower(s) -> string
|
||||
|
||||
Return a copy of the string s converted to lowercase.
|
||||
|
||||
"""
|
||||
return s.lower()
|
||||
|
||||
# Convert lower case letters to UPPER CASE
|
||||
def upper(s):
|
||||
"""upper(s) -> string
|
||||
|
||||
Return a copy of the string s converted to uppercase.
|
||||
|
||||
"""
|
||||
return s.upper()
|
||||
|
||||
# Swap lower case letters and UPPER CASE
|
||||
def swapcase(s):
|
||||
"""swapcase(s) -> string
|
||||
|
||||
Return a copy of the string s with upper case characters
|
||||
converted to lowercase and vice versa.
|
||||
|
||||
"""
|
||||
return s.swapcase()
|
||||
|
||||
# Strip leading and trailing tabs and spaces
|
||||
def strip(s, chars=None):
|
||||
"""strip(s [,chars]) -> string
|
||||
|
||||
Return a copy of the string s with leading and trailing
|
||||
whitespace removed.
|
||||
If chars is given and not None, remove characters in chars instead.
|
||||
If chars is unicode, S will be converted to unicode before stripping.
|
||||
|
||||
"""
|
||||
return s.strip(chars)
|
||||
|
||||
# Strip leading tabs and spaces
|
||||
def lstrip(s, chars=None):
|
||||
"""lstrip(s [,chars]) -> string
|
||||
|
||||
Return a copy of the string s with leading whitespace removed.
|
||||
If chars is given and not None, remove characters in chars instead.
|
||||
|
||||
"""
|
||||
return s.lstrip(chars)
|
||||
|
||||
# Strip trailing tabs and spaces
|
||||
def rstrip(s, chars=None):
|
||||
"""rstrip(s [,chars]) -> string
|
||||
|
||||
Return a copy of the string s with trailing whitespace removed.
|
||||
If chars is given and not None, remove characters in chars instead.
|
||||
|
||||
"""
|
||||
return s.rstrip(chars)
|
||||
|
||||
|
||||
# Split a string into a list of space/tab-separated words
|
||||
def split(s, sep=None, maxsplit=-1):
|
||||
"""split(s [,sep [,maxsplit]]) -> list of strings
|
||||
|
||||
Return a list of the words in the string s, using sep as the
|
||||
delimiter string. If maxsplit is given, splits at no more than
|
||||
maxsplit places (resulting in at most maxsplit+1 words). If sep
|
||||
is not specified or is None, any whitespace string is a separator.
|
||||
|
||||
(split and splitfields are synonymous)
|
||||
|
||||
"""
|
||||
return s.split(sep, maxsplit)
|
||||
splitfields = split
|
||||
|
||||
# Split a string into a list of space/tab-separated words
|
||||
def rsplit(s, sep=None, maxsplit=-1):
|
||||
"""rsplit(s [,sep [,maxsplit]]) -> list of strings
|
||||
|
||||
Return a list of the words in the string s, using sep as the
|
||||
delimiter string, starting at the end of the string and working
|
||||
to the front. If maxsplit is given, at most maxsplit splits are
|
||||
done. If sep is not specified or is None, any whitespace string
|
||||
is a separator.
|
||||
"""
|
||||
return s.rsplit(sep, maxsplit)
|
||||
|
||||
# Join fields with optional separator
|
||||
def join(words, sep = ' '):
|
||||
"""join(list [,sep]) -> string
|
||||
|
||||
Return a string composed of the words in list, with
|
||||
intervening occurrences of sep. The default separator is a
|
||||
single space.
|
||||
|
||||
(joinfields and join are synonymous)
|
||||
|
||||
"""
|
||||
return sep.join(words)
|
||||
joinfields = join
|
||||
|
||||
# Find substring, raise exception if not found
|
||||
def index(s, *args):
|
||||
"""index(s, sub [,start [,end]]) -> int
|
||||
|
||||
Like find but raises ValueError when the substring is not found.
|
||||
|
||||
"""
|
||||
return s.index(*args)
|
||||
|
||||
# Find last substring, raise exception if not found
|
||||
def rindex(s, *args):
|
||||
"""rindex(s, sub [,start [,end]]) -> int
|
||||
|
||||
Like rfind but raises ValueError when the substring is not found.
|
||||
|
||||
"""
|
||||
return s.rindex(*args)
|
||||
|
||||
# Count non-overlapping occurrences of substring
|
||||
def count(s, *args):
|
||||
"""count(s, sub[, start[,end]]) -> int
|
||||
|
||||
Return the number of occurrences of substring sub in string
|
||||
s[start:end]. Optional arguments start and end are
|
||||
interpreted as in slice notation.
|
||||
|
||||
"""
|
||||
return s.count(*args)
|
||||
|
||||
# Find substring, return -1 if not found
|
||||
def find(s, *args):
|
||||
"""find(s, sub [,start [,end]]) -> in
|
||||
|
||||
Return the lowest index in s where substring sub is found,
|
||||
such that sub is contained within s[start,end]. Optional
|
||||
arguments start and end are interpreted as in slice notation.
|
||||
|
||||
Return -1 on failure.
|
||||
|
||||
"""
|
||||
return s.find(*args)
|
||||
|
||||
# Find last substring, return -1 if not found
|
||||
def rfind(s, *args):
|
||||
"""rfind(s, sub [,start [,end]]) -> int
|
||||
|
||||
Return the highest index in s where substring sub is found,
|
||||
such that sub is contained within s[start,end]. Optional
|
||||
arguments start and end are interpreted as in slice notation.
|
||||
|
||||
Return -1 on failure.
|
||||
|
||||
"""
|
||||
return s.rfind(*args)
|
||||
|
||||
# for a bit of speed
|
||||
_float = float
|
||||
_int = int
|
||||
_long = long
|
||||
|
||||
# Convert string to float
|
||||
def atof(s):
|
||||
"""atof(s) -> float
|
||||
|
||||
Return the floating point number represented by the string s.
|
||||
|
||||
"""
|
||||
return _float(s)
|
||||
|
||||
|
||||
# Convert string to integer
|
||||
def atoi(s , base=10):
|
||||
"""atoi(s [,base]) -> int
|
||||
|
||||
Return the integer represented by the string s in the given
|
||||
base, which defaults to 10. The string s must consist of one
|
||||
or more digits, possibly preceded by a sign. If base is 0, it
|
||||
is chosen from the leading characters of s, 0 for octal, 0x or
|
||||
0X for hexadecimal. If base is 16, a preceding 0x or 0X is
|
||||
accepted.
|
||||
|
||||
"""
|
||||
return _int(s, base)
|
||||
|
||||
|
||||
# Convert string to long integer
|
||||
def atol(s, base=10):
|
||||
"""atol(s [,base]) -> long
|
||||
|
||||
Return the long integer represented by the string s in the
|
||||
given base, which defaults to 10. The string s must consist
|
||||
of one or more digits, possibly preceded by a sign. If base
|
||||
is 0, it is chosen from the leading characters of s, 0 for
|
||||
octal, 0x or 0X for hexadecimal. If base is 16, a preceding
|
||||
0x or 0X is accepted. A trailing L or l is not accepted,
|
||||
unless base is 0.
|
||||
|
||||
"""
|
||||
return _long(s, base)
|
||||
|
||||
|
||||
# Left-justify a string
|
||||
def ljust(s, width, *args):
|
||||
"""ljust(s, width[, fillchar]) -> string
|
||||
|
||||
Return a left-justified version of s, in a field of the
|
||||
specified width, padded with spaces as needed. The string is
|
||||
never truncated. If specified the fillchar is used instead of spaces.
|
||||
|
||||
"""
|
||||
return s.ljust(width, *args)
|
||||
|
||||
# Right-justify a string
|
||||
def rjust(s, width, *args):
|
||||
"""rjust(s, width[, fillchar]) -> string
|
||||
|
||||
Return a right-justified version of s, in a field of the
|
||||
specified width, padded with spaces as needed. The string is
|
||||
never truncated. If specified the fillchar is used instead of spaces.
|
||||
|
||||
"""
|
||||
return s.rjust(width, *args)
|
||||
|
||||
# Center a string
|
||||
def center(s, width, *args):
|
||||
"""center(s, width[, fillchar]) -> string
|
||||
|
||||
Return a center version of s, in a field of the specified
|
||||
width. padded with spaces as needed. The string is never
|
||||
truncated. If specified the fillchar is used instead of spaces.
|
||||
|
||||
"""
|
||||
return s.center(width, *args)
|
||||
|
||||
# Zero-fill a number, e.g., (12, 3) --> '012' and (-3, 3) --> '-03'
|
||||
# Decadent feature: the argument may be a string or a number
|
||||
# (Use of this is deprecated; it should be a string as with ljust c.s.)
|
||||
def zfill(x, width):
|
||||
"""zfill(x, width) -> string
|
||||
|
||||
Pad a numeric string x with zeros on the left, to fill a field
|
||||
of the specified width. The string x is never truncated.
|
||||
|
||||
"""
|
||||
if not isinstance(x, basestring):
|
||||
x = repr(x)
|
||||
return x.zfill(width)
|
||||
|
||||
# Expand tabs in a string.
|
||||
# Doesn't take non-printing chars into account, but does understand \n.
|
||||
def expandtabs(s, tabsize=8):
|
||||
"""expandtabs(s [,tabsize]) -> string
|
||||
|
||||
Return a copy of the string s with all tab characters replaced
|
||||
by the appropriate number of spaces, depending on the current
|
||||
column, and the tabsize (default 8).
|
||||
|
||||
"""
|
||||
return s.expandtabs(tabsize)
|
||||
|
||||
# Character translation through look-up table.
|
||||
def translate(s, table, deletions=""):
|
||||
"""translate(s,table [,deletions]) -> string
|
||||
|
||||
Return a copy of the string s, where all characters occurring
|
||||
in the optional argument deletions are removed, and the
|
||||
remaining characters have been mapped through the given
|
||||
translation table, which must be a string of length 256. The
|
||||
deletions argument is not allowed for Unicode strings.
|
||||
|
||||
"""
|
||||
if deletions or table is None:
|
||||
return s.translate(table, deletions)
|
||||
else:
|
||||
# Add s[:0] so that if s is Unicode and table is an 8-bit string,
|
||||
# table is converted to Unicode. This means that table *cannot*
|
||||
# be a dictionary -- for that feature, use u.translate() directly.
|
||||
return s.translate(table + s[:0])
|
||||
|
||||
# Capitalize a string, e.g. "aBc dEf" -> "Abc def".
|
||||
def capitalize(s):
|
||||
"""capitalize(s) -> string
|
||||
|
||||
Return a copy of the string s with only its first character
|
||||
capitalized.
|
||||
|
||||
"""
|
||||
return s.capitalize()
|
||||
|
||||
# Substring replacement (global)
|
||||
def replace(s, old, new, maxreplace=-1):
|
||||
"""replace (str, old, new[, maxreplace]) -> string
|
||||
|
||||
Return a copy of string str with all occurrences of substring
|
||||
old replaced by new. If the optional argument maxreplace is
|
||||
given, only the first maxreplace occurrences are replaced.
|
||||
|
||||
"""
|
||||
return s.replace(old, new, maxreplace)
|
||||
|
||||
|
||||
# Try importing optional built-in module "strop" -- if it exists,
|
||||
# it redefines some string operations that are 100-1000 times faster.
|
||||
# It also defines values for whitespace, lowercase and uppercase
|
||||
# that match <ctype.h>'s definitions.
|
||||
|
||||
try:
|
||||
from strop import maketrans, lowercase, uppercase, whitespace
|
||||
letters = lowercase + uppercase
|
||||
except ImportError:
|
||||
pass # Use the original versions
|
||||
|
||||
########################################################################
|
||||
# the Formatter class
|
||||
# see PEP 3101 for details and purpose of this class
|
||||
|
||||
# The hard parts are reused from the C implementation. They're exposed as "_"
|
||||
# prefixed methods of str and unicode.
|
||||
|
||||
# The overall parser is implemented in str._formatter_parser.
|
||||
# The field name parser is implemented in str._formatter_field_name_split
|
||||
|
||||
class Formatter(object):
|
||||
def format(*args, **kwargs):
|
||||
if not args:
|
||||
raise TypeError("descriptor 'format' of 'Formatter' object "
|
||||
"needs an argument")
|
||||
self, args = args[0], args[1:] # allow the "self" keyword be passed
|
||||
try:
|
||||
format_string, args = args[0], args[1:] # allow the "format_string" keyword be passed
|
||||
except IndexError:
|
||||
if 'format_string' in kwargs:
|
||||
format_string = kwargs.pop('format_string')
|
||||
else:
|
||||
raise TypeError("format() missing 1 required positional "
|
||||
"argument: 'format_string'")
|
||||
return self.vformat(format_string, args, kwargs)
|
||||
|
||||
def vformat(self, format_string, args, kwargs):
|
||||
used_args = set()
|
||||
result = self._vformat(format_string, args, kwargs, used_args, 2)
|
||||
self.check_unused_args(used_args, args, kwargs)
|
||||
return result
|
||||
|
||||
def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
|
||||
if recursion_depth < 0:
|
||||
raise ValueError('Max string recursion exceeded')
|
||||
result = []
|
||||
for literal_text, field_name, format_spec, conversion in \
|
||||
self.parse(format_string):
|
||||
|
||||
# output the literal text
|
||||
if literal_text:
|
||||
result.append(literal_text)
|
||||
|
||||
# if there's a field, output it
|
||||
if field_name is not None:
|
||||
# this is some markup, find the object and do
|
||||
# the formatting
|
||||
|
||||
# given the field_name, find the object it references
|
||||
# and the argument it came from
|
||||
obj, arg_used = self.get_field(field_name, args, kwargs)
|
||||
used_args.add(arg_used)
|
||||
|
||||
# do any conversion on the resulting object
|
||||
obj = self.convert_field(obj, conversion)
|
||||
|
||||
# expand the format spec, if needed
|
||||
format_spec = self._vformat(format_spec, args, kwargs,
|
||||
used_args, recursion_depth-1)
|
||||
|
||||
# format the object and append to the result
|
||||
result.append(self.format_field(obj, format_spec))
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
|
||||
def get_value(self, key, args, kwargs):
|
||||
if isinstance(key, (int, long)):
|
||||
return args[key]
|
||||
else:
|
||||
return kwargs[key]
|
||||
|
||||
|
||||
def check_unused_args(self, used_args, args, kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def format_field(self, value, format_spec):
|
||||
return format(value, format_spec)
|
||||
|
||||
|
||||
def convert_field(self, value, conversion):
|
||||
# do any conversion on the resulting object
|
||||
if conversion is None:
|
||||
return value
|
||||
elif conversion == 's':
|
||||
return str(value)
|
||||
elif conversion == 'r':
|
||||
return repr(value)
|
||||
raise ValueError("Unknown conversion specifier {0!s}".format(conversion))
|
||||
|
||||
|
||||
# returns an iterable that contains tuples of the form:
|
||||
# (literal_text, field_name, format_spec, conversion)
|
||||
# literal_text can be zero length
|
||||
# field_name can be None, in which case there's no
|
||||
# object to format and output
|
||||
# if field_name is not None, it is looked up, formatted
|
||||
# with format_spec and conversion and then used
|
||||
def parse(self, format_string):
|
||||
return format_string._formatter_parser()
|
||||
|
||||
|
||||
# given a field_name, find the object it references.
|
||||
# field_name: the field being looked up, e.g. "0.name"
|
||||
# or "lookup[3]"
|
||||
# used_args: a set of which args have been used
|
||||
# args, kwargs: as passed in to vformat
|
||||
def get_field(self, field_name, args, kwargs):
|
||||
first, rest = field_name._formatter_field_name_split()
|
||||
|
||||
obj = self.get_value(first, args, kwargs)
|
||||
|
||||
# loop through the rest of the field_name, doing
|
||||
# getattr or getitem as needed
|
||||
for is_attr, i in rest:
|
||||
if is_attr:
|
||||
obj = getattr(obj, i)
|
||||
else:
|
||||
obj = obj[i]
|
||||
|
||||
return obj, first
|
@ -1,639 +0,0 @@
|
||||
"""Temporary files.
|
||||
|
||||
This module provides generic, low- and high-level interfaces for
|
||||
creating temporary files and directories. All of the interfaces
|
||||
provided by this module can be used without fear of race conditions
|
||||
except for 'mktemp'. 'mktemp' is subject to race conditions and
|
||||
should not be used; it is provided for backward compatibility only.
|
||||
|
||||
This module also provides some data items to the user:
|
||||
|
||||
TMP_MAX - maximum number of names that will be tried before
|
||||
giving up.
|
||||
template - the default prefix for all temporary names.
|
||||
You may change this to control the default prefix.
|
||||
tempdir - If this is set to a string before the first use of
|
||||
any routine from this module, it will be considered as
|
||||
another candidate location to store temporary files.
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
"NamedTemporaryFile", "TemporaryFile", # high level safe interfaces
|
||||
"SpooledTemporaryFile",
|
||||
"mkstemp", "mkdtemp", # low level safe interfaces
|
||||
"mktemp", # deprecated unsafe interface
|
||||
"TMP_MAX", "gettempprefix", # constants
|
||||
"tempdir", "gettempdir"
|
||||
]
|
||||
|
||||
|
||||
# Imports.
|
||||
|
||||
import io as _io
|
||||
import os as _os
|
||||
import errno as _errno
|
||||
from random import Random as _Random
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO as _StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO as _StringIO
|
||||
|
||||
try:
|
||||
import fcntl as _fcntl
|
||||
except ImportError:
|
||||
def _set_cloexec(fd):
|
||||
pass
|
||||
else:
|
||||
def _set_cloexec(fd):
|
||||
try:
|
||||
flags = _fcntl.fcntl(fd, _fcntl.F_GETFD, 0)
|
||||
except IOError:
|
||||
pass
|
||||
else:
|
||||
# flags read successfully, modify
|
||||
flags |= _fcntl.FD_CLOEXEC
|
||||
_fcntl.fcntl(fd, _fcntl.F_SETFD, flags)
|
||||
|
||||
|
||||
try:
|
||||
import thread as _thread
|
||||
except ImportError:
|
||||
import dummy_thread as _thread
|
||||
_allocate_lock = _thread.allocate_lock
|
||||
|
||||
_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
|
||||
if hasattr(_os, 'O_NOINHERIT'):
|
||||
_text_openflags |= _os.O_NOINHERIT
|
||||
if hasattr(_os, 'O_NOFOLLOW'):
|
||||
_text_openflags |= _os.O_NOFOLLOW
|
||||
|
||||
_bin_openflags = _text_openflags
|
||||
if hasattr(_os, 'O_BINARY'):
|
||||
_bin_openflags |= _os.O_BINARY
|
||||
|
||||
if hasattr(_os, 'TMP_MAX'):
|
||||
TMP_MAX = _os.TMP_MAX
|
||||
else:
|
||||
TMP_MAX = 10000
|
||||
|
||||
template = "tmp"
|
||||
|
||||
# Internal routines.
|
||||
|
||||
_once_lock = _allocate_lock()
|
||||
|
||||
if hasattr(_os, "lstat"):
|
||||
_stat = _os.lstat
|
||||
elif hasattr(_os, "stat"):
|
||||
_stat = _os.stat
|
||||
else:
|
||||
# Fallback. All we need is something that raises os.error if the
|
||||
# file doesn't exist.
|
||||
def _stat(fn):
|
||||
try:
|
||||
f = open(fn)
|
||||
except IOError:
|
||||
raise _os.error
|
||||
f.close()
|
||||
|
||||
def _exists(fn):
|
||||
try:
|
||||
_stat(fn)
|
||||
except _os.error:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
class _RandomNameSequence:
|
||||
"""An instance of _RandomNameSequence generates an endless
|
||||
sequence of unpredictable strings which can safely be incorporated
|
||||
into file names. Each string is six characters long. Multiple
|
||||
threads can safely use the same instance at the same time.
|
||||
|
||||
_RandomNameSequence is an iterator."""
|
||||
|
||||
characters = ("abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"0123456789_")
|
||||
|
||||
def __init__(self):
|
||||
self.mutex = _allocate_lock()
|
||||
self.normcase = _os.path.normcase
|
||||
|
||||
@property
|
||||
def rng(self):
|
||||
cur_pid = _os.getpid()
|
||||
if cur_pid != getattr(self, '_rng_pid', None):
|
||||
self._rng = _Random()
|
||||
self._rng_pid = cur_pid
|
||||
return self._rng
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
m = self.mutex
|
||||
c = self.characters
|
||||
choose = self.rng.choice
|
||||
|
||||
m.acquire()
|
||||
try:
|
||||
letters = [choose(c) for dummy in "123456"]
|
||||
finally:
|
||||
m.release()
|
||||
|
||||
return self.normcase(''.join(letters))
|
||||
|
||||
def _candidate_tempdir_list():
|
||||
"""Generate a list of candidate temporary directories which
|
||||
_get_default_tempdir will try."""
|
||||
|
||||
dirlist = []
|
||||
|
||||
# First, try the environment.
|
||||
for envname in 'TMPDIR', 'TEMP', 'TMP':
|
||||
dirname = _os.getenv(envname)
|
||||
if dirname: dirlist.append(dirname)
|
||||
|
||||
# Failing that, try OS-specific locations.
|
||||
if _os.name == 'riscos':
|
||||
dirname = _os.getenv('Wimp$ScrapDir')
|
||||
if dirname: dirlist.append(dirname)
|
||||
elif _os.name == 'nt':
|
||||
dirlist.extend([ r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ])
|
||||
else:
|
||||
dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ])
|
||||
|
||||
# As a last resort, the current directory.
|
||||
try:
|
||||
dirlist.append(_os.getcwd())
|
||||
except (AttributeError, _os.error):
|
||||
dirlist.append(_os.curdir)
|
||||
|
||||
return dirlist
|
||||
|
||||
def _get_default_tempdir():
|
||||
"""Calculate the default directory to use for temporary files.
|
||||
This routine should be called exactly once.
|
||||
|
||||
We determine whether or not a candidate temp dir is usable by
|
||||
trying to create and write to a file in that directory. If this
|
||||
is successful, the test file is deleted. To prevent denial of
|
||||
service, the name of the test file must be randomized."""
|
||||
|
||||
namer = _RandomNameSequence()
|
||||
dirlist = _candidate_tempdir_list()
|
||||
flags = _text_openflags
|
||||
|
||||
for dir in dirlist:
|
||||
if dir != _os.curdir:
|
||||
dir = _os.path.normcase(_os.path.abspath(dir))
|
||||
# Try only a few names per directory.
|
||||
for seq in xrange(100):
|
||||
name = namer.next()
|
||||
filename = _os.path.join(dir, name)
|
||||
try:
|
||||
fd = _os.open(filename, flags, 0o600)
|
||||
try:
|
||||
try:
|
||||
with _io.open(fd, 'wb', closefd=False) as fp:
|
||||
fp.write(b'blat')
|
||||
finally:
|
||||
_os.close(fd)
|
||||
finally:
|
||||
_os.unlink(filename)
|
||||
return dir
|
||||
except (OSError, IOError) as e:
|
||||
if e.args[0] == _errno.EEXIST:
|
||||
continue
|
||||
if (_os.name == 'nt' and e.args[0] == _errno.EACCES and
|
||||
_os.path.isdir(dir) and _os.access(dir, _os.W_OK)):
|
||||
# On windows, when a directory with the chosen name already
|
||||
# exists, EACCES error code is returned instead of EEXIST.
|
||||
continue
|
||||
break # no point trying more names in this directory
|
||||
raise IOError, (_errno.ENOENT,
|
||||
("No usable temporary directory found in %s" % dirlist))
|
||||
|
||||
_name_sequence = None
|
||||
|
||||
def _get_candidate_names():
|
||||
"""Common setup sequence for all user-callable interfaces."""
|
||||
|
||||
global _name_sequence
|
||||
if _name_sequence is None:
|
||||
_once_lock.acquire()
|
||||
try:
|
||||
if _name_sequence is None:
|
||||
_name_sequence = _RandomNameSequence()
|
||||
finally:
|
||||
_once_lock.release()
|
||||
return _name_sequence
|
||||
|
||||
|
||||
def _mkstemp_inner(dir, pre, suf, flags):
|
||||
"""Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""
|
||||
|
||||
names = _get_candidate_names()
|
||||
|
||||
for seq in xrange(TMP_MAX):
|
||||
name = names.next()
|
||||
file = _os.path.join(dir, pre + name + suf)
|
||||
try:
|
||||
fd = _os.open(file, flags, 0600)
|
||||
_set_cloexec(fd)
|
||||
return (fd, _os.path.abspath(file))
|
||||
except OSError, e:
|
||||
if e.errno == _errno.EEXIST:
|
||||
continue # try again
|
||||
if (_os.name == 'nt' and e.errno == _errno.EACCES and
|
||||
_os.path.isdir(dir) and _os.access(dir, _os.W_OK)):
|
||||
# On windows, when a directory with the chosen name already
|
||||
# exists, EACCES error code is returned instead of EEXIST.
|
||||
continue
|
||||
raise
|
||||
|
||||
raise IOError, (_errno.EEXIST, "No usable temporary file name found")
|
||||
|
||||
|
||||
# User visible interfaces.
|
||||
|
||||
def gettempprefix():
|
||||
"""Accessor for tempdir.template."""
|
||||
return template
|
||||
|
||||
tempdir = None
|
||||
|
||||
def gettempdir():
|
||||
"""Accessor for tempfile.tempdir."""
|
||||
global tempdir
|
||||
if tempdir is None:
|
||||
_once_lock.acquire()
|
||||
try:
|
||||
if tempdir is None:
|
||||
tempdir = _get_default_tempdir()
|
||||
finally:
|
||||
_once_lock.release()
|
||||
return tempdir
|
||||
|
||||
def mkstemp(suffix="", prefix=template, dir=None, text=False):
|
||||
"""User-callable function to create and return a unique temporary
|
||||
file. The return value is a pair (fd, name) where fd is the
|
||||
file descriptor returned by os.open, and name is the filename.
|
||||
|
||||
If 'suffix' is specified, the file name will end with that suffix,
|
||||
otherwise there will be no suffix.
|
||||
|
||||
If 'prefix' is specified, the file name will begin with that prefix,
|
||||
otherwise a default prefix is used.
|
||||
|
||||
If 'dir' is specified, the file will be created in that directory,
|
||||
otherwise a default directory is used.
|
||||
|
||||
If 'text' is specified and true, the file is opened in text
|
||||
mode. Else (the default) the file is opened in binary mode. On
|
||||
some operating systems, this makes no difference.
|
||||
|
||||
The file is readable and writable only by the creating user ID.
|
||||
If the operating system uses permission bits to indicate whether a
|
||||
file is executable, the file is executable by no one. The file
|
||||
descriptor is not inherited by children of this process.
|
||||
|
||||
Caller is responsible for deleting the file when done with it.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
if text:
|
||||
flags = _text_openflags
|
||||
else:
|
||||
flags = _bin_openflags
|
||||
|
||||
return _mkstemp_inner(dir, prefix, suffix, flags)
|
||||
|
||||
|
||||
def mkdtemp(suffix="", prefix=template, dir=None):
|
||||
"""User-callable function to create and return a unique temporary
|
||||
directory. The return value is the pathname of the directory.
|
||||
|
||||
Arguments are as for mkstemp, except that the 'text' argument is
|
||||
not accepted.
|
||||
|
||||
The directory is readable, writable, and searchable only by the
|
||||
creating user.
|
||||
|
||||
Caller is responsible for deleting the directory when done with it.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
names = _get_candidate_names()
|
||||
|
||||
for seq in xrange(TMP_MAX):
|
||||
name = names.next()
|
||||
file = _os.path.join(dir, prefix + name + suffix)
|
||||
try:
|
||||
_os.mkdir(file, 0700)
|
||||
return file
|
||||
except OSError, e:
|
||||
if e.errno == _errno.EEXIST:
|
||||
continue # try again
|
||||
if (_os.name == 'nt' and e.errno == _errno.EACCES and
|
||||
_os.path.isdir(dir) and _os.access(dir, _os.W_OK)):
|
||||
# On windows, when a directory with the chosen name already
|
||||
# exists, EACCES error code is returned instead of EEXIST.
|
||||
continue
|
||||
raise
|
||||
|
||||
raise IOError, (_errno.EEXIST, "No usable temporary directory name found")
|
||||
|
||||
def mktemp(suffix="", prefix=template, dir=None):
|
||||
"""User-callable function to return a unique temporary file name. The
|
||||
file is not created.
|
||||
|
||||
Arguments are as for mkstemp, except that the 'text' argument is
|
||||
not accepted.
|
||||
|
||||
This function is unsafe and should not be used. The file name
|
||||
refers to a file that did not exist at some point, but by the time
|
||||
you get around to creating it, someone else may have beaten you to
|
||||
the punch.
|
||||
"""
|
||||
|
||||
## from warnings import warn as _warn
|
||||
## _warn("mktemp is a potential security risk to your program",
|
||||
## RuntimeWarning, stacklevel=2)
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
names = _get_candidate_names()
|
||||
for seq in xrange(TMP_MAX):
|
||||
name = names.next()
|
||||
file = _os.path.join(dir, prefix + name + suffix)
|
||||
if not _exists(file):
|
||||
return file
|
||||
|
||||
raise IOError, (_errno.EEXIST, "No usable temporary filename found")
|
||||
|
||||
|
||||
class _TemporaryFileWrapper:
|
||||
"""Temporary file wrapper
|
||||
|
||||
This class provides a wrapper around files opened for
|
||||
temporary use. In particular, it seeks to automatically
|
||||
remove the file when it is no longer needed.
|
||||
"""
|
||||
|
||||
def __init__(self, file, name, delete=True):
|
||||
self.file = file
|
||||
self.name = name
|
||||
self.close_called = False
|
||||
self.delete = delete
|
||||
|
||||
def __getattr__(self, name):
|
||||
# Attribute lookups are delegated to the underlying file
|
||||
# and cached for non-numeric results
|
||||
# (i.e. methods are cached, closed and friends are not)
|
||||
file = self.__dict__['file']
|
||||
a = getattr(file, name)
|
||||
if not issubclass(type(a), type(0)):
|
||||
setattr(self, name, a)
|
||||
return a
|
||||
|
||||
# The underlying __enter__ method returns the wrong object
|
||||
# (self.file) so override it to return the wrapper
|
||||
def __enter__(self):
|
||||
self.file.__enter__()
|
||||
return self
|
||||
|
||||
# NT provides delete-on-close as a primitive, so we don't need
|
||||
# the wrapper to do anything special. We still use it so that
|
||||
# file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile.
|
||||
if _os.name != 'nt':
|
||||
# Cache the unlinker so we don't get spurious errors at
|
||||
# shutdown when the module-level "os" is None'd out. Note
|
||||
# that this must be referenced as self.unlink, because the
|
||||
# name TemporaryFileWrapper may also get None'd out before
|
||||
# __del__ is called.
|
||||
unlink = _os.unlink
|
||||
|
||||
def close(self):
|
||||
if not self.close_called:
|
||||
self.close_called = True
|
||||
try:
|
||||
self.file.close()
|
||||
finally:
|
||||
if self.delete:
|
||||
self.unlink(self.name)
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
# Need to trap __exit__ as well to ensure the file gets
|
||||
# deleted when used in a with statement
|
||||
def __exit__(self, exc, value, tb):
|
||||
result = self.file.__exit__(exc, value, tb)
|
||||
self.close()
|
||||
return result
|
||||
else:
|
||||
def __exit__(self, exc, value, tb):
|
||||
self.file.__exit__(exc, value, tb)
|
||||
|
||||
|
||||
def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="",
|
||||
prefix=template, dir=None, delete=True):
|
||||
"""Create and return a temporary file.
|
||||
Arguments:
|
||||
'prefix', 'suffix', 'dir' -- as for mkstemp.
|
||||
'mode' -- the mode argument to os.fdopen (default "w+b").
|
||||
'bufsize' -- the buffer size argument to os.fdopen (default -1).
|
||||
'delete' -- whether the file is deleted on close (default True).
|
||||
The file is created as mkstemp() would do it.
|
||||
|
||||
Returns an object with a file-like interface; the name of the file
|
||||
is accessible as file.name. The file will be automatically deleted
|
||||
when it is closed unless the 'delete' argument is set to False.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
if 'b' in mode:
|
||||
flags = _bin_openflags
|
||||
else:
|
||||
flags = _text_openflags
|
||||
|
||||
# Setting O_TEMPORARY in the flags causes the OS to delete
|
||||
# the file when it is closed. This is only supported by Windows.
|
||||
if _os.name == 'nt' and delete:
|
||||
flags |= _os.O_TEMPORARY
|
||||
|
||||
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
|
||||
try:
|
||||
file = _os.fdopen(fd, mode, bufsize)
|
||||
return _TemporaryFileWrapper(file, name, delete)
|
||||
except:
|
||||
_os.close(fd)
|
||||
raise
|
||||
|
||||
if _os.name != 'posix' or _os.sys.platform == 'cygwin':
|
||||
# On non-POSIX and Cygwin systems, assume that we cannot unlink a file
|
||||
# while it is open.
|
||||
TemporaryFile = NamedTemporaryFile
|
||||
|
||||
else:
|
||||
def TemporaryFile(mode='w+b', bufsize=-1, suffix="",
|
||||
prefix=template, dir=None):
|
||||
"""Create and return a temporary file.
|
||||
Arguments:
|
||||
'prefix', 'suffix', 'dir' -- as for mkstemp.
|
||||
'mode' -- the mode argument to os.fdopen (default "w+b").
|
||||
'bufsize' -- the buffer size argument to os.fdopen (default -1).
|
||||
The file is created as mkstemp() would do it.
|
||||
|
||||
Returns an object with a file-like interface. The file has no
|
||||
name, and will cease to exist when it is closed.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
if 'b' in mode:
|
||||
flags = _bin_openflags
|
||||
else:
|
||||
flags = _text_openflags
|
||||
|
||||
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
|
||||
try:
|
||||
_os.unlink(name)
|
||||
return _os.fdopen(fd, mode, bufsize)
|
||||
except:
|
||||
_os.close(fd)
|
||||
raise
|
||||
|
||||
class SpooledTemporaryFile:
|
||||
"""Temporary file wrapper, specialized to switch from
|
||||
StringIO to a real file when it exceeds a certain size or
|
||||
when a fileno is needed.
|
||||
"""
|
||||
_rolled = False
|
||||
|
||||
def __init__(self, max_size=0, mode='w+b', bufsize=-1,
|
||||
suffix="", prefix=template, dir=None):
|
||||
self._file = _StringIO()
|
||||
self._max_size = max_size
|
||||
self._rolled = False
|
||||
self._TemporaryFileArgs = (mode, bufsize, suffix, prefix, dir)
|
||||
|
||||
def _check(self, file):
|
||||
if self._rolled: return
|
||||
max_size = self._max_size
|
||||
if max_size and file.tell() > max_size:
|
||||
self.rollover()
|
||||
|
||||
def rollover(self):
|
||||
if self._rolled: return
|
||||
file = self._file
|
||||
newfile = self._file = TemporaryFile(*self._TemporaryFileArgs)
|
||||
del self._TemporaryFileArgs
|
||||
|
||||
newfile.write(file.getvalue())
|
||||
newfile.seek(file.tell(), 0)
|
||||
|
||||
self._rolled = True
|
||||
|
||||
# The method caching trick from NamedTemporaryFile
|
||||
# won't work here, because _file may change from a
|
||||
# _StringIO instance to a real file. So we list
|
||||
# all the methods directly.
|
||||
|
||||
# Context management protocol
|
||||
def __enter__(self):
|
||||
if self._file.closed:
|
||||
raise ValueError("Cannot enter context with closed file")
|
||||
return self
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
self._file.close()
|
||||
|
||||
# file protocol
|
||||
def __iter__(self):
|
||||
return self._file.__iter__()
|
||||
|
||||
def close(self):
|
||||
self._file.close()
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
return self._file.closed
|
||||
|
||||
def fileno(self):
|
||||
self.rollover()
|
||||
return self._file.fileno()
|
||||
|
||||
def flush(self):
|
||||
self._file.flush()
|
||||
|
||||
def isatty(self):
|
||||
return self._file.isatty()
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
try:
|
||||
return self._file.mode
|
||||
except AttributeError:
|
||||
return self._TemporaryFileArgs[0]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
try:
|
||||
return self._file.name
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def next(self):
|
||||
return self._file.next
|
||||
|
||||
def read(self, *args):
|
||||
return self._file.read(*args)
|
||||
|
||||
def readline(self, *args):
|
||||
return self._file.readline(*args)
|
||||
|
||||
def readlines(self, *args):
|
||||
return self._file.readlines(*args)
|
||||
|
||||
def seek(self, *args):
|
||||
self._file.seek(*args)
|
||||
|
||||
@property
|
||||
def softspace(self):
|
||||
return self._file.softspace
|
||||
|
||||
def tell(self):
|
||||
return self._file.tell()
|
||||
|
||||
def truncate(self):
|
||||
self._file.truncate()
|
||||
|
||||
def write(self, s):
|
||||
file = self._file
|
||||
rv = file.write(s)
|
||||
self._check(file)
|
||||
return rv
|
||||
|
||||
def writelines(self, iterable):
|
||||
file = self._file
|
||||
rv = file.writelines(iterable)
|
||||
self._check(file)
|
||||
return rv
|
||||
|
||||
def xreadlines(self, *args):
|
||||
if hasattr(self._file, 'xreadlines'): # real file
|
||||
return iter(self._file)
|
||||
else: # StringIO()
|
||||
return iter(self._file.readlines(*args))
|
@ -1,429 +0,0 @@
|
||||
"""Text wrapping and filling.
|
||||
"""
|
||||
|
||||
# Copyright (C) 1999-2001 Gregory P. Ward.
|
||||
# Copyright (C) 2002, 2003 Python Software Foundation.
|
||||
# Written by Greg Ward <gward@python.net>
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import string, re
|
||||
|
||||
try:
|
||||
_unicode = unicode
|
||||
except NameError:
|
||||
# If Python is built without Unicode support, the unicode type
|
||||
# will not exist. Fake one.
|
||||
class _unicode(object):
|
||||
pass
|
||||
|
||||
# Do the right thing with boolean values for all known Python versions
|
||||
# (so this module can be copied to projects that don't depend on Python
|
||||
# 2.3, e.g. Optik and Docutils) by uncommenting the block of code below.
|
||||
#try:
|
||||
# True, False
|
||||
#except NameError:
|
||||
# (True, False) = (1, 0)
|
||||
|
||||
__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent']
|
||||
|
||||
# Hardcode the recognized whitespace characters to the US-ASCII
|
||||
# whitespace characters. The main reason for doing this is that in
|
||||
# ISO-8859-1, 0xa0 is non-breaking whitespace, so in certain locales
|
||||
# that character winds up in string.whitespace. Respecting
|
||||
# string.whitespace in those cases would 1) make textwrap treat 0xa0 the
|
||||
# same as any other whitespace char, which is clearly wrong (it's a
|
||||
# *non-breaking* space), 2) possibly cause problems with Unicode,
|
||||
# since 0xa0 is not in range(128).
|
||||
_whitespace = '\t\n\x0b\x0c\r '
|
||||
|
||||
class TextWrapper:
|
||||
"""
|
||||
Object for wrapping/filling text. The public interface consists of
|
||||
the wrap() and fill() methods; the other methods are just there for
|
||||
subclasses to override in order to tweak the default behaviour.
|
||||
If you want to completely replace the main wrapping algorithm,
|
||||
you'll probably have to override _wrap_chunks().
|
||||
|
||||
Several instance attributes control various aspects of wrapping:
|
||||
width (default: 70)
|
||||
the maximum width of wrapped lines (unless break_long_words
|
||||
is false)
|
||||
initial_indent (default: "")
|
||||
string that will be prepended to the first line of wrapped
|
||||
output. Counts towards the line's width.
|
||||
subsequent_indent (default: "")
|
||||
string that will be prepended to all lines save the first
|
||||
of wrapped output; also counts towards each line's width.
|
||||
expand_tabs (default: true)
|
||||
Expand tabs in input text to spaces before further processing.
|
||||
Each tab will become 1 .. 8 spaces, depending on its position in
|
||||
its line. If false, each tab is treated as a single character.
|
||||
replace_whitespace (default: true)
|
||||
Replace all whitespace characters in the input text by spaces
|
||||
after tab expansion. Note that if expand_tabs is false and
|
||||
replace_whitespace is true, every tab will be converted to a
|
||||
single space!
|
||||
fix_sentence_endings (default: false)
|
||||
Ensure that sentence-ending punctuation is always followed
|
||||
by two spaces. Off by default because the algorithm is
|
||||
(unavoidably) imperfect.
|
||||
break_long_words (default: true)
|
||||
Break words longer than 'width'. If false, those words will not
|
||||
be broken, and some lines might be longer than 'width'.
|
||||
break_on_hyphens (default: true)
|
||||
Allow breaking hyphenated words. If true, wrapping will occur
|
||||
preferably on whitespaces and right after hyphens part of
|
||||
compound words.
|
||||
drop_whitespace (default: true)
|
||||
Drop leading and trailing whitespace from lines.
|
||||
"""
|
||||
|
||||
whitespace_trans = string.maketrans(_whitespace, ' ' * len(_whitespace))
|
||||
|
||||
unicode_whitespace_trans = {}
|
||||
uspace = ord(u' ')
|
||||
for x in map(ord, _whitespace):
|
||||
unicode_whitespace_trans[x] = uspace
|
||||
|
||||
# This funky little regex is just the trick for splitting
|
||||
# text up into word-wrappable chunks. E.g.
|
||||
# "Hello there -- you goof-ball, use the -b option!"
|
||||
# splits into
|
||||
# Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option!
|
||||
# (after stripping out empty strings).
|
||||
wordsep_re = re.compile(
|
||||
r'(\s+|' # any whitespace
|
||||
r'[^\s\w]*\w+[^0-9\W]-(?=\w+[^0-9\W])|' # hyphenated words
|
||||
r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
|
||||
|
||||
# This less funky little regex just split on recognized spaces. E.g.
|
||||
# "Hello there -- you goof-ball, use the -b option!"
|
||||
# splits into
|
||||
# Hello/ /there/ /--/ /you/ /goof-ball,/ /use/ /the/ /-b/ /option!/
|
||||
wordsep_simple_re = re.compile(r'(\s+)')
|
||||
|
||||
# XXX this is not locale- or charset-aware -- string.lowercase
|
||||
# is US-ASCII only (and therefore English-only)
|
||||
sentence_end_re = re.compile(r'[%s]' # lowercase letter
|
||||
r'[\.\!\?]' # sentence-ending punct.
|
||||
r'[\"\']?' # optional end-of-quote
|
||||
r'\Z' # end of chunk
|
||||
% string.lowercase)
|
||||
|
||||
|
||||
def __init__(self,
|
||||
width=70,
|
||||
initial_indent="",
|
||||
subsequent_indent="",
|
||||
expand_tabs=True,
|
||||
replace_whitespace=True,
|
||||
fix_sentence_endings=False,
|
||||
break_long_words=True,
|
||||
drop_whitespace=True,
|
||||
break_on_hyphens=True):
|
||||
self.width = width
|
||||
self.initial_indent = initial_indent
|
||||
self.subsequent_indent = subsequent_indent
|
||||
self.expand_tabs = expand_tabs
|
||||
self.replace_whitespace = replace_whitespace
|
||||
self.fix_sentence_endings = fix_sentence_endings
|
||||
self.break_long_words = break_long_words
|
||||
self.drop_whitespace = drop_whitespace
|
||||
self.break_on_hyphens = break_on_hyphens
|
||||
|
||||
# recompile the regexes for Unicode mode -- done in this clumsy way for
|
||||
# backwards compatibility because it's rather common to monkey-patch
|
||||
# the TextWrapper class' wordsep_re attribute.
|
||||
self.wordsep_re_uni = re.compile(self.wordsep_re.pattern, re.U)
|
||||
self.wordsep_simple_re_uni = re.compile(
|
||||
self.wordsep_simple_re.pattern, re.U)
|
||||
|
||||
|
||||
# -- Private methods -----------------------------------------------
|
||||
# (possibly useful for subclasses to override)
|
||||
|
||||
def _munge_whitespace(self, text):
|
||||
"""_munge_whitespace(text : string) -> string
|
||||
|
||||
Munge whitespace in text: expand tabs and convert all other
|
||||
whitespace characters to spaces. Eg. " foo\\tbar\\n\\nbaz"
|
||||
becomes " foo bar baz".
|
||||
"""
|
||||
if self.expand_tabs:
|
||||
text = text.expandtabs()
|
||||
if self.replace_whitespace:
|
||||
if isinstance(text, str):
|
||||
text = text.translate(self.whitespace_trans)
|
||||
elif isinstance(text, _unicode):
|
||||
text = text.translate(self.unicode_whitespace_trans)
|
||||
return text
|
||||
|
||||
|
||||
def _split(self, text):
|
||||
"""_split(text : string) -> [string]
|
||||
|
||||
Split the text to wrap into indivisible chunks. Chunks are
|
||||
not quite the same as words; see _wrap_chunks() for full
|
||||
details. As an example, the text
|
||||
Look, goof-ball -- use the -b option!
|
||||
breaks into the following chunks:
|
||||
'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ',
|
||||
'use', ' ', 'the', ' ', '-b', ' ', 'option!'
|
||||
if break_on_hyphens is True, or in:
|
||||
'Look,', ' ', 'goof-ball', ' ', '--', ' ',
|
||||
'use', ' ', 'the', ' ', '-b', ' ', option!'
|
||||
otherwise.
|
||||
"""
|
||||
if isinstance(text, _unicode):
|
||||
if self.break_on_hyphens:
|
||||
pat = self.wordsep_re_uni
|
||||
else:
|
||||
pat = self.wordsep_simple_re_uni
|
||||
else:
|
||||
if self.break_on_hyphens:
|
||||
pat = self.wordsep_re
|
||||
else:
|
||||
pat = self.wordsep_simple_re
|
||||
chunks = pat.split(text)
|
||||
chunks = filter(None, chunks) # remove empty chunks
|
||||
return chunks
|
||||
|
||||
def _fix_sentence_endings(self, chunks):
|
||||
"""_fix_sentence_endings(chunks : [string])
|
||||
|
||||
Correct for sentence endings buried in 'chunks'. Eg. when the
|
||||
original text contains "... foo.\\nBar ...", munge_whitespace()
|
||||
and split() will convert that to [..., "foo.", " ", "Bar", ...]
|
||||
which has one too few spaces; this method simply changes the one
|
||||
space to two.
|
||||
"""
|
||||
i = 0
|
||||
patsearch = self.sentence_end_re.search
|
||||
while i < len(chunks)-1:
|
||||
if chunks[i+1] == " " and patsearch(chunks[i]):
|
||||
chunks[i+1] = " "
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
|
||||
def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
|
||||
"""_handle_long_word(chunks : [string],
|
||||
cur_line : [string],
|
||||
cur_len : int, width : int)
|
||||
|
||||
Handle a chunk of text (most likely a word, not whitespace) that
|
||||
is too long to fit in any line.
|
||||
"""
|
||||
# Figure out when indent is larger than the specified width, and make
|
||||
# sure at least one character is stripped off on every pass
|
||||
if width < 1:
|
||||
space_left = 1
|
||||
else:
|
||||
space_left = width - cur_len
|
||||
|
||||
# If we're allowed to break long words, then do so: put as much
|
||||
# of the next chunk onto the current line as will fit.
|
||||
if self.break_long_words:
|
||||
cur_line.append(reversed_chunks[-1][:space_left])
|
||||
reversed_chunks[-1] = reversed_chunks[-1][space_left:]
|
||||
|
||||
# Otherwise, we have to preserve the long word intact. Only add
|
||||
# it to the current line if there's nothing already there --
|
||||
# that minimizes how much we violate the width constraint.
|
||||
elif not cur_line:
|
||||
cur_line.append(reversed_chunks.pop())
|
||||
|
||||
# If we're not allowed to break long words, and there's already
|
||||
# text on the current line, do nothing. Next time through the
|
||||
# main loop of _wrap_chunks(), we'll wind up here again, but
|
||||
# cur_len will be zero, so the next line will be entirely
|
||||
# devoted to the long word that we can't handle right now.
|
||||
|
||||
def _wrap_chunks(self, chunks):
|
||||
"""_wrap_chunks(chunks : [string]) -> [string]
|
||||
|
||||
Wrap a sequence of text chunks and return a list of lines of
|
||||
length 'self.width' or less. (If 'break_long_words' is false,
|
||||
some lines may be longer than this.) Chunks correspond roughly
|
||||
to words and the whitespace between them: each chunk is
|
||||
indivisible (modulo 'break_long_words'), but a line break can
|
||||
come between any two chunks. Chunks should not have internal
|
||||
whitespace; ie. a chunk is either all whitespace or a "word".
|
||||
Whitespace chunks will be removed from the beginning and end of
|
||||
lines, but apart from that whitespace is preserved.
|
||||
"""
|
||||
lines = []
|
||||
if self.width <= 0:
|
||||
raise ValueError("invalid width %r (must be > 0)" % self.width)
|
||||
|
||||
# Arrange in reverse order so items can be efficiently popped
|
||||
# from a stack of chucks.
|
||||
chunks.reverse()
|
||||
|
||||
while chunks:
|
||||
|
||||
# Start the list of chunks that will make up the current line.
|
||||
# cur_len is just the length of all the chunks in cur_line.
|
||||
cur_line = []
|
||||
cur_len = 0
|
||||
|
||||
# Figure out which static string will prefix this line.
|
||||
if lines:
|
||||
indent = self.subsequent_indent
|
||||
else:
|
||||
indent = self.initial_indent
|
||||
|
||||
# Maximum width for this line.
|
||||
width = self.width - len(indent)
|
||||
|
||||
# First chunk on line is whitespace -- drop it, unless this
|
||||
# is the very beginning of the text (ie. no lines started yet).
|
||||
if self.drop_whitespace and chunks[-1].strip() == '' and lines:
|
||||
del chunks[-1]
|
||||
|
||||
while chunks:
|
||||
l = len(chunks[-1])
|
||||
|
||||
# Can at least squeeze this chunk onto the current line.
|
||||
if cur_len + l <= width:
|
||||
cur_line.append(chunks.pop())
|
||||
cur_len += l
|
||||
|
||||
# Nope, this line is full.
|
||||
else:
|
||||
break
|
||||
|
||||
# The current line is full, and the next chunk is too big to
|
||||
# fit on *any* line (not just this one).
|
||||
if chunks and len(chunks[-1]) > width:
|
||||
self._handle_long_word(chunks, cur_line, cur_len, width)
|
||||
|
||||
# If the last chunk on this line is all whitespace, drop it.
|
||||
if self.drop_whitespace and cur_line and cur_line[-1].strip() == '':
|
||||
del cur_line[-1]
|
||||
|
||||
# Convert current line back to a string and store it in list
|
||||
# of all lines (return value).
|
||||
if cur_line:
|
||||
lines.append(indent + ''.join(cur_line))
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
# -- Public interface ----------------------------------------------
|
||||
|
||||
def wrap(self, text):
|
||||
"""wrap(text : string) -> [string]
|
||||
|
||||
Reformat the single paragraph in 'text' so it fits in lines of
|
||||
no more than 'self.width' columns, and return a list of wrapped
|
||||
lines. Tabs in 'text' are expanded with string.expandtabs(),
|
||||
and all other whitespace characters (including newline) are
|
||||
converted to space.
|
||||
"""
|
||||
text = self._munge_whitespace(text)
|
||||
chunks = self._split(text)
|
||||
if self.fix_sentence_endings:
|
||||
self._fix_sentence_endings(chunks)
|
||||
return self._wrap_chunks(chunks)
|
||||
|
||||
def fill(self, text):
|
||||
"""fill(text : string) -> string
|
||||
|
||||
Reformat the single paragraph in 'text' to fit in lines of no
|
||||
more than 'self.width' columns, and return a new string
|
||||
containing the entire wrapped paragraph.
|
||||
"""
|
||||
return "\n".join(self.wrap(text))
|
||||
|
||||
|
||||
# -- Convenience interface ---------------------------------------------
|
||||
|
||||
def wrap(text, width=70, **kwargs):
|
||||
"""Wrap a single paragraph of text, returning a list of wrapped lines.
|
||||
|
||||
Reformat the single paragraph in 'text' so it fits in lines of no
|
||||
more than 'width' columns, and return a list of wrapped lines. By
|
||||
default, tabs in 'text' are expanded with string.expandtabs(), and
|
||||
all other whitespace characters (including newline) are converted to
|
||||
space. See TextWrapper class for available keyword args to customize
|
||||
wrapping behaviour.
|
||||
"""
|
||||
w = TextWrapper(width=width, **kwargs)
|
||||
return w.wrap(text)
|
||||
|
||||
def fill(text, width=70, **kwargs):
|
||||
"""Fill a single paragraph of text, returning a new string.
|
||||
|
||||
Reformat the single paragraph in 'text' to fit in lines of no more
|
||||
than 'width' columns, and return a new string containing the entire
|
||||
wrapped paragraph. As with wrap(), tabs are expanded and other
|
||||
whitespace characters converted to space. See TextWrapper class for
|
||||
available keyword args to customize wrapping behaviour.
|
||||
"""
|
||||
w = TextWrapper(width=width, **kwargs)
|
||||
return w.fill(text)
|
||||
|
||||
|
||||
# -- Loosely related functionality -------------------------------------
|
||||
|
||||
_whitespace_only_re = re.compile('^[ \t]+$', re.MULTILINE)
|
||||
_leading_whitespace_re = re.compile('(^[ \t]*)(?:[^ \t\n])', re.MULTILINE)
|
||||
|
||||
def dedent(text):
|
||||
"""Remove any common leading whitespace from every line in `text`.
|
||||
|
||||
This can be used to make triple-quoted strings line up with the left
|
||||
edge of the display, while still presenting them in the source code
|
||||
in indented form.
|
||||
|
||||
Note that tabs and spaces are both treated as whitespace, but they
|
||||
are not equal: the lines " hello" and "\\thello" are
|
||||
considered to have no common leading whitespace. (This behaviour is
|
||||
new in Python 2.5; older versions of this module incorrectly
|
||||
expanded tabs before searching for common leading whitespace.)
|
||||
"""
|
||||
# Look for the longest leading string of spaces and tabs common to
|
||||
# all lines.
|
||||
margin = None
|
||||
text = _whitespace_only_re.sub('', text)
|
||||
indents = _leading_whitespace_re.findall(text)
|
||||
for indent in indents:
|
||||
if margin is None:
|
||||
margin = indent
|
||||
|
||||
# Current line more deeply indented than previous winner:
|
||||
# no change (previous winner is still on top).
|
||||
elif indent.startswith(margin):
|
||||
pass
|
||||
|
||||
# Current line consistent with and no deeper than previous winner:
|
||||
# it's the new winner.
|
||||
elif margin.startswith(indent):
|
||||
margin = indent
|
||||
|
||||
# Find the largest common whitespace between current line and previous
|
||||
# winner.
|
||||
else:
|
||||
for i, (x, y) in enumerate(zip(margin, indent)):
|
||||
if x != y:
|
||||
margin = margin[:i]
|
||||
break
|
||||
else:
|
||||
margin = margin[:len(indent)]
|
||||
|
||||
# sanity check (testing/debugging only)
|
||||
if 0 and margin:
|
||||
for line in text.split("\n"):
|
||||
assert not line or line.startswith(margin), \
|
||||
"line = %r, margin = %r" % (line, margin)
|
||||
|
||||
if margin:
|
||||
text = re.sub(r'(?m)^' + margin, '', text)
|
||||
return text
|
||||
|
||||
if __name__ == "__main__":
|
||||
#print dedent("\tfoo\n\tbar")
|
||||
#print dedent(" \thello there\n \t how are you?")
|
||||
print dedent("Hello there.\n This is indented.")
|
@ -1,86 +0,0 @@
|
||||
"""Define names for all type symbols known in the standard interpreter.
|
||||
|
||||
Types that are part of optional modules (e.g. array) are not listed.
|
||||
"""
|
||||
import sys
|
||||
|
||||
# Iterators in Python aren't a matter of type but of protocol. A large
|
||||
# and changing number of builtin types implement *some* flavor of
|
||||
# iterator. Don't check the type! Use hasattr to check for both
|
||||
# "__iter__" and "next" attributes instead.
|
||||
|
||||
NoneType = type(None)
|
||||
TypeType = type
|
||||
ObjectType = object
|
||||
|
||||
IntType = int
|
||||
LongType = long
|
||||
FloatType = float
|
||||
BooleanType = bool
|
||||
try:
|
||||
ComplexType = complex
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
StringType = str
|
||||
|
||||
# StringTypes is already outdated. Instead of writing "type(x) in
|
||||
# types.StringTypes", you should use "isinstance(x, basestring)". But
|
||||
# we keep around for compatibility with Python 2.2.
|
||||
try:
|
||||
UnicodeType = unicode
|
||||
StringTypes = (StringType, UnicodeType)
|
||||
except NameError:
|
||||
StringTypes = (StringType,)
|
||||
|
||||
BufferType = buffer
|
||||
|
||||
TupleType = tuple
|
||||
ListType = list
|
||||
DictType = DictionaryType = dict
|
||||
|
||||
def _f(): pass
|
||||
FunctionType = type(_f)
|
||||
LambdaType = type(lambda: None) # Same as FunctionType
|
||||
CodeType = type(_f.func_code)
|
||||
|
||||
def _g():
|
||||
yield 1
|
||||
GeneratorType = type(_g())
|
||||
|
||||
class _C:
|
||||
def _m(self): pass
|
||||
ClassType = type(_C)
|
||||
UnboundMethodType = type(_C._m) # Same as MethodType
|
||||
_x = _C()
|
||||
InstanceType = type(_x)
|
||||
MethodType = type(_x._m)
|
||||
|
||||
BuiltinFunctionType = type(len)
|
||||
BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
|
||||
|
||||
ModuleType = type(sys)
|
||||
FileType = file
|
||||
XRangeType = xrange
|
||||
|
||||
try:
|
||||
raise TypeError
|
||||
except TypeError:
|
||||
tb = sys.exc_info()[2]
|
||||
TracebackType = type(tb)
|
||||
FrameType = type(tb.tb_frame)
|
||||
del tb
|
||||
|
||||
SliceType = slice
|
||||
EllipsisType = type(Ellipsis)
|
||||
|
||||
DictProxyType = type(TypeType.__dict__)
|
||||
NotImplementedType = type(NotImplemented)
|
||||
|
||||
# For Jython, the following two types are identical
|
||||
GetSetDescriptorType = type(FunctionType.func_code)
|
||||
MemberDescriptorType = type(FunctionType.func_globals)
|
||||
|
||||
del sys, _f, _g, _C, _x # Not for export
|
||||
|
||||
__all__ = list(n for n in globals() if n[:1] != '_')
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,428 +0,0 @@
|
||||
"""Parse (absolute and relative) URLs.
|
||||
|
||||
urlparse module is based upon the following RFC specifications.
|
||||
|
||||
RFC 3986 (STD66): "Uniform Resource Identifiers" by T. Berners-Lee, R. Fielding
|
||||
and L. Masinter, January 2005.
|
||||
|
||||
RFC 2732 : "Format for Literal IPv6 Addresses in URL's by R.Hinden, B.Carpenter
|
||||
and L.Masinter, December 1999.
|
||||
|
||||
RFC 2396: "Uniform Resource Identifiers (URI)": Generic Syntax by T.
|
||||
Berners-Lee, R. Fielding, and L. Masinter, August 1998.
|
||||
|
||||
RFC 2368: "The mailto URL scheme", by P.Hoffman , L Masinter, J. Zwinski, July 1998.
|
||||
|
||||
RFC 1808: "Relative Uniform Resource Locators", by R. Fielding, UC Irvine, June
|
||||
1995.
|
||||
|
||||
RFC 1738: "Uniform Resource Locators (URL)" by T. Berners-Lee, L. Masinter, M.
|
||||
McCahill, December 1994
|
||||
|
||||
RFC 3986 is considered the current standard and any future changes to
|
||||
urlparse module should conform with it. The urlparse module is
|
||||
currently not entirely compliant with this RFC due to defacto
|
||||
scenarios for parsing, and for backward compatibility purposes, some
|
||||
parsing quirks from older RFCs are retained. The testcases in
|
||||
test_urlparse.py provides a good indicator of parsing behavior.
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
|
||||
"urlsplit", "urlunsplit", "parse_qs", "parse_qsl"]
|
||||
|
||||
# A classification of schemes ('' means apply by default)
|
||||
uses_relative = ['ftp', 'http', 'gopher', 'nntp', 'imap',
|
||||
'wais', 'file', 'https', 'shttp', 'mms',
|
||||
'prospero', 'rtsp', 'rtspu', '', 'sftp',
|
||||
'svn', 'svn+ssh']
|
||||
uses_netloc = ['ftp', 'http', 'gopher', 'nntp', 'telnet',
|
||||
'imap', 'wais', 'file', 'mms', 'https', 'shttp',
|
||||
'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '',
|
||||
'svn', 'svn+ssh', 'sftp','nfs','git', 'git+ssh']
|
||||
uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap',
|
||||
'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips',
|
||||
'mms', '', 'sftp', 'tel']
|
||||
|
||||
# These are not actually used anymore, but should stay for backwards
|
||||
# compatibility. (They are undocumented, but have a public-looking name.)
|
||||
non_hierarchical = ['gopher', 'hdl', 'mailto', 'news',
|
||||
'telnet', 'wais', 'imap', 'snews', 'sip', 'sips']
|
||||
uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms',
|
||||
'gopher', 'rtsp', 'rtspu', 'sip', 'sips', '']
|
||||
uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news',
|
||||
'nntp', 'wais', 'https', 'shttp', 'snews',
|
||||
'file', 'prospero', '']
|
||||
|
||||
# Characters valid in scheme names
|
||||
scheme_chars = ('abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789'
|
||||
'+-.')
|
||||
|
||||
MAX_CACHE_SIZE = 20
|
||||
_parse_cache = {}
|
||||
|
||||
def clear_cache():
|
||||
"""Clear the parse cache."""
|
||||
_parse_cache.clear()
|
||||
|
||||
|
||||
class ResultMixin(object):
|
||||
"""Shared methods for the parsed result objects."""
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
netloc = self.netloc
|
||||
if "@" in netloc:
|
||||
userinfo = netloc.rsplit("@", 1)[0]
|
||||
if ":" in userinfo:
|
||||
userinfo = userinfo.split(":", 1)[0]
|
||||
return userinfo
|
||||
return None
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
netloc = self.netloc
|
||||
if "@" in netloc:
|
||||
userinfo = netloc.rsplit("@", 1)[0]
|
||||
if ":" in userinfo:
|
||||
return userinfo.split(":", 1)[1]
|
||||
return None
|
||||
|
||||
@property
|
||||
def hostname(self):
|
||||
netloc = self.netloc.split('@')[-1]
|
||||
if '[' in netloc and ']' in netloc:
|
||||
return netloc.split(']')[0][1:].lower()
|
||||
elif ':' in netloc:
|
||||
return netloc.split(':')[0].lower()
|
||||
elif netloc == '':
|
||||
return None
|
||||
else:
|
||||
return netloc.lower()
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
netloc = self.netloc.split('@')[-1].split(']')[-1]
|
||||
if ':' in netloc:
|
||||
port = netloc.split(':')[1]
|
||||
if port:
|
||||
port = int(port, 10)
|
||||
# verify legal port
|
||||
if (0 <= port <= 65535):
|
||||
return port
|
||||
return None
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
class SplitResult(namedtuple('SplitResult', 'scheme netloc path query fragment'), ResultMixin):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def geturl(self):
|
||||
return urlunsplit(self)
|
||||
|
||||
|
||||
class ParseResult(namedtuple('ParseResult', 'scheme netloc path params query fragment'), ResultMixin):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def geturl(self):
|
||||
return urlunparse(self)
|
||||
|
||||
|
||||
def urlparse(url, scheme='', allow_fragments=True):
|
||||
"""Parse a URL into 6 components:
|
||||
<scheme>://<netloc>/<path>;<params>?<query>#<fragment>
|
||||
Return a 6-tuple: (scheme, netloc, path, params, query, fragment).
|
||||
Note that we don't break the components up in smaller bits
|
||||
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||
tuple = urlsplit(url, scheme, allow_fragments)
|
||||
scheme, netloc, url, query, fragment = tuple
|
||||
if scheme in uses_params and ';' in url:
|
||||
url, params = _splitparams(url)
|
||||
else:
|
||||
params = ''
|
||||
return ParseResult(scheme, netloc, url, params, query, fragment)
|
||||
|
||||
def _splitparams(url):
|
||||
if '/' in url:
|
||||
i = url.find(';', url.rfind('/'))
|
||||
if i < 0:
|
||||
return url, ''
|
||||
else:
|
||||
i = url.find(';')
|
||||
return url[:i], url[i+1:]
|
||||
|
||||
def _splitnetloc(url, start=0):
|
||||
delim = len(url) # position of end of domain part of url, default is end
|
||||
for c in '/?#': # look for delimiters; the order is NOT important
|
||||
wdelim = url.find(c, start) # find first of this delim
|
||||
if wdelim >= 0: # if found
|
||||
delim = min(delim, wdelim) # use earliest delim position
|
||||
return url[start:delim], url[delim:] # return (domain, rest)
|
||||
|
||||
def urlsplit(url, scheme='', allow_fragments=True):
|
||||
"""Parse a URL into 5 components:
|
||||
<scheme>://<netloc>/<path>?<query>#<fragment>
|
||||
Return a 5-tuple: (scheme, netloc, path, query, fragment).
|
||||
Note that we don't break the components up in smaller bits
|
||||
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||
allow_fragments = bool(allow_fragments)
|
||||
key = url, scheme, allow_fragments, type(url), type(scheme)
|
||||
cached = _parse_cache.get(key, None)
|
||||
if cached:
|
||||
return cached
|
||||
if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth
|
||||
clear_cache()
|
||||
netloc = query = fragment = ''
|
||||
i = url.find(':')
|
||||
if i > 0:
|
||||
if url[:i] == 'http': # optimize the common case
|
||||
scheme = url[:i].lower()
|
||||
url = url[i+1:]
|
||||
if url[:2] == '//':
|
||||
netloc, url = _splitnetloc(url, 2)
|
||||
if (('[' in netloc and ']' not in netloc) or
|
||||
(']' in netloc and '[' not in netloc)):
|
||||
raise ValueError("Invalid IPv6 URL")
|
||||
if allow_fragments and '#' in url:
|
||||
url, fragment = url.split('#', 1)
|
||||
if '?' in url:
|
||||
url, query = url.split('?', 1)
|
||||
v = SplitResult(scheme, netloc, url, query, fragment)
|
||||
_parse_cache[key] = v
|
||||
return v
|
||||
for c in url[:i]:
|
||||
if c not in scheme_chars:
|
||||
break
|
||||
else:
|
||||
# make sure "url" is not actually a port number (in which case
|
||||
# "scheme" is really part of the path)
|
||||
rest = url[i+1:]
|
||||
if not rest or any(c not in '0123456789' for c in rest):
|
||||
# not a port number
|
||||
scheme, url = url[:i].lower(), rest
|
||||
|
||||
if url[:2] == '//':
|
||||
netloc, url = _splitnetloc(url, 2)
|
||||
if (('[' in netloc and ']' not in netloc) or
|
||||
(']' in netloc and '[' not in netloc)):
|
||||
raise ValueError("Invalid IPv6 URL")
|
||||
if allow_fragments and '#' in url:
|
||||
url, fragment = url.split('#', 1)
|
||||
if '?' in url:
|
||||
url, query = url.split('?', 1)
|
||||
v = SplitResult(scheme, netloc, url, query, fragment)
|
||||
_parse_cache[key] = v
|
||||
return v
|
||||
|
||||
def urlunparse(data):
|
||||
"""Put a parsed URL back together again. This may result in a
|
||||
slightly different, but equivalent URL, if the URL that was parsed
|
||||
originally had redundant delimiters, e.g. a ? with an empty query
|
||||
(the draft states that these are equivalent)."""
|
||||
scheme, netloc, url, params, query, fragment = data
|
||||
if params:
|
||||
url = "%s;%s" % (url, params)
|
||||
return urlunsplit((scheme, netloc, url, query, fragment))
|
||||
|
||||
def urlunsplit(data):
|
||||
"""Combine the elements of a tuple as returned by urlsplit() into a
|
||||
complete URL as a string. The data argument can be any five-item iterable.
|
||||
This may result in a slightly different, but equivalent URL, if the URL that
|
||||
was parsed originally had unnecessary delimiters (for example, a ? with an
|
||||
empty query; the RFC states that these are equivalent)."""
|
||||
scheme, netloc, url, query, fragment = data
|
||||
if netloc or (scheme and scheme in uses_netloc and url[:2] != '//'):
|
||||
if url and url[:1] != '/': url = '/' + url
|
||||
url = '//' + (netloc or '') + url
|
||||
if scheme:
|
||||
url = scheme + ':' + url
|
||||
if query:
|
||||
url = url + '?' + query
|
||||
if fragment:
|
||||
url = url + '#' + fragment
|
||||
return url
|
||||
|
||||
def urljoin(base, url, allow_fragments=True):
|
||||
"""Join a base URL and a possibly relative URL to form an absolute
|
||||
interpretation of the latter."""
|
||||
if not base:
|
||||
return url
|
||||
if not url:
|
||||
return base
|
||||
bscheme, bnetloc, bpath, bparams, bquery, bfragment = \
|
||||
urlparse(base, '', allow_fragments)
|
||||
scheme, netloc, path, params, query, fragment = \
|
||||
urlparse(url, bscheme, allow_fragments)
|
||||
if scheme != bscheme or scheme not in uses_relative:
|
||||
return url
|
||||
if scheme in uses_netloc:
|
||||
if netloc:
|
||||
return urlunparse((scheme, netloc, path,
|
||||
params, query, fragment))
|
||||
netloc = bnetloc
|
||||
if path[:1] == '/':
|
||||
return urlunparse((scheme, netloc, path,
|
||||
params, query, fragment))
|
||||
if not path and not params:
|
||||
path = bpath
|
||||
params = bparams
|
||||
if not query:
|
||||
query = bquery
|
||||
return urlunparse((scheme, netloc, path,
|
||||
params, query, fragment))
|
||||
segments = bpath.split('/')[:-1] + path.split('/')
|
||||
# XXX The stuff below is bogus in various ways...
|
||||
if segments[-1] == '.':
|
||||
segments[-1] = ''
|
||||
while '.' in segments:
|
||||
segments.remove('.')
|
||||
while 1:
|
||||
i = 1
|
||||
n = len(segments) - 1
|
||||
while i < n:
|
||||
if (segments[i] == '..'
|
||||
and segments[i-1] not in ('', '..')):
|
||||
del segments[i-1:i+1]
|
||||
break
|
||||
i = i+1
|
||||
else:
|
||||
break
|
||||
if segments == ['', '..']:
|
||||
segments[-1] = ''
|
||||
elif len(segments) >= 2 and segments[-1] == '..':
|
||||
segments[-2:] = ['']
|
||||
return urlunparse((scheme, netloc, '/'.join(segments),
|
||||
params, query, fragment))
|
||||
|
||||
def urldefrag(url):
|
||||
"""Removes any existing fragment from URL.
|
||||
|
||||
Returns a tuple of the defragmented URL and the fragment. If
|
||||
the URL contained no fragments, the second element is the
|
||||
empty string.
|
||||
"""
|
||||
if '#' in url:
|
||||
s, n, p, a, q, frag = urlparse(url)
|
||||
defrag = urlunparse((s, n, p, a, q, ''))
|
||||
return defrag, frag
|
||||
else:
|
||||
return url, ''
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
def _is_unicode(x):
|
||||
return 0
|
||||
else:
|
||||
def _is_unicode(x):
|
||||
return isinstance(x, unicode)
|
||||
|
||||
# unquote method for parse_qs and parse_qsl
|
||||
# Cannot use directly from urllib as it would create a circular reference
|
||||
# because urllib uses urlparse methods (urljoin). If you update this function,
|
||||
# update it also in urllib. This code duplication does not existin in Python3.
|
||||
|
||||
_hexdig = '0123456789ABCDEFabcdef'
|
||||
_hextochr = dict((a+b, chr(int(a+b,16)))
|
||||
for a in _hexdig for b in _hexdig)
|
||||
_asciire = re.compile('([\x00-\x7f]+)')
|
||||
|
||||
def unquote(s):
|
||||
"""unquote('abc%20def') -> 'abc def'."""
|
||||
if _is_unicode(s):
|
||||
if '%' not in s:
|
||||
return s
|
||||
bits = _asciire.split(s)
|
||||
res = [bits[0]]
|
||||
append = res.append
|
||||
for i in range(1, len(bits), 2):
|
||||
append(unquote(str(bits[i])).decode('latin1'))
|
||||
append(bits[i + 1])
|
||||
return ''.join(res)
|
||||
|
||||
bits = s.split('%')
|
||||
# fastpath
|
||||
if len(bits) == 1:
|
||||
return s
|
||||
res = [bits[0]]
|
||||
append = res.append
|
||||
for item in bits[1:]:
|
||||
try:
|
||||
append(_hextochr[item[:2]])
|
||||
append(item[2:])
|
||||
except KeyError:
|
||||
append('%')
|
||||
append(item)
|
||||
return ''.join(res)
|
||||
|
||||
def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
|
||||
"""Parse a query given as a string argument.
|
||||
|
||||
Arguments:
|
||||
|
||||
qs: percent-encoded query string to be parsed
|
||||
|
||||
keep_blank_values: flag indicating whether blank values in
|
||||
percent-encoded queries should be treated as blank strings.
|
||||
A true value indicates that blanks should be retained as
|
||||
blank strings. The default false value indicates that
|
||||
blank values are to be ignored and treated as if they were
|
||||
not included.
|
||||
|
||||
strict_parsing: flag indicating what to do with parsing errors.
|
||||
If false (the default), errors are silently ignored.
|
||||
If true, errors raise a ValueError exception.
|
||||
"""
|
||||
dict = {}
|
||||
for name, value in parse_qsl(qs, keep_blank_values, strict_parsing):
|
||||
if name in dict:
|
||||
dict[name].append(value)
|
||||
else:
|
||||
dict[name] = [value]
|
||||
return dict
|
||||
|
||||
def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
|
||||
"""Parse a query given as a string argument.
|
||||
|
||||
Arguments:
|
||||
|
||||
qs: percent-encoded query string to be parsed
|
||||
|
||||
keep_blank_values: flag indicating whether blank values in
|
||||
percent-encoded queries should be treated as blank strings. A
|
||||
true value indicates that blanks should be retained as blank
|
||||
strings. The default false value indicates that blank values
|
||||
are to be ignored and treated as if they were not included.
|
||||
|
||||
strict_parsing: flag indicating what to do with parsing errors. If
|
||||
false (the default), errors are silently ignored. If true,
|
||||
errors raise a ValueError exception.
|
||||
|
||||
Returns a list, as G-d intended.
|
||||
"""
|
||||
pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
|
||||
r = []
|
||||
for name_value in pairs:
|
||||
if not name_value and not strict_parsing:
|
||||
continue
|
||||
nv = name_value.split('=', 1)
|
||||
if len(nv) != 2:
|
||||
if strict_parsing:
|
||||
raise ValueError, "bad query field: %r" % (name_value,)
|
||||
# Handle case of a control-name with no equal sign
|
||||
if keep_blank_values:
|
||||
nv.append('')
|
||||
else:
|
||||
continue
|
||||
if len(nv[1]) or keep_blank_values:
|
||||
name = unquote(nv[0].replace('+', ' '))
|
||||
value = unquote(nv[1].replace('+', ' '))
|
||||
r.append((name, value))
|
||||
|
||||
return r
|
@ -1,422 +0,0 @@
|
||||
"""Python part of the warnings subsystem."""
|
||||
|
||||
# Note: function level imports should *not* be used
|
||||
# in this module as it may cause import lock deadlock.
|
||||
# See bug 683658.
|
||||
import linecache
|
||||
import sys
|
||||
import types
|
||||
|
||||
__all__ = ["warn", "warn_explicit", "showwarning",
|
||||
"formatwarning", "filterwarnings", "simplefilter",
|
||||
"resetwarnings", "catch_warnings"]
|
||||
|
||||
|
||||
def warnpy3k(message, category=None, stacklevel=1):
|
||||
"""Issue a deprecation warning for Python 3.x related changes.
|
||||
|
||||
Warnings are omitted unless Python is started with the -3 option.
|
||||
"""
|
||||
if sys.py3kwarning:
|
||||
if category is None:
|
||||
category = DeprecationWarning
|
||||
warn(message, category, stacklevel+1)
|
||||
|
||||
def _show_warning(message, category, filename, lineno, file=None, line=None):
|
||||
"""Hook to write a warning to a file; replace if you like."""
|
||||
if file is None:
|
||||
file = sys.stderr
|
||||
if file is None:
|
||||
# sys.stderr is None - warnings get lost
|
||||
return
|
||||
try:
|
||||
file.write(formatwarning(message, category, filename, lineno, line))
|
||||
except (IOError, UnicodeError):
|
||||
pass # the file (probably stderr) is invalid - this warning gets lost.
|
||||
# Keep a working version around in case the deprecation of the old API is
|
||||
# triggered.
|
||||
showwarning = _show_warning
|
||||
|
||||
def formatwarning(message, category, filename, lineno, line=None):
|
||||
"""Function to format a warning the standard way."""
|
||||
try:
|
||||
unicodetype = unicode
|
||||
except NameError:
|
||||
unicodetype = ()
|
||||
try:
|
||||
message = str(message)
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
s = "%s: %s: %s\n" % (lineno, category.__name__, message)
|
||||
line = linecache.getline(filename, lineno) if line is None else line
|
||||
if line:
|
||||
line = line.strip()
|
||||
if isinstance(s, unicodetype) and isinstance(line, str):
|
||||
line = unicode(line, 'latin1')
|
||||
s += " %s\n" % line
|
||||
if isinstance(s, unicodetype) and isinstance(filename, str):
|
||||
enc = sys.getfilesystemencoding()
|
||||
if enc:
|
||||
try:
|
||||
filename = unicode(filename, enc)
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
s = "%s:%s" % (filename, s)
|
||||
return s
|
||||
|
||||
def filterwarnings(action, message="", category=Warning, module="", lineno=0,
|
||||
append=0):
|
||||
"""Insert an entry into the list of warnings filters (at the front).
|
||||
|
||||
'action' -- one of "error", "ignore", "always", "default", "module",
|
||||
or "once"
|
||||
'message' -- a regex that the warning message must match
|
||||
'category' -- a class that the warning must be a subclass of
|
||||
'module' -- a regex that the module name must match
|
||||
'lineno' -- an integer line number, 0 matches all warnings
|
||||
'append' -- if true, append to the list of filters
|
||||
"""
|
||||
import re
|
||||
assert action in ("error", "ignore", "always", "default", "module",
|
||||
"once"), "invalid action: %r" % (action,)
|
||||
assert isinstance(message, basestring), "message must be a string"
|
||||
assert isinstance(category, (type, types.ClassType)), \
|
||||
"category must be a class"
|
||||
assert issubclass(category, Warning), "category must be a Warning subclass"
|
||||
assert isinstance(module, basestring), "module must be a string"
|
||||
assert isinstance(lineno, int) and lineno >= 0, \
|
||||
"lineno must be an int >= 0"
|
||||
item = (action, re.compile(message, re.I), category,
|
||||
re.compile(module), lineno)
|
||||
if append:
|
||||
filters.append(item)
|
||||
else:
|
||||
filters.insert(0, item)
|
||||
|
||||
def simplefilter(action, category=Warning, lineno=0, append=0):
|
||||
"""Insert a simple entry into the list of warnings filters (at the front).
|
||||
|
||||
A simple filter matches all modules and messages.
|
||||
'action' -- one of "error", "ignore", "always", "default", "module",
|
||||
or "once"
|
||||
'category' -- a class that the warning must be a subclass of
|
||||
'lineno' -- an integer line number, 0 matches all warnings
|
||||
'append' -- if true, append to the list of filters
|
||||
"""
|
||||
assert action in ("error", "ignore", "always", "default", "module",
|
||||
"once"), "invalid action: %r" % (action,)
|
||||
assert isinstance(lineno, int) and lineno >= 0, \
|
||||
"lineno must be an int >= 0"
|
||||
item = (action, None, category, None, lineno)
|
||||
if append:
|
||||
filters.append(item)
|
||||
else:
|
||||
filters.insert(0, item)
|
||||
|
||||
def resetwarnings():
|
||||
"""Clear the list of warning filters, so that no filters are active."""
|
||||
filters[:] = []
|
||||
|
||||
class _OptionError(Exception):
|
||||
"""Exception used by option processing helpers."""
|
||||
pass
|
||||
|
||||
# Helper to process -W options passed via sys.warnoptions
|
||||
def _processoptions(args):
|
||||
for arg in args:
|
||||
try:
|
||||
_setoption(arg)
|
||||
except _OptionError, msg:
|
||||
print >>sys.stderr, "Invalid -W option ignored:", msg
|
||||
|
||||
# Helper for _processoptions()
|
||||
def _setoption(arg):
|
||||
import re
|
||||
parts = arg.split(':')
|
||||
if len(parts) > 5:
|
||||
raise _OptionError("too many fields (max 5): %r" % (arg,))
|
||||
while len(parts) < 5:
|
||||
parts.append('')
|
||||
action, message, category, module, lineno = [s.strip()
|
||||
for s in parts]
|
||||
action = _getaction(action)
|
||||
message = re.escape(message)
|
||||
category = _getcategory(category)
|
||||
module = re.escape(module)
|
||||
if module:
|
||||
module = module + '$'
|
||||
if lineno:
|
||||
try:
|
||||
lineno = int(lineno)
|
||||
if lineno < 0:
|
||||
raise ValueError
|
||||
except (ValueError, OverflowError):
|
||||
raise _OptionError("invalid lineno %r" % (lineno,))
|
||||
else:
|
||||
lineno = 0
|
||||
filterwarnings(action, message, category, module, lineno)
|
||||
|
||||
# Helper for _setoption()
|
||||
def _getaction(action):
|
||||
if not action:
|
||||
return "default"
|
||||
if action == "all": return "always" # Alias
|
||||
for a in ('default', 'always', 'ignore', 'module', 'once', 'error'):
|
||||
if a.startswith(action):
|
||||
return a
|
||||
raise _OptionError("invalid action: %r" % (action,))
|
||||
|
||||
# Helper for _setoption()
|
||||
def _getcategory(category):
|
||||
import re
|
||||
if not category:
|
||||
return Warning
|
||||
if re.match("^[a-zA-Z0-9_]+$", category):
|
||||
try:
|
||||
cat = eval(category)
|
||||
except NameError:
|
||||
raise _OptionError("unknown warning category: %r" % (category,))
|
||||
else:
|
||||
i = category.rfind(".")
|
||||
module = category[:i]
|
||||
klass = category[i+1:]
|
||||
try:
|
||||
m = __import__(module, None, None, [klass])
|
||||
except ImportError:
|
||||
raise _OptionError("invalid module name: %r" % (module,))
|
||||
try:
|
||||
cat = getattr(m, klass)
|
||||
except AttributeError:
|
||||
raise _OptionError("unknown warning category: %r" % (category,))
|
||||
if not issubclass(cat, Warning):
|
||||
raise _OptionError("invalid warning category: %r" % (category,))
|
||||
return cat
|
||||
|
||||
|
||||
# Code typically replaced by _warnings
|
||||
def warn(message, category=None, stacklevel=1):
|
||||
"""Issue a warning, or maybe ignore it or raise an exception."""
|
||||
# Check if message is already a Warning object
|
||||
if isinstance(message, Warning):
|
||||
category = message.__class__
|
||||
# Check category argument
|
||||
if category is None:
|
||||
category = UserWarning
|
||||
assert issubclass(category, Warning)
|
||||
# Get context information
|
||||
try:
|
||||
caller = sys._getframe(stacklevel)
|
||||
except ValueError:
|
||||
globals = sys.__dict__
|
||||
lineno = 1
|
||||
else:
|
||||
globals = caller.f_globals
|
||||
lineno = caller.f_lineno
|
||||
if '__name__' in globals:
|
||||
module = globals['__name__']
|
||||
else:
|
||||
module = "<string>"
|
||||
filename = globals.get('__file__')
|
||||
if filename:
|
||||
fnl = filename.lower()
|
||||
if fnl.endswith((".pyc", ".pyo")):
|
||||
filename = filename[:-1]
|
||||
else:
|
||||
if module == "__main__":
|
||||
try:
|
||||
filename = sys.argv[0]
|
||||
except AttributeError:
|
||||
# embedded interpreters don't have sys.argv, see bug #839151
|
||||
filename = '__main__'
|
||||
if not filename:
|
||||
filename = module
|
||||
registry = globals.setdefault("__warningregistry__", {})
|
||||
warn_explicit(message, category, filename, lineno, module, registry,
|
||||
globals)
|
||||
|
||||
def warn_explicit(message, category, filename, lineno,
|
||||
module=None, registry=None, module_globals=None):
|
||||
lineno = int(lineno)
|
||||
if module is None:
|
||||
module = filename or "<unknown>"
|
||||
if module[-3:].lower() == ".py":
|
||||
module = module[:-3] # XXX What about leading pathname?
|
||||
if registry is None:
|
||||
registry = {}
|
||||
if isinstance(message, Warning):
|
||||
text = str(message)
|
||||
category = message.__class__
|
||||
else:
|
||||
text = message
|
||||
message = category(message)
|
||||
key = (text, category, lineno)
|
||||
# Quick test for common case
|
||||
if registry.get(key):
|
||||
return
|
||||
# Search the filters
|
||||
for item in filters:
|
||||
action, msg, cat, mod, ln = item
|
||||
if ((msg is None or msg.match(text)) and
|
||||
issubclass(category, cat) and
|
||||
(mod is None or mod.match(module)) and
|
||||
(ln == 0 or lineno == ln)):
|
||||
break
|
||||
else:
|
||||
action = defaultaction
|
||||
# Early exit actions
|
||||
if action == "ignore":
|
||||
registry[key] = 1
|
||||
return
|
||||
|
||||
# Prime the linecache for formatting, in case the
|
||||
# "file" is actually in a zipfile or something.
|
||||
linecache.getlines(filename, module_globals)
|
||||
|
||||
if action == "error":
|
||||
raise message
|
||||
# Other actions
|
||||
if action == "once":
|
||||
registry[key] = 1
|
||||
oncekey = (text, category)
|
||||
if onceregistry.get(oncekey):
|
||||
return
|
||||
onceregistry[oncekey] = 1
|
||||
elif action == "always":
|
||||
pass
|
||||
elif action == "module":
|
||||
registry[key] = 1
|
||||
altkey = (text, category, 0)
|
||||
if registry.get(altkey):
|
||||
return
|
||||
registry[altkey] = 1
|
||||
elif action == "default":
|
||||
registry[key] = 1
|
||||
else:
|
||||
# Unrecognized actions are errors
|
||||
raise RuntimeError(
|
||||
"Unrecognized action (%r) in warnings.filters:\n %s" %
|
||||
(action, item))
|
||||
# Print message and context
|
||||
showwarning(message, category, filename, lineno)
|
||||
|
||||
|
||||
class WarningMessage(object):
|
||||
|
||||
"""Holds the result of a single showwarning() call."""
|
||||
|
||||
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
|
||||
"line")
|
||||
|
||||
def __init__(self, message, category, filename, lineno, file=None,
|
||||
line=None):
|
||||
local_values = locals()
|
||||
for attr in self._WARNING_DETAILS:
|
||||
setattr(self, attr, local_values[attr])
|
||||
self._category_name = category.__name__ if category else None
|
||||
|
||||
def __str__(self):
|
||||
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
|
||||
"line : %r}" % (self.message, self._category_name,
|
||||
self.filename, self.lineno, self.line))
|
||||
|
||||
|
||||
class catch_warnings(object):
|
||||
|
||||
"""A context manager that copies and restores the warnings filter upon
|
||||
exiting the context.
|
||||
|
||||
The 'record' argument specifies whether warnings should be captured by a
|
||||
custom implementation of warnings.showwarning() and be appended to a list
|
||||
returned by the context manager. Otherwise None is returned by the context
|
||||
manager. The objects appended to the list are arguments whose attributes
|
||||
mirror the arguments to showwarning().
|
||||
|
||||
The 'module' argument is to specify an alternative module to the module
|
||||
named 'warnings' and imported under that name. This argument is only useful
|
||||
when testing the warnings module itself.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, record=False, module=None):
|
||||
"""Specify whether to record warnings and if an alternative module
|
||||
should be used other than sys.modules['warnings'].
|
||||
|
||||
For compatibility with Python 3.0, please consider all arguments to be
|
||||
keyword-only.
|
||||
|
||||
"""
|
||||
self._record = record
|
||||
self._module = sys.modules['warnings'] if module is None else module
|
||||
self._entered = False
|
||||
|
||||
def __repr__(self):
|
||||
args = []
|
||||
if self._record:
|
||||
args.append("record=True")
|
||||
if self._module is not sys.modules['warnings']:
|
||||
args.append("module=%r" % self._module)
|
||||
name = type(self).__name__
|
||||
return "%s(%s)" % (name, ", ".join(args))
|
||||
|
||||
def __enter__(self):
|
||||
if self._entered:
|
||||
raise RuntimeError("Cannot enter %r twice" % self)
|
||||
self._entered = True
|
||||
self._filters = self._module.filters
|
||||
self._module.filters = self._filters[:]
|
||||
self._showwarning = self._module.showwarning
|
||||
if self._record:
|
||||
log = []
|
||||
def showwarning(*args, **kwargs):
|
||||
log.append(WarningMessage(*args, **kwargs))
|
||||
self._module.showwarning = showwarning
|
||||
return log
|
||||
else:
|
||||
return None
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
if not self._entered:
|
||||
raise RuntimeError("Cannot exit %r without entering first" % self)
|
||||
self._module.filters = self._filters
|
||||
self._module.showwarning = self._showwarning
|
||||
|
||||
|
||||
# filters contains a sequence of filter 5-tuples
|
||||
# The components of the 5-tuple are:
|
||||
# - an action: error, ignore, always, default, module, or once
|
||||
# - a compiled regex that must match the warning message
|
||||
# - a class representing the warning category
|
||||
# - a compiled regex that must match the module that is being warned
|
||||
# - a line number for the line being warning, or 0 to mean any line
|
||||
# If either if the compiled regexs are None, match anything.
|
||||
_warnings_defaults = False
|
||||
try:
|
||||
from _warnings import (filters, default_action, once_registry,
|
||||
warn, warn_explicit)
|
||||
defaultaction = default_action
|
||||
onceregistry = once_registry
|
||||
_warnings_defaults = True
|
||||
except ImportError:
|
||||
filters = []
|
||||
defaultaction = "default"
|
||||
onceregistry = {}
|
||||
|
||||
|
||||
# Module initialization
|
||||
_processoptions(sys.warnoptions)
|
||||
if not _warnings_defaults:
|
||||
silence = [ImportWarning, PendingDeprecationWarning]
|
||||
# Don't silence DeprecationWarning if -3 or -Q was used.
|
||||
if not sys.py3kwarning and not sys.flags.division_warning:
|
||||
silence.append(DeprecationWarning)
|
||||
for cls in silence:
|
||||
simplefilter("ignore", category=cls)
|
||||
bytes_warning = sys.flags.bytes_warning
|
||||
if bytes_warning > 1:
|
||||
bytes_action = "error"
|
||||
elif bytes_warning:
|
||||
bytes_action = "default"
|
||||
else:
|
||||
bytes_action = "ignore"
|
||||
simplefilter(bytes_action, category=BytesWarning, append=1)
|
||||
del _warnings_defaults
|
@ -1,458 +0,0 @@
|
||||
"""Weak reference support for Python.
|
||||
|
||||
This module is an implementation of PEP 205:
|
||||
|
||||
http://www.python.org/dev/peps/pep-0205/
|
||||
"""
|
||||
|
||||
# Naming convention: Variables named "wr" are weak reference objects;
|
||||
# they are called this instead of "ref" to avoid name collisions with
|
||||
# the module-global ref() function imported from _weakref.
|
||||
|
||||
import UserDict
|
||||
|
||||
from _weakref import (
|
||||
getweakrefcount,
|
||||
getweakrefs,
|
||||
ref,
|
||||
proxy,
|
||||
CallableProxyType,
|
||||
ProxyType,
|
||||
ReferenceType)
|
||||
|
||||
from _weakrefset import WeakSet, _IterationGuard
|
||||
|
||||
from exceptions import ReferenceError
|
||||
|
||||
|
||||
ProxyTypes = (ProxyType, CallableProxyType)
|
||||
|
||||
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
|
||||
"WeakKeyDictionary", "ReferenceError", "ReferenceType", "ProxyType",
|
||||
"CallableProxyType", "ProxyTypes", "WeakValueDictionary", 'WeakSet']
|
||||
|
||||
|
||||
class WeakValueDictionary(UserDict.UserDict):
|
||||
"""Mapping class that references values weakly.
|
||||
|
||||
Entries in the dictionary will be discarded when no strong
|
||||
reference to the value exists anymore
|
||||
"""
|
||||
# We inherit the constructor without worrying about the input
|
||||
# dictionary; since it uses our .update() method, we get the right
|
||||
# checks (if the other dictionary is a WeakValueDictionary,
|
||||
# objects are unwrapped on the way out, and we always wrap on the
|
||||
# way in).
|
||||
|
||||
def __init__(*args, **kw):
|
||||
if not args:
|
||||
raise TypeError("descriptor '__init__' of 'WeakValueDictionary' "
|
||||
"object needs an argument")
|
||||
self = args[0]
|
||||
args = args[1:]
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
def remove(wr, selfref=ref(self)):
|
||||
self = selfref()
|
||||
if self is not None:
|
||||
if self._iterating:
|
||||
self._pending_removals.append(wr.key)
|
||||
else:
|
||||
del self.data[wr.key]
|
||||
self._remove = remove
|
||||
# A list of keys to be removed
|
||||
self._pending_removals = []
|
||||
self._iterating = set()
|
||||
UserDict.UserDict.__init__(self, *args, **kw)
|
||||
|
||||
def _commit_removals(self):
|
||||
l = self._pending_removals
|
||||
d = self.data
|
||||
# We shouldn't encounter any KeyError, because this method should
|
||||
# always be called *before* mutating the dict.
|
||||
while l:
|
||||
del d[l.pop()]
|
||||
|
||||
def __getitem__(self, key):
|
||||
o = self.data[key]()
|
||||
if o is None:
|
||||
raise KeyError, key
|
||||
else:
|
||||
return o
|
||||
|
||||
def __delitem__(self, key):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
del self.data[key]
|
||||
|
||||
def __contains__(self, key):
|
||||
try:
|
||||
o = self.data[key]()
|
||||
except KeyError:
|
||||
return False
|
||||
return o is not None
|
||||
|
||||
def has_key(self, key):
|
||||
try:
|
||||
o = self.data[key]()
|
||||
except KeyError:
|
||||
return False
|
||||
return o is not None
|
||||
|
||||
def __repr__(self):
|
||||
return "<WeakValueDictionary at %s>" % id(self)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data[key] = KeyedRef(value, self._remove, key)
|
||||
|
||||
def clear(self):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data.clear()
|
||||
|
||||
def copy(self):
|
||||
new = WeakValueDictionary()
|
||||
for key, wr in self.data.items():
|
||||
o = wr()
|
||||
if o is not None:
|
||||
new[key] = o
|
||||
return new
|
||||
|
||||
__copy__ = copy
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
from copy import deepcopy
|
||||
new = self.__class__()
|
||||
for key, wr in self.data.items():
|
||||
o = wr()
|
||||
if o is not None:
|
||||
new[deepcopy(key, memo)] = o
|
||||
return new
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
wr = self.data[key]
|
||||
except KeyError:
|
||||
return default
|
||||
else:
|
||||
o = wr()
|
||||
if o is None:
|
||||
# This should only happen
|
||||
return default
|
||||
else:
|
||||
return o
|
||||
|
||||
def items(self):
|
||||
L = []
|
||||
for key, wr in self.data.items():
|
||||
o = wr()
|
||||
if o is not None:
|
||||
L.append((key, o))
|
||||
return L
|
||||
|
||||
def iteritems(self):
|
||||
with _IterationGuard(self):
|
||||
for wr in self.data.itervalues():
|
||||
value = wr()
|
||||
if value is not None:
|
||||
yield wr.key, value
|
||||
|
||||
def iterkeys(self):
|
||||
with _IterationGuard(self):
|
||||
for k in self.data.iterkeys():
|
||||
yield k
|
||||
|
||||
__iter__ = iterkeys
|
||||
|
||||
def itervaluerefs(self):
|
||||
"""Return an iterator that yields the weak references to the values.
|
||||
|
||||
The references are not guaranteed to be 'live' at the time
|
||||
they are used, so the result of calling the references needs
|
||||
to be checked before being used. This can be used to avoid
|
||||
creating references that will cause the garbage collector to
|
||||
keep the values around longer than needed.
|
||||
|
||||
"""
|
||||
with _IterationGuard(self):
|
||||
for wr in self.data.itervalues():
|
||||
yield wr
|
||||
|
||||
def itervalues(self):
|
||||
with _IterationGuard(self):
|
||||
for wr in self.data.itervalues():
|
||||
obj = wr()
|
||||
if obj is not None:
|
||||
yield obj
|
||||
|
||||
def popitem(self):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
while 1:
|
||||
key, wr = self.data.popitem()
|
||||
o = wr()
|
||||
if o is not None:
|
||||
return key, o
|
||||
|
||||
def pop(self, key, *args):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
try:
|
||||
o = self.data.pop(key)()
|
||||
except KeyError:
|
||||
if args:
|
||||
return args[0]
|
||||
raise
|
||||
if o is None:
|
||||
raise KeyError, key
|
||||
else:
|
||||
return o
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
try:
|
||||
wr = self.data[key]
|
||||
except KeyError:
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data[key] = KeyedRef(default, self._remove, key)
|
||||
return default
|
||||
else:
|
||||
return wr()
|
||||
|
||||
def update(*args, **kwargs):
|
||||
if not args:
|
||||
raise TypeError("descriptor 'update' of 'WeakValueDictionary' "
|
||||
"object needs an argument")
|
||||
self = args[0]
|
||||
args = args[1:]
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
dict = args[0] if args else None
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
d = self.data
|
||||
if dict is not None:
|
||||
if not hasattr(dict, "items"):
|
||||
dict = type({})(dict)
|
||||
for key, o in dict.items():
|
||||
d[key] = KeyedRef(o, self._remove, key)
|
||||
if len(kwargs):
|
||||
self.update(kwargs)
|
||||
|
||||
def valuerefs(self):
|
||||
"""Return a list of weak references to the values.
|
||||
|
||||
The references are not guaranteed to be 'live' at the time
|
||||
they are used, so the result of calling the references needs
|
||||
to be checked before being used. This can be used to avoid
|
||||
creating references that will cause the garbage collector to
|
||||
keep the values around longer than needed.
|
||||
|
||||
"""
|
||||
return self.data.values()
|
||||
|
||||
def values(self):
|
||||
L = []
|
||||
for wr in self.data.values():
|
||||
o = wr()
|
||||
if o is not None:
|
||||
L.append(o)
|
||||
return L
|
||||
|
||||
|
||||
class KeyedRef(ref):
|
||||
"""Specialized reference that includes a key corresponding to the value.
|
||||
|
||||
This is used in the WeakValueDictionary to avoid having to create
|
||||
a function object for each key stored in the mapping. A shared
|
||||
callback object can use the 'key' attribute of a KeyedRef instead
|
||||
of getting a reference to the key from an enclosing scope.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "key",
|
||||
|
||||
def __new__(type, ob, callback, key):
|
||||
self = ref.__new__(type, ob, callback)
|
||||
self.key = key
|
||||
return self
|
||||
|
||||
def __init__(self, ob, callback, key):
|
||||
super(KeyedRef, self).__init__(ob, callback)
|
||||
|
||||
|
||||
class WeakKeyDictionary(UserDict.UserDict):
|
||||
""" Mapping class that references keys weakly.
|
||||
|
||||
Entries in the dictionary will be discarded when there is no
|
||||
longer a strong reference to the key. This can be used to
|
||||
associate additional data with an object owned by other parts of
|
||||
an application without adding attributes to those objects. This
|
||||
can be especially useful with objects that override attribute
|
||||
accesses.
|
||||
"""
|
||||
|
||||
def __init__(self, dict=None):
|
||||
self.data = {}
|
||||
def remove(k, selfref=ref(self)):
|
||||
self = selfref()
|
||||
if self is not None:
|
||||
if self._iterating:
|
||||
self._pending_removals.append(k)
|
||||
else:
|
||||
del self.data[k]
|
||||
self._remove = remove
|
||||
# A list of dead weakrefs (keys to be removed)
|
||||
self._pending_removals = []
|
||||
self._iterating = set()
|
||||
if dict is not None:
|
||||
self.update(dict)
|
||||
|
||||
def _commit_removals(self):
|
||||
# NOTE: We don't need to call this method before mutating the dict,
|
||||
# because a dead weakref never compares equal to a live weakref,
|
||||
# even if they happened to refer to equal objects.
|
||||
# However, it means keys may already have been removed.
|
||||
l = self._pending_removals
|
||||
d = self.data
|
||||
while l:
|
||||
try:
|
||||
del d[l.pop()]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.data[ref(key)]
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.data[ref(key)]
|
||||
|
||||
def __repr__(self):
|
||||
return "<WeakKeyDictionary at %s>" % id(self)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.data[ref(key, self._remove)] = value
|
||||
|
||||
def copy(self):
|
||||
new = WeakKeyDictionary()
|
||||
for key, value in self.data.items():
|
||||
o = key()
|
||||
if o is not None:
|
||||
new[o] = value
|
||||
return new
|
||||
|
||||
__copy__ = copy
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
from copy import deepcopy
|
||||
new = self.__class__()
|
||||
for key, value in self.data.items():
|
||||
o = key()
|
||||
if o is not None:
|
||||
new[o] = deepcopy(value, memo)
|
||||
return new
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.data.get(ref(key),default)
|
||||
|
||||
def has_key(self, key):
|
||||
try:
|
||||
wr = ref(key)
|
||||
except TypeError:
|
||||
return 0
|
||||
return wr in self.data
|
||||
|
||||
def __contains__(self, key):
|
||||
try:
|
||||
wr = ref(key)
|
||||
except TypeError:
|
||||
return 0
|
||||
return wr in self.data
|
||||
|
||||
def items(self):
|
||||
L = []
|
||||
for key, value in self.data.items():
|
||||
o = key()
|
||||
if o is not None:
|
||||
L.append((o, value))
|
||||
return L
|
||||
|
||||
def iteritems(self):
|
||||
with _IterationGuard(self):
|
||||
for wr, value in self.data.iteritems():
|
||||
key = wr()
|
||||
if key is not None:
|
||||
yield key, value
|
||||
|
||||
def iterkeyrefs(self):
|
||||
"""Return an iterator that yields the weak references to the keys.
|
||||
|
||||
The references are not guaranteed to be 'live' at the time
|
||||
they are used, so the result of calling the references needs
|
||||
to be checked before being used. This can be used to avoid
|
||||
creating references that will cause the garbage collector to
|
||||
keep the keys around longer than needed.
|
||||
|
||||
"""
|
||||
with _IterationGuard(self):
|
||||
for wr in self.data.iterkeys():
|
||||
yield wr
|
||||
|
||||
def iterkeys(self):
|
||||
with _IterationGuard(self):
|
||||
for wr in self.data.iterkeys():
|
||||
obj = wr()
|
||||
if obj is not None:
|
||||
yield obj
|
||||
|
||||
__iter__ = iterkeys
|
||||
|
||||
def itervalues(self):
|
||||
with _IterationGuard(self):
|
||||
for value in self.data.itervalues():
|
||||
yield value
|
||||
|
||||
def keyrefs(self):
|
||||
"""Return a list of weak references to the keys.
|
||||
|
||||
The references are not guaranteed to be 'live' at the time
|
||||
they are used, so the result of calling the references needs
|
||||
to be checked before being used. This can be used to avoid
|
||||
creating references that will cause the garbage collector to
|
||||
keep the keys around longer than needed.
|
||||
|
||||
"""
|
||||
return self.data.keys()
|
||||
|
||||
def keys(self):
|
||||
L = []
|
||||
for wr in self.data.keys():
|
||||
o = wr()
|
||||
if o is not None:
|
||||
L.append(o)
|
||||
return L
|
||||
|
||||
def popitem(self):
|
||||
while 1:
|
||||
key, value = self.data.popitem()
|
||||
o = key()
|
||||
if o is not None:
|
||||
return o, value
|
||||
|
||||
def pop(self, key, *args):
|
||||
return self.data.pop(ref(key), *args)
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
return self.data.setdefault(ref(key, self._remove),default)
|
||||
|
||||
def update(self, dict=None, **kwargs):
|
||||
d = self.data
|
||||
if dict is not None:
|
||||
if not hasattr(dict, "items"):
|
||||
dict = type({})(dict)
|
||||
for key, value in dict.items():
|
||||
d[ref(key, self._remove)] = value
|
||||
if len(kwargs):
|
||||
self.update(kwargs)
|
@ -1,121 +0,0 @@
|
||||
#####################################################################################
|
||||
#
|
||||
# Copyright (c) Harry Pierson. All rights reserved.
|
||||
#
|
||||
# This source code is subject to terms and conditions of the Microsoft Public License.
|
||||
# A copy of the license can be found at http://opensource.org/licenses/ms-pl.html
|
||||
# By using this source code in any fashion, you are agreeing to be bound
|
||||
# by the terms of the Microsoft Public License.
|
||||
#
|
||||
# You must not remove this notice, or any other, from this software.
|
||||
#
|
||||
#####################################################################################
|
||||
|
||||
import ipypulldom
|
||||
from System.Xml import XmlNodeType
|
||||
|
||||
class _type_factory(object):
|
||||
class _type_node(object):
|
||||
def __init__(self, node):
|
||||
ty = type(node)
|
||||
self.name = ty.__name__
|
||||
self.namespace = ty.xmlns
|
||||
|
||||
def __init__(self):
|
||||
self.types = {}
|
||||
|
||||
def find_type(self, node, parent):
|
||||
def create_type(node, parent):
|
||||
return type(node.name, (parent,), {'xmlns':node.namespace})
|
||||
|
||||
if parent not in self.types:
|
||||
self.types[parent] = {}
|
||||
|
||||
tp = self.types[parent]
|
||||
if node.name not in tp:
|
||||
tp[node.name] = [create_type(node, parent)]
|
||||
|
||||
tpn = tp[node.name]
|
||||
|
||||
for t in tpn:
|
||||
if t.xmlns == node.namespace:
|
||||
return t
|
||||
|
||||
#if there's no matching namespace type, create one and add it to the list
|
||||
new_type = create_type(node, parent)
|
||||
tpn.append(new_type)
|
||||
return new_type
|
||||
|
||||
def __call__(self, node, parent=object):
|
||||
if isinstance(node, ipypulldom.XmlNode):
|
||||
return self.find_type(node, parent)
|
||||
return self.find_type(self._type_node(node), parent)
|
||||
|
||||
|
||||
xtype = _type_factory()
|
||||
|
||||
|
||||
def xml2py(nodelist):
|
||||
|
||||
def children(nodelist):
|
||||
while True:
|
||||
child = xml2py(nodelist)
|
||||
if child is None:
|
||||
break
|
||||
yield child
|
||||
|
||||
def set_attribute(parent, child):
|
||||
name = type(child).__name__
|
||||
if not hasattr(parent, name):
|
||||
setattr(parent, name, child)
|
||||
else:
|
||||
val = getattr(parent, name)
|
||||
if isinstance(val, list):
|
||||
val.append(child)
|
||||
else:
|
||||
setattr(parent, name, [val, child])
|
||||
|
||||
node = nodelist.next()
|
||||
if node.nodeType == XmlNodeType.EndElement:
|
||||
return None
|
||||
|
||||
elif node.nodeType == XmlNodeType.Text or node.nodeType == XmlNodeType.CDATA:
|
||||
return node.value
|
||||
|
||||
elif node.nodeType == XmlNodeType.Element:
|
||||
|
||||
#create a new object type named for the element name
|
||||
cur = xtype(node)()
|
||||
cur._nodetype = XmlNodeType.Element
|
||||
|
||||
#collect all the attributes and children in lists
|
||||
attributes = [xtype(attr, str)(attr.value) for attr in node.attributes]
|
||||
children = [child for child in children(nodelist)]
|
||||
|
||||
if len(children) == 1 and isinstance(children[0], str):
|
||||
#fold up elements with a single text node
|
||||
cur = xtype(cur, str)(children[0])
|
||||
cur._nodetype = XmlNodeType.Element
|
||||
else:
|
||||
#otherwise, add child elements as properties on the current node
|
||||
for child in children:
|
||||
set_attribute(cur, child)
|
||||
|
||||
for attr in attributes:
|
||||
attr._nodetype = XmlNodeType.Attribute
|
||||
set_attribute(cur, attr)
|
||||
|
||||
return cur
|
||||
|
||||
|
||||
def parse(xml):
|
||||
return xml2py(ipypulldom.parse(xml))
|
||||
|
||||
def parseString(xml):
|
||||
return xml2py(ipypulldom.parseString(xml))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
rss = parse('http://feeds.feedburner.com/Devhawk')
|
||||
for item in rss.channel.item:
|
||||
print item.title
|
Loading…
Reference in New Issue
Block a user