From bf3b3d3d74c0eb9b6822a6c065fc55fc7e76f81d Mon Sep 17 00:00:00 2001 From: Matthew Scragg Date: Wed, 22 Oct 2014 15:38:32 -0500 Subject: [PATCH] added more tests include img in manifest disclaimer in root cli script add blinker to dev tools version file included in package cli bug fixes --- MANIFEST.in | 3 +- VERSION | 1 - realms-wiki | 12 ++++- realms/__init__.py | 29 +++-------- realms/{commands.py => cli.py} | 72 ++++++++++++++++----------- realms/lib/test.py | 4 ++ realms/modules/wiki/models.py | 11 +++-- realms/modules/wiki/tests.py | 89 +++++++++++++++++++++++----------- realms/modules/wiki/views.py | 28 ++++++----- realms/version.py | 1 + requirements-dev.txt | 1 + requirements.txt | 19 +------- setup.py | 28 +++++++++-- 13 files changed, 178 insertions(+), 120 deletions(-) delete mode 100644 VERSION rename realms/{commands.py => cli.py} (81%) create mode 100644 realms/version.py diff --git a/MANIFEST.in b/MANIFEST.in index d72fe63..a5cc0d6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ -include requirements.txt VERSION LICENSE README.md +include requirements.txt LICENSE README.md +recursive-include realms/static/img * recursive-include realms/static/fonts * recursive-include realms/static/css * recursive-include realms/static/js * diff --git a/VERSION b/VERSION deleted file mode 100644 index 771f209..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.3.20 \ No newline at end of file diff --git a/realms-wiki b/realms-wiki index b212a62..1baa18f 100755 --- a/realms-wiki +++ b/realms-wiki @@ -1,6 +1,14 @@ #!/usr/bin/env python - -from realms.commands import cli +import pip +from realms.cli import cli if __name__ == '__main__': + print + print "----------------------------------" + print "This script is for development.\n" \ + "If you installed via pip, " \ + "you should have realms-wiki in your PATH that should be used instead." + print "----------------------------------" + print + cli() diff --git a/realms/__init__.py b/realms/__init__.py index 630434e..6bfa461 100644 --- a/realms/__init__.py +++ b/realms/__init__.py @@ -18,9 +18,10 @@ from flask.ext.assets import Environment, Bundle from werkzeug.routing import BaseConverter from werkzeug.exceptions import HTTPException -from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict -from realms.lib.hook import HookModelMeta -from realms.lib.util import is_su, in_virtualenv +from .lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict +from .lib.hook import HookModelMeta +from .lib.util import is_su, in_virtualenv +from .version import __version__ class Application(Flask): @@ -79,7 +80,7 @@ class Application(Flask): if hasattr(sources.hooks, 'before_first_request'): self.before_first_request(sources.hooks.before_first_request) - # print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time)) + # print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time)) def make_response(self, rv): if rv is None: @@ -200,17 +201,6 @@ def create_app(config=None): return app -@click.group() -@click.pass_context -def cli(ctx): - # This could probably done better - if ctx.invoked_subcommand in ['setup', 'setup_upstart', 'pip']: - if not in_virtualenv() and not is_su(): - # This does not account for people the have user level python installs - # that aren't virtual environments! Should be rare I think - click.secho("This command requires root privileges, use sudo or run as root.", fg='red') - sys.exit() - # Init plugins here if possible login_manager = LoginManager() login_manager.login_view = 'auth.login' @@ -242,9 +232,6 @@ assets.register('main.css', 'css/style.css') - - - - - - +@click.group() +def cli(): + pass diff --git a/realms/commands.py b/realms/cli.py similarity index 81% rename from realms/commands.py rename to realms/cli.py index a859ea7..4645fe6 100644 --- a/realms/commands.py +++ b/realms/cli.py @@ -1,14 +1,40 @@ -from realms import config, create_app, db, cli -from realms.lib.util import random_string, in_virtualenv, green, yellow, red +from realms import config, create_app, db, cli as cli_, __version__ +from realms.lib.util import is_su, random_string, in_virtualenv, green, yellow, red from subprocess import call, Popen from multiprocessing import cpu_count import click import json import sys import os +import pip -app = create_app() +def print_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + green(__version__) + ctx.exit() + + +@click.group() +@click.option('--version', is_flag=True, callback=print_version, + expose_value=False, is_eager=True) +@click.pass_context +def cli(ctx): + # This could probably done better + if ctx.invoked_subcommand in ['setup', 'pip']: + if not in_virtualenv() and not is_su(): + # This does not account for people the have user level python installs + # that aren't virtual environments! Should be rare I think + click.secho("This command requires root privileges, use sudo or run as root.", fg='red') + sys.exit() + + if ctx.invoked_subcommand in ['setup_upstart']: + if not is_su(): + click.secho("This command requires root privileges, use sudo or run as root.", fg='red') + sys.exit() + +cli.add_command(cli_) def get_user(): @@ -117,37 +143,28 @@ def get_prefix(): return sys.prefix -def get_pip(): - """ Get virtualenv path for pip - """ - if in_virtualenv(): - return get_prefix() + '/bin/pip' - else: - return 'pip' - - -@cli.command() +@cli.command(name='pip') @click.argument('cmd', nargs=-1) -def pip(cmd): +def pip_(cmd): """ Execute pip commands, useful for virtualenvs """ - call(get_pip() + ' ' + ' '.join(cmd), shell=True) + pip.main(cmd) def install_redis(): - call([get_pip(), 'install', 'redis']) + pip.main(['install', 'redis']) def install_mysql(): - call([get_pip(), 'install', 'MySQL-Python']) + pip.main(['install', 'MySQL-Python']) def install_postgres(): - call([get_pip(), 'install', 'psycopg2']) + pip.main(['install', 'psycopg2']) def install_memcached(): - call([get_pip(), 'install', 'python-memcached']) + pip.main(['install', 'python-memcached']) @click.command() @@ -220,9 +237,9 @@ def dev(port): """ Run development server """ green("Starting development server") - app.run(host="0.0.0.0", - port=port, - debug=True) + create_app().run(host="0.0.0.0", + port=port, + debug=True) def start_server(): @@ -235,7 +252,7 @@ def start_server(): green("Server started. Port: %s" % config.PORT) Popen("gunicorn 'realms:create_app()' -b 0.0.0.0:%s -k gevent %s" % - (config.PORT, flags), shell=True, executable='/bin/bash') + (config.PORT, flags), shell=True, executable='/bin/bash') def stop_server(): @@ -308,21 +325,20 @@ def drop_db(): def test(): """ Run tests """ - for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose')]: + for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose'), ('blinker', 'blinker')]: if not module_exists(mod[0]): - call([get_pip(), 'install', mod[1]]) + pip.main(['install', mod[1]]) nosetests = get_prefix() + "/bin/nosetests" if in_virtualenv() else "nosetests" - call([nosetests, config.APP_PATH]) + call([nosetests, 'realms']) @cli.command() def version(): """ Output version """ - with open(os.path.join(config.APP_PATH, 'VERSION')) as f: - click.echo(f.read().strip()) + green(__version__) if __name__ == '__main__': diff --git a/realms/lib/test.py b/realms/lib/test.py index a9d441c..9a0f230 100644 --- a/realms/lib/test.py +++ b/realms/lib/test.py @@ -12,8 +12,12 @@ class BaseTest(TestCase): app.config['PRESERVE_CONTEXT_ON_EXCEPTION'] = False app.config['WIKI_PATH'] = '/tmp/%s' % random_string(12) app.config['DB_URI'] = 'sqlite:////tmp/%s.db' % random_string(12) + app.config.update(self.configure()) return app + def configure(self): + return {} + def tearDown(self): call(['rm', '-rf', self.app.config['WIKI_PATH']]) call(['rm', '-f', self.app.config['DB_URI'][10:]]) \ No newline at end of file diff --git a/realms/modules/wiki/models.py b/realms/modules/wiki/models.py index 41d43d6..dc7d515 100644 --- a/realms/modules/wiki/models.py +++ b/realms/modules/wiki/models.py @@ -20,7 +20,7 @@ def cname_to_filename(cname): :return: str -- Filename """ - return cname.lower() + ".md" + return cname + ".md" def filename_to_cname(filename): @@ -71,25 +71,26 @@ class Wiki(HookMixin): return username, email - def revert_page(self, name, commit_sha, message, username): + def revert_page(self, name, commit_sha, message, username, email): """Revert page to passed commit sha1 :param name: Name of page to revert. :param commit_sha: Commit Sha1 to revert to. :param message: Commit message. - :param username: + :param username: Committer name. + :param email: Committer email. :return: Git commit sha1 """ page = self.get_page(name, commit_sha) if not page: - raise PageNotFound() + raise PageNotFound('Commit not found') if not message: commit_info = gittle.utils.git.commit_info(self.gittle[commit_sha.encode('latin-1')]) message = commit_info['message'] - return self.write_page(name, page['data'], message=message, username=username) + return self.write_page(name, page['data'], message=message, username=username, email=email) def write_page(self, name, content, message=None, create=False, username=None, email=None): """Write page to git repo diff --git a/realms/modules/wiki/tests.py b/realms/modules/wiki/tests.py index aeff725..daaa723 100644 --- a/realms/modules/wiki/tests.py +++ b/realms/modules/wiki/tests.py @@ -1,39 +1,72 @@ +import json from nose.tools import * from flask import url_for -from realms.modules.wiki.models import Wiki, cname_to_filename, filename_to_cname +from realms.modules.wiki.models import cname_to_filename, filename_to_cname from realms.lib.test import BaseTest -class WikiTest(BaseTest): +class WikiBaseTest(BaseTest): + def write_page(self, name, message=None, content=None): + return self.client.post(url_for('wiki.page_write', name=name), + data=dict(message=message, content=content)) - def test_wiki_routes(self): - - self.assert_200(self.client.get(url_for("wiki.create"))) - - """ Create a test page first! - for route in ['page', 'edit', 'history', 'index']: - rv = self.client.get(url_for("wiki.%s" % route, name='test')) - self.assert_200(rv, "wiki.%s: %s" % (route, rv.status_code)) - """ - - def test_write_page(self): - self.assert_200( - self.client.post(url_for('wiki.page_write', name='test'), data=dict( - content='testing', - message='test message' - ))) - - self.assert_200(self.client.get(url_for('wiki.page', name='test'))) - - def test_delete_page(self): - self.assert_200(self.client.delete(url_for('wiki.page_write', name='test'))) - self.assert_status(self.client.get(url_for('wiki.page', name='test')), 302) - - def test_revert(self): - pass +class UtilTest(WikiBaseTest): def test_cname_to_filename(self): eq_(cname_to_filename('test'), 'test.md') def test_filename_to_cname(self): - eq_(filename_to_cname('test-1-2-3.md'), 'test-1-2-3') \ No newline at end of file + eq_(filename_to_cname('test-1-2-3.md'), 'test-1-2-3') + + +class WikiTest(WikiBaseTest): + def test_routes(self): + self.assert_200(self.client.get(url_for("wiki.create"))) + self.write_page('test', message='test message', content='testing') + + for route in ['page', 'edit', 'history']: + rv = self.client.get(url_for("wiki.%s" % route, name='test')) + self.assert_200(rv, "wiki.%s: %s" % (route, rv.status_code)) + + self.assert_200(self.client.get(url_for('wiki.index'))) + + def test_write_page(self): + self.assert_200(self.write_page('test', message='test message', content='testing')) + + rv = self.client.get(url_for('wiki.page', name='test')) + self.assert_200(rv) + + self.assert_context('name', 'test') + eq_(self.get_context_variable('page')['info']['message'], 'test message') + eq_(self.get_context_variable('page')['data'], 'testing') + + def test_history(self): + self.assert_200(self.client.get(url_for('wiki.history', name='test'))) + + def test_delete_page(self): + self.app.config['WIKI_LOCKED_PAGES'] = ['test'] + self.assert_403(self.client.delete(url_for('wiki.page_write', name='test'))) + self.app.config['WIKI_LOCKED_PAGES'] = [] + + self.assert_200(self.client.delete(url_for('wiki.page_write', name='test'))) + + rv = self.client.get(url_for('wiki.page', name='test')) + self.assert_status(rv, 302) + + def test_revert(self): + rv1 = self.write_page('test', message='test message', content='testing_old') + self.write_page('test', message='test message', content='testing_new') + data = json.loads(rv1.data) + self.client.post(url_for('wiki.revert'), data=dict(name='test', commit=data['sha'])) + self.client.get(url_for('wiki.page', name='test')) + eq_(self.get_context_variable('page')['data'], 'testing_old') + self.assert_404(self.client.post(url_for('wiki.revert'), data=dict(name='test', commit='does not exist'))) + + self.app.config['WIKI_LOCKED_PAGES'] = ['test'] + self.assert_403(self.client.post(url_for('wiki.revert'), data=dict(name='test', commit=data['sha']))) + self.app.config['WIKI_LOCKED_PAGES'] = [] + + +class RelativePathTest(WikiTest): + def configure(self): + return dict(RELATIVE_PATH='wiki') diff --git a/realms/modules/wiki/views.py b/realms/modules/wiki/views.py index 0ba639f..d6f0329 100644 --- a/realms/modules/wiki/views.py +++ b/realms/modules/wiki/views.py @@ -1,7 +1,7 @@ from flask import abort, g, render_template, request, redirect, Blueprint, flash, url_for, current_app from flask.ext.login import login_required, current_user from realms.lib.util import to_canonical, remove_ext - +from .models import PageNotFound blueprint = Blueprint('wiki', __name__) @@ -28,19 +28,22 @@ def compare(name, fsha, dots, lsha): @blueprint.route("/_revert", methods=['POST']) @login_required def revert(): - name = request.form.get('name') + cname = to_canonical(request.form.get('name')) commit = request.form.get('commit') - cname = to_canonical(name) message = request.form.get('message', "Reverting %s" % cname) if cname in current_app.config.get('WIKI_LOCKED_PAGES'): - return dict(error=True, message="Page is locked") + return dict(error=True, message="Page is locked"), 403 + + try: + sha = g.current_wiki.revert_page(cname, + commit, + message=message, + username=current_user.username, + email=current_user.email) + except PageNotFound as e: + return dict(error=True, message=e.message), 404 - sha = g.current_wiki.revert_page(name, - commit, - message=message, - username=current_user.username, - email=current_user.email) if sha: flash("Page reverted") @@ -104,7 +107,7 @@ def page_write(name): if request.method == 'POST': # Create if cname in current_app.config.get('WIKI_LOCKED_PAGES'): - return dict(error=True, message="Page is locked") + return dict(error=True, message="Page is locked"), 403 sha = g.current_wiki.write_page(cname, request.form['content'], @@ -117,7 +120,7 @@ def page_write(name): edit_cname = to_canonical(request.form['name']) if edit_cname in current_app.config.get('WIKI_LOCKED_PAGES'): - return dict(error=True, message="Page is locked") + return dict(error=True, message="Page is locked"), 403 if edit_cname != cname.lower(): g.current_wiki.rename_page(cname, edit_cname) @@ -132,6 +135,9 @@ def page_write(name): else: # DELETE + if cname in current_app.config.get('WIKI_LOCKED_PAGES'): + return dict(error=True, message="Page is locked"), 403 + sha = g.current_wiki.delete_page(name, username=current_user.username, email=current_user.email) diff --git a/realms/version.py b/realms/version.py new file mode 100644 index 0000000..7b5c7a3 --- /dev/null +++ b/realms/version.py @@ -0,0 +1 @@ +__version__ = '0.3.24' \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index d129051..041842c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ -r requirements.txt Flask-Testing==0.4.2 nose==1.3.4 +blinker==1.3 diff --git a/requirements.txt b/requirements.txt index 473dcb3..d6e1198 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1 @@ -Flask==0.10.1 -Flask-Assets==0.10 -Flask-Cache==0.13.1 -Flask-Login==0.2.11 -Flask-SQLAlchemy==2.0 -Flask-WTF==0.10.2 -PyYAML==3.11 -bcrypt==1.0.2 -beautifulsoup4==4.3.2 -click==3.3 -gevent==1.0.1 -ghdiff==0.4 -gittle==0.4.0 -gunicorn==19.1.1 -itsdangerous==0.24 -lxml==3.4.0 -markdown2==2.3.0 -simplejson==3.6.3 +-e . diff --git a/setup.py b/setup.py index 55ad062..f5c332f 100644 --- a/setup.py +++ b/setup.py @@ -12,8 +12,8 @@ with open('README.md') as f: with open('requirements.txt') as f: required = f.read().splitlines() -with open('VERSION') as f: - VERSION = f.read().strip() +__version__ = None +exec(open('realms/version.py').read()) CLASSIFIERS = [ 'Intended Audience :: Developers', @@ -23,10 +23,28 @@ CLASSIFIERS = [ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content'] setup(name='realms-wiki', - version=VERSION, + version=__version__, packages=find_packages(), - install_requires=required, - #scripts=['realms-wiki'], + install_requires=[ + 'Flask==0.10.1', + 'Flask-Assets==0.10', + 'Flask-Cache==0.13.1', + 'Flask-Login==0.2.11', + 'Flask-SQLAlchemy==2.0', + 'Flask-WTF==0.10.2', + 'PyYAML==3.11', + 'bcrypt==1.0.2', + 'beautifulsoup4==4.3.2', + 'click==3.3', + 'gevent==1.0.1', + 'ghdiff==0.4', + 'gittle==0.4.0', + 'gunicorn==19.1.1', + 'itsdangerous==0.24', + 'lxml==3.4.0', + 'markdown2==2.3.0', + 'simplejson==3.6.3' + ], entry_points={ 'console_scripts': [ 'realms-wiki = realms.cli:cli'