diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2011-08-17 11:51:08 -0400 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2011-08-17 11:51:08 -0400 |
| commit | f3a121563512d51133cbacf50c96b1a3dccde31d (patch) | |
| tree | 081e1a83805003ebfb8b433f1513c413040aa944 /passlib/_setup | |
| parent | d2ffaa3eecd9866918a20547d02c3a1b45dc8fdb (diff) | |
| download | passlib-f3a121563512d51133cbacf50c96b1a3dccde31d.tar.gz | |
setup script enhancements
* added code to make builtin snapshots & releases with correct version # easier,
no longer dependant on egg_info's tag_date (which didn't make PEP compatible
version strings anyways).
* moved passlib.setup to passlib._setup - not really publically useful anyways,
and name was causing nose/unitest to get confused
* added tests/*.cfg to setup & manifest
Diffstat (limited to 'passlib/_setup')
| -rw-r--r-- | passlib/_setup/__init__.py | 5 | ||||
| -rw-r--r-- | passlib/_setup/cond2to3.py | 152 | ||||
| -rw-r--r-- | passlib/_setup/stamp.py | 57 |
3 files changed, 214 insertions, 0 deletions
diff --git a/passlib/_setup/__init__.py b/passlib/_setup/__init__.py new file mode 100644 index 0000000..814c93a --- /dev/null +++ b/passlib/_setup/__init__.py @@ -0,0 +1,5 @@ +"""passlib.setup - helpers used by passlib's setup.py script + +note that unlike the rest of passlib, the code in this package must +work *unaltered* under both python 2 & 3 +""" diff --git a/passlib/_setup/cond2to3.py b/passlib/_setup/cond2to3.py new file mode 100644 index 0000000..12cb0a8 --- /dev/null +++ b/passlib/_setup/cond2to3.py @@ -0,0 +1,152 @@ +"""passlib.setup.cond2to3 - moneypatches 2to3 to provide conditional macros, ala SQLAlchemy""" +#========================================================= +#imports +#========================================================= +#core +from lib2to3.refactor import RefactoringTool +import re +#site +#local +__all__ = [ + "patch2to3", +] + +#========================================================= +#macro preprocessor +#========================================================= +py3k_start_re = re.compile(r"^(\s*)# Py3K #", re.I) +py3k_stop_re = re.compile(r"^(\s*)# end Py3K #", re.I) + +py2k_start_re = re.compile(r"^(\s*)# Py2K #", re.I) +py2k_stop_re = re.compile(r"^(\s*)# end Py2K #", re.I) + +bare_comment_re = re.compile(r"^(\s*)#(.*)") +bare_re = re.compile(r"^(\s*)(.*)") + +def preprocess(data, name): + #TODO: add flag so this can also function in reverse, for 3to2 + changed = False + + lines = data.split("\n") + state = 0 + #0: parsing normally, looking for start-p3k or start-py2k + #1: in Py3K block - removing comment chars until end-py3k + #2: in Py2K block - adding comment chars until end-py2k + idx = 0 + indent = '' + while idx < len(lines): + line = lines[idx] + + #hack to detect ''"abc" strings - using this as py25-compat way to indicate bytes. + #should really turn into a proper fixer. + #also, this check is really weak, and might fail in some cases + if '\'\'".*"' in line: + line = lines[idx] = line.replace("''", "b") + changed = True + + #check for py3k start marker + m = py3k_start_re.match(line) + if m: + if state in (0,2): + ident = m.group(1) + state = 1 + idx += 1 + continue + #states 1 this is an error... + raise SyntaxError("unexpected py3k-start marker on line %d of %r: %r" % (idx, name, line)) + + #check for py3k stop marker + if py3k_stop_re.match(line): + if state == 1: + state = 0 + idx += 1 + continue + #states 0,2 this is an error... + raise SyntaxError("unexpected py3k-stop marker on line %d of %r: %r" % (idx, name, line)) + + #check for py2k start marker + m = py2k_start_re.match(line) + if m: + if state in (0,1): + ident = m.group(1) + state = 2 + idx += 1 + continue + #states 2 this is an error... + raise SyntaxError("unexpected py2k-start marker on line %d of %r: %r" % (idx, name, line)) + + #check for py2k end marker + if py2k_stop_re.match(line): + if state == 2: + state = 0 + idx += 1 + continue + #states 0,1 this is an error... + raise SyntaxError("unexpected py2k-stop marker on line %d of %r: %r" % (idx, name, line)) + + #state 0 - leave non-marker lines alone + if state == 0: + idx += 1 + continue + + #state 1 - uncomment comment lines, throw error on bare lines + if state == 1: + m = bare_comment_re.match(line) + if not m: + raise SyntaxError("unexpected non-comment in py3k block on line %d of %r: %r" % (idx,name, line)) + pad, content = m.group(1,2) + lines[idx] = pad + content + changed = True + idx += 1 + continue + + #state 2 - comment out all lines + if state == 2: + m = bare_re.match(line) + if not m: + raise RuntimeError("unexpected failure to parse line %d of %r: %r" % (idx, name, line)) + pad, content = m.group(1,2) + if pad.startswith(ident): #try to put comments on same level + content = pad[len(ident):] + content + pad = ident + lines[idx] = "%s#%s" % (pad,content) + changed = True + idx += 1 + continue + + #should never get here + raise AssertionError("invalid state: %r" % (state,)) + + if changed: + return "\n".join(lines) + else: + return data + +orig_rs = RefactoringTool.refactor_string + +def refactor_string(self, data, name): + "replacement for RefactoringTool.refactor_string which honors conditional includes" + newdata = preprocess(data, name) + tree = orig_rs(self, newdata, name) + if tree and newdata != data: + tree.was_changed = True + return tree + +#========================================================= +#main +#========================================================= + +def patch2to3(): + "frontend to patch preprocessor into lib2to3" + RefactoringTool.refactor_string = refactor_string + +#helper for development purposes - runs 2to3 w/ patch +if __name__ == "__main__": + import sys + from lib2to3.main import main + patch2to3() + sys.exit(main("lib2to3.fixes")) + +#========================================================= +#eof +#========================================================= diff --git a/passlib/_setup/stamp.py b/passlib/_setup/stamp.py new file mode 100644 index 0000000..14abd59 --- /dev/null +++ b/passlib/_setup/stamp.py @@ -0,0 +1,57 @@ +"update version string during build" +#========================================================= +# imports +#========================================================= +from __future__ import with_statement +#core +import os +import re +import time +from distutils.dist import Distribution +#pkg +#local +__all__ = [ + "stamp_source", + "stamp_distutils_output", +] +#========================================================= +# helpers +#========================================================= +def get_command_class(opts, name): + return opts['cmdclass'].get(name) or Distribution().get_command_class(name) + +def stamp_source(base_dir, version, dry_run=False): + "update version string in passlib dist" + path = os.path.join(base_dir, "passlib", "__init__.py") + with open(path) as fh: + input = fh.read() + output = re.sub('(?m)^__version__\s*=.*$', + '__version__ = ' + repr(version), + input) + assert output != input, "failed to match" + if not dry_run: + os.unlink(path) # sdist likes to use hardlinks + with open(path, "w") as fh: + fh.write(output) + +def stamp_distutils_output(opts, version): + + # subclass buildpy to update version string in source + _build_py = get_command_class(opts, "build_py") + class build_py(_build_py): + def build_packages(self): + _build_py.build_packages(self) + stamp_source(self.build_lib, version, self.dry_run) + opts['cmdclass']['build_py'] = build_py + + # subclass sdist to do same thing + _sdist = get_command_class(opts, "sdist") + class sdist(_sdist): + def make_release_tree(self, base_dir, files): + _sdist.make_release_tree(self, base_dir, files) + stamp_source(base_dir, version, self.dry_run) + opts['cmdclass']['sdist'] = sdist + +#========================================================= +# eof +#========================================================= |
