From 7ccfa1c1dd7f4665efef1cddecccad45a639055b Mon Sep 17 00:00:00 2001 From: Gabriel Falcao Date: Mon, 30 Jul 2012 12:16:05 -0400 Subject: [PATCH] first commit --- .travis.yml | 10 ++++ COPYING | 23 ++++++++ Makefile | 22 ++++++++ README.md | 35 ++++++++++++ requirements.pip | 2 + setup.py | 57 +++++++++++++++++++ steadymark/__init__.py | 124 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 273 insertions(+) create mode 100644 .travis.yml create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README.md create mode 100644 requirements.pip create mode 100755 setup.py create mode 100644 steadymark/__init__.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..38649e9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python +python: + - "2.5" + - "2.6" + - "2.7" +# command to install dependencies +install: + - pip install -r requirements.pip +# command to run tests +script: make test diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d6aa4e5 --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +# +# Copyright (C) <2012> Gabriel Falcão +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aeade79 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +all: test + +filename=steadymark-`python -c 'import steadymark;print steadymark.version'`.tar.gz + +export PYTHONPATH:= ${PWD} + +test: clean + @echo "Running code examples from README.md as tests" + @python steadymark/__init__.py + +clean: + @printf "Cleaning up files that are already in .gitignore... " + @for pattern in `cat .gitignore`; do rm -rf $$pattern; find . -name "$$pattern" -exec rm -rf {} \;; done + @echo "OK!" + +release: test publish + @printf "Exporting to $(filename)... " + @tar czf $(filename) steadymark setup.py README.md COPYING + @echo "DONE!" + +publish: + @python setup.py sdist register upload diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfd64bc --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Steady Mark +[![Build Status](https://secure.travis-ci.org/gabrielfalcao/steadymark.png)](http://travis-ci.org/gabrielfalcao/steadymark) + +# Markdown files + python snippets = regression tests + +Steady Mark was created for python developers that love Github and +markdown. + +## How it works: + +Write your documentation using github-flavored markdown, surround your +snippets with python code blocks and steadymark will automatically +find and run them, if there is a header preceeding your python snippet +it will be used as title for your test. + +# Example + +Given the following `README.md`: + + # My project name + `version 0.1` + + ## usage + + ```python + from mylibrary import whatever + + whatever.awesome() + ``` + +Just run with: + +```console +$ steadymark README.md +``` diff --git a/requirements.pip b/requirements.pip new file mode 100644 index 0000000..3e16f2c --- /dev/null +++ b/requirements.pip @@ -0,0 +1,2 @@ +distribute>=0.6.24 +misaka>=1.0.2 diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..83ec388 --- /dev/null +++ b/setup.py @@ -0,0 +1,57 @@ +# #!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) <2010> Gabriel Falcão +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +import os +from steadymark import version +from setuptools import setup + + +def get_packages(): + # setuptools can't do the job :( + packages = [] + for root, dirnames, filenames in os.walk('steadymark'): + if '__init__.py' in filenames: + packages.append(".".join(os.path.split(root)).strip(".")) + + return packages + +setup(name='steadymark', + version=version, + description='assertion toolbox for python', + author=u'Gabriel Falcao', + author_email='gabriel@nacaolivre.org', + url='http://github.com/gabrielfalcao/steadymark', + packages=get_packages(), + install_requires=[ + 'misaka', + ], + entry_points={ + 'console_scripts': ['steadymark = steadymark:main'], + }, + package_data={ + 'lettuce': ['COPYING', '*.md'], + }, +) diff --git a/steadymark/__init__.py b/steadymark/__init__.py new file mode 100644 index 0000000..aa20cf0 --- /dev/null +++ b/steadymark/__init__.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) <2012> Gabriel Falcão +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +version = '0.1' +import os +import sys +import traceback +from datetime import datetime + +from misaka import ( + BaseRenderer, + Markdown, + EXT_FENCED_CODE, + EXT_NO_INTRA_EMPHASIS, +) + + +class READMETestRunner(BaseRenderer): + tests = [{}] + filename = None + + def block_code(self, code, language): + if language != 'python': + return + + item = self.tests[-1] + item[u'code'] = unicode(code) + if 'title' not in item: + item[u'title'] = u'Test #{0}'.format(len(self.tests)) + self.tests.append({}) + + def header(self, title, level): + self.tests.append({ + u'title': unicode(title), + }) + + def postprocess(self, full_document): + actual_tests = [t for t in self.tests if 'code' in t] + if actual_tests: + print "Running code snippets from {0}".format(self.filename) + else: + print "No tests found in {0}".format(self.filename) + + for test in actual_tests: + sys.stdout.write("{0} ...".format(test['title'])) + before = datetime.now() + failure = None + lines = test['code'].splitlines() + try: + code = compile(test['code'], "README.md", "exec") + eval(code) + except Exception: + failure = sys.exc_info() + + after = datetime.now() + + shift = before - after + ms = shift.microseconds / 1000 + if not failure: + print "OK ({0}ms)".format(ms) + else: + print "Failed ({0}ms)".format(ms) + exc, name, tb = failure + tb = tb.tb_next + line = lines[tb.tb_lineno - 1] + print "Traceback (most recent call last):" + print "{0} {1}".format(traceback.format_tb(tb)[-1], line) + # print u' File README.md, line {0}'.format(tb.next) + + +def main(): + import argparse + + parser = argparse.ArgumentParser( + description=(u'Use python code snippets from your README.md or ' + 'any markdown files as regression tests'), + ) + parser.add_argument( + 'filename', + metavar='FILENAME.md', + type=unicode, + nargs='*', + default='README.md', + help='the path to a markdown file to be inspected for tests', + ) + args = parser.parse_args() + + renderer = READMETestRunner() + renderer.filename = args.filename + + extensions = EXT_FENCED_CODE | EXT_NO_INTRA_EMPHASIS + md = Markdown(renderer, extensions=extensions) + if not os.path.exists(renderer.filename): + print 'steadymark could not find {0}'.format(renderer.filename) + sys.exit(1) + text = open(renderer.filename).read() + md.render(text) + + +if __name__ == '__main__': + main()