- removed 2.3 compat stuff
- updated MANIFEST for the newer build
This commit is contained in:
parent
bd3d262640
commit
a3ab10c3f3
|
@ -1,2 +1,2 @@
|
|||
include doc/*.html
|
||||
include doc/*.css
|
||||
recursive-include doc *.html *.css *.txt *.js
|
||||
prune doc/build/output
|
|
@ -5,8 +5,7 @@ SQLALCHEMY UNIT TESTS
|
|||
SETUP
|
||||
-----
|
||||
SQLite support is required. These instructions assume standard Python 2.4 or
|
||||
higher. See the section on alternate Python implementations for information on
|
||||
testing with 2.3 and other Pythons.
|
||||
higher.
|
||||
|
||||
The 'test' directory must be on the PYTHONPATH.
|
||||
|
||||
|
|
15
setup.py
15
setup.py
|
@ -6,13 +6,6 @@ try:
|
|||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
if sys.version_info < (2, 4):
|
||||
raise Exception("SQLAlchemy requires Python 2.4 or higher.")
|
||||
|
||||
v = file(os.path.join(os.path.dirname(__file__), 'lib', 'sqlalchemy', '__init__.py'))
|
||||
VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v.read()).group(1)
|
||||
v.close()
|
||||
|
||||
def find_packages(dir_):
|
||||
packages = []
|
||||
for _dir, subdirectories, files in os.walk(os.path.join(dir_, 'sqlalchemy')):
|
||||
|
@ -21,6 +14,14 @@ def find_packages(dir_):
|
|||
packages.append(fragment.replace(os.sep, '.'))
|
||||
return packages
|
||||
|
||||
|
||||
if sys.version_info < (2, 4):
|
||||
raise Exception("SQLAlchemy requires Python 2.4 or higher.")
|
||||
|
||||
v = file(os.path.join(os.path.dirname(__file__), 'lib', 'sqlalchemy', '__init__.py'))
|
||||
VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v.read()).group(1)
|
||||
v.close()
|
||||
|
||||
setup(name = "SQLAlchemy",
|
||||
version = VERSION,
|
||||
description = "Database Abstraction Library",
|
||||
|
|
175
test/clone.py
175
test/clone.py
|
@ -1,175 +0,0 @@
|
|||
# only tested with cpython!
|
||||
import optparse, os, shutil, sys
|
||||
from os import path
|
||||
from testlib import filters
|
||||
|
||||
__doc__ = """
|
||||
Creates and maintains a 'clone' of the test suite, optionally transforming
|
||||
the source code through a filter. The primary purpose of this utility is
|
||||
to allow the tests to run on Python VMs that do not implement a parser that
|
||||
groks 2.4 style @decorations.
|
||||
|
||||
Creating a clone:
|
||||
|
||||
Create a new, exact clone of the suite:
|
||||
$ python test/clone.py -c myclone
|
||||
|
||||
Create a new clone using the 2.3 filter:
|
||||
$ python test/clone.py -c --filter=py23 myclone
|
||||
|
||||
After the clone is set up, changes in the master can be pulled into the clone
|
||||
with the -u or --update switch. If the clone was created with a filter, it
|
||||
will be applied automatically when updating.
|
||||
|
||||
Update the clone:
|
||||
$ python test/clone.py -u myclone
|
||||
|
||||
The updating algorithm is very simple: if the version in test/ is newer than
|
||||
the one in your clone, the clone version is overwritten.
|
||||
"""
|
||||
|
||||
options = None
|
||||
clone, clone_path = None, None
|
||||
filter = lambda x: x[:]
|
||||
|
||||
def optparser():
|
||||
parser = optparse.OptionParser(
|
||||
usage=('usage: %prog [options] CLONE-NAME\n' + __doc__ ).rstrip())
|
||||
parser.add_option('-n', '--dry-run', dest='dryrun',
|
||||
action='store_true',
|
||||
help=('Do not actually change any files; '
|
||||
'just print what would happen.'))
|
||||
parser.add_option('-u', '--update', dest='update', action='store_true',
|
||||
help='Update an existing clone.')
|
||||
parser.add_option('-c', '--create', dest='create', action='store_true',
|
||||
help='Create a new clone.')
|
||||
parser.add_option('--filter', dest='filter',
|
||||
help='Run source code through a filter.')
|
||||
parser.add_option('-l', '--filter-list', dest='filter_list',
|
||||
action='store_true',
|
||||
help='Show available filters.')
|
||||
parser.add_option('-f', '--force', dest='force', action='store_true',
|
||||
help='Overwrite clone files even if unchanged.')
|
||||
parser.add_option('-q', '--quiet', dest='quiet', action='store_true',
|
||||
help='Run quietly.')
|
||||
parser.set_defaults(update=False, create=False,
|
||||
dryrun=False, filter_list=False,
|
||||
force=False, quiet=False)
|
||||
return parser
|
||||
|
||||
def config():
|
||||
global clone, clone_path, options, filter
|
||||
|
||||
parser = optparser()
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.filter_list:
|
||||
if options.quiet:
|
||||
print '\n'.join(filters.__all__)
|
||||
else:
|
||||
print 'Available filters:'
|
||||
for name in filters.__all__:
|
||||
print '\t%s' % name
|
||||
sys.exit(0)
|
||||
|
||||
if not options.update and not options.create:
|
||||
parser.error('One of -u or -c is required.')
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error('A clone name is required.')
|
||||
|
||||
clone = args[0]
|
||||
clone_path = path.abspath(clone)
|
||||
|
||||
if options.update and not path.exists(clone_path):
|
||||
parser.error(
|
||||
'Clone %s does not exist; create it with --create first.' % clone)
|
||||
if options.create and path.exists(clone_path):
|
||||
parser.error('Clone %s already exists.' % clone)
|
||||
|
||||
if options.filter:
|
||||
if options.filter not in filters.__all__:
|
||||
parser.error(('Filter "%s" unknown; use --filter-list to see '
|
||||
'available filters.') % options.filter)
|
||||
filter = getattr(filters, options.filter)
|
||||
|
||||
def setup():
|
||||
global filter
|
||||
|
||||
if options.create:
|
||||
if not options.quiet:
|
||||
print "mkdir %s" % clone_path
|
||||
if not options.dryrun:
|
||||
os.mkdir(clone_path)
|
||||
|
||||
if options.filter and not options.dryrun:
|
||||
if not options.quiet:
|
||||
print 'storing filter "%s" in %s/.filter' % (
|
||||
options.filter, clone)
|
||||
stash = open(path.join(clone_path, '.filter'), 'w')
|
||||
stash.write(options.filter)
|
||||
stash.close()
|
||||
else:
|
||||
stash_file = path.join(clone_path, '.filter')
|
||||
if path.exists(stash_file):
|
||||
stash = open(stash_file)
|
||||
stashed = stash.read().strip()
|
||||
stash.close()
|
||||
if options.filter:
|
||||
if (options.filter != stashed and stashed in filters.__all__ and
|
||||
not options.quiet):
|
||||
print (('Warning: --filter=%s overrides %s specified in '
|
||||
'%s/.filter') % (options.filter, stashed, clone))
|
||||
else:
|
||||
if stashed not in filters.__all__:
|
||||
sys.stderr.write(
|
||||
'Filter "%s" in %s/.filter is not valid, aborting.' %
|
||||
(stashed, clone))
|
||||
sys.exit(-1)
|
||||
filter = getattr(filters, stashed)
|
||||
|
||||
def sync():
|
||||
source_path, _ = path.split(path.abspath(__file__))
|
||||
|
||||
ls = lambda root: [fn
|
||||
for fn in os.listdir(root)
|
||||
if (fn.endswith('.py') and not fn.startswith('.'))]
|
||||
|
||||
def walker(x, dirname, fnames):
|
||||
if '.svn' in fnames:
|
||||
fnames.remove('.svn')
|
||||
|
||||
rel_path = dirname[len(source_path) + 1:]
|
||||
dest_path = path.join(clone_path, rel_path)
|
||||
|
||||
if not path.exists(dest_path):
|
||||
if not options.quiet:
|
||||
print "mkdir %s/%s" % (clone, rel_path)
|
||||
if not options.dryrun:
|
||||
os.mkdir(dest_path)
|
||||
|
||||
for filename in ls(dirname):
|
||||
source_file = path.join(source_path, rel_path, filename)
|
||||
dest_file = path.join(dest_path, filename)
|
||||
|
||||
if (options.force or
|
||||
(not path.exists(dest_file) or
|
||||
os.stat(source_file)[-1] > os.stat(dest_file)[-1])):
|
||||
if not options.quiet:
|
||||
print "syncing %s" % path.join(rel_path, filename)
|
||||
|
||||
raw = open(source_file)
|
||||
filtered = filter(raw.readlines())
|
||||
raw.close()
|
||||
|
||||
if not options.dryrun:
|
||||
synced = open(dest_file, 'w')
|
||||
synced.writelines(filtered)
|
||||
synced.close()
|
||||
|
||||
os.path.walk(source_path, walker, None)
|
||||
|
||||
if __name__ == '__main__':
|
||||
config()
|
||||
setup()
|
||||
sync()
|
|
@ -1,239 +0,0 @@
|
|||
"""A collection of Python source transformers.
|
||||
|
||||
Supports the 'clone' command, providing source code transforms to run the test
|
||||
suite on pre Python 2.4-level parser implementations.
|
||||
|
||||
Includes::
|
||||
|
||||
py23
|
||||
Converts 2.4-level source code into 2.3-parsable source.
|
||||
Currently only rewrites @decorators, but generator transformations
|
||||
are possible.
|
||||
py23_decorators
|
||||
py23 is currently an alias for py23_decorators.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from tokenize import generate_tokens, INDENT, DEDENT, NAME, OP, NL, NEWLINE, \
|
||||
NUMBER, STRING, COMMENT
|
||||
|
||||
__all__ = ['py23_decorators', 'py23']
|
||||
|
||||
|
||||
def py23_decorators(lines):
|
||||
"""Translates @decorators in source lines to 2.3 syntax."""
|
||||
|
||||
tokens = peekable(generate_tokens(iter(lines).next))
|
||||
text = untokenize(backport_decorators(tokens))
|
||||
return [x + '\n' for x in text.split('\n')]
|
||||
|
||||
py23 = py23_decorators
|
||||
|
||||
|
||||
def backport_decorators(stream):
|
||||
"""Restates @decorators in 2.3 syntax
|
||||
|
||||
Operates on token streams. Converts::
|
||||
|
||||
@foo
|
||||
@bar(1, 2)
|
||||
def quux():
|
||||
pass
|
||||
into::
|
||||
|
||||
def quux():
|
||||
pass
|
||||
quux = bar(1, 2)(quux)
|
||||
quux = foo(quux)
|
||||
|
||||
Fails on decorated one-liners::
|
||||
|
||||
@decorator
|
||||
def fn(): pass
|
||||
"""
|
||||
|
||||
if not hasattr(stream, 'peek'):
|
||||
stream = peekable(iter(stream))
|
||||
|
||||
stack = [_DecoratorState('')]
|
||||
emit = []
|
||||
for ttype, tok, _, _, _ in stream:
|
||||
current = stack[-1]
|
||||
if ttype == INDENT:
|
||||
current = _DecoratorState(tok)
|
||||
stack.append(current)
|
||||
elif ttype == DEDENT:
|
||||
previous = stack.pop()
|
||||
assert not previous.decorations
|
||||
current = stack[-1]
|
||||
if current.decorations:
|
||||
ws = pop_trailing_whitespace(emit)
|
||||
|
||||
emit.append((ttype, tok))
|
||||
for decorator, misc in reversed(current.decorations):
|
||||
if not decorator or decorator[0][1] != '@':
|
||||
emit.extend(decorator)
|
||||
else:
|
||||
emit.extend(
|
||||
[(NAME, current.fn_name), (OP, '=')] +
|
||||
decorator[1:] +
|
||||
[(OP, '('), (NAME, current.fn_name), (OP, ')')])
|
||||
emit.extend(misc)
|
||||
current.decorations = []
|
||||
emit.extend(ws)
|
||||
continue
|
||||
elif ttype == OP and tok == '@':
|
||||
current.in_decorator = True
|
||||
decoration = [(ttype, tok)]
|
||||
current.decorations.append((decoration, []))
|
||||
current.consume_identifier(stream)
|
||||
if stream.peek()[1] == '(':
|
||||
current.consume_parened(stream)
|
||||
continue
|
||||
elif ttype == NAME and tok == 'def':
|
||||
current.in_decorator = False
|
||||
current.fn_name = stream.peek()[1]
|
||||
elif current.in_decorator:
|
||||
current.append_misc((ttype, tok))
|
||||
continue
|
||||
|
||||
emit.append((ttype, tok))
|
||||
return emit
|
||||
|
||||
class _DecoratorState(object):
|
||||
"""Holds state for restating decorators as function calls."""
|
||||
|
||||
in_decorator = False
|
||||
fn_name = None
|
||||
def __init__(self, indent):
|
||||
self.indent = indent
|
||||
self.decorations = []
|
||||
def append_misc(self, token):
|
||||
if not self.decorations:
|
||||
self.decorations.append(([], []))
|
||||
self.decorations[-1][1].append(token)
|
||||
def consume_identifier(self, stream):
|
||||
while True:
|
||||
typ, value = stream.peek()[:2]
|
||||
if not (typ == NAME or (typ == OP and value == '.')):
|
||||
break
|
||||
self.decorations[-1][0].append(stream.next()[:2])
|
||||
def consume_parened(self, stream):
|
||||
"""Consume a (paren) sequence from a token seq starting with ("""
|
||||
depth, offsets = 0, {'(':1, ')':-1}
|
||||
while True:
|
||||
typ, value = stream.next()[:2]
|
||||
if typ == OP:
|
||||
depth += offsets.get(value, 0)
|
||||
self.decorations[-1][0].append((typ, value))
|
||||
if depth == 0:
|
||||
break
|
||||
|
||||
def pop_trailing_whitespace(tokens):
|
||||
"""Removes trailing whitespace tokens from a token list."""
|
||||
|
||||
popped = []
|
||||
for token in reversed(list(tokens)):
|
||||
if token[0] not in (NL, COMMENT):
|
||||
break
|
||||
popped.append(tokens.pop())
|
||||
return popped
|
||||
|
||||
def untokenize(iterable):
|
||||
"""Turns a stream of tokens into a Python source str.
|
||||
|
||||
A PEP-8-ish variant of Python 2.5+'s tokenize.untokenize. Produces output
|
||||
that's not perfect, but is at least readable. The stdlib version is
|
||||
basically unusable.
|
||||
"""
|
||||
|
||||
if not hasattr(iterable, 'peek'):
|
||||
iterable = peekable(iter(iterable))
|
||||
|
||||
startline = False
|
||||
indents = []
|
||||
toks = []
|
||||
toks_append = toks.append
|
||||
|
||||
# this is pretty roughly hacked. i think it could get very close to
|
||||
# perfect by rewriting to operate over a sliding window of
|
||||
# (prev, current, next) token sets + making some grouping macros to
|
||||
# include all the tokens and operators this omits.
|
||||
for tok in iterable:
|
||||
toknum, tokval = tok[:2]
|
||||
|
||||
try:
|
||||
next_num, next_val = iterable.peek()[:2]
|
||||
except StopIteration:
|
||||
next_num, next_val = None, None
|
||||
|
||||
if toknum == NAME:
|
||||
if tokval == 'in':
|
||||
tokval += ' '
|
||||
elif next_num == OP:
|
||||
if next_val not in ('(', ')', '[', ']', '{', '}',
|
||||
':', '.', ',',):
|
||||
tokval += ' '
|
||||
elif next_num != NEWLINE:
|
||||
tokval += ' '
|
||||
elif toknum == OP:
|
||||
if tokval in ('(', '@', '.', '[', '{', '*', '**'):
|
||||
pass
|
||||
elif tokval in ('%', ':') and next_num not in (NEWLINE, ):
|
||||
tokval += ' '
|
||||
elif next_num in (NAME, COMMENT,
|
||||
NUMBER, STRING):
|
||||
tokval += ' '
|
||||
elif (tokval in (')', ']', '}') and next_num == OP and
|
||||
'=' in next_val):
|
||||
tokval += ' '
|
||||
elif tokval == ',' or '=' in tokval:
|
||||
tokval += ' '
|
||||
elif toknum in (NUMBER, STRING):
|
||||
if next_num == OP and next_val not in (')', ']', '}', ',', ':'):
|
||||
tokval += ' '
|
||||
elif next_num == NAME:
|
||||
tokval += ' '
|
||||
|
||||
# would be nice to indent continued lines...
|
||||
if toknum == INDENT:
|
||||
indents.append(tokval)
|
||||
continue
|
||||
elif toknum == DEDENT:
|
||||
indents.pop()
|
||||
continue
|
||||
elif toknum in (NEWLINE, COMMENT, NL):
|
||||
startline = True
|
||||
elif startline and indents:
|
||||
toks_append(indents[-1])
|
||||
startline = False
|
||||
toks_append(tokval)
|
||||
return ''.join(toks)
|
||||
|
||||
|
||||
class peekable(object):
|
||||
"""A iterator wrapper that allows peek()ing at the next value."""
|
||||
|
||||
def __init__(self, iterator):
|
||||
self.iterator = iterator
|
||||
self.buffer = []
|
||||
def next(self):
|
||||
if self.buffer:
|
||||
return self.buffer.pop(0)
|
||||
return self.iterator.next()
|
||||
def peek(self):
|
||||
if self.buffer:
|
||||
return self.buffer[0]
|
||||
x = self.iterator.next()
|
||||
self.buffer.append(x)
|
||||
return x
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
if __name__ == '__main__':
|
||||
# runnable. converts a named file to 2.3.
|
||||
input = open(len(sys.argv) == 2 and sys.argv[1] or __file__)
|
||||
|
||||
tokens = generate_tokens(input.readline)
|
||||
back = backport_decorators(tokens)
|
||||
print untokenize(back)
|
Loading…
Reference in New Issue