From: Christopher Allan Webber Date: Tue, 16 Sep 2014 19:01:43 +0000 (-0500) Subject: Merge branch 'master' into merge-python3-port X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=f6bad0eb26fa7e092570afe1fb7f38b3d1a1941d;p=mediagoblin.git Merge branch 'master' into merge-python3-port Has some issues, will iteratively fix! Conflicts: mediagoblin/gmg_commands/__init__.py mediagoblin/gmg_commands/deletemedia.py mediagoblin/gmg_commands/users.py mediagoblin/oauth/views.py mediagoblin/plugins/api/views.py mediagoblin/tests/test_api.py mediagoblin/tests/test_edit.py mediagoblin/tests/test_oauth1.py mediagoblin/tests/test_util.py mediagoblin/tools/mail.py mediagoblin/webfinger/views.py setup.py --- f6bad0eb26fa7e092570afe1fb7f38b3d1a1941d diff --cc mediagoblin/db/migrations.py index b3cea871,04588ad1..349d16d5 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@@ -17,17 -17,14 +17,16 @@@ import datetime import uuid +import six + from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger, Integer, Unicode, UnicodeText, DateTime, - ForeignKey, Date) + ForeignKey, Date, Index) from sqlalchemy.exc import ProgrammingError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql import and_ -from migrate.changeset.constraint import UniqueConstraint +from sqlalchemy.schema import UniqueConstraint - from mediagoblin.db.extratypes import JSONEncoded, MutationDict from mediagoblin.db.migration_tools import ( RegisterMigration, inspect_table, replace_table_hack) diff --cc mediagoblin/decorators.py index 61d078b2,5bf60048..f3be679d --- a/mediagoblin/decorators.py +++ b/mediagoblin/decorators.py @@@ -19,11 -19,10 +19,11 @@@ from functools import wrap from werkzeug.exceptions import Forbidden, NotFound from oauthlib.oauth1 import ResourceEndpoint +from six.moves.urllib.parse import urljoin + from mediagoblin import mg_globals as mgg from mediagoblin import messages - from mediagoblin.db.models import MediaEntry, User, MediaComment + from mediagoblin.db.models import MediaEntry, User, MediaComment, AccessToken from mediagoblin.tools.response import ( redirect, render_404, render_user_banned, json_response) diff --cc mediagoblin/gmg_commands/__init__.py index 79fb7701,0cb239a2..22fef91c --- a/mediagoblin/gmg_commands/__init__.py +++ b/mediagoblin/gmg_commands/__init__.py @@@ -59,10 -61,10 +63,14 @@@ SUBCOMMAND_MAP = 'setup': 'mediagoblin.gmg_commands.deletemedia:parser_setup', 'func': 'mediagoblin.gmg_commands.deletemedia:deletemedia', 'help': 'Delete media entries'}, + 'serve': { + 'setup': 'mediagoblin.gmg_commands.serve:parser_setup', + 'func': 'mediagoblin.gmg_commands.serve:serve', + 'help': 'PasteScript replacement'}, + 'batchaddmedia': { + 'setup': 'mediagoblin.gmg_commands.batchaddmedia:parser_setup', + 'func': 'mediagoblin.gmg_commands.batchaddmedia:batchaddmedia', + 'help': 'Add many media entries at once'}, # 'theme': { # 'setup': 'mediagoblin.gmg_commands.theme:theme_parser_setup', # 'func': 'mediagoblin.gmg_commands.theme:theme', diff --cc mediagoblin/gmg_commands/deletemedia.py index d8926a6b,ab5a81f6..415389c4 --- a/mediagoblin/gmg_commands/deletemedia.py +++ b/mediagoblin/gmg_commands/deletemedia.py @@@ -14,7 -14,7 +14,8 @@@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from __future__ import print_function + import sys from mediagoblin.gmg_commands import util as commands_util @@@ -34,7 -37,8 +38,8 @@@ def deletemedia(args) for media in medias: found_medias.add(media.id) media.delete() - print 'Media ID %d has been deleted.' % media.id + print('Media ID %d has been deleted.' % media.id) for media in media_ids - found_medias: - print 'Can\'t find a media with ID %d.' % media - print 'Done.' + print('Can\'t find a media with ID %d.' % media) + print('Done.') + sys.exit(0) diff --cc mediagoblin/gmg_commands/users.py index 050c5348,71149497..93b72ea4 --- a/mediagoblin/gmg_commands/users.py +++ b/mediagoblin/gmg_commands/users.py @@@ -116,6 -112,26 +116,26 @@@ def changepw(args) if user: user.pw_hash = auth.gen_password_hash(args.password) user.save() - print 'Password successfully changed' + print(u'Password successfully changed') else: - print 'The user doesn\'t exist' + print(u'The user doesn\'t exist') + + + def deleteuser_parser_setup(subparser): + subparser.add_argument( + 'username', + help="Username to delete") + + + def deleteuser(args): + commands_util.setup_app(args) + + db = mg_globals.database + + user = db.User.query.filter_by( + username=unicode(args.username.lower())).one() + if user: + user.delete() - print 'The user %s has been deleted' % args.username ++ print('The user %s has been deleted' % args.username) + else: - print 'The user %s doesn\'t exist' % args.username ++ print('The user %s doesn\'t exist' % args.username) diff --cc mediagoblin/init/celery/__init__.py index ffc7ca0e,19c13f7d..c3cdd43c --- a/mediagoblin/init/celery/__init__.py +++ b/mediagoblin/init/celery/__init__.py @@@ -16,10 -16,9 +16,11 @@@ import os import sys + import datetime import logging +import six + from celery import Celery from mediagoblin.tools.pluginapi import hook_runall diff --cc mediagoblin/oauth/views.py index fd848467,90ad5bbf..ce12fbe0 --- a/mediagoblin/oauth/views.py +++ b/mediagoblin/oauth/views.py @@@ -15,9 -15,9 +15,10 @@@ # along with this program. If not, see . import datetime -import string + +import six + from oauthlib.oauth1.rfc5849.utils import UNICODE_ASCII_CHARACTER_SET from oauthlib.oauth1 import (RequestTokenEndpoint, AuthorizationEndpoint, AccessTokenEndpoint) diff --cc mediagoblin/plugins/api/views.py index 02fd8107,e8af7988..ef0b87e3 --- a/mediagoblin/plugins/api/views.py +++ b/mediagoblin/plugins/api/views.py @@@ -63,9 -61,10 +63,10 @@@ def post_entry(request) mg_app=request.app, user=request.user, submitted_file=request.files['file'], filename=request.files['file'].filename, - title=unicode(request.form.get('title')), - description=unicode(request.form.get('description')), - license=unicode(request.form.get('license', '')), - tags_string=unicode(request.form.get('tags', '')), + title=six.text_type(request.form.get('title')), + description=six.text_type(request.form.get('description')), + license=six.text_type(request.form.get('license', '')), ++ tags_string=six.text_type(request.form.get('tags', '')), upload_limit=upload_limit, max_file_size=max_file_size, callback_url=callback_url) diff --cc mediagoblin/tests/test_api.py index c8a254d4,93e82f18..329c387d --- a/mediagoblin/tests/test_api.py +++ b/mediagoblin/tests/test_api.py @@@ -13,81 -13,476 +13,476 @@@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . + import json - - import logging - import base64 - + import mock import pytest + from webtest import AppError + + from .resources import GOOD_JPG from mediagoblin import mg_globals - from mediagoblin.tools import template, pluginapi + from mediagoblin.db.models import User, MediaEntry, MediaComment from mediagoblin.tests.tools import fixture_add_user - from .resources import GOOD_JPG, GOOD_PNG, EVIL_FILE, EVIL_JPG, EVIL_PNG, \ - BIG_BLUE + from mediagoblin.moderation.tools import take_away_privileges + class TestAPI(object): + """ Test mediagoblin's pump.io complient APIs """ - _log = logging.getLogger(__name__) + @pytest.fixture(autouse=True) + def setup(self, test_app): + self.test_app = test_app + self.db = mg_globals.database + self.user = fixture_add_user(privileges=[u'active', u'uploader', u'commenter']) + self.other_user = fixture_add_user( + username="otheruser", + privileges=[u'active', u'uploader', u'commenter'] + ) + self.active_user = self.user - class TestAPI(object): - def setup(self): - self.db = mg_globals.database + def _activity_to_feed(self, test_app, activity, headers=None): + """ Posts an activity to the user's feed """ + if headers: + headers.setdefault("Content-Type", "application/json") + else: + headers = {"Content-Type": "application/json"} + + with self.mock_oauth(): + response = test_app.post( + "/api/user/{0}/feed".format(self.active_user.username), + json.dumps(activity), + headers=headers + ) + + return response, json.loads(response.body) + + def _upload_image(self, test_app, image): + """ Uploads and image to MediaGoblin via pump.io API """ + data = open(image, "rb").read() + headers = { + "Content-Type": "image/jpeg", + "Content-Length": str(len(data)) + } + + + with self.mock_oauth(): + response = test_app.post( + "/api/user/{0}/uploads".format(self.active_user.username), + data, + headers=headers + ) + image = json.loads(response.body) + + return response, image + + def _post_image_to_feed(self, test_app, image): + """ Posts an already uploaded image to feed """ + activity = { + "verb": "post", + "object": image, + } + + return self._activity_to_feed(test_app, activity) + + def mocked_oauth_required(self, *args, **kwargs): + """ Mocks mediagoblin.decorator.oauth_required to always validate """ + + def fake_controller(controller, request, *args, **kwargs): + request.user = User.query.filter_by(id=self.active_user.id).first() + return controller(request, *args, **kwargs) + + def oauth_required(c): + return lambda *args, **kwargs: fake_controller(c, *args, **kwargs) + + return oauth_required + + def mock_oauth(self): + """ Returns a mock.patch for the oauth_required decorator """ + return mock.patch( + target="mediagoblin.decorators.oauth_required", + new_callable=self.mocked_oauth_required + ) + + def test_can_post_image(self, test_app): + """ Tests that an image can be posted to the API """ + # First request we need to do is to upload the image + response, image = self._upload_image(test_app, GOOD_JPG) + + # I should have got certain things back + assert response.status_code == 200 + + assert "id" in image + assert "fullImage" in image + assert "url" in image["fullImage"] + assert "url" in image + assert "author" in image + assert "published" in image + assert "updated" in image + assert image["objectType"] == "image" + + # Check that we got the response we're expecting + response, _ = self._post_image_to_feed(test_app, image) + assert response.status_code == 200 + + def test_unable_to_upload_as_someone_else(self, test_app): + """ Test that can't upload as someoen else """ + data = open(GOOD_JPG, "rb").read() + headers = { + "Content-Type": "image/jpeg", + "Content-Length": str(len(data)) + } + + with self.mock_oauth(): + # Will be self.user trying to upload as self.other_user + with pytest.raises(AppError) as excinfo: + test_app.post( + "/api/user/{0}/uploads".format(self.other_user.username), + data, + headers=headers + ) + + assert "403 FORBIDDEN" in excinfo.value.message + + def test_unable_to_post_feed_as_someone_else(self, test_app): + """ Tests that can't post an image to someone else's feed """ + response, data = self._upload_image(test_app, GOOD_JPG) + + activity = { + "verb": "post", + "object": data + } + + headers = { + "Content-Type": "application/json", + } + + with self.mock_oauth(): + with pytest.raises(AppError) as excinfo: + test_app.post( + "/api/user/{0}/feed".format(self.other_user.username), + json.dumps(activity), + headers=headers + ) + + assert "403 FORBIDDEN" in excinfo.value.message + + def test_only_able_to_update_own_image(self, test_app): + """ Test's that the uploader is the only person who can update an image """ + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + + activity = { + "verb": "update", + "object": data["object"], + } + + headers = { + "Content-Type": "application/json", + } + + # Lets change the image uploader to be self.other_user, this is easier + # than uploading the image as someone else as the way self.mocked_oauth_required + # and self._upload_image. + media = MediaEntry.query.filter_by(id=data["object"]["id"]).first() + media.uploader = self.other_user.id + media.save() + + # Now lets try and edit the image as self.user, this should produce a 403 error. + with self.mock_oauth(): + with pytest.raises(AppError) as excinfo: + test_app.post( + "/api/user/{0}/feed".format(self.user.username), + json.dumps(activity), + headers=headers + ) + + assert "403 FORBIDDEN" in excinfo.value.message + + def test_upload_image_with_filename(self, test_app): + """ Tests that you can upload an image with filename and description """ + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + + image = data["object"] + + # Now we need to add a title and description + title = "My image ^_^" + description = "This is my super awesome image :D" + license = "CC-BY-SA" + + image["displayName"] = title + image["content"] = description + image["license"] = license + + activity = {"verb": "update", "object": image} + + with self.mock_oauth(): + response = test_app.post( + "/api/user/{0}/feed".format(self.user.username), + json.dumps(activity), + headers={"Content-Type": "application/json"} + ) + + image = json.loads(response.body)["object"] + + # Check everything has been set on the media correctly + media = MediaEntry.query.filter_by(id=image["id"]).first() + assert media.title == title + assert media.description == description + assert media.license == license + + # Check we're being given back everything we should on an update + assert image["id"] == media.id + assert image["displayName"] == title + assert image["content"] == description + assert image["license"] == license + + + def test_only_uploaders_post_image(self, test_app): + """ Test that only uploaders can upload images """ + # Remove uploader permissions from user + take_away_privileges(self.user.username, u"uploader") + + # Now try and upload a image + data = open(GOOD_JPG, "rb").read() + headers = { + "Content-Type": "image/jpeg", + "Content-Length": str(len(data)), + } + + with self.mock_oauth(): + with pytest.raises(AppError) as excinfo: + test_app.post( + "/api/user/{0}/uploads".format(self.user.username), + data, + headers=headers + ) + + # Assert that we've got a 403 + assert "403 FORBIDDEN" in excinfo.value.message + + def test_object_endpoint(self, test_app): + """ Tests that object can be looked up at endpoint """ + # Post an image + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + + # Now lookup image to check that endpoint works. + image = data["object"] + + assert "links" in image + assert "self" in image["links"] + + # Get URI and strip testing host off + object_uri = image["links"]["self"]["href"] + object_uri = object_uri.replace("http://localhost:80", "") + + with self.mock_oauth(): + request = test_app.get(object_uri) + + image = json.loads(request.body) + entry = MediaEntry.query.filter_by(id=image["id"]).first() + + assert request.status_code == 200 + assert entry.id == image["id"] + + assert "image" in image + assert "fullImage" in image + assert "pump_io" in image + assert "links" in image + + def test_post_comment(self, test_app): + """ Tests that I can post an comment media """ + # Upload some media to comment on + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + + content = "Hai this is a comment on this lovely picture ^_^" + + activity = { + "verb": "post", + "object": { + "objectType": "comment", + "content": content, + "inReplyTo": data["object"], + } + } + + response, comment_data = self._activity_to_feed(test_app, activity) + assert response.status_code == 200 + + # Find the objects in the database + media = MediaEntry.query.filter_by(id=data["object"]["id"]).first() + comment = media.get_comments()[0] + + # Tests that it matches in the database + assert comment.author == self.user.id + assert comment.content == content + + # Test that the response is what we should be given + assert comment.id == comment_data["object"]["id"] + assert comment.content == comment_data["object"]["content"] + + def test_unable_to_post_comment_as_someone_else(self, test_app): + """ Tests that you're unable to post a comment as someone else. """ + # Upload some media to comment on + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + + activity = { + "verb": "post", + "object": { + "objectType": "comment", + "content": "comment commenty comment ^_^", + "inReplyTo": data["object"], + } + } + + headers = { + "Content-Type": "application/json", + } + + with self.mock_oauth(): + with pytest.raises(AppError) as excinfo: + test_app.post( + "/api/user/{0}/feed".format(self.other_user.username), + json.dumps(activity), + headers=headers + ) + + assert "403 FORBIDDEN" in excinfo.value.message + + def test_unable_to_update_someone_elses_comment(self, test_app): + """ Test that you're able to update someoen elses comment. """ + # Upload some media to comment on + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + + activity = { + "verb": "post", + "object": { + "objectType": "comment", + "content": "comment commenty comment ^_^", + "inReplyTo": data["object"], + } + } + + headers = { + "Content-Type": "application/json", + } + + # Post the comment. + response, comment_data = self._activity_to_feed(test_app, activity) + + # change who uploaded the comment as it's easier than changing + comment_id = comment_data["object"]["id"] + comment = MediaComment.query.filter_by(id=comment_id).first() + comment.author = self.other_user.id + comment.save() + + # Update the comment as someone else. + comment_data["object"]["content"] = "Yep" + activity = { + "verb": "update", + "object": comment_data["object"] + } + + with self.mock_oauth(): + with pytest.raises(AppError) as excinfo: + test_app.post( + "/api/user/{0}/feed".format(self.user.username), + json.dumps(activity), + headers=headers + ) + + assert "403 FORBIDDEN" in excinfo.value.message + + def test_profile(self, test_app): + """ Tests profile endpoint """ + uri = "/api/user/{0}/profile".format(self.user.username) + with self.mock_oauth(): + response = test_app.get(uri) + profile = json.loads(response.body) + + assert response.status_code == 200 + + assert profile["preferredUsername"] == self.user.username + assert profile["objectType"] == "person" + + assert "links" in profile + + def test_user(self, test_app): + """ Test the user endpoint """ + uri = "/api/user/{0}/".format(self.user.username) + with self.mock_oauth(): + response = test_app.get(uri) + user = json.loads(response.body) - self.user_password = u'4cc355_70k3N' - self.user = fixture_add_user(u'joapi', self.user_password, - privileges=[u'active',u'uploader']) + assert response.status_code == 200 - def login(self, test_app): - test_app.post( - '/auth/login/', { - 'username': self.user.username, - 'password': self.user_password}) + assert user["nickname"] == self.user.username + assert user["updated"] == self.user.created.isoformat() + assert user["published"] == self.user.created.isoformat() - def get_context(self, template_name): - return template.TEMPLATE_TEST_CONTEXT[template_name] + # Test profile exists but self.test_profile will test the value + assert "profile" in response - def http_auth_headers(self): - return {'Authorization': ('Basic {0}'.format( - base64.b64encode((':'.join([ - self.user.username, - self.user_password])).encode('ascii')).decode()))} + def test_whoami_without_login(self, test_app): + """ Test that whoami endpoint returns error when not logged in """ + with pytest.raises(AppError) as excinfo: + response = test_app.get("/api/whoami") - def do_post(self, data, test_app, **kwargs): - url = kwargs.pop('url', '/api/submit') - do_follow = kwargs.pop('do_follow', False) + assert "401 UNAUTHORIZED" in excinfo.value.message - if 'headers' not in kwargs.keys(): - kwargs['headers'] = self.http_auth_headers() + def test_read_feed(self, test_app): + """ Test able to read objects from the feed """ + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) - response = test_app.post(url, data, **kwargs) + uri = "/api/user/{0}/feed".format(self.active_user.username) + with self.mock_oauth(): + response = test_app.get(uri) + feed = json.loads(response.body) - if do_follow: - response.follow() + assert response.status_code == 200 - return response + # Check it has the attributes it should + assert "displayName" in feed + assert "objectTypes" in feed + assert "url" in feed + assert "links" in feed + assert "author" in feed + assert "items" in feed - def upload_data(self, filename): - return {'upload_files': [('file', filename)]} + # Check that image i uploaded is there + assert feed["items"][0]["verb"] == "post" + assert feed["items"][0]["actor"] - def test_1_test_test_view(self, test_app): - self.login(test_app) + def test_cant_post_to_someone_elses_feed(self, test_app): + """ Test that can't post to someone elses feed """ + response, data = self._upload_image(test_app, GOOD_JPG) + self.active_user = self.other_user - response = test_app.get( - '/api/test', - headers=self.http_auth_headers()) + with self.mock_oauth(): + with pytest.raises(AppError) as excinfo: + self._post_image_to_feed(test_app, data) - assert response.body == \ - b'{"email": "joapi@example.com", "username": "joapi"}' + assert "403 FORBIDDEN" in excinfo.value.message - def test_2_test_submission(self, test_app): - self.login(test_app) - def test_object_endpoint(self, test_app): ++ def test_object_endpoint_requestable(self, test_app): + """ Test that object endpoint can be requested """ + response, data = self._upload_image(test_app, GOOD_JPG) + response, data = self._post_image_to_feed(test_app, data) + object_id = data["object"]["id"] - response = self.do_post( - {'title': 'Great JPG!'}, - test_app, - **self.upload_data(GOOD_JPG)) + with self.mock_oauth(): + response = test_app.get(data["object"]["links"]["self"]["href"]) + data = json.loads(response.body) - assert response.status_int == 200 + assert response.status_code == 200 - assert self.db.MediaEntry.query.filter_by(title=u'Great JPG!').first() + assert object_id == data["id"] + assert "url" in data + assert "links" in data + assert data["objectType"] == "image" diff --cc mediagoblin/tests/test_edit.py index cf72f308,dc9c422f..54f43d68 --- a/mediagoblin/tests/test_edit.py +++ b/mediagoblin/tests/test_edit.py @@@ -14,11 -14,11 +14,12 @@@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import urlparse, os, pytest +import six.moves.urllib.parse as urlparse ++import pytest from mediagoblin import mg_globals - from mediagoblin.db.models import User - from mediagoblin.tests.tools import fixture_add_user + from mediagoblin.db.models import User, MediaEntry + from mediagoblin.tests.tools import fixture_add_user, fixture_media_entry from mediagoblin import auth from mediagoblin.tools import template, mail diff --cc mediagoblin/tests/test_util.py index e239d628,36563e75..e1c3c7e5 --- a/mediagoblin/tests/test_util.py +++ b/mediagoblin/tests/test_util.py @@@ -14,10 -14,13 +14,15 @@@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . + import mock import email + import pytest + import smtplib + import pkg_resources +import six + + from mediagoblin.tests.tools import get_app from mediagoblin.tools import common, url, translate, mail, text, testing testing._activate_testing() diff --cc mediagoblin/tools/mail.py index ad2e5a19,ab355835..ab3e0eaa --- a/mediagoblin/tools/mail.py +++ b/mediagoblin/tools/mail.py @@@ -14,11 -14,11 +14,13 @@@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from __future__ import print_function, unicode_literals + + import six import smtplib + import sys -from email.MIMEText import MIMEText from mediagoblin import mg_globals, messages +from mediagoblin._compat import MIMEText from mediagoblin.tools import common ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --cc setup.py index ed10df02,468ea294..e1c3c618 --- a/setup.py +++ b/setup.py @@@ -40,57 -32,6 +40,60 @@@ def get_version() raise RuntimeError("Unable to find version string in %s." % VERSIONFILE) +py2_only_install_requires = [] +if PY2: + py2_only_install_requires.append('argparse') # only for < 2.7 + py2_only_install_requires.append('PasteScript') + # newer sqlalchemy-migrate requires pbr which BREAKS EVERYTHING AND IS + # TERRIBLE AND IS THE END OF ALL THINGS + # I'd love to remove this restriction. + py2_only_install_requires.append('sqlalchemy-migrate<0.8') - # Annoying. Please remove once we can! We only indirectly - # use pbr, and currently it breaks things, presumably till - # their next release. - py2_only_install_requires.append('pbr==0.5.22') ++ # # Annoying. Please remove once we can! We only indirectly ++ # # use pbr, and currently it breaks things, presumably till ++ # # their next release. ++ # py2_only_install_requires.append('pbr==0.5.22') + py2_only_install_requires.append('mock') # mock is in the stdlib for 3.3+ + +install_requires = [ + 'gunicorn', + 'alembic==0.6.6', + 'python-dateutil', + 'wtforms', + 'py-bcrypt', + 'pytest>=2.3.1', + 'pytest-xdist', + 'werkzeug>=0.7', + 'celery>=3.0', + 'kombu', + 'jinja2', + 'Babel>=1.3', + 'webtest<2', + 'ConfigObj', + 'Markdown', + 'sqlalchemy<0.9.0, >0.8.0', + 'itsdangerous', + 'pytz', + # PLEASE change this when we can; a dependency is forcing us to set this + # specific number and it is breaking setup.py develop + 'six==1.5.2', - 'oauthlib>=0.5.0', ++ 'oauthlib', + 'unidecode', ++ 'jsonschema', + 'ExifRead', # TODO(berker): Install develop branch for Python 3 + 'PasteDeploy', ++ 'requests', ++ 'pyld', + # This is optional: + # 'translitcodec', + # For now we're expecting that users will install this from + # their package managers. + # 'lxml', + # 'Pillow', +] + py2_only_install_requires + +with open(READMEFILE, encoding="utf-8") as fobj: + long_description = fobj.read() + try: setup( name="mediagoblin", @@@ -99,8 -40,58 +102,8 @@@ zip_safe=False, include_package_data = True, # scripts and dependencies - install_requires=[ - 'setuptools', - 'python-dateutil', - 'PasteScript', - 'wtforms', - 'py-bcrypt', - 'pytest>=2.3.1', - 'pytest-xdist', - 'werkzeug>=0.7', - 'celery>=3.0', - 'kombu', - 'jinja2', - 'sphinx', - 'Babel>=1.0', - 'argparse', - 'webtest<2', - 'ConfigObj', - 'Markdown', - 'sqlalchemy<0.9.0, >0.8.0', - # newer sqlalchemy-migrate requires pbr which BREAKS EVERYTHING AND IS - # TERRIBLE AND IS THE END OF ALL THINGS - # I'd love to remove this restriction. - 'sqlalchemy-migrate<0.8', - 'mock', - 'itsdangerous', - 'pytz', - 'six>=1.4.1', - 'oauthlib', - 'unidecode', - 'jsonschema', - 'requests', - 'pyld', - 'ExifRead', - - # PLEASE change this when we can; a dependency is forcing us to set this - # specific number and it is breaking setup.py develop - 'six==1.5.2' - - ## Annoying. Please remove once we can! We only indirectly - ## use pbr, and currently it breaks things, presumably till - ## their next release. - # 'pbr==0.5.22', - - ## This is optional! - # 'translitcodec', - ## For now we're expecting that users will install this from - ## their package managers. - # 'lxml', - # 'PIL', - ], - # requires=['gst'], + install_requires=install_requires, - test_suite='nose.collector', # TODO: We are using py.test now? + test_suite='nose.collector', entry_points="""\ [console_scripts] gmg = mediagoblin.gmg_commands:main_cli @@@ -123,7 -114,8 +126,8 @@@ author_email='cwebber@gnu.org', url="http://mediagoblin.org/", download_url="http://mediagoblin.org/download/", - long_description=open(READMEFILE).read(), + long_description=long_description, + description='MediaGoblin is a web application for publishing all kinds of media', classifiers=[ "Development Status :: 3 - Alpha", "Environment :: Web Environment",