Projet

Général

Profil

0001-django-1.11-support.patch

Emmanuel Cazenave, 12 janvier 2018 12:26

Télécharger (11,8 ko)

Voir les différences:

Subject: [PATCH] django 1.11 support

Breaks backward compatibility : now nothing is importable directly from the root module
(because django 1.11 requires almost empty root __init__.py for application)
Bump version to 2.0.0
Use tox and pytest to run tests.
 django_journal/__init__.py | 76 +---------------------------------------------
 django_journal/actions.py  |  6 ++--
 django_journal/main.py     | 72 +++++++++++++++++++++++++++++++++++++++++++
 django_journal/models.py   |  4 +--
 django_journal/tests.py    | 28 ++++++++---------
 setup.py                   |  4 +--
 tox.ini                    | 10 ++++++
 7 files changed, 103 insertions(+), 97 deletions(-)
 create mode 100644 django_journal/main.py
 create mode 100644 tox.ini
django_journal/__init__.py
1
import logging
2

  
3
from exceptions import JournalException
4
from models import (Journal, Tag, Template)
5

  
6
import django.db.models
7
from django.conf import settings
8

  
9
from decorator import atomic
10

  
11
__all__ = ('record', 'error_record', 'Journal')
12
__version__ = '1.25.1'
13

  
14
def unicode_truncate(s, length, encoding='utf-8'):
15
    '''Truncate an unicode string so that its UTF-8 encoding is less than
16
       length.'''
17
    encoded = s.encode(encoding)[:length]
18
    return encoded.decode(encoding, 'ignore')
19

  
20
@atomic
21
def record(tag, template, using=None, **kwargs):
22
    '''Record an event in the journal. The modification is done inside the
23
       current transaction.
24

  
25
       tag:
26
           a string identifier giving the type of the event
27
       tpl:
28
           a format string to describe the event
29
       kwargs:
30
           a mapping of object or data to interpolate in the format string
31
    '''
32
    template = unicode(template)
33
    tag = Tag.objects.using(using).get_cached(name=tag)
34
    template = Template.objects.using(using).get_cached(content=template)
35
    try:
36
        message = template.content.format(**kwargs)
37
    except (KeyError, IndexError), e:
38
        raise JournalException(
39
                'Missing variable for the template message', template, e)
40
    try:
41
        logger = logging.getLogger('django.journal.%s' % tag)
42
        if tag.name == 'error' or tag.name.startswith('error-'):
43
            logger.error(message)
44
        elif tag.name == 'warning' or tag.name.startswith('warning-'):
45
            logger.warning(message)
46
        else:
47
            logger.info(message)
48
    except:
49
        try:
50
            logging.getLogger('django.journal').exception('Unable to log msg')
51
        except:
52
            pass # we tried, really, we tried
53
    journal = Journal.objects.using(using).create(tag=tag, template=template,
54
            message=unicode_truncate(message, 128))
55
    for name, value in kwargs.iteritems():
56
        if value is None:
57
            continue
58
        tag = Tag.objects.using(using).get_cached(name=name)
59
        if isinstance(value, django.db.models.Model):
60
            journal.objectdata_set.create(tag=tag, content_object=value)
61
        else:
62
            journal.stringdata_set.create(tag=tag, content=unicode(value))
63
    return journal
64

  
65
def error_record(tag, tpl, **kwargs):
66
    '''Records error events.
67

  
68
       You must use this function when logging error events. It uses another
69
       database alias than the default one to be immune to transaction rollback
70
       when logging in the middle of a transaction which is going to
71
       rollback.
72
    '''
73
    return record(tag, tpl,
74
            using=getattr(settings, 'JOURNAL_DB_FOR_ERROR_ALIAS', 'default'),
75
            **kwargs)
1
__version__ = '2.0.0'
django_journal/actions.py
6 6
from django.core import exceptions
7 7

  
8 8

  
9
from . import models
9
from .models import Tag
10 10

  
11 11
def export_as_csv_generator(queryset):
12 12
    header = ['time', 'tag', 'message']
13
    tags = set(models.Tag.objects.filter(objectdata__journal__in=queryset).values_list('name', flat=True))
13
    tags = set(Tag.objects.filter(objectdata__journal__in=queryset).values_list('name', flat=True))
14 14
    for tag in list(tags):
15 15
        tags.add('%s__id' % tag)
16
    tags |= set(models.Tag.objects.filter(stringdata__journal__in=queryset).values_list('name', flat=True))
16
    tags |= set(Tag.objects.filter(stringdata__journal__in=queryset).values_list('name', flat=True))
17 17
    extra_headers = map(lambda s: s.encode('utf-8'), sorted(tags))
18 18
    yield header+extra_headers
19 19
    for journal in queryset:
django_journal/main.py
1
import logging
2

  
3
from django.conf import settings
4
import django.db.models
5

  
6
from .decorator import atomic
7
from .exceptions import JournalException
8
from .models import (Journal, Tag, Template)
9

  
10

  
11
def unicode_truncate(s, length, encoding='utf-8'):
12
    '''Truncate an unicode string so that its UTF-8 encoding is less than
13
       length.'''
14
    encoded = s.encode(encoding)[:length]
15
    return encoded.decode(encoding, 'ignore')
16

  
17
@atomic
18
def record(tag, template, using=None, **kwargs):
19
    '''Record an event in the journal. The modification is done inside the
20
       current transaction.
21

  
22
       tag:
23
           a string identifier giving the type of the event
24
       tpl:
25
           a format string to describe the event
26
       kwargs:
27
           a mapping of object or data to interpolate in the format string
28
    '''
29
    template = unicode(template)
30
    tag = Tag.objects.using(using).get_cached(name=tag)
31
    template = Template.objects.using(using).get_cached(content=template)
32
    try:
33
        message = template.content.format(**kwargs)
34
    except (KeyError, IndexError), e:
35
        raise JournalException(
36
                'Missing variable for the template message', template, e)
37
    try:
38
        logger = logging.getLogger('django.journal.%s' % tag)
39
        if tag.name == 'error' or tag.name.startswith('error-'):
40
            logger.error(message)
41
        elif tag.name == 'warning' or tag.name.startswith('warning-'):
42
            logger.warning(message)
43
        else:
44
            logger.info(message)
45
    except:
46
        try:
47
            logging.getLogger('django.journal').exception('Unable to log msg')
48
        except:
49
            pass # we tried, really, we tried
50
    journal = Journal.objects.using(using).create(tag=tag, template=template,
51
            message=unicode_truncate(message, 128))
52
    for name, value in kwargs.iteritems():
53
        if value is None:
54
            continue
55
        tag = Tag.objects.using(using).get_cached(name=name)
56
        if isinstance(value, django.db.models.Model):
57
            journal.objectdata_set.create(tag=tag, content_object=value)
58
        else:
59
            journal.stringdata_set.create(tag=tag, content=unicode(value))
60
    return journal
61

  
62
def error_record(tag, tpl, **kwargs):
63
    '''Records error events.
64

  
65
       You must use this function when logging error events. It uses another
66
       database alias than the default one to be immune to transaction rollback
67
       when logging in the middle of a transaction which is going to
68
       rollback.
69
    '''
70
    return record(tag, tpl,
71
            using=getattr(settings, 'JOURNAL_DB_FOR_ERROR_ALIAS', 'default'),
72
            **kwargs)
django_journal/models.py
1 1
import string
2 2

  
3 3
from django.db import models
4
from django.contrib.contenttypes import generic
4
from django.contrib.contenttypes.fields import GenericForeignKey
5 5
from django.utils.translation import ugettext_lazy as _
6 6

  
7 7
import managers
......
143 143
            verbose_name=_('content type'))
144 144
    object_id = models.PositiveIntegerField(db_index=True,
145 145
            verbose_name=_('object id'))
146
    content_object = generic.GenericForeignKey('content_type',
146
    content_object = GenericForeignKey('content_type',
147 147
            'object_id')
148 148

  
149 149
    class Meta:
django_journal/tests.py
1 1
from django.test import TestCase
2 2
from django.contrib.auth.models import User, Group
3
from django.db import transaction
3
from django.db.transaction import atomic
4 4

  
5

  
6
from . import record
7
from . import actions
8
from . import models
5
from .main import record
6
from .actions import export_as_csv_generator
7
from .models import Journal
9 8

  
10 9

  
11 10
class JournalTestCase(TestCase):
12 11
    def setUp(self):
13
        models.JOURNAL_METADATA_CACHE_TIMEOUT = 0
14 12
        self.users = []
15 13
        self.groups = []
16
        with transaction.commit_on_success():
14
        with atomic():
17 15
            for i in range(20):
18 16
                self.users.append(
19 17
                        User.objects.create(username='user%s' % i))
......
30 28
                record('logout', '{user} logged out', user=self.users[i])
31 29

  
32 30
    def test_login(self):
33
        for i, event in zip(range(20), models.Journal.objects.for_tag('login').order_by('id')):
31
        for i, event in zip(range(20), Journal.objects.for_tag('login').order_by('id')):
34 32
            self.assertEqual(unicode(event), 'user{0} logged in'.format(i))
35 33

  
36 34
    def test_groups(self):
37
        for i, event in zip(range(40), models.Journal.objects.for_tag('group-changed').order_by('id')):
35
        for i, event in zip(range(40), Journal.objects.for_tag('group-changed').order_by('id')):
38 36
            self.assertEqual(unicode(event),
39 37
                    'user{0} gave group group{0} to user{1}'.format(i, (i+1)%20))
40 38

  
41 39
    def test_logout(self):
42
        for i, event in zip(range(20), models.Journal.objects.for_tag('logout').order_by('id')):
40
        for i, event in zip(range(20), Journal.objects.for_tag('logout').order_by('id')):
43 41
            self.assertEqual(unicode(event), 'user{0} logged out'.format(i))
44 42

  
45 43
    def test_export_as_csv(self):
46
        qs = models.Journal.objects.all()
47
        l = list(actions.export_as_csv_generator(qs))
44
        qs = Journal.objects.all()
45
        l = list(export_as_csv_generator(qs))
48 46
        self.assertEquals(set(l[0]), set(['time', 'tag', 'message', 'group', 'group__id', 'user', 'user__id', 'user1', 'user1__id', 'user2', 'user2__id']))
49
        l = list(actions.export_as_csv_generator(qs[:5]))
47
        l = list(export_as_csv_generator(qs[:5]))
50 48
        self.assertEquals(set(l[0]), set(['time', 'tag', 'message', 'user', 'user__id']))
51 49
        for user in self.users:
52 50
            user.delete()
53
        qs = models.Journal.objects.all()
54
        l = list(actions.export_as_csv_generator(qs))
51
        qs = Journal.objects.all()
52
        l = list(export_as_csv_generator(qs))
55 53
        self.assertEquals(l[1]['user'], '<deleted>')
56 54

  
setup.py
120 120
          'test': test
121 121
      },
122 122
      install_requires=[
123
          'django >= 1.7',
123
          'django >= 1.11,<2.0',
124 124
          'django-model-utils',
125 125
      ],
126 126
      setup_requires=[
127
          'django >= 1.4.2',
127
          'django >= 1.11,<2.0',
128 128
      ])
tox.ini
1
[testenv]
2
usedevelop = True
3
deps =
4
  django<2.0
5
  pytest
6
  pytest-django
7
setenv =
8
  DJANGO_SETTINGS_MODULE=test_settings
9
commands =
10
  py.test {posargs:django_journal/tests.py}
0
-