diff --git a/.travis.yml b/.travis.yml index 4388985..d467c64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,22 +5,22 @@ language: python sudo: false python: + - 3.6 - 3.5 - 3.4 - 3.3 - 2.7 - - 2.6 env: matrix: - - DJANGO='django16-sekizai' - - DJANGO='django16-nosekizai' - - DJANGO='django17-sekizai' - - DJANGO='django17-nosekizai' - DJANGO='django18-sekizai' - DJANGO='django18-nosekizai' - DJANGO='django19-sekizai' - DJANGO='django19-nosekizai' + - DJANGO='django110-sekizai' + - DJANGO='django110-nosekizai' + - DJANGO='django111-sekizai' + - DJANGO='django111-nosekizai' - TOXENV='pep8' - TOXENV='isort' - TOXENV='docs' @@ -29,11 +29,11 @@ env: # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors install: - pip install -U tox>=1.8 coveralls - - "if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then export PYVER=py26; fi" - "if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then export PYVER=py27; fi" - "if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then export PYVER=py33; fi" - "if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then export PYVER=py34; fi" - "if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then export PYVER=py35; fi" + - "if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then export PYVER=py36; fi" - "if [[ ${DJANGO}z != 'z' ]]; then export TOXENV=$PYVER-$DJANGO; fi" # command to run tests, e.g. python setup.py test @@ -47,39 +47,19 @@ after_success: matrix: exclude: - - python: 2.6 - env: DJANGO='django17-sekizai' - - python: 2.6 - env: DJANGO='django17-nosekizai' - - python: 2.6 - env: DJANGO='django18-sekizai' - - python: 2.6 - env: DJANGO='django18-nosekizai' - - python: 2.6 - env: DJANGO='django19-sekizai' - - python: 2.6 - env: DJANGO='django19-nosekizai' - - python: 3.3 env: DJANGO='django19-sekizai' - python: 3.3 env: DJANGO='django19-nosekizai' + - python: 3.3 + env: DJANGO='django110-sekizai' + - python: 3.3 + env: DJANGO='django110-nosekizai' + - python: 3.3 + env: DJANGO='django111-sekizai' + - python: 3.3 + env: DJANGO='django111-nosekizai' - - python: 3.5 - env: DJANGO='django16-sekizai' - - python: 3.5 - env: DJANGO='django16-nosekizai' - - python: 3.5 - env: DJANGO='django17-sekizai' - - python: 3.5 - env: DJANGO='django17-nosekizai' - - - python: 2.6 - env: TOXENV='pep8' - - python: 2.6 - env: TOXENV='isort' - - python: 2.6 - env: TOXENV='docs' - python: 2.7 env: TOXENV='pep8' - python: 2.7 @@ -98,6 +78,12 @@ matrix: env: TOXENV='isort' - python: 3.4 env: TOXENV='docs' + - python: 3.5 + env: TOXENV='pep8' + - python: 3.5 + env: TOXENV='isort' + - python: 3.5 + env: TOXENV='docs' cache: directories: diff --git a/HISTORY.rst b/HISTORY.rst index 1188873..927544b 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,10 @@ History ******* +1.4.0 (unreleased) +================== + +* Drop python 2.6/ Django<1.8 1.3.2 (2016-10-26) ================== diff --git a/README.rst b/README.rst index f277068..9908430 100644 --- a/README.rst +++ b/README.rst @@ -41,6 +41,8 @@ OpenGraph_, Twitter, and Google Plus properties to their HTML responses. django-meta 1.0 is a drop in replacement for django-meta-mixin: as a result django-meta-mixin is no longer actively developed +.. warning:: As of version 1.4, the support for Python 2.6 and Django<1.8 has been dropped + .. contents:: Installation @@ -54,13 +56,13 @@ Supported versions Django ------ -1.6 to 1.9 (newer versions might work but are not tested yet) +1.8 to 1.11 (newer versions might work but are not tested yet) Python ------ -Python 2.6 to 3.5 +Python 2.7 to 3.6 Basic concept ============= diff --git a/cms_helper.py b/cms_helper.py index a7774e5..fc46aac 100755 --- a/cms_helper.py +++ b/cms_helper.py @@ -2,6 +2,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +from tempfile import mkdtemp + HELPER_SETTINGS = dict( ROOT_URLCONF='tests.example_app.urls', INSTALLED_APPS=[ @@ -13,6 +15,7 @@ META_USE_OG_PROPERTIES=True, META_USE_TWITTER_PROPERTIES=True, META_USE_GOOGLEPLUS_PROPERTIES=True, + FILE_UPLOAD_TEMP_DIR=mkdtemp() ) try: @@ -36,5 +39,6 @@ def setup(): from djangocms_helper import runner runner.setup('meta', sys.modules[__name__]) + if __name__ == '__main__': run() diff --git a/meta/models.py b/meta/models.py index a18044e..b2576a4 100644 --- a/meta/models.py +++ b/meta/models.py @@ -8,6 +8,7 @@ from . import settings + NEED_REQUEST_OBJECT_ERR_MSG = """ Meta models needs request objects when initializing if sites framework is not used. """.strip() diff --git a/meta/templatetags/meta.py b/meta/templatetags/meta.py index fb2641e..c2c772a 100644 --- a/meta/templatetags/meta.py +++ b/meta/templatetags/meta.py @@ -2,30 +2,20 @@ from __future__ import absolute_import, print_function, unicode_literals from django import template -from django.conf import settings +from django.apps import apps from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.six import string_types register = template.Library() -try: - from django.apps import apps - # Use sekizai if installed, otherwise define a templatetag stub - if apps.is_installed('sekizai'): - from sekizai.templatetags.sekizai_tags import Addtoblock - register.tag('addtoblock', Addtoblock) - else: - from meta.compat import addtoblock - register.tag('addtoblock', addtoblock) - -except ImportError: - if 'sekizai' in settings.INSTALLED_APPS: - from sekizai.templatetags.sekizai_tags import Addtoblock - register.tag('addtoblock', Addtoblock) - else: - from meta.compat import addtoblock - register.tag('addtoblock', addtoblock) +# Use sekizai if installed, otherwise define a templatetag stub +if apps.is_installed('sekizai'): + from sekizai.templatetags.sekizai_tags import Addtoblock + register.tag('addtoblock', Addtoblock) +else: + from meta.compat import addtoblock + register.tag('addtoblock', addtoblock) @register.simple_tag diff --git a/meta/views.py b/meta/views.py index 60a7a50..aea03d2 100644 --- a/meta/views.py +++ b/meta/views.py @@ -150,6 +150,7 @@ def __init__(self, **kwargs): self.use_sites = settings.USE_SITES self.use_og = settings.USE_OG_PROPERTIES self.use_title_tag = settings.USE_TITLE_TAG + super(MetadataMixin, self).__init__(**kwargs) def get_meta_class(self): return self.meta_class diff --git a/setup.py b/setup.py index a5970cb..301f7ae 100644 --- a/setup.py +++ b/setup.py @@ -27,14 +27,15 @@ 'Framework :: Django :: 1.7', 'Framework :: Django :: 1.8', 'Framework :: Django :: 1.9', + 'Framework :: Django :: 1.10', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', ], ) diff --git a/tests/example_app/models.py b/tests/example_app/models.py index d345c04..ca5b4c6 100644 --- a/tests/example_app/models.py +++ b/tests/example_app/models.py @@ -1,13 +1,16 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import os.path + +from django.conf import settings from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.db import models from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from meta_mixin.models import ModelMeta +from meta.models import ModelMeta class Post(ModelMeta, models.Model): diff --git a/tests/example_app/south_migrations/0001_initial.py b/tests/example_app/south_migrations/0001_initial.py deleted file mode 100644 index 642025a..0000000 --- a/tests/example_app/south_migrations/0001_initial.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'Post' - db.create_table(u'example_app_post', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50)), - ('abstract', self.gf('django.db.models.fields.TextField')()), - ('meta_description', self.gf('django.db.models.fields.TextField')(default=u'', blank=True)), - ('meta_keywords', self.gf('django.db.models.fields.TextField')(default=u'', blank=True)), - ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), - ('date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('date_modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), - ('date_published', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - ('date_published_end', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), - ('main_image', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True)), - ('text', self.gf('django.db.models.fields.TextField')(default=u'', blank=True)), - ('image_url', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)), - )) - db.send_create_signal(u'example_app', ['Post']) - - - def backwards(self, orm): - # Deleting model 'Post' - db.delete_table(u'example_app_post') - - - models = { - u'auth.group': { - 'Meta': {'object_name': 'Group'}, - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - u'auth.permission': { - 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - u'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - u'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - u'example_app.post': { - 'Meta': {'ordering': "(u'-date_published', u'-date_created')", 'object_name': 'Post'}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image_url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), - 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'meta_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), - 'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), - 'text': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - } - } - - complete_apps = ['example_app'] \ No newline at end of file diff --git a/tests/example_app/south_migrations/__init__.py b/tests/example_app/south_migrations/__init__.py deleted file mode 100644 index ba25ec7..0000000 --- a/tests/example_app/south_migrations/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals diff --git a/tests/example_app/urls.py b/tests/example_app/urls.py index 6fc7773..0ef4ad3 100644 --- a/tests/example_app/urls.py +++ b/tests/example_app/urls.py @@ -16,11 +16,9 @@ urlpatterns = [ url(r'^media/(?P.*)$', django.views.static.serve, # NOQA {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), + url(r'^admin/', include(admin.site.urls)), # NOQA url(r'^jsi18n/(?P\S+?)/$', django.views.i18n.javascript_catalog), # NOQA url(r'^mixin/(?P\w[-\w]*)/$', PostMixinDetailView.as_view(), name='post-detail-mixinx'), url(r'^(?P\w[-\w]*)/$', PostDetailView.as_view(), name='post-detail'), url(r'^$', PostListView.as_view(), name='post-list'), ] -urlpatterns += i18n_patterns('', - url(r'^admin/', include(admin.site.urls)), # NOQA -) diff --git a/tests/test_mixin.py b/tests/test_mixin.py index 6deb872..b31f8f7 100644 --- a/tests/test_mixin.py +++ b/tests/test_mixin.py @@ -29,15 +29,17 @@ def setUp(self): author=self.user, date_published_end=timezone.now() + timedelta(days=2), text='post text', - main_image='/path/to/image', image_url='/path/to/image' ) + self.post.main_image = self.create_django_image_object() + self.post.save() + self.image_url = self.post.main_image.url @override_settings(META_SITE_PROTOCOL='http') def test_as_meta(self): expected = { 'locale': 'dummy_locale', - 'image': 'http://example.com/path/to/image', + 'image': 'http://example.com{}'.format(self.image_url), 'object_type': 'Article', 'tag': False, 'keywords': ['post keyword1', 'post keyword 2'], @@ -89,7 +91,7 @@ def test_as_meta_with_request(self): settings.FB_APPID = 'appid' expected = { 'locale': 'dummy_locale', - 'image': 'https://testserver/path/to/image', + 'image': 'https://testserver{}'.format(self.image_url), 'object_type': 'Article', 'tag': False, 'keywords': ['post keyword1', 'post keyword 2'], @@ -134,7 +136,7 @@ def test_templatetag(self): self.assertContains(response, '') self.assertNotContains(response, ' itemscope itemtype="http://schema.org/Article"') self.assertContains(response, 'article:published_time"') - self.assertContains(response, '') + self.assertContains(response, ''.format(self.image_url)) self.assertContains(response, ''.format(meta.gplus_author)) self.assertContains(response, ''.format(self.post.meta_description)) self.assertContains(response, ''.format(self.post.meta_description)) @@ -195,7 +197,7 @@ def test_google_plus_scope_works(self): @override_settings(META_SITE_PROTOCOL='https') def test_image_protocol(self): meta = self.post.as_meta() - self.assertEqual('https://example.com/path/to/image', getattr(meta, 'image')) + self.assertEqual('https://example.com{}'.format(self.image_url), getattr(meta, 'image')) def test_not_use_sites(self): with override_settings(META_USE_SITES=False): diff --git a/tox.ini b/tox.ini index 8ca28c0..f6f89cb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pep8,isort,py{35,34,27}-django{19}-{sekizai,nosekizai},py{35,34,33,27}-django{18}-{sekizai,nosekizai},py{34,33,27}-django{17,16}-{sekizai,nosekizai},py{26}-django{16}-{sekizai,nosekizai} +envlist = pep8,isort,py{36,35,34,27}-django{111,110,19}-{sekizai,nosekizai},py{35,34,33,27}-django{18}-{sekizai,nosekizai} skip_missing_interpreters=True [testenv] @@ -8,10 +8,10 @@ setenv: commands = {env:COMMAND:python} cms_helper.py deps = py26: unittest2 - django16: django<1.7 - django17: django<1.8 django18: django<1.9 django19: django<1.10 + django110: django<1.11 + django111: django==1.11rc1 sekizai: django-sekizai -r{toxinidir}/requirements-test.txt