diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..1fa4289 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +source = dbtemplates +branch = 1 + +[report] +omit = *tests*,*migrations* diff --git a/.gitignore b/.gitignore index 9d5809f..5636b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ MANIFEST build dist *.egg-info -example/example.db docs/_build .tox/ *.egg/ diff --git a/.travis.yml b/.travis.yml index 7d2e5fc..126daef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,34 +1,32 @@ +language: python +python: 3.5 sudo: false cache: pip -language: python -python: -- 2.6 -- 2.7 -install: -- pip install -e . -- pip install -r requirements/tests.txt Django==$DJANGO -before_script: -- flake8 dbtemplates --ignore=E501 --exclude=migrations -script: -- coverage run --branch --source=dbtemplates `which django-admin.py` test dbtemplates -- coverage report --omit="dbtemplates/test*,dbtemplates/migrations*" env: - global: - - DJANGO_SETTINGS_MODULE=dbtemplates.test_settings - matrix: - - DJANGO="1.4.5 importlib" - - DJANGO="1.5.1 importlib" - - DJANGO=1.7.8 - - DJANGO=1.8.11 - - DJANGO=1.9.4 -matrix: - exclude: - - python: 2.6 - env: DJANGO=1.7.8 - - python: 2.6 - env: DJANGO=1.8.11 - - python: 2.6 - env: DJANGO=1.9.4 + - TOXENV=flake8-py27 + - TOXENV=flake8-py35 + - TOXENV=readme-py27 + - TOXENV=py27-dj18 + - TOXENV=py27-dj19 + - TOXENV=py27-dj110 + - TOXENV=py27-djmaster + - TOXENV=py34-dj18 + - TOXENV=py34-dj19 + - TOXENV=py34-dj110 + - TOXENV=py34-djmaster + - TOXENV=py35-dj18 + - TOXENV=py35-dj19 + - TOXENV=py35-dj110 + - TOXENV=py35-djmaster + - TOXENV=pypy-dj18 + - TOXENV=pypy-dj19 + - TOXENV=pypy-dj110 + - TOXENV=pypy-djmaster +install: + - pip install tox codecov +script: tox -v +after_success: + - codecov deploy: provider: pypi user: jazzband @@ -39,4 +37,4 @@ deploy: tags: true repo: jazzband/django-dbtemplates python: "2.7" - condition: "$DJANGO = 1.8.11" + condition: "$TOXENV = py27-dj110" diff --git a/LICENSE b/LICENSE index 00c83fd..4dbe380 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007-2012, Jannis Leidel and contributors +Copyright (c) 2007-2016, Jannis Leidel and contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.rst b/README.rst index b5038c4..973b169 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,18 @@ django-dbtemplates ================== -.. image:: https://secure.travis-ci.org/jazzband/django-dbtemplates.png - :alt: Build Status - :target: http://travis-ci.org/jazzband/django-dbtemplates - .. image:: https://jazzband.co/static/img/badge.svg :alt: Jazzband :target: https://jazzband.co/ +.. image:: https://travis-ci.org/jazzband/django-dbtemplates.svg?branch=master + :alt: Build Status + :target: http://travis-ci.org/jazzband/django-dbtemplates + +.. image:: https://codecov.io/github/jazzband/django-dbtemplates/coverage.svg?branch=master + :alt: Codecov + :target: https://codecov.io/github/jazzband/django-dbtemplates?branch=master + ``dbtemplates`` is a Django app that consists of two parts: 1. It allows you to store templates in your database @@ -24,10 +28,4 @@ The source code and issue tracker can be found on Github: https://github.com/jazzband/django-dbtemplates -Compatibility Roadmap ---------------------- - -- 1.3.2 ``dbtemplates`` dropped support for Django < 1.4 -- 1.4 will be supported only Django >= 1.7, please freeze your requirements on specific version of ``dbtemplates`` ! - .. _template loader: http://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types diff --git a/dbtemplates/__init__.py b/dbtemplates/__init__.py index 974a8ff..f2dc0e4 100644 --- a/dbtemplates/__init__.py +++ b/dbtemplates/__init__.py @@ -1,2 +1 @@ -# following PEP 386 -__version__ = "1.3.2" +__version__ = "2.0" diff --git a/dbtemplates/loader.py b/dbtemplates/loader.py index 4355772..c542f7e 100644 --- a/dbtemplates/loader.py +++ b/dbtemplates/loader.py @@ -1,19 +1,14 @@ -import django from django.contrib.sites.models import Site from django.db import router from django.template import TemplateDoesNotExist +from django.template.loaders.base import Loader as BaseLoader from dbtemplates.models import Template from dbtemplates.utils.cache import (cache, get_cache_key, set_and_return, get_cache_notfound_key) -if django.get_version() >= '1.8': - from django.template.loaders.base import Loader as tLoaderCls -else: - from django.template.loader import BaseLoader as tLoaderCls # noqa - -class Loader(tLoaderCls): +class Loader(BaseLoader): """ A custom template loader to load templates from the database. diff --git a/dbtemplates/management/commands/check_template_syntax.py b/dbtemplates/management/commands/check_template_syntax.py index 214f531..c82ea96 100644 --- a/dbtemplates/management/commands/check_template_syntax.py +++ b/dbtemplates/management/commands/check_template_syntax.py @@ -1,13 +1,13 @@ -from django.core.management.base import CommandError, NoArgsCommand +from django.core.management.base import CommandError, BaseCommand from dbtemplates.models import Template from dbtemplates.utils.template import check_template_syntax -class Command(NoArgsCommand): +class Command(BaseCommand): help = "Ensures templates stored in the database don't have syntax errors." - def handle_noargs(self, **options): + def handle(self, **options): errors = [] for template in Template.objects.all(): valid, error = check_template_syntax(template) @@ -16,6 +16,4 @@ class Command(NoArgsCommand): if errors: raise CommandError( 'Some templates contained errors\n%s' % '\n'.join(errors)) - # NOTE: printing instead of using self.stdout.write to maintain - # Django 1.2 compatibility - print('OK') + self.stdout.write('OK') diff --git a/dbtemplates/management/commands/create_error_templates.py b/dbtemplates/management/commands/create_error_templates.py index cbaa1d5..f09fe48 100644 --- a/dbtemplates/management/commands/create_error_templates.py +++ b/dbtemplates/management/commands/create_error_templates.py @@ -1,7 +1,5 @@ import sys -from optparse import make_option - -from django.core.management.base import CommandError, NoArgsCommand +from django.core.management.base import CommandError, BaseCommand from django.contrib.sites.models import Site from dbtemplates.models import Template @@ -26,14 +24,15 @@ TEMPLATES = { } -class Command(NoArgsCommand): +class Command(BaseCommand): help = "Creates the default error templates as database template objects." - option_list = NoArgsCommand.option_list + ( - make_option("-f", "--force", action="store_true", dest="force", - default=False, - help="overwrite existing database templates"),) - def handle_noargs(self, **options): + def add_arguments(self, parser): + parser.add_argument( + "-f", "--force", action="store_true", dest="force", + default=False, help="overwrite existing database templates") + + def handle(self, **options): force = options.get('force') try: site = Site.objects.get_current() diff --git a/dbtemplates/management/commands/sync_templates.py b/dbtemplates/management/commands/sync_templates.py index 3529e82..5a93b4f 100644 --- a/dbtemplates/management/commands/sync_templates.py +++ b/dbtemplates/management/commands/sync_templates.py @@ -1,56 +1,54 @@ +import io import os -import codecs -from optparse import make_option -from django import VERSION from django.contrib.sites.models import Site -from django.core.management.base import CommandError, NoArgsCommand +from django.core.management.base import CommandError, BaseCommand +from django.template.utils import get_app_template_dirs +from django.template.loader import _engine_list try: from django.utils.six import input as raw_input except ImportError: pass -from dbtemplates.conf import settings from dbtemplates.models import Template ALWAYS_ASK, FILES_TO_DATABASE, DATABASE_TO_FILES = ('0', '1', '2') DIRS = [] - -if VERSION[:2] < (1, 8): - from django.template.loaders.app_directories import app_template_dirs - DIRS = settings.TEMPLATE_DIRS -else: - from django.template.utils import get_app_template_dirs - from django.template.loader import _engine_list - for engine in _engine_list(): - DIRS.extend(engine.dirs) - app_template_dirs = get_app_template_dirs('templates') +for engine in _engine_list(): + DIRS.extend(engine.dirs) +app_template_dirs = get_app_template_dirs('templates') -class Command(NoArgsCommand): +class Command(BaseCommand): help = "Syncs file system templates with the database bidirectionally." - option_list = NoArgsCommand.option_list + ( - make_option("-e", "--ext", - dest="ext", action="store", default="html", - help="extension of the files you want to " - "sync with the database [default: %default]"), - make_option("-f", "--force", - action="store_true", dest="force", default=False, - help="overwrite existing database templates"), - make_option("-o", "--overwrite", - action="store", dest="overwrite", default='0', - help="'0' - ask always, '1' - overwrite database " - "templates from template files, '2' - overwrite " - "template files from database templates"), - make_option("-a", "--app-first", - action="store_true", dest="app_first", default=False, - help="look for templates in applications " - "directories before project templates"), - make_option("-d", "--delete", - action="store_true", dest="delete", default=False, - help="Delete templates after syncing")) - def handle_noargs(self, **options): + def add_arguments(self, parser): + parser.add_argument( + "-e", "--ext", + dest="ext", action="store", default="html", + help="extension of the files you want to " + "sync with the database [default: %default]") + parser.add_argument( + "-f", "--force", + action="store_true", dest="force", default=False, + help="overwrite existing database templates") + parser.add_argument( + "-o", "--overwrite", + action="store", dest="overwrite", default='0', + help="'0' - ask always, '1' - overwrite database " + "templates from template files, '2' - overwrite " + "template files from database templates") + parser.add_argument( + "-a", "--app-first", + action="store_true", dest="app_first", default=False, + help="look for templates in applications " + "directories before project templates") + parser.add_argument( + "-d", "--delete", + action="store_true", dest="delete", default=False, + help="Delete templates after syncing") + + def handle(self, **options): extension = options.get('ext') force = options.get('force') overwrite = options.get('overwrite') @@ -66,10 +64,6 @@ class Command(NoArgsCommand): raise CommandError("Please make sure to have the sites contrib " "app installed and setup with a site object") - if not type(settings.TEMPLATE_DIRS) in (tuple, list): - raise CommandError("Please make sure settings.TEMPLATE_DIRS is a " - "list or tuple.") - if app_first: tpl_dirs = app_template_dirs + DIRS else: @@ -93,8 +87,8 @@ class Command(NoArgsCommand): "database.\nCreate it with '%s'?" " (y/[n]): """ % (name, path)) if force or confirm.lower().startswith('y'): - t = Template(name=name, - content=codecs.open(path, "r").read()) + with io.open(path, encoding='utf-8') as f: + t = Template(name=name, content=f.read()) t.save() t.sites.add(site) else: @@ -111,9 +105,10 @@ class Command(NoArgsCommand): if confirm in ('', FILES_TO_DATABASE, DATABASE_TO_FILES): if confirm == FILES_TO_DATABASE: - t.content = codecs.open(path, 'r').read() - t.save() - t.sites.add(site) + with io.open(path, encoding='utf-8') as f: + t.content = f.read() + t.save() + t.sites.add(site) if delete: try: os.remove(path) @@ -121,11 +116,8 @@ class Command(NoArgsCommand): raise CommandError( u"Couldn't delete %s" % path) elif confirm == DATABASE_TO_FILES: - f = codecs.open(path, 'w', 'utf-8') - try: + with io.open(path, 'w', encoding='utf-8') as f: f.write(t.content) - finally: - f.close() if delete: t.delete() break diff --git a/dbtemplates/migrations/0001_initial.py b/dbtemplates/migrations/0001_initial.py index c307d7e..b581b49 100644 --- a/dbtemplates/migrations/0001_initial.py +++ b/dbtemplates/migrations/0001_initial.py @@ -12,60 +12,33 @@ class Migration(migrations.Migration): ('sites', '0001_initial'), ] - if django.get_version() >= '1.8': - operations = [ - migrations.CreateModel( - name='Template', - fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField( - help_text="Example: 'flatpages/default.html'", max_length=100, verbose_name='name')), - ('content', models.TextField(verbose_name='content', blank=True)), - ('creation_date', models.DateTimeField( - default=django.utils.timezone.now, verbose_name='creation date')), - ('last_changed', models.DateTimeField( - default=django.utils.timezone.now, verbose_name='last changed')), - ('sites', models.ManyToManyField( - to='sites.Site', verbose_name='sites', blank=True)), - ], - options={ - 'ordering': ('name',), - 'db_table': 'django_template', - 'verbose_name': 'template', - 'verbose_name_plural': 'templates', - }, - bases=(models.Model,), - managers = [ - ('objects', django.db.models.manager.Manager()), - ('on_site', django.contrib.sites.managers.CurrentSiteManager( - b'sites')), - ], - ), - ] - else: - operations = [ - migrations.CreateModel( - name='Template', - fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField( - help_text="Example: 'flatpages/default.html'", max_length=100, verbose_name='name')), - ('content', models.TextField(verbose_name='content', blank=True)), - ('creation_date', models.DateTimeField( - default=django.utils.timezone.now, verbose_name='creation date')), - ('last_changed', models.DateTimeField( - default=django.utils.timezone.now, verbose_name='last changed')), - ('sites', models.ManyToManyField( - to='sites.Site', verbose_name='sites', blank=True)), - ], - options={ - 'ordering': ('name',), - 'db_table': 'django_template', - 'verbose_name': 'template', - 'verbose_name_plural': 'templates', - }, - bases=(models.Model,), - ), - ] + operations = [ + migrations.CreateModel( + name='Template', + fields=[ + ('id', models.AutoField( + verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField( + help_text="Example: 'flatpages/default.html'", max_length=100, verbose_name='name')), + ('content', models.TextField(verbose_name='content', blank=True)), + ('creation_date', models.DateTimeField( + default=django.utils.timezone.now, verbose_name='creation date')), + ('last_changed', models.DateTimeField( + default=django.utils.timezone.now, verbose_name='last changed')), + ('sites', models.ManyToManyField( + to='sites.Site', verbose_name='sites', blank=True)), + ], + options={ + 'ordering': ('name',), + 'db_table': 'django_template', + 'verbose_name': 'template', + 'verbose_name_plural': 'templates', + }, + bases=(models.Model,), + managers=[ + ('objects', django.db.models.manager.Manager()), + ('on_site', django.contrib.sites.managers.CurrentSiteManager( + b'sites')), + ], + ), + ] diff --git a/dbtemplates/models.py b/dbtemplates/models.py index b8e668c..e459fe3 100644 --- a/dbtemplates/models.py +++ b/dbtemplates/models.py @@ -9,12 +9,7 @@ from django.db import models from django.db.models import signals from django.template import TemplateDoesNotExist from django.utils.translation import ugettext_lazy as _ - -try: - from django.utils.timezone import now -except ImportError: - from datetime import datetime - now = datetime.now +from django.utils.timezone import now class Template(models.Model): diff --git a/dbtemplates/south_migrations/0001_initial.py b/dbtemplates/south_migrations/0001_initial.py deleted file mode 100644 index 0e08603..0000000 --- a/dbtemplates/south_migrations/0001_initial.py +++ /dev/null @@ -1,55 +0,0 @@ -# encoding: utf-8 -import 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 'Template' - db.create_table('django_template', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)), - ('content', self.gf('django.db.models.fields.TextField')(blank=True)), - ('creation_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - ('last_changed', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - )) - db.send_create_signal('dbtemplates', ['Template']) - - # Adding M2M table for field sites on 'Template' - db.create_table('django_template_sites', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('template', models.ForeignKey(orm['dbtemplates.template'], null=False)), - ('site', models.ForeignKey(orm['sites.site'], null=False)) - )) - db.create_unique('django_template_sites', ['template_id', 'site_id']) - - def backwards(self, orm): - # Deleting model 'Template' - db.delete_table('django_template') - - # Removing M2M table for field sites on 'Template' - db.delete_table('django_template_sites') - - models = { - 'dbtemplates.template': { - 'Meta': {'ordering': "('name',)", 'object_name': 'Template', 'db_table': "'django_template'"}, - 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'last_changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}) - }, - 'sites.site': { - 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, - 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['dbtemplates'] diff --git a/dbtemplates/south_migrations/0002_auto__del_unique_template_name.py b/dbtemplates/south_migrations/0002_auto__del_unique_template_name.py deleted file mode 100644 index 418a3ab..0000000 --- a/dbtemplates/south_migrations/0002_auto__del_unique_template_name.py +++ /dev/null @@ -1,34 +0,0 @@ -# encoding: utf-8 -from south.db import db -from south.v2 import SchemaMigration - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Removing unique constraint on 'Template', fields ['name'] - db.delete_unique('django_template', ['name']) - - def backwards(self, orm): - # Adding unique constraint on 'Template', fields ['name'] - db.create_unique('django_template', ['name']) - - models = { - 'dbtemplates.template': { - 'Meta': {'ordering': "('name',)", 'object_name': 'Template', 'db_table': "'django_template'"}, - 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'last_changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}) - }, - 'sites.site': { - 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, - 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['dbtemplates'] diff --git a/dbtemplates/south_migrations/__init__.py b/dbtemplates/south_migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dbtemplates/test_cases.py b/dbtemplates/test_cases.py index be9578e..b13f3f0 100644 --- a/dbtemplates/test_cases.py +++ b/dbtemplates/test_cases.py @@ -1,4 +1,4 @@ -import codecs +import io import os import shutil import tempfile @@ -6,7 +6,7 @@ import tempfile from django.conf import settings as django_settings from django.core.cache.backends.base import BaseCache from django.core.management import call_command -from django.template import loader, Context, TemplateDoesNotExist +from django.template import loader, TemplateDoesNotExist from django.test import TestCase from django.contrib.sites.models import Site @@ -82,9 +82,9 @@ class DbTemplatesTestCase(TestCase): settings.DBTEMPLATES_ADD_DEFAULT_SITE = old_add_default_site def test_load_templates(self): - result = loader.get_template("base.html").render(Context({})) + result = loader.get_template("base.html").render() self.assertEqual(result, 'base') - result2 = loader.get_template("sub.html").render(Context({})) + result2 = loader.get_template("sub.html").render() self.assertEqual(result2, 'sub') def test_error_templates_creation(self): @@ -99,17 +99,17 @@ class DbTemplatesTestCase(TestCase): self.assertEqual(admin_base_template, template.content) def test_sync_templates(self): - old_template_dirs = settings.TEMPLATE_DIRS + old_template_dirs = settings.TEMPLATES[0].get('DIRS', []) temp_template_dir = tempfile.mkdtemp('dbtemplates') temp_template_path = os.path.join(temp_template_dir, 'temp_test.html') - temp_template = codecs.open(temp_template_path, 'w') + temp_template = io.open(temp_template_path, 'w', encoding='utf-8') try: - temp_template.write('temp test') - settings.TEMPLATE_DIRS = (temp_template_dir,) + temp_template.write(u'temp test') + settings.TEMPLATES[0]['DIRS'] = (temp_template_dir,) # these works well if is not settings patched at runtime # for supporting django < 1.7 tests we must patch dirs in runtime from dbtemplates.management.commands import sync_templates - sync_templates.DIRS = settings.TEMPLATE_DIRS + sync_templates.DIRS = settings.TEMPLATES[0]['DIRS'] self.assertFalse( Template.objects.filter(name='temp_test.html').exists()) @@ -119,12 +119,13 @@ class DbTemplatesTestCase(TestCase): Template.objects.filter(name='temp_test.html').exists()) t = Template.objects.get(name='temp_test.html') - t.content = 'temp test modified' + t.content = u'temp test modified' t.save() call_command('sync_templates', force=True, verbosity=0, overwrite=DATABASE_TO_FILES) - self.assertTrue( - 'modified' in codecs.open(temp_template_path).read()) + self.assertEqual(u'temp test modified', + io.open(temp_template_path, + encoding='utf-8').read()) call_command('sync_templates', force=True, verbosity=0, delete=True, overwrite=DATABASE_TO_FILES) @@ -133,7 +134,7 @@ class DbTemplatesTestCase(TestCase): Template.objects.filter(name='temp_test.html').exists()) finally: temp_template.close() - settings.TEMPLATE_DIRS = old_template_dirs + settings.TEMPLATES[0]['DIRS'] = old_template_dirs shutil.rmtree(temp_template_dir) def test_get_cache(self): diff --git a/dbtemplates/test_settings.py b/dbtemplates/test_settings.py index 2a358cf..31a1946 100644 --- a/dbtemplates/test_settings.py +++ b/dbtemplates/test_settings.py @@ -1,6 +1,3 @@ - -import django - DBTEMPLATES_CACHE_BACKEND = 'dummy://' DATABASE_ENGINE = 'sqlite3' @@ -37,5 +34,11 @@ TEMPLATE_LOADERS = ( 'dbtemplates.loader.Loader', ) -if django.get_version() <= '1.6': - TEST_RUNNER = 'discover_runner.DiscoverRunner' +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'OPTIONS': { + 'loaders': TEMPLATE_LOADERS, + } + }, +] diff --git a/dbtemplates/utils/cache.py b/dbtemplates/utils/cache.py index 077c597..1d8b246 100644 --- a/dbtemplates/utils/cache.py +++ b/dbtemplates/utils/cache.py @@ -9,16 +9,10 @@ def get_cache_backend(): """ Compatibilty wrapper for getting Django's cache backend instance """ - try: - from django.core.cache import _create_cache - except ImportError: - # Django < 1.7 - from django.core.cache import get_cache as _get_cache - return _get_cache(settings.DBTEMPLATES_CACHE_BACKEND) - + from django.core.cache import _create_cache cache = _create_cache(settings.DBTEMPLATES_CACHE_BACKEND) - # Some caches -- python-memcached in particular -- need to do a cleanup at the - # end of a request cycle. If not implemented in a particular backend + # Some caches -- python-memcached in particular -- need to do a cleanup at + # the end of a request cycle. If not implemented in a particular backend # cache.close is a no-op signals.request_finished.connect(cache.close) return cache diff --git a/dbtemplates/utils/template.py b/dbtemplates/utils/template.py index 0f4dfb3..8fd74be 100644 --- a/dbtemplates/utils/template.py +++ b/dbtemplates/utils/template.py @@ -1,29 +1,14 @@ -from django import VERSION from django.template import (Template, TemplateDoesNotExist, TemplateSyntaxError) from importlib import import_module def get_loaders(): - if VERSION[:2] < (1, 8): - from django.template.loader import template_source_loaders - if template_source_loaders is None: - try: - from django.template.loader import find_template as finder - except ImportError: - from django.template.loader import find_template_source as finder # noqa - try: - source, name = finder('test') - except TemplateDoesNotExist: - pass - from django.template.loader import template_source_loaders - return template_source_loaders or [] - else: - from django.template.loader import _engine_list - loaders = [] - for engine in _engine_list(): - loaders.extend(engine.engine.template_loaders) - return loaders + from django.template.loader import _engine_list + loaders = [] + for engine in _engine_list(): + loaders.extend(engine.engine.template_loaders) + return loaders def get_template_source(name): @@ -45,15 +30,6 @@ def get_template_source(name): pass except TemplateDoesNotExist: pass - if source is None and VERSION[:2] < (1, 2): - # Django supported template source extraction still :/ - try: - from django.template.loader import find_template_source - template, origin = find_template_source(name, None) - if not hasattr(template, 'render'): - return template - except (ImportError, TemplateDoesNotExist): - pass return None diff --git a/docs/advanced.txt b/docs/advanced.txt index 6d339f8..f66dccc 100644 --- a/docs/advanced.txt +++ b/docs/advanced.txt @@ -20,7 +20,7 @@ To enable one of them you need to specify a setting called .. note:: Starting in version 1.0 ``dbtemplates`` allows you also to set the new dict-based ``CACHES`` setting, which was introduced in Django 1.3. - + All you have to do is to provide a new entry in the ``CACHES`` dict named ``'dbtemplates'``, e.g.:: diff --git a/docs/changelog.txt b/docs/changelog.txt index 1ed055b..1703b4c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -1,11 +1,21 @@ Changelog ========= -v1.4 (unreleased) +v2.0 (unreleased) ----------------- * Moved to Jazzband: https://github.com/jazzband/django-dbtemplates +* Dropped support for Python 2.6 + +* Added support for Python 3.4 and 3.5 + +* Dropped support for Django < 1.8 + +* Removed South migrations. Please use Django's native migration system instead + +* Removed the example project since it's out-of-date quickly + v1.3.2 (2015-06-15) ------------------- @@ -179,7 +189,7 @@ v0.7.1 (2010-07-07) * Fixed problem with the ``DBTEMPLATES_MEDIA_PREFIX`` setting, which defaults now to ``os.path.join(settings.MEDIA_ROOT, 'dbtemplates')`` now. - + In other words, if you don't specify a ``DBTEMPLATES_MEDIA_PREFIX`` setting and have the CodeMirror textarea enabled, dbtemplates will look in a subdirectory of your site's ``MEDIA_ROOT`` for the CodeMirror media files. @@ -289,10 +299,10 @@ v0.5.0 empty by using Django's other template loaders * added caching backend system with two default backends: - + * ``FileSystemBackend`` * ``DjangoCacheBackend`` - + More about it in the `blog post`_ and in the docs. .. _django-reversion: http://code.google.com/p/django-reversion/ diff --git a/example/__init__.py b/example/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/example/manage.py b/example/manage.py deleted file mode 100755 index d660b59..0000000 --- a/example/manage.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python -import os, sys -from django.core.management import execute_manager - -sys.path.insert(0, os.path.abspath('./..')) - -try: - import settings # Assumed to be in the same directory. -except ImportError: - import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) - sys.exit(1) - -if __name__ == "__main__": - execute_manager(settings) diff --git a/example/requirements.txt b/example/requirements.txt deleted file mode 100644 index 0819cc5..0000000 --- a/example/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -south -django>=1.3 \ No newline at end of file diff --git a/example/settings.py b/example/settings.py deleted file mode 100644 index 392214c..0000000 --- a/example/settings.py +++ /dev/null @@ -1,113 +0,0 @@ -# Django settings for example project. -import os, posixpath - -PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) - -DEBUG = True -TEMPLATE_DEBUG = DEBUG - -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), -) - -MANAGERS = ADMINS - -DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. -DATABASE_NAME = 'example.db' # Or path to database file if using sqlite3. -DATABASE_USER = '' # Not used with sqlite3. -DATABASE_PASSWORD = '' # Not used with sqlite3. -DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. -DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": DATABASE_NAME, - } -} - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# If running in a Windows environment this must be set to the same as your -# system time zone. -TIME_ZONE = 'America/Chicago' - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en-us' - -SITE_ID = 1 - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = True - -# Absolute path to the directory that holds media. -# Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = os.path.join(PROJECT_ROOT, "site_media", "media") - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash if there is a path component (optional in other cases). -# Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = "/site_media/media/" - -# Absolute path to the directory that holds static files like app media. -# Example: "/home/media/media.lawrence.com/apps/" -STATIC_ROOT = os.path.join(PROJECT_ROOT, "site_media", "static") - -# URL that handles the static files like app media. -# Example: "http://media.lawrence.com" -STATIC_URL = "/site_media/static/" - -# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a -# trailing slash. -# Examples: "http://foo.com/media/", "/media/". -ADMIN_MEDIA_PREFIX = posixpath.join(STATIC_URL, "admin/") - -# Make this unique, and don't share it with anybody. -SECRET_KEY = 'e-%(1e1f8ar2v)_8d!%-75a2ag(w(ht*l%n-wts5$li!5=97)8' - -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'django.template.loaders.eggs.Loader', - 'dbtemplates.loader.Loader', -) - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', -) - -ROOT_URLCONF = 'example.urls' - -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. -) - -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.admin', - 'django.contrib.flatpages', - 'django.contrib.staticfiles', - 'dbtemplates', - 'south', - #'reversion', -) - -# Uncomment the following two settings to use the file system cache backend. -# It will cache in the directory "cache" inside the example project directory. -DBTEMPLATES_CACHE_BACKEND = "locmem://" - -DBTEMPLATES_MEDIA_PREFIX = posixpath.join(STATIC_URL, "dbtemplates/") -DBTEMPLATES_USE_CODEMIRROR = True - diff --git a/example/urls.py b/example/urls.py deleted file mode 100644 index 7b8c5ff..0000000 --- a/example/urls.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.conf import settings -from django.conf.urls.defaults import patterns, include - -# Uncomment the next two lines to enable the admin: -from django.contrib import admin -admin.autodiscover() - -urlpatterns = patterns('', - # Example: - # (r'^example/', include('example.foo.urls')), - - # Uncomment the admin/doc line below and add 'django.contrib.admindocs' - # to INSTALLED_APPS to enable admin documentation: - # (r'^admin/doc/', include('django.contrib.admindocs.urls')), - - # Uncomment the next line to enable the admin: - (r'^admin/', include(admin.site.urls)), -) diff --git a/requirements/tests.txt b/requirements/tests.txt index d5fe594..25152de 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,3 +1,2 @@ -flake8<3 -django-discover-runner +flake8 coverage diff --git a/setup.py b/setup.py index 20a2a81..e4c532f 100644 --- a/setup.py +++ b/setup.py @@ -1,22 +1,28 @@ +import ast import os -import re -import codecs +import io from setuptools import setup, find_packages +class VersionFinder(ast.NodeVisitor): + def __init__(self): + self.version = None + + def visit_Assign(self, node): + if node.targets[0].id == '__version__': + self.version = node.value.s + + def read(*parts): filename = os.path.join(os.path.dirname(__file__), *parts) - with codecs.open(filename, encoding='utf-8') as fp: + with io.open(filename, encoding='utf-8') as fp: return fp.read() -def find_version(*file_paths): - version_file = read(*file_paths) - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", - version_file, re.M) - if version_match: - return version_match.group(1) - raise RuntimeError("Unable to find version string.") +def find_version(*parts): + finder = VersionFinder() + finder.visit(ast.parse(read(*parts))) + return finder.version setup( @@ -27,7 +33,7 @@ setup( author='Jannis Leidel', author_email='jannis@leidel.info', url='https://django-dbtemplates.readthedocs.io/', - packages=find_packages(exclude=['example']), + packages=find_packages(), zip_safe=False, package_data={ 'dbtemplates': [ @@ -44,9 +50,10 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.5', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Framework :: Django', ], install_requires=['django-appconf >= 0.4'], diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..61f1bb6 --- /dev/null +++ b/tox.ini @@ -0,0 +1,46 @@ +[tox] +skipsdist = True +usedevelop = True +minversion = 1.8 +envlist = + flake8-py27, + flake8-py35, + readme-py27, + py{27,34,35,py}-dj{18,19,110,master} + +[testenv] +basepython = + py27: python2.7 + py34: python3.4 + py35: python3.5 + pypy: pypy +usedevelop = true +setenv = + DJANGO_SETTINGS_MODULE = dbtemplates.test_settings +deps = + -rrequirements/tests.txt + dj18: https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django + dj19: https://github.com/django/django/archive/stable/1.9.x.tar.gz#egg=django + dj110: https://github.com/django/django/archive/stable/1.10.x.tar.gz#egg=django + djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django + +commands = + python --version + coverage run {envbindir}/django-admin.py test -v2 {posargs:dbtemplates} + coverage report + +[testenv:readme-py27] +commands = python setup.py check -r -s +deps = readme_renderer + +[testenv:flake8-py27] +commands = flake8 dbtemplates +deps = flake8 + +[testenv:flake8-py35] +commands = flake8 dbtemplates +deps = flake8 + +[flake8] +exclude=.tox +ignore=E501,E127,E128,E124