~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_source.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-08-20 05:20:56 UTC
  • mfrom: (5380.3.3 doc)
  • Revision ID: pqm@pqm.ubuntu.com-20100820052056-gwad7dz2otckrjax
(mbp) Start whatsnew for 2.3; update ppa developer docs (Martin Pool)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
2
 
#   Authors: Robert Collins <robert.collins@canonical.com>
3
 
#            and others
 
1
# Copyright (C) 2005-2010 Canonical Ltd
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
42
40
 
43
41
# Files which are listed here will be skipped when testing for Copyright (or
44
42
# GPL) statements.
45
 
COPYRIGHT_EXCEPTIONS = ['bzrlib/lsprof.py', 'bzrlib/_bencode_py.py']
 
43
COPYRIGHT_EXCEPTIONS = [
 
44
    'bzrlib/_bencode_py.py',
 
45
    'bzrlib/doc_generate/conf.py',
 
46
    'bzrlib/lsprof.py',
 
47
    ]
46
48
 
47
 
LICENSE_EXCEPTIONS = ['bzrlib/lsprof.py', 'bzrlib/_bencode_py.py']
 
49
LICENSE_EXCEPTIONS = [
 
50
    'bzrlib/_bencode_py.py',
 
51
    'bzrlib/doc_generate/conf.py',
 
52
    'bzrlib/lsprof.py',
 
53
    ]
48
54
# Technically, 'bzrlib/lsprof.py' should be 'bzrlib/util/lsprof.py',
49
55
# (we do not check bzrlib/util/, since that is code bundled from elsewhere)
50
56
# but for compatibility with previous releases, we don't want to move it.
 
57
#
 
58
# sphinx_conf is semi-autogenerated.
51
59
 
52
60
 
53
61
class TestSourceHelper(TestCase):
357
365
                    return True
358
366
            return False
359
367
        badfiles = []
 
368
        assert_re = re.compile(r'\bassert\b')
360
369
        for fname, text in self.get_source_file_contents():
361
370
            if not self.is_our_code(fname):
362
371
                continue
363
 
            ast = parser.ast2tuple(parser.suite(''.join(text)))
 
372
            if not assert_re.search(text):
 
373
                continue
 
374
            ast = parser.ast2tuple(parser.suite(text))
364
375
            if search(ast):
365
376
                badfiles.append(fname)
366
377
        if badfiles:
367
378
            self.fail(
368
379
                "these files contain an assert statement and should not:\n%s"
369
380
                % '\n'.join(badfiles))
 
381
 
 
382
    def test_extension_exceptions(self):
 
383
        """Extension functions should propagate exceptions.
 
384
 
 
385
        Either they should return an object, have an 'except' clause, or have a
 
386
        "# cannot_raise" to indicate that we've audited them and defined them as not
 
387
        raising exceptions.
 
388
        """
 
389
        both_exc_and_no_exc = []
 
390
        missing_except = []
 
391
        class_re = re.compile(r'^(cdef\s+)?(public\s+)?(api\s+)?class (\w+).*:',
 
392
                              re.MULTILINE)
 
393
        except_re = re.compile(r'cdef\s+' # start with cdef
 
394
                               r'([\w *]*?)\s*' # this is the return signature
 
395
                               r'(\w+)\s*\(' # the function name
 
396
                               r'[^)]*\)\s*' # parameters
 
397
                               r'(.*)\s*:' # the except clause
 
398
                               r'\s*(#\s*cannot[- _]raise)?' # cannot raise comment
 
399
                              )
 
400
        for fname, text in self.get_source_file_contents(
 
401
                extensions=('.pyx',)):
 
402
            known_classes = set([m[-1] for m in class_re.findall(text)])
 
403
            cdefs = except_re.findall(text)
 
404
            for sig, func, exc_clause, no_exc_comment in cdefs:
 
405
                if sig.startswith('api '):
 
406
                    sig = sig[4:]
 
407
                if not sig or sig in known_classes:
 
408
                    sig = 'object'
 
409
                if 'nogil' in exc_clause:
 
410
                    exc_clause = exc_clause.replace('nogil', '').strip()
 
411
                if exc_clause and no_exc_comment:
 
412
                    both_exc_and_no_exc.append((fname, func))
 
413
                if sig != 'object' and not (exc_clause or no_exc_comment):
 
414
                    missing_except.append((fname, func))
 
415
        error_msg = []
 
416
        if both_exc_and_no_exc:
 
417
            error_msg.append('The following functions had "cannot raise" comments'
 
418
                             ' but did have an except clause set:')
 
419
            for fname, func in both_exc_and_no_exc:
 
420
                error_msg.append('%s:%s' % (fname, func))
 
421
            error_msg.extend(('', ''))
 
422
        if missing_except:
 
423
            error_msg.append('The following functions have fixed return types,'
 
424
                             ' but no except clause.')
 
425
            error_msg.append('Either add an except or append "# cannot_raise".')
 
426
            for fname, func in missing_except:
 
427
                error_msg.append('%s:%s' % (fname, func))
 
428
            error_msg.extend(('', ''))
 
429
        if error_msg:
 
430
            self.fail('\n'.join(error_msg))