Merge lp:~mitya57/ubuntu/trusty/python-coverage/merge-rc-bugfix into lp:ubuntu/trusty/python-coverage

Proposed by Dmitry Shachnev
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~mitya57/ubuntu/trusty/python-coverage/merge-rc-bugfix
Merge into: lp:ubuntu/trusty/python-coverage
Diff against target: 2654 lines (+656/-1404)
35 files modified
.pc/02.rename-public-programs.patch/setup.py (+196/-0)
.pc/02.use-system-ecmascript-libraries.patch/coverage/html.py (+0/-387)
.pc/03.rename-public-programs.patch/setup.py (+0/-196)
.pc/applied-patches (+1/-2)
CHANGES.txt (+9/-0)
PKG-INFO (+2/-2)
coverage.egg-info/PKG-INFO (+2/-2)
coverage/annotate.py (+4/-3)
coverage/cmdline.py (+5/-1)
coverage/control.py (+5/-2)
coverage/html.py (+21/-25)
coverage/misc.py (+5/-1)
coverage/parser.py (+46/-12)
coverage/phystokens.py (+6/-4)
coverage/results.py (+1/-1)
coverage/templite.py (+136/-94)
coverage/version.py (+1/-1)
coverage/xmlreport.py (+1/-1)
debian/changelog (+31/-0)
debian/patches/01.omit-resource-files-from-distutils-setup.patch (+11/-0)
debian/patches/02.rename-public-programs.patch (+50/-0)
debian/patches/02.use-system-ecmascript-libraries.patch (+0/-48)
debian/patches/03.rename-public-programs.patch (+0/-39)
debian/patches/series (+1/-2)
debian/python-coverage.1 (+0/-279)
debian/python-coverage.1.tmp (+0/-278)
debian/repack (+2/-2)
debian/rules (+22/-8)
doc/changes.rst (+16/-1)
doc/cmd.rst (+2/-2)
doc/index.rst (+2/-1)
doc/install.rst (+3/-2)
tests/coveragetest.py (+4/-3)
tests/test_html.py (+23/-0)
tests/test_templite.py (+48/-5)
To merge this branch: bzr merge lp:~mitya57/ubuntu/trusty/python-coverage/merge-rc-bugfix
Reviewer Review Type Date Requested Status
Martin Pitt Approve
Review via email: mp+209836@code.launchpad.net

Description of the change

This is a merge from Debian.

Upstream changes: new bugfix release.
Debian changes: fixes an RC bug (#740053) and another packaging bug (#736121).

To post a comment you must log in.
15. By Dmitry Shachnev

Merge with Debian unstable, remaining change:
- Add python3-coverage.preinst to correctly handle upgrades from
  previous releases.

Revision history for this message
Martin Pitt (pitti) wrote :

Thanks! Sponsored.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc/02.rename-public-programs.patch'
2=== added file '.pc/02.rename-public-programs.patch/setup.py'
3--- .pc/02.rename-public-programs.patch/setup.py 1970-01-01 00:00:00 +0000
4+++ .pc/02.rename-public-programs.patch/setup.py 2014-03-07 05:33:58 +0000
5@@ -0,0 +1,196 @@
6+# setup.py for coverage.py
7+
8+"""Code coverage measurement for Python
9+
10+Coverage.py measures code coverage, typically during test execution. It uses
11+the code analysis tools and tracing hooks provided in the Python standard
12+library to determine which lines are executable, and which have been executed.
13+
14+Coverage.py runs on Pythons 2.3 through 3.3, and PyPy 1.9.
15+
16+Documentation is at `nedbatchelder.com <%s>`_. Code repository and issue
17+tracker are on `Bitbucket <http://bitbucket.org/ned/coveragepy>`_, with a
18+mirrored repo on `Github <https://github.com/nedbat/coveragepy>`_.
19+
20+New in 3.7: ``--debug``, and 12 bugs closed.
21+
22+New in 3.6: ``--fail-under``, and >20 bugs closed.
23+
24+New in 3.5: Branch coverage exclusions, keyboard shortcuts in HTML report.
25+
26+New in 3.4: Better control over source to measure, and unexecuted files
27+can be reported.
28+
29+New in 3.3: .coveragerc files.
30+
31+New in 3.2: Branch coverage!
32+"""
33+
34+# This file is used unchanged under all versions of Python, 2.x and 3.x.
35+
36+classifiers = """\
37+Environment :: Console
38+Intended Audience :: Developers
39+License :: OSI Approved :: BSD License
40+Operating System :: OS Independent
41+Programming Language :: Python :: 2
42+Programming Language :: Python :: 3
43+Topic :: Software Development :: Quality Assurance
44+Topic :: Software Development :: Testing
45+"""
46+
47+# Pull in the tools we need.
48+import os, sys
49+
50+from setuptools import setup
51+from distutils.core import Extension # pylint: disable=E0611,F0401
52+from distutils.command.build_ext import build_ext # pylint: disable=E0611,F0401,C0301
53+from distutils import errors # pylint: disable=E0611,F0401
54+
55+# Get or massage our metadata. We exec coverage/version.py so we can avoid
56+# importing the product code into setup.py.
57+
58+doc = __doc__ # __doc__ will be overwritten by version.py.
59+__version__ = __url__ = "" # Keep pylint happy.
60+
61+cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py")
62+version_file = open(cov_ver_py)
63+try:
64+ exec(compile(version_file.read(), cov_ver_py, 'exec'))
65+finally:
66+ version_file.close()
67+
68+doclines = (doc % __url__).splitlines()
69+classifier_list = classifiers.splitlines()
70+
71+if 'a' in __version__:
72+ devstat = "3 - Alpha"
73+elif 'b' in __version__:
74+ devstat = "4 - Beta"
75+else:
76+ devstat = "5 - Production/Stable"
77+classifier_list.append("Development Status :: " + devstat)
78+
79+# Install a script as "coverage", and as "coverage[23]", and as
80+# "coverage-2.7" (or whatever).
81+scripts = [
82+ 'coverage = coverage:main',
83+ 'coverage%d = coverage:main' % sys.version_info[:1],
84+ 'coverage-%d.%d = coverage:main' % sys.version_info[:2],
85+ ]
86+
87+# Create the keyword arguments for setup()
88+
89+setup_args = dict(
90+ name = 'coverage',
91+ version = __version__,
92+
93+ packages = [
94+ 'coverage',
95+ ],
96+
97+ entry_points = {'console_scripts': scripts},
98+
99+ # We need to get HTML assets from our htmlfiles dir.
100+ zip_safe = False,
101+
102+ author = 'Ned Batchelder and others',
103+ author_email = 'ned@nedbatchelder.com',
104+ description = doclines[0],
105+ long_description = '\n'.join(doclines[2:]),
106+ keywords = 'code coverage testing',
107+ license = 'BSD',
108+ classifiers = classifier_list,
109+ url = __url__,
110+ )
111+
112+# A replacement for the build_ext command which raises a single exception
113+# if the build fails, so we can fallback nicely.
114+
115+ext_errors = (
116+ errors.CCompilerError,
117+ errors.DistutilsExecError,
118+ errors.DistutilsPlatformError,
119+)
120+if sys.platform == 'win32' and sys.version_info > (2, 6):
121+ # 2.6's distutils.msvc9compiler can raise an IOError when failing to
122+ # find the compiler
123+ ext_errors += (IOError,)
124+
125+class BuildFailed(Exception):
126+ """Raise this to indicate the C extension wouldn't build."""
127+ def __init__(self):
128+ Exception.__init__(self)
129+ self.cause = sys.exc_info()[1] # work around py 2/3 different syntax
130+
131+class ve_build_ext(build_ext):
132+ """Build C extensions, but fail with a straightforward exception."""
133+
134+ def run(self):
135+ """Wrap `run` with `BuildFailed`."""
136+ try:
137+ build_ext.run(self)
138+ except errors.DistutilsPlatformError:
139+ raise BuildFailed()
140+
141+ def build_extension(self, ext):
142+ """Wrap `build_extension` with `BuildFailed`."""
143+ try:
144+ # Uncomment to test compile failures:
145+ # raise errors.CCompilerError("OOPS")
146+ build_ext.build_extension(self, ext)
147+ except ext_errors:
148+ raise BuildFailed()
149+ except ValueError:
150+ # this can happen on Windows 64 bit, see Python issue 7511
151+ if "'path'" in str(sys.exc_info()[1]): # works with both py 2/3
152+ raise BuildFailed()
153+ raise
154+
155+# There are a few reasons we might not be able to compile the C extension.
156+# Figure out if we should attempt the C extension or not.
157+
158+compile_extension = True
159+
160+if sys.platform.startswith('java'):
161+ # Jython can't compile C extensions
162+ compile_extension = False
163+
164+if '__pypy__' in sys.builtin_module_names:
165+ # Pypy can't compile C extensions
166+ compile_extension = False
167+
168+if compile_extension:
169+ setup_args.update(dict(
170+ ext_modules = [
171+ Extension("coverage.tracer", sources=["coverage/tracer.c"])
172+ ],
173+ cmdclass = {
174+ 'build_ext': ve_build_ext,
175+ },
176+ ))
177+
178+# Py3.x-specific details.
179+
180+if sys.version_info >= (3, 0):
181+ setup_args.update(dict(
182+ use_2to3 = False,
183+ ))
184+
185+def main():
186+ """Actually invoke setup() with the arguments we built above."""
187+ # For a variety of reasons, it might not be possible to install the C
188+ # extension. Try it with, and if it fails, try it without.
189+ try:
190+ setup(**setup_args)
191+ except BuildFailed:
192+ msg = "Couldn't install with extension module, trying without it..."
193+ exc = sys.exc_info()[1]
194+ exc_msg = "%s: %s" % (exc.__class__.__name__, exc.cause)
195+ print("**\n** %s\n** %s\n**" % (msg, exc_msg))
196+
197+ del setup_args['ext_modules']
198+ setup(**setup_args)
199+
200+if __name__ == '__main__':
201+ main()
202
203=== removed directory '.pc/02.use-system-ecmascript-libraries.patch'
204=== removed directory '.pc/02.use-system-ecmascript-libraries.patch/coverage'
205=== removed file '.pc/02.use-system-ecmascript-libraries.patch/coverage/html.py'
206--- .pc/02.use-system-ecmascript-libraries.patch/coverage/html.py 2013-10-15 13:16:36 +0000
207+++ .pc/02.use-system-ecmascript-libraries.patch/coverage/html.py 1970-01-01 00:00:00 +0000
208@@ -1,387 +0,0 @@
209-"""HTML reporting for Coverage."""
210-
211-import os, re, shutil, sys
212-
213-import coverage
214-from coverage.backward import pickle
215-from coverage.misc import CoverageException, Hasher
216-from coverage.phystokens import source_token_lines, source_encoding
217-from coverage.report import Reporter
218-from coverage.results import Numbers
219-from coverage.templite import Templite
220-
221-
222-# Static files are looked for in a list of places.
223-STATIC_PATH = [
224- # The place Debian puts system Javascript libraries.
225- "/usr/share/javascript",
226-
227- # Our htmlfiles directory.
228- os.path.join(os.path.dirname(__file__), "htmlfiles"),
229-]
230-
231-def data_filename(fname):
232- """Return the path to a data file of ours.
233-
234- The file is searched for on `STATIC_PATH`, and the first place it's found,
235- is returned.
236-
237- """
238- for static_dir in STATIC_PATH:
239- static_filename = os.path.join(static_dir, fname)
240- if os.path.exists(static_filename):
241- return static_filename
242- raise CoverageException("Couldn't find static file %r" % fname)
243-
244-def data(fname):
245- """Return the contents of a data file of ours."""
246- data_file = open(data_filename(fname))
247- try:
248- return data_file.read()
249- finally:
250- data_file.close()
251-
252-
253-class HtmlReporter(Reporter):
254- """HTML reporting."""
255-
256- # These files will be copied from the htmlfiles dir to the output dir.
257- STATIC_FILES = [
258- "style.css",
259- "jquery.min.js",
260- "jquery.hotkeys.js",
261- "jquery.isonscreen.js",
262- "jquery.tablesorter.min.js",
263- "coverage_html.js",
264- "keybd_closed.png",
265- "keybd_open.png",
266- ]
267-
268- def __init__(self, cov, config):
269- super(HtmlReporter, self).__init__(cov, config)
270- self.directory = None
271- self.template_globals = {
272- 'escape': escape,
273- 'title': self.config.html_title,
274- '__url__': coverage.__url__,
275- '__version__': coverage.__version__,
276- }
277- self.source_tmpl = Templite(
278- data("pyfile.html"), self.template_globals
279- )
280-
281- self.coverage = cov
282-
283- self.files = []
284- self.arcs = self.coverage.data.has_arcs()
285- self.status = HtmlStatus()
286- self.extra_css = None
287- self.totals = Numbers()
288-
289- def report(self, morfs):
290- """Generate an HTML report for `morfs`.
291-
292- `morfs` is a list of modules or filenames.
293-
294- """
295- assert self.config.html_dir, "must give a directory for html reporting"
296-
297- # Read the status data.
298- self.status.read(self.config.html_dir)
299-
300- # Check that this run used the same settings as the last run.
301- m = Hasher()
302- m.update(self.config)
303- these_settings = m.digest()
304- if self.status.settings_hash() != these_settings:
305- self.status.reset()
306- self.status.set_settings_hash(these_settings)
307-
308- # The user may have extra CSS they want copied.
309- if self.config.extra_css:
310- self.extra_css = os.path.basename(self.config.extra_css)
311-
312- # Process all the files.
313- self.report_files(self.html_file, morfs, self.config.html_dir)
314-
315- if not self.files:
316- raise CoverageException("No data to report.")
317-
318- # Write the index file.
319- self.index_file()
320-
321- self.make_local_static_report_files()
322-
323- return self.totals.pc_covered
324-
325- def make_local_static_report_files(self):
326- """Make local instances of static files for HTML report."""
327- # The files we provide must always be copied.
328- for static in self.STATIC_FILES:
329- shutil.copyfile(
330- data_filename(static),
331- os.path.join(self.directory, static)
332- )
333-
334- # The user may have extra CSS they want copied.
335- if self.extra_css:
336- shutil.copyfile(
337- self.config.extra_css,
338- os.path.join(self.directory, self.extra_css)
339- )
340-
341- def write_html(self, fname, html):
342- """Write `html` to `fname`, properly encoded."""
343- fout = open(fname, "wb")
344- try:
345- fout.write(html.encode('ascii', 'xmlcharrefreplace'))
346- finally:
347- fout.close()
348-
349- def file_hash(self, source, cu):
350- """Compute a hash that changes if the file needs to be re-reported."""
351- m = Hasher()
352- m.update(source)
353- self.coverage.data.add_to_hash(cu.filename, m)
354- return m.digest()
355-
356- def html_file(self, cu, analysis):
357- """Generate an HTML file for one source file."""
358- source_file = cu.source_file()
359- try:
360- source = source_file.read()
361- finally:
362- source_file.close()
363-
364- # Find out if the file on disk is already correct.
365- flat_rootname = cu.flat_rootname()
366- this_hash = self.file_hash(source, cu)
367- that_hash = self.status.file_hash(flat_rootname)
368- if this_hash == that_hash:
369- # Nothing has changed to require the file to be reported again.
370- self.files.append(self.status.index_info(flat_rootname))
371- return
372-
373- self.status.set_file_hash(flat_rootname, this_hash)
374-
375- # If need be, determine the encoding of the source file. We use it
376- # later to properly write the HTML.
377- if sys.version_info < (3, 0):
378- encoding = source_encoding(source)
379- # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it.
380- if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf":
381- source = source[3:]
382- encoding = "utf-8"
383-
384- # Get the numbers for this file.
385- nums = analysis.numbers
386-
387- missing_branch_arcs = analysis.missing_branch_arcs()
388-
389- # These classes determine which lines are highlighted by default.
390- c_run = "run hide_run"
391- c_exc = "exc"
392- c_mis = "mis"
393- c_par = "par " + c_run
394-
395- lines = []
396-
397- for lineno, line in enumerate(source_token_lines(source)):
398- lineno += 1 # 1-based line numbers.
399- # Figure out how to mark this line.
400- line_class = []
401- annotate_html = ""
402- annotate_title = ""
403- if lineno in analysis.statements:
404- line_class.append("stm")
405- if lineno in analysis.excluded:
406- line_class.append(c_exc)
407- elif lineno in analysis.missing:
408- line_class.append(c_mis)
409- elif self.arcs and lineno in missing_branch_arcs:
410- line_class.append(c_par)
411- annlines = []
412- for b in missing_branch_arcs[lineno]:
413- if b < 0:
414- annlines.append("exit")
415- else:
416- annlines.append(str(b))
417- annotate_html = "&nbsp;&nbsp; ".join(annlines)
418- if len(annlines) > 1:
419- annotate_title = "no jumps to these line numbers"
420- elif len(annlines) == 1:
421- annotate_title = "no jump to this line number"
422- elif lineno in analysis.statements:
423- line_class.append(c_run)
424-
425- # Build the HTML for the line
426- html = []
427- for tok_type, tok_text in line:
428- if tok_type == "ws":
429- html.append(escape(tok_text))
430- else:
431- tok_html = escape(tok_text) or '&nbsp;'
432- html.append(
433- "<span class='%s'>%s</span>" % (tok_type, tok_html)
434- )
435-
436- lines.append({
437- 'html': ''.join(html),
438- 'number': lineno,
439- 'class': ' '.join(line_class) or "pln",
440- 'annotate': annotate_html,
441- 'annotate_title': annotate_title,
442- })
443-
444- # Write the HTML page for this file.
445- html = spaceless(self.source_tmpl.render({
446- 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run,
447- 'arcs': self.arcs, 'extra_css': self.extra_css,
448- 'cu': cu, 'nums': nums, 'lines': lines,
449- }))
450-
451- if sys.version_info < (3, 0):
452- html = html.decode(encoding)
453-
454- html_filename = flat_rootname + ".html"
455- html_path = os.path.join(self.directory, html_filename)
456- self.write_html(html_path, html)
457-
458- # Save this file's information for the index file.
459- index_info = {
460- 'nums': nums,
461- 'html_filename': html_filename,
462- 'name': cu.name,
463- }
464- self.files.append(index_info)
465- self.status.set_index_info(flat_rootname, index_info)
466-
467- def index_file(self):
468- """Write the index.html file for this report."""
469- index_tmpl = Templite(
470- data("index.html"), self.template_globals
471- )
472-
473- self.totals = sum([f['nums'] for f in self.files])
474-
475- html = index_tmpl.render({
476- 'arcs': self.arcs,
477- 'extra_css': self.extra_css,
478- 'files': self.files,
479- 'totals': self.totals,
480- })
481-
482- if sys.version_info < (3, 0):
483- html = html.decode("utf-8")
484- self.write_html(
485- os.path.join(self.directory, "index.html"),
486- html
487- )
488-
489- # Write the latest hashes for next time.
490- self.status.write(self.directory)
491-
492-
493-class HtmlStatus(object):
494- """The status information we keep to support incremental reporting."""
495-
496- STATUS_FILE = "status.dat"
497- STATUS_FORMAT = 1
498-
499- def __init__(self):
500- self.reset()
501-
502- def reset(self):
503- """Initialize to empty."""
504- self.settings = ''
505- self.files = {}
506-
507- def read(self, directory):
508- """Read the last status in `directory`."""
509- usable = False
510- try:
511- status_file = os.path.join(directory, self.STATUS_FILE)
512- fstatus = open(status_file, "rb")
513- try:
514- status = pickle.load(fstatus)
515- finally:
516- fstatus.close()
517- except (IOError, ValueError):
518- usable = False
519- else:
520- usable = True
521- if status['format'] != self.STATUS_FORMAT:
522- usable = False
523- elif status['version'] != coverage.__version__:
524- usable = False
525-
526- if usable:
527- self.files = status['files']
528- self.settings = status['settings']
529- else:
530- self.reset()
531-
532- def write(self, directory):
533- """Write the current status to `directory`."""
534- status_file = os.path.join(directory, self.STATUS_FILE)
535- status = {
536- 'format': self.STATUS_FORMAT,
537- 'version': coverage.__version__,
538- 'settings': self.settings,
539- 'files': self.files,
540- }
541- fout = open(status_file, "wb")
542- try:
543- pickle.dump(status, fout)
544- finally:
545- fout.close()
546-
547- def settings_hash(self):
548- """Get the hash of the coverage.py settings."""
549- return self.settings
550-
551- def set_settings_hash(self, settings):
552- """Set the hash of the coverage.py settings."""
553- self.settings = settings
554-
555- def file_hash(self, fname):
556- """Get the hash of `fname`'s contents."""
557- return self.files.get(fname, {}).get('hash', '')
558-
559- def set_file_hash(self, fname, val):
560- """Set the hash of `fname`'s contents."""
561- self.files.setdefault(fname, {})['hash'] = val
562-
563- def index_info(self, fname):
564- """Get the information for index.html for `fname`."""
565- return self.files.get(fname, {}).get('index', {})
566-
567- def set_index_info(self, fname, info):
568- """Set the information for index.html for `fname`."""
569- self.files.setdefault(fname, {})['index'] = info
570-
571-
572-# Helpers for templates and generating HTML
573-
574-def escape(t):
575- """HTML-escape the text in `t`."""
576- return (t
577- # Convert HTML special chars into HTML entities.
578- .replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
579- .replace("'", "&#39;").replace('"', "&quot;")
580- # Convert runs of spaces: "......" -> "&nbsp;.&nbsp;.&nbsp;."
581- .replace(" ", "&nbsp; ")
582- # To deal with odd-length runs, convert the final pair of spaces
583- # so that "....." -> "&nbsp;.&nbsp;&nbsp;."
584- .replace(" ", "&nbsp; ")
585- )
586-
587-def spaceless(html):
588- """Squeeze out some annoying extra space from an HTML string.
589-
590- Nicely-formatted templates mean lots of extra space in the result.
591- Get rid of some.
592-
593- """
594- html = re.sub(r">\s+<p ", ">\n<p ", html)
595- return html
596
597=== removed directory '.pc/03.rename-public-programs.patch'
598=== removed file '.pc/03.rename-public-programs.patch/setup.py'
599--- .pc/03.rename-public-programs.patch/setup.py 2013-10-15 13:16:36 +0000
600+++ .pc/03.rename-public-programs.patch/setup.py 1970-01-01 00:00:00 +0000
601@@ -1,196 +0,0 @@
602-# setup.py for coverage.py
603-
604-"""Code coverage measurement for Python
605-
606-Coverage.py measures code coverage, typically during test execution. It uses
607-the code analysis tools and tracing hooks provided in the Python standard
608-library to determine which lines are executable, and which have been executed.
609-
610-Coverage.py runs on Pythons 2.3 through 3.3, and PyPy 1.9.
611-
612-Documentation is at `nedbatchelder.com <%s>`_. Code repository and issue
613-tracker are on `Bitbucket <http://bitbucket.org/ned/coveragepy>`_, with a
614-mirrored repo on `Github <https://github.com/nedbat/coveragepy>`_.
615-
616-New in 3.7: ``--debug``, and 12 bugs closed.
617-
618-New in 3.6: ``--fail-under``, and >20 bugs closed.
619-
620-New in 3.5: Branch coverage exclusions, keyboard shortcuts in HTML report.
621-
622-New in 3.4: Better control over source to measure, and unexecuted files
623-can be reported.
624-
625-New in 3.3: .coveragerc files.
626-
627-New in 3.2: Branch coverage!
628-"""
629-
630-# This file is used unchanged under all versions of Python, 2.x and 3.x.
631-
632-classifiers = """\
633-Environment :: Console
634-Intended Audience :: Developers
635-License :: OSI Approved :: BSD License
636-Operating System :: OS Independent
637-Programming Language :: Python :: 2
638-Programming Language :: Python :: 3
639-Topic :: Software Development :: Quality Assurance
640-Topic :: Software Development :: Testing
641-"""
642-
643-# Pull in the tools we need.
644-import os, sys
645-
646-from setuptools import setup
647-from distutils.core import Extension # pylint: disable=E0611,F0401
648-from distutils.command.build_ext import build_ext # pylint: disable=E0611,F0401,C0301
649-from distutils import errors # pylint: disable=E0611,F0401
650-
651-# Get or massage our metadata. We exec coverage/version.py so we can avoid
652-# importing the product code into setup.py.
653-
654-doc = __doc__ # __doc__ will be overwritten by version.py.
655-__version__ = __url__ = "" # Keep pylint happy.
656-
657-cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py")
658-version_file = open(cov_ver_py)
659-try:
660- exec(compile(version_file.read(), cov_ver_py, 'exec'))
661-finally:
662- version_file.close()
663-
664-doclines = (doc % __url__).splitlines()
665-classifier_list = classifiers.splitlines()
666-
667-if 'a' in __version__:
668- devstat = "3 - Alpha"
669-elif 'b' in __version__:
670- devstat = "4 - Beta"
671-else:
672- devstat = "5 - Production/Stable"
673-classifier_list.append("Development Status :: " + devstat)
674-
675-# Install a script as "coverage", and as "coverage[23]", and as
676-# "coverage-2.7" (or whatever).
677-scripts = [
678- 'coverage = coverage:main',
679- 'coverage%d = coverage:main' % sys.version_info[:1],
680- 'coverage-%d.%d = coverage:main' % sys.version_info[:2],
681- ]
682-
683-# Create the keyword arguments for setup()
684-
685-setup_args = dict(
686- name = 'coverage',
687- version = __version__,
688-
689- packages = [
690- 'coverage',
691- ],
692-
693- entry_points = {'console_scripts': scripts},
694-
695- # We need to get HTML assets from our htmlfiles dir.
696- zip_safe = False,
697-
698- author = 'Ned Batchelder and others',
699- author_email = 'ned@nedbatchelder.com',
700- description = doclines[0],
701- long_description = '\n'.join(doclines[2:]),
702- keywords = 'code coverage testing',
703- license = 'BSD',
704- classifiers = classifier_list,
705- url = __url__,
706- )
707-
708-# A replacement for the build_ext command which raises a single exception
709-# if the build fails, so we can fallback nicely.
710-
711-ext_errors = (
712- errors.CCompilerError,
713- errors.DistutilsExecError,
714- errors.DistutilsPlatformError,
715-)
716-if sys.platform == 'win32' and sys.version_info > (2, 6):
717- # 2.6's distutils.msvc9compiler can raise an IOError when failing to
718- # find the compiler
719- ext_errors += (IOError,)
720-
721-class BuildFailed(Exception):
722- """Raise this to indicate the C extension wouldn't build."""
723- def __init__(self):
724- Exception.__init__(self)
725- self.cause = sys.exc_info()[1] # work around py 2/3 different syntax
726-
727-class ve_build_ext(build_ext):
728- """Build C extensions, but fail with a straightforward exception."""
729-
730- def run(self):
731- """Wrap `run` with `BuildFailed`."""
732- try:
733- build_ext.run(self)
734- except errors.DistutilsPlatformError:
735- raise BuildFailed()
736-
737- def build_extension(self, ext):
738- """Wrap `build_extension` with `BuildFailed`."""
739- try:
740- # Uncomment to test compile failures:
741- # raise errors.CCompilerError("OOPS")
742- build_ext.build_extension(self, ext)
743- except ext_errors:
744- raise BuildFailed()
745- except ValueError:
746- # this can happen on Windows 64 bit, see Python issue 7511
747- if "'path'" in str(sys.exc_info()[1]): # works with both py 2/3
748- raise BuildFailed()
749- raise
750-
751-# There are a few reasons we might not be able to compile the C extension.
752-# Figure out if we should attempt the C extension or not.
753-
754-compile_extension = True
755-
756-if sys.platform.startswith('java'):
757- # Jython can't compile C extensions
758- compile_extension = False
759-
760-if '__pypy__' in sys.builtin_module_names:
761- # Pypy can't compile C extensions
762- compile_extension = False
763-
764-if compile_extension:
765- setup_args.update(dict(
766- ext_modules = [
767- Extension("coverage.tracer", sources=["coverage/tracer.c"])
768- ],
769- cmdclass = {
770- 'build_ext': ve_build_ext,
771- },
772- ))
773-
774-# Py3.x-specific details.
775-
776-if sys.version_info >= (3, 0):
777- setup_args.update(dict(
778- use_2to3 = False,
779- ))
780-
781-def main():
782- """Actually invoke setup() with the arguments we built above."""
783- # For a variety of reasons, it might not be possible to install the C
784- # extension. Try it with, and if it fails, try it without.
785- try:
786- setup(**setup_args)
787- except BuildFailed:
788- msg = "Couldn't install with extension module, trying without it..."
789- exc = sys.exc_info()[1]
790- exc_msg = "%s: %s" % (exc.__class__.__name__, exc.cause)
791- print("**\n** %s\n** %s\n**" % (msg, exc_msg))
792-
793- del setup_args['ext_modules']
794- setup(**setup_args)
795-
796-if __name__ == '__main__':
797- main()
798
799=== modified file '.pc/applied-patches'
800--- .pc/applied-patches 2014-01-06 21:48:50 +0000
801+++ .pc/applied-patches 2014-03-07 05:33:58 +0000
802@@ -1,3 +1,2 @@
803 01.omit-resource-files-from-distutils-setup.patch
804-02.use-system-ecmascript-libraries.patch
805-03.rename-public-programs.patch
806+02.rename-public-programs.patch
807
808=== modified file 'CHANGES.txt'
809--- CHANGES.txt 2014-01-06 21:48:50 +0000
810+++ CHANGES.txt 2014-03-07 05:33:58 +0000
811@@ -2,6 +2,15 @@
812 Change history for Coverage.py
813 ------------------------------
814
815+3.7.1 -- 13 December 2013
816+-------------------------
817+
818+- Improved the speed of HTML report generation by about 20%.
819+
820+- Fixed the mechanism for finding OS-installed static files for the HTML report
821+ so that it will actually find OS-installed static files.
822+
823+
824 3.7 --- 6 October 2013
825 ----------------------
826
827
828=== modified file 'PKG-INFO'
829--- PKG-INFO 2014-01-06 21:48:50 +0000
830+++ PKG-INFO 2014-03-07 05:33:58 +0000
831@@ -1,6 +1,6 @@
832-Metadata-Version: 1.1
833+Metadata-Version: 1.0
834 Name: coverage
835-Version: 3.7
836+Version: 3.7.1
837 Summary: Code coverage measurement for Python
838 Home-page: http://nedbatchelder.com/code/coverage
839 Author: Ned Batchelder and others
840
841=== modified file 'coverage.egg-info/PKG-INFO'
842--- coverage.egg-info/PKG-INFO 2014-01-06 21:48:50 +0000
843+++ coverage.egg-info/PKG-INFO 2014-03-07 05:33:58 +0000
844@@ -1,6 +1,6 @@
845-Metadata-Version: 1.1
846+Metadata-Version: 1.0
847 Name: coverage
848-Version: 3.7
849+Version: 3.7.1
850 Summary: Code coverage measurement for Python
851 Home-page: http://nedbatchelder.com/code/coverage
852 Author: Ned Batchelder and others
853
854=== modified file 'coverage/annotate.py'
855--- coverage/annotate.py 2014-01-06 21:48:50 +0000
856+++ coverage/annotate.py 2014-03-07 05:33:58 +0000
857@@ -2,6 +2,7 @@
858
859 import os, re
860
861+from coverage.backward import sorted # pylint: disable=W0622
862 from coverage.report import Reporter
863
864 class AnnotateReporter(Reporter):
865@@ -59,9 +60,9 @@
866 dest_file = filename + ",cover"
867 dest = open(dest_file, 'w')
868
869- statements = analysis.statements
870- missing = analysis.missing
871- excluded = analysis.excluded
872+ statements = sorted(analysis.statements)
873+ missing = sorted(analysis.missing)
874+ excluded = sorted(analysis.excluded)
875
876 lineno = 0
877 i = 0
878
879=== modified file 'coverage/cmdline.py'
880--- coverage/cmdline.py 2014-01-06 21:48:50 +0000
881+++ coverage/cmdline.py 2014-03-07 05:33:58 +0000
882@@ -1,6 +1,6 @@
883 """Command-line support for Coverage."""
884
885-import optparse, os, sys, traceback
886+import optparse, os, sys, time, traceback
887
888 from coverage.backward import sorted # pylint: disable=W0622
889 from coverage.execfile import run_python_file, run_python_module
890@@ -717,7 +717,11 @@
891 if argv is None:
892 argv = sys.argv[1:]
893 try:
894+ start = time.clock()
895 status = CoverageScript().command_line(argv)
896+ end = time.clock()
897+ if 0:
898+ print("time: %.3fs" % (end - start))
899 except ExceptionDuringRun:
900 # An exception was caught while running the product code. The
901 # sys.exc_info() return tuple is packed into an ExceptionDuringRun
902
903=== modified file 'coverage/control.py'
904--- coverage/control.py 2014-01-06 21:48:50 +0000
905+++ coverage/control.py 2014-03-07 05:33:58 +0000
906@@ -572,8 +572,11 @@
907 """
908 analysis = self._analyze(morf)
909 return (
910- analysis.filename, analysis.statements, analysis.excluded,
911- analysis.missing, analysis.missing_formatted()
912+ analysis.filename,
913+ sorted(analysis.statements),
914+ sorted(analysis.excluded),
915+ sorted(analysis.missing),
916+ analysis.missing_formatted(),
917 )
918
919 def _analyze(self, it):
920
921=== modified file 'coverage/html.py'
922--- coverage/html.py 2014-01-06 21:48:50 +0000
923+++ coverage/html.py 2014-03-07 05:33:58 +0000
924@@ -20,19 +20,27 @@
925 os.path.join(os.path.dirname(__file__), "htmlfiles"),
926 ]
927
928-def data_filename(fname):
929+def data_filename(fname, pkgdir=""):
930 """Return the path to a data file of ours.
931
932 The file is searched for on `STATIC_PATH`, and the first place it's found,
933 is returned.
934
935+ Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
936+ is provided, at that subdirectory.
937+
938 """
939 for static_dir in STATIC_PATH:
940 static_filename = os.path.join(static_dir, fname)
941 if os.path.exists(static_filename):
942 return static_filename
943+ if pkgdir:
944+ static_filename = os.path.join(static_dir, pkgdir, fname)
945+ if os.path.exists(static_filename):
946+ return static_filename
947 raise CoverageException("Couldn't find static file %r" % fname)
948
949+
950 def data(fname):
951 """Return the contents of a data file of ours."""
952 data_file = open(data_filename(fname))
953@@ -47,10 +55,14 @@
954
955 # These files will be copied from the htmlfiles dir to the output dir.
956 STATIC_FILES = [
957- "style.css",
958- "coverage_html.js",
959- "keybd_closed.png",
960- "keybd_open.png",
961+ ("style.css", ""),
962+ ("jquery.min.js", "jquery"),
963+ ("jquery.hotkeys.js", "jquery-hotkeys"),
964+ ("jquery.isonscreen.js", "jquery-isonscreen"),
965+ ("jquery.tablesorter.min.js", "jquery-tablesorter"),
966+ ("coverage_html.js", ""),
967+ ("keybd_closed.png", ""),
968+ ("keybd_open.png", ""),
969 ]
970
971 def __init__(self, cov, config):
972@@ -113,29 +125,12 @@
973 def make_local_static_report_files(self):
974 """Make local instances of static files for HTML report."""
975 # The files we provide must always be copied.
976- for static in self.STATIC_FILES:
977+ for static, pkgdir in self.STATIC_FILES:
978 shutil.copyfile(
979- data_filename(static),
980+ data_filename(static, pkgdir),
981 os.path.join(self.directory, static)
982 )
983
984- system_javascript_path = os.path.join(
985- os.sep, "usr", "share", "javascript")
986- system_javascript_libraries = {
987- "jquery.min.js":
988- os.path.join(system_javascript_path, "jquery"),
989- "jquery.hotkeys.js":
990- os.path.join(system_javascript_path, "jquery-hotkeys"),
991- "jquery.isonscreen.js":
992- os.path.join(system_javascript_path, "jquery-isonscreen"),
993- "jquery.tablesorter.min.js":
994- os.path.join(system_javascript_path, "jquery-tablesorter"),
995- }
996- for static, source_dir in system_javascript_libraries.items():
997- shutil.copyfile(
998- os.path.join(source_dir, static),
999- os.path.join(self.directory, static))
1000-
1001 # The user may have extra CSS they want copied.
1002 if self.extra_css:
1003 shutil.copyfile(
1004@@ -189,7 +184,8 @@
1005 # Get the numbers for this file.
1006 nums = analysis.numbers
1007
1008- missing_branch_arcs = analysis.missing_branch_arcs()
1009+ if self.arcs:
1010+ missing_branch_arcs = analysis.missing_branch_arcs()
1011
1012 # These classes determine which lines are highlighted by default.
1013 c_run = "run hide_run"
1014
1015=== modified file 'coverage/misc.py'
1016--- coverage/misc.py 2014-01-06 21:48:50 +0000
1017+++ coverage/misc.py 2014-03-07 05:33:58 +0000
1018@@ -38,6 +38,8 @@
1019 i = 0
1020 j = 0
1021 start = None
1022+ statements = sorted(statements)
1023+ lines = sorted(lines)
1024 while i < len(statements) and j < len(lines):
1025 if statements[i] == lines[j]:
1026 if start == None:
1027@@ -113,8 +115,10 @@
1028 self.md5.update(to_bytes(str(type(v))))
1029 if isinstance(v, string_class):
1030 self.md5.update(to_bytes(v))
1031+ elif v is None:
1032+ pass
1033 elif isinstance(v, (int, float)):
1034- self.update(str(v))
1035+ self.md5.update(to_bytes(str(v)))
1036 elif isinstance(v, (tuple, list)):
1037 for e in v:
1038 self.update(e)
1039
1040=== modified file 'coverage/parser.py'
1041--- coverage/parser.py 2014-01-06 21:48:50 +0000
1042+++ coverage/parser.py 2014-03-07 05:33:58 +0000
1043@@ -108,7 +108,7 @@
1044 first_line = None
1045 empty = True
1046
1047- tokgen = tokenize.generate_tokens(StringIO(self.text).readline)
1048+ tokgen = generate_tokens(self.text)
1049 for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
1050 if self.show_tokens: # pragma: not covered
1051 print("%10s %5s %-20r %r" % (
1052@@ -175,16 +175,18 @@
1053 first_line = line
1054 return first_line
1055
1056- def first_lines(self, lines, ignore=None):
1057+ def first_lines(self, lines, *ignores):
1058 """Map the line numbers in `lines` to the correct first line of the
1059 statement.
1060
1061- Skip any line mentioned in `ignore`.
1062+ Skip any line mentioned in any of the sequences in `ignores`.
1063
1064- Returns a sorted list of the first lines.
1065+ Returns a set of the first lines.
1066
1067 """
1068- ignore = ignore or []
1069+ ignore = set()
1070+ for ign in ignores:
1071+ ignore.update(ign)
1072 lset = set()
1073 for l in lines:
1074 if l in ignore:
1075@@ -192,13 +194,13 @@
1076 new_l = self.first_line(l)
1077 if new_l not in ignore:
1078 lset.add(new_l)
1079- return sorted(lset)
1080+ return lset
1081
1082 def parse_source(self):
1083 """Parse source text to find executable lines, excluded lines, etc.
1084
1085- Return values are 1) a sorted list of executable line numbers, and
1086- 2) a sorted list of excluded line numbers.
1087+ Return values are 1) a set of executable line numbers, and 2) a set of
1088+ excluded line numbers.
1089
1090 Reported line numbers are normalized to the first line of multi-line
1091 statements.
1092@@ -215,8 +217,11 @@
1093 )
1094
1095 excluded_lines = self.first_lines(self.excluded)
1096- ignore = excluded_lines + list(self.docstrings)
1097- lines = self.first_lines(self.statement_starts, ignore)
1098+ lines = self.first_lines(
1099+ self.statement_starts,
1100+ excluded_lines,
1101+ self.docstrings
1102+ )
1103
1104 return lines, excluded_lines
1105
1106@@ -444,14 +449,15 @@
1107
1108 # Get a set of all of the jump-to points.
1109 jump_to = set()
1110- for bc in ByteCodes(self.code.co_code):
1111+ bytecodes = list(ByteCodes(self.code.co_code))
1112+ for bc in bytecodes:
1113 if bc.jump_to >= 0:
1114 jump_to.add(bc.jump_to)
1115
1116 chunk_lineno = 0
1117
1118 # Walk the byte codes building chunks.
1119- for bc in ByteCodes(self.code.co_code):
1120+ for bc in bytecodes:
1121 # Maybe have to start a new chunk
1122 start_new_chunk = False
1123 first_chunk = False
1124@@ -664,3 +670,31 @@
1125 return "<%d+%d @%d%s %r>" % (
1126 self.byte, self.length, self.line, bang, list(self.exits)
1127 )
1128+
1129+
1130+class CachedTokenizer(object):
1131+ """A one-element cache around tokenize.generate_tokens.
1132+
1133+ When reporting, coverage.py tokenizes files twice, once to find the
1134+ structure of the file, and once to syntax-color it. Tokenizing is
1135+ expensive, and easily cached.
1136+
1137+ This is a one-element cache so that our twice-in-a-row tokenizing doesn't
1138+ actually tokenize twice.
1139+
1140+ """
1141+ def __init__(self):
1142+ self.last_text = None
1143+ self.last_tokens = None
1144+
1145+ def generate_tokens(self, text):
1146+ """A stand-in for `tokenize.generate_tokens`."""
1147+ if text != self.last_text:
1148+ self.last_text = text
1149+ self.last_tokens = list(
1150+ tokenize.generate_tokens(StringIO(text).readline)
1151+ )
1152+ return self.last_tokens
1153+
1154+# Create our generate_tokens cache as a callable replacement function.
1155+generate_tokens = CachedTokenizer().generate_tokens
1156
1157=== modified file 'coverage/phystokens.py'
1158--- coverage/phystokens.py 2014-01-06 21:48:50 +0000
1159+++ coverage/phystokens.py 2014-03-07 05:33:58 +0000
1160@@ -1,7 +1,9 @@
1161 """Better tokenizing for coverage.py."""
1162
1163 import codecs, keyword, re, sys, token, tokenize
1164-from coverage.backward import StringIO # pylint: disable=W0622
1165+from coverage.backward import set # pylint: disable=W0622
1166+from coverage.parser import generate_tokens
1167+
1168
1169 def phys_tokens(toks):
1170 """Return all physical tokens, even line continuations.
1171@@ -18,7 +20,7 @@
1172 last_ttype = None
1173 for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks:
1174 if last_lineno != elineno:
1175- if last_line and last_line[-2:] == "\\\n":
1176+ if last_line and last_line.endswith("\\\n"):
1177 # We are at the beginning of a new line, and the last line
1178 # ended with a backslash. We probably have to inject a
1179 # backslash token into the stream. Unfortunately, there's more
1180@@ -74,11 +76,11 @@
1181 is indistinguishable from a final line with a newline.
1182
1183 """
1184- ws_tokens = [token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL]
1185+ ws_tokens = set([token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL])
1186 line = []
1187 col = 0
1188 source = source.expandtabs(8).replace('\r\n', '\n')
1189- tokgen = tokenize.generate_tokens(StringIO(source).readline)
1190+ tokgen = generate_tokens(source)
1191 for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen):
1192 mark_start = True
1193 for part in re.split('(\n)', ttext):
1194
1195=== modified file 'coverage/results.py'
1196--- coverage/results.py 2014-01-06 21:48:50 +0000
1197+++ coverage/results.py 2014-03-07 05:33:58 +0000
1198@@ -26,7 +26,7 @@
1199 # Identify missing statements.
1200 executed = self.coverage.data.executed_lines(self.filename)
1201 exec1 = self.parser.first_lines(executed)
1202- self.missing = sorted(set(self.statements) - set(exec1))
1203+ self.missing = self.statements - exec1
1204
1205 if self.coverage.data.has_arcs():
1206 self.no_branch = self.parser.lines_matching(
1207
1208=== modified file 'coverage/templite.py'
1209--- coverage/templite.py 2011-07-27 16:23:25 +0000
1210+++ coverage/templite.py 2014-03-07 05:33:58 +0000
1211@@ -2,7 +2,53 @@
1212
1213 # Coincidentally named the same as http://code.activestate.com/recipes/496702/
1214
1215-import re, sys
1216+import re
1217+
1218+from coverage.backward import set # pylint: disable=W0622
1219+
1220+
1221+class CodeBuilder(object):
1222+ """Build source code conveniently."""
1223+
1224+ def __init__(self, indent=0):
1225+ self.code = []
1226+ self.indent_amount = indent
1227+
1228+ def add_line(self, line):
1229+ """Add a line of source to the code.
1230+
1231+ Don't include indentations or newlines.
1232+
1233+ """
1234+ self.code.append(" " * self.indent_amount)
1235+ self.code.append(line)
1236+ self.code.append("\n")
1237+
1238+ def add_section(self):
1239+ """Add a section, a sub-CodeBuilder."""
1240+ sect = CodeBuilder(self.indent_amount)
1241+ self.code.append(sect)
1242+ return sect
1243+
1244+ def indent(self):
1245+ """Increase the current indent for following lines."""
1246+ self.indent_amount += 4
1247+
1248+ def dedent(self):
1249+ """Decrease the current indent for following lines."""
1250+ self.indent_amount -= 4
1251+
1252+ def __str__(self):
1253+ return "".join([str(c) for c in self.code])
1254+
1255+ def get_function(self, fn_name):
1256+ """Compile the code, and return the function `fn_name`."""
1257+ assert self.indent_amount == 0
1258+ g = {}
1259+ code_text = str(self)
1260+ exec(code_text, g)
1261+ return g[fn_name]
1262+
1263
1264 class Templite(object):
1265 """A simple template renderer, for a nano-subset of Django syntax.
1266@@ -39,53 +85,104 @@
1267 for context in contexts:
1268 self.context.update(context)
1269
1270+ # We construct a function in source form, then compile it and hold onto
1271+ # it, and execute it to render the template.
1272+ code = CodeBuilder()
1273+
1274+ code.add_line("def render(ctx, dot):")
1275+ code.indent()
1276+ vars_code = code.add_section()
1277+ self.all_vars = set()
1278+ self.loop_vars = set()
1279+ code.add_line("result = []")
1280+ code.add_line("a = result.append")
1281+ code.add_line("e = result.extend")
1282+ code.add_line("s = str")
1283+
1284+ buffered = []
1285+ def flush_output():
1286+ """Force `buffered` to the code builder."""
1287+ if len(buffered) == 1:
1288+ code.add_line("a(%s)" % buffered[0])
1289+ elif len(buffered) > 1:
1290+ code.add_line("e([%s])" % ",".join(buffered))
1291+ del buffered[:]
1292+
1293 # Split the text to form a list of tokens.
1294 toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
1295
1296- # Parse the tokens into a nested list of operations. Each item in the
1297- # list is a tuple with an opcode, and arguments. They'll be
1298- # interpreted by TempliteEngine.
1299- #
1300- # When parsing an action tag with nested content (if, for), the current
1301- # ops list is pushed onto ops_stack, and the parsing continues in a new
1302- # ops list that is part of the arguments to the if or for op.
1303- ops = []
1304 ops_stack = []
1305 for tok in toks:
1306 if tok.startswith('{{'):
1307- # Expression: ('exp', expr)
1308- ops.append(('exp', tok[2:-2].strip()))
1309+ # An expression to evaluate.
1310+ buffered.append("s(%s)" % self.expr_code(tok[2:-2].strip()))
1311 elif tok.startswith('{#'):
1312 # Comment: ignore it and move on.
1313 continue
1314 elif tok.startswith('{%'):
1315 # Action tag: split into words and parse further.
1316+ flush_output()
1317 words = tok[2:-2].strip().split()
1318 if words[0] == 'if':
1319- # If: ('if', (expr, body_ops))
1320- if_ops = []
1321+ # An if statement: evaluate the expression to determine if.
1322 assert len(words) == 2
1323- ops.append(('if', (words[1], if_ops)))
1324- ops_stack.append(ops)
1325- ops = if_ops
1326+ ops_stack.append('if')
1327+ code.add_line("if %s:" % self.expr_code(words[1]))
1328+ code.indent()
1329 elif words[0] == 'for':
1330- # For: ('for', (varname, listexpr, body_ops))
1331+ # A loop: iterate over expression result.
1332 assert len(words) == 4 and words[2] == 'in'
1333- for_ops = []
1334- ops.append(('for', (words[1], words[3], for_ops)))
1335- ops_stack.append(ops)
1336- ops = for_ops
1337+ ops_stack.append('for')
1338+ self.loop_vars.add(words[1])
1339+ code.add_line(
1340+ "for c_%s in %s:" % (
1341+ words[1],
1342+ self.expr_code(words[3])
1343+ )
1344+ )
1345+ code.indent()
1346 elif words[0].startswith('end'):
1347 # Endsomething. Pop the ops stack
1348- ops = ops_stack.pop()
1349- assert ops[-1][0] == words[0][3:]
1350+ end_what = words[0][3:]
1351+ if ops_stack[-1] != end_what:
1352+ raise SyntaxError("Mismatched end tag: %r" % end_what)
1353+ ops_stack.pop()
1354+ code.dedent()
1355 else:
1356- raise SyntaxError("Don't understand tag %r" % words)
1357+ raise SyntaxError("Don't understand tag: %r" % words[0])
1358 else:
1359- ops.append(('lit', tok))
1360-
1361- assert not ops_stack, "Unmatched action tag: %r" % ops_stack[-1][0]
1362- self.ops = ops
1363+ # Literal content. If it isn't empty, output it.
1364+ if tok:
1365+ buffered.append("%r" % tok)
1366+ flush_output()
1367+
1368+ for var_name in self.all_vars - self.loop_vars:
1369+ vars_code.add_line("c_%s = ctx[%r]" % (var_name, var_name))
1370+
1371+ if ops_stack:
1372+ raise SyntaxError("Unmatched action tag: %r" % ops_stack[-1])
1373+
1374+ code.add_line("return ''.join(result)")
1375+ code.dedent()
1376+ self.render_function = code.get_function('render')
1377+
1378+ def expr_code(self, expr):
1379+ """Generate a Python expression for `expr`."""
1380+ if "|" in expr:
1381+ pipes = expr.split("|")
1382+ code = self.expr_code(pipes[0])
1383+ for func in pipes[1:]:
1384+ self.all_vars.add(func)
1385+ code = "c_%s(%s)" % (func, code)
1386+ elif "." in expr:
1387+ dots = expr.split(".")
1388+ code = self.expr_code(dots[0])
1389+ args = [repr(d) for d in dots[1:]]
1390+ code = "dot(%s, %s)" % (code, ", ".join(args))
1391+ else:
1392+ self.all_vars.add(expr)
1393+ code = "c_%s" % expr
1394+ return code
1395
1396 def render(self, context=None):
1397 """Render this template by applying it to `context`.
1398@@ -97,70 +194,15 @@
1399 ctx = dict(self.context)
1400 if context:
1401 ctx.update(context)
1402-
1403- # Run it through an engine, and return the result.
1404- engine = _TempliteEngine(ctx)
1405- engine.execute(self.ops)
1406- return "".join(engine.result)
1407-
1408-
1409-class _TempliteEngine(object):
1410- """Executes Templite objects to produce strings."""
1411- def __init__(self, context):
1412- self.context = context
1413- self.result = []
1414-
1415- def execute(self, ops):
1416- """Execute `ops` in the engine.
1417-
1418- Called recursively for the bodies of if's and loops.
1419-
1420- """
1421- for op, args in ops:
1422- if op == 'lit':
1423- self.result.append(args)
1424- elif op == 'exp':
1425- try:
1426- self.result.append(str(self.evaluate(args)))
1427- except:
1428- exc_class, exc, _ = sys.exc_info()
1429- new_exc = exc_class("Couldn't evaluate {{ %s }}: %s"
1430- % (args, exc))
1431- raise new_exc
1432- elif op == 'if':
1433- expr, body = args
1434- if self.evaluate(expr):
1435- self.execute(body)
1436- elif op == 'for':
1437- var, lis, body = args
1438- vals = self.evaluate(lis)
1439- for val in vals:
1440- self.context[var] = val
1441- self.execute(body)
1442- else:
1443- raise AssertionError("TempliteEngine doesn't grok op %r" % op)
1444-
1445- def evaluate(self, expr):
1446- """Evaluate an expression.
1447-
1448- `expr` can have pipes and dots to indicate data access and filtering.
1449-
1450- """
1451- if "|" in expr:
1452- pipes = expr.split("|")
1453- value = self.evaluate(pipes[0])
1454- for func in pipes[1:]:
1455- value = self.evaluate(func)(value)
1456- elif "." in expr:
1457- dots = expr.split('.')
1458- value = self.evaluate(dots[0])
1459- for dot in dots[1:]:
1460- try:
1461- value = getattr(value, dot)
1462- except AttributeError:
1463- value = value[dot]
1464- if hasattr(value, '__call__'):
1465- value = value()
1466- else:
1467- value = self.context[expr]
1468+ return self.render_function(ctx, self.do_dots)
1469+
1470+ def do_dots(self, value, *dots):
1471+ """Evaluate dotted expressions at runtime."""
1472+ for dot in dots:
1473+ try:
1474+ value = getattr(value, dot)
1475+ except AttributeError:
1476+ value = value[dot]
1477+ if hasattr(value, '__call__'):
1478+ value = value()
1479 return value
1480
1481=== modified file 'coverage/version.py'
1482--- coverage/version.py 2014-01-06 21:48:50 +0000
1483+++ coverage/version.py 2014-03-07 05:33:58 +0000
1484@@ -1,7 +1,7 @@
1485 """The version and URL for coverage.py"""
1486 # This file is exec'ed in setup.py, don't import anything!
1487
1488-__version__ = "3.7" # see detailed history in CHANGES.txt
1489+__version__ = "3.7.1" # see detailed history in CHANGES.txt
1490
1491 __url__ = "http://nedbatchelder.com/code/coverage"
1492 if max(__version__).isalpha():
1493
1494=== modified file 'coverage/xmlreport.py'
1495--- coverage/xmlreport.py 2014-01-06 21:48:50 +0000
1496+++ coverage/xmlreport.py 2014-03-07 05:33:58 +0000
1497@@ -117,7 +117,7 @@
1498 branch_stats = analysis.branch_stats()
1499
1500 # For each statement, create an XML 'line' element.
1501- for line in analysis.statements:
1502+ for line in sorted(analysis.statements):
1503 xline = self.xml_out.createElement("line")
1504 xline.setAttribute("number", str(line))
1505
1506
1507=== modified file 'debian/changelog'
1508--- debian/changelog 2014-02-19 20:37:07 +0000
1509+++ debian/changelog 2014-03-07 05:33:58 +0000
1510@@ -1,3 +1,34 @@
1511+python-coverage (3.7.1+dfsg.1-1ubuntu1) trusty; urgency=medium
1512+
1513+ * Merge with Debian unstable, remaining change:
1514+ - Add python3-coverage.preinst to correctly handle upgrades from
1515+ previous releases.
1516+
1517+ -- Dmitry Shachnev <mitya57@ubuntu.com> Fri, 07 Mar 2014 09:26:38 +0400
1518+
1519+python-coverage (3.7.1+dfsg.1-1) unstable; urgency=medium
1520+
1521+ * The “Ziauddin Yousafzai” release.
1522+ * Urgency “medium” because we fix a bug of severity “serious”.
1523+ * New upstream version.
1524+ (Closes: bug#735359)
1525+ * debian/patches/:
1526+ + Patches incorporated upstream:
1527+ * 02.use-system-ecmascript-libraries.patch
1528+ Remaining patch files re-sequenced contiguously.
1529+ + Add editor hints to patch files.
1530+ * debian/repack:
1531+ + Correct quoting of parameters (quote parameters used as filesystem
1532+ entry names, don't quote parameters used as numbers).
1533+ * debian/rules:
1534+ + Explicitly replace shebang for programs needing default Python
1535+ interpreter (Closes: bug#736121).
1536+ + Correct generation of Python version strings for object code
1537+ filenames (Closes: bug#740053).
1538+ Thanks to Dmitry Shachnev for the bug report and patch.
1539+
1540+ -- Ben Finney <ben+debian@benfinney.id.au> Wed, 05 Mar 2014 13:14:27 +1100
1541+
1542 python-coverage (3.7+dfsg.1-4ubuntu2) trusty; urgency=medium
1543
1544 * Build for python 3.4.
1545
1546=== modified file 'debian/patches/01.omit-resource-files-from-distutils-setup.patch'
1547--- debian/patches/01.omit-resource-files-from-distutils-setup.patch 2013-10-15 13:16:36 +0000
1548+++ debian/patches/01.omit-resource-files-from-distutils-setup.patch 2014-03-07 05:33:58 +0000
1549@@ -32,3 +32,14 @@
1550
1551 entry_points = {'console_scripts': scripts},
1552
1553+
1554+
1555
1556+Local variables:
1557+coding: utf-8
1558+mode: diff
1559+time-stamp-format: "%:y-%02m-%02d"
1560+time-stamp-start: "^Last-Update:[ ]+"
1561+time-stamp-end: "$"
1562+time-stamp-line-limit: 20
1563+End:
1564+vim: fileencoding=utf-8 filetype=diff :
1565
1566=== added file 'debian/patches/02.rename-public-programs.patch'
1567--- debian/patches/02.rename-public-programs.patch 1970-01-01 00:00:00 +0000
1568+++ debian/patches/02.rename-public-programs.patch 2014-03-07 05:33:58 +0000
1569@@ -0,0 +1,50 @@
1570+Description: Rename public programs to be clear what they're for.
1571+ .
1572+ The upstream choice of ‘coverage’ is too broad a command name for a
1573+ Python-specific programmer tool.
1574+ .
1575+ Created to work with “entry points” feature of Python's Distribute.
1576+Bug: https://bitbucket.org/ned/coveragepy/issue/272/
1577+Author: Ben Finney <ben+debian@benfinney.id.au>
1578+Last-Update: 2013-10-16
1579+
1580+--- old/setup.py 2013-10-07 07:40:00.000000000 +1100
1581++++ new/setup.py 2013-10-08 10:21:00.000000000 +1100
1582+@@ -1,3 +1,4 @@
1583++# coding: utf-8
1584+ # setup.py for coverage.py
1585+
1586+ """Code coverage measurement for Python
1587+@@ -71,13 +72,15 @@
1588+ devstat = "5 - Production/Stable"
1589+ classifier_list.append("Development Status :: " + devstat)
1590+
1591+-# Install a script as "coverage", and as "coverage[23]", and as
1592+-# "coverage-2.7" (or whatever).
1593++# Install a default ‘python-coverage’ program, and versioned as
1594++# ‘python2.7-coverage’, ‘python3-coverage’, etc.
1595+ scripts = [
1596+- 'coverage = coverage:main',
1597+- 'coverage%d = coverage:main' % sys.version_info[:1],
1598+- 'coverage-%d.%d = coverage:main' % sys.version_info[:2],
1599+- ]
1600++ "python-coverage = coverage:main",
1601++ "python{version[0]}-coverage = coverage:main".format(
1602++ version=sys.version_info),
1603++ "python{version[0]}.{version[1]}-coverage = coverage:main".format(
1604++ version=sys.version_info),
1605++ ]
1606+
1607+ # Create the keyword arguments for setup()
1608+
1609+
1610+
1611
1612+Local variables:
1613+coding: utf-8
1614+mode: diff
1615+time-stamp-format: "%:y-%02m-%02d"
1616+time-stamp-start: "^Last-Update:[ ]+"
1617+time-stamp-end: "$"
1618+time-stamp-line-limit: 20
1619+End:
1620+vim: fileencoding=utf-8 filetype=diff :
1621
1622=== removed file 'debian/patches/02.use-system-ecmascript-libraries.patch'
1623--- debian/patches/02.use-system-ecmascript-libraries.patch 2013-10-15 13:16:36 +0000
1624+++ debian/patches/02.use-system-ecmascript-libraries.patch 1970-01-01 00:00:00 +0000
1625@@ -1,48 +0,0 @@
1626-Description: Use Debian packaged ECMAScript libraries.
1627- .
1628- Per Debian policy §4.13 “Convenience copies of code”, packages should not
1629- bundle third-party code. Instead, the program should use the system
1630- packages of those libraries.
1631-Bug: https://bitbucket.org/ned/coveragepy/issue/259/
1632-Bug-Debian: http://bugs.debian.org/596212
1633-Author: Ben Finney <ben+debian@benfinney.id.au>
1634-Last-Update: 2013-10-08
1635-
1636-diff -ruN old/coverage/html.py new/coverage/html.py
1637---- old/coverage/html.py 2013-10-07 07:40:00.000000000 +1100
1638-+++ new/coverage/html.py 2013-10-08 10:21:00.000000000 +1100
1639-@@ -48,10 +48,6 @@
1640- # These files will be copied from the htmlfiles dir to the output dir.
1641- STATIC_FILES = [
1642- "style.css",
1643-- "jquery.min.js",
1644-- "jquery.hotkeys.js",
1645-- "jquery.isonscreen.js",
1646-- "jquery.tablesorter.min.js",
1647- "coverage_html.js",
1648- "keybd_closed.png",
1649- "keybd_open.png",
1650-@@ -123,6 +119,23 @@
1651- os.path.join(self.directory, static)
1652- )
1653-
1654-+ system_javascript_path = os.path.join(
1655-+ os.sep, "usr", "share", "javascript")
1656-+ system_javascript_libraries = {
1657-+ "jquery.min.js":
1658-+ os.path.join(system_javascript_path, "jquery"),
1659-+ "jquery.hotkeys.js":
1660-+ os.path.join(system_javascript_path, "jquery-hotkeys"),
1661-+ "jquery.isonscreen.js":
1662-+ os.path.join(system_javascript_path, "jquery-isonscreen"),
1663-+ "jquery.tablesorter.min.js":
1664-+ os.path.join(system_javascript_path, "jquery-tablesorter"),
1665-+ }
1666-+ for static, source_dir in system_javascript_libraries.items():
1667-+ shutil.copyfile(
1668-+ os.path.join(source_dir, static),
1669-+ os.path.join(self.directory, static))
1670-+
1671- # The user may have extra CSS they want copied.
1672- if self.extra_css:
1673- shutil.copyfile(
1674
1675=== removed file 'debian/patches/03.rename-public-programs.patch'
1676--- debian/patches/03.rename-public-programs.patch 2013-10-29 09:13:11 +0000
1677+++ debian/patches/03.rename-public-programs.patch 1970-01-01 00:00:00 +0000
1678@@ -1,39 +0,0 @@
1679-Description: Rename public programs to be clear what they're for.
1680- .
1681- The upstream choice of ‘coverage’ is too broad a command name for a
1682- Python-specific programmer tool.
1683- .
1684- Created to work with “entry points” feature of Python's Distribute.
1685-Bug: https://bitbucket.org/ned/coveragepy/issue/272/
1686-Author: Ben Finney <ben+debian@benfinney.id.au>
1687-Last-Update: 2013-10-16
1688-
1689---- old/setup.py 2013-10-07 07:40:00.000000000 +1100
1690-+++ new/setup.py 2013-10-08 10:21:00.000000000 +1100
1691-@@ -1,3 +1,4 @@
1692-+# coding: utf-8
1693- # setup.py for coverage.py
1694-
1695- """Code coverage measurement for Python
1696-@@ -71,13 +72,15 @@
1697- devstat = "5 - Production/Stable"
1698- classifier_list.append("Development Status :: " + devstat)
1699-
1700--# Install a script as "coverage", and as "coverage[23]", and as
1701--# "coverage-2.7" (or whatever).
1702-+# Install a default ‘python-coverage’ program, and versioned as
1703-+# ‘python2.7-coverage’, ‘python3-coverage’, etc.
1704- scripts = [
1705-- 'coverage = coverage:main',
1706-- 'coverage%d = coverage:main' % sys.version_info[:1],
1707-- 'coverage-%d.%d = coverage:main' % sys.version_info[:2],
1708-- ]
1709-+ "python-coverage = coverage:main",
1710-+ "python{version[0]}-coverage = coverage:main".format(
1711-+ version=sys.version_info),
1712-+ "python{version[0]}.{version[1]}-coverage = coverage:main".format(
1713-+ version=sys.version_info),
1714-+ ]
1715-
1716- # Create the keyword arguments for setup()
1717-
1718
1719=== modified file 'debian/patches/series'
1720--- debian/patches/series 2014-01-06 21:48:50 +0000
1721+++ debian/patches/series 2014-03-07 05:33:58 +0000
1722@@ -1,3 +1,2 @@
1723 01.omit-resource-files-from-distutils-setup.patch
1724-02.use-system-ecmascript-libraries.patch
1725-03.rename-public-programs.patch
1726+02.rename-public-programs.patch
1727
1728=== removed file 'debian/python-coverage.1'
1729--- debian/python-coverage.1 2014-02-19 20:37:07 +0000
1730+++ debian/python-coverage.1 1970-01-01 00:00:00 +0000
1731@@ -1,279 +0,0 @@
1732-'\" -*- coding: utf-8 -*-
1733-.\" Man page generated from reStructuredText.
1734-.
1735-.TH PYTHON-COVERAGE 1 "2013-10-07" "" "Coverage"
1736-.SH NAME
1737-python-coverage \- measure code coverage of Python program execution
1738-.
1739-.nr rst2man-indent-level 0
1740-.
1741-.de1 rstReportMargin
1742-\\$1 \\n[an-margin]
1743-level \\n[rst2man-indent-level]
1744-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
1745--
1746-\\n[rst2man-indent0]
1747-\\n[rst2man-indent1]
1748-\\n[rst2man-indent2]
1749-..
1750-.de1 INDENT
1751-.\" .rstReportMargin pre:
1752-. RS \\$1
1753-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
1754-. nr rst2man-indent-level +1
1755-.\" .rstReportMargin post:
1756-..
1757-.de UNINDENT
1758-. RE
1759-.\" indent \\n[an-margin]
1760-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
1761-.nr rst2man-indent-level -1
1762-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
1763-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
1764-..
1765-.SH SYNOPSIS
1766-.nf
1767-\fBpython\-coverage\fP \fIcommand\fP [ \fIoption\fP ... ]
1768-\fBpython\-coverage\fP \fBhelp\fP [ \fIcommand\fP ]
1769-\fBpython\-coverage\fP \fBhelp\fP \fBclassic\fP
1770-.fi
1771-.sp
1772-.SH DESCRIPTION
1773-.sp
1774-\fBpython\-coverage\fP executes a Python program, measures which of its statements are
1775-executed and which are not, and reports these coverage measurements.
1776-.SH COMMAND OVERVIEW
1777-.INDENT 0.0
1778-.TP
1779-.B \fBpython\-coverage\fP \fBannotate\fP
1780-Annotate source files with execution information.
1781-.TP
1782-.B \fBpython\-coverage\fP \fBcombine\fP
1783-Combine a number of data files.
1784-.TP
1785-.B \fBpython\-coverage\fP \fBerase\fP
1786-Erase previously collected coverage data.
1787-.TP
1788-.B \fBpython\-coverage\fP \fBhelp\fP
1789-Get help on using coverage.py.
1790-.TP
1791-.B \fBpython\-coverage\fP \fBhtml\fP
1792-Create an HTML report.
1793-.TP
1794-.B \fBpython\-coverage\fP \fBreport\fP
1795-Report coverage stats on modules.
1796-.TP
1797-.B \fBpython\-coverage\fP \fBrun\fP
1798-Run a Python program and measure code execution.
1799-.TP
1800-.B \fBpython\-coverage\fP \fBxml\fP
1801-Create an XML report of coverage results.
1802-.UNINDENT
1803-.SH GLOBAL OPTIONS
1804-.INDENT 0.0
1805-.TP
1806-.B \fB\-\-help\fP, \fB\-h\fP
1807-Describe how to use Coverage, in general or a command.
1808-.TP
1809-.B \fB\-\-rcfile\fP \fIRCFILE\fP
1810-Specify configuration file \fIRCFILE\fP\&. Defaults to \fB\&.coveragerc\fP\&.
1811-.TP
1812-.B \fB\-\-omit\fP \fIPATTERN\fP ...
1813-Omit files when their filename matches one of these PATTERNs.
1814-Usually needs quoting on the command line.
1815-.TP
1816-.B \fB\-\-include\fP \fIPATTERN\fP ...
1817-Include files only when their filename path matches one of these
1818-PATTERNs. Usually needs quoting on the command line.
1819-.UNINDENT
1820-.SH COMMAND REFERENCE
1821-.sp
1822-\fBannotate\fP
1823-.INDENT 0.0
1824-.INDENT 3.5
1825-Options:
1826-.INDENT 0.0
1827-.TP
1828-.B \-d \fIDIR\fP, \-\-directory \fIDIR\fP
1829-Write the output files to DIR.
1830-.TP
1831-.B \-i, \-\-ignore\-errors
1832-Ignore errors while reading source files.
1833-.UNINDENT
1834-.UNINDENT
1835-.UNINDENT
1836-.sp
1837-\fBcombine\fP
1838-.INDENT 0.0
1839-.INDENT 3.5
1840-Combine data from multiple coverage files collected with \fBrun \-p\fP\&.
1841-The combined results are written to a single file representing the
1842-union of the data.
1843-.UNINDENT
1844-.UNINDENT
1845-.sp
1846-\fBerase\fP
1847-.INDENT 0.0
1848-.INDENT 3.5
1849-Erase previously collected coverage data.
1850-.UNINDENT
1851-.UNINDENT
1852-.sp
1853-\fBhelp\fP [ \fIcommand\fP ]
1854-.INDENT 0.0
1855-.INDENT 3.5
1856-Describe how to use Coverage.
1857-.UNINDENT
1858-.UNINDENT
1859-.sp
1860-\fBhelp\fP \fBclassic\fP
1861-.INDENT 0.0
1862-.INDENT 3.5
1863-Describe help on older command syntax.
1864-.UNINDENT
1865-.UNINDENT
1866-.sp
1867-\fBhtml\fP [ \fIoption\fP ... ] [ \fIMODULE\fP ... ]
1868-.INDENT 0.0
1869-.INDENT 3.5
1870-Create an HTML report of the coverage of each \fIMODULE\fP file. Each file
1871-gets its own page, with the source decorated to show executed,
1872-excluded, and missed lines.
1873-.sp
1874-Options:
1875-.INDENT 0.0
1876-.TP
1877-.B \-d \fIDIR\fP, \-\-directory \fIDIR\fP
1878-Write the output files to \fIDIR\fP\&.
1879-.TP
1880-.B \-\-title \fITITLE\fP
1881-Use the text string \fITITLE\fP as the title on the HTML.
1882-.TP
1883-.B \-\-fail\-under \fIMIN\fP
1884-Exit with a status of 2 if the total coverage is less than \fIMIN\fP\&.
1885-.TP
1886-.B \-i, \-\-ignore\-errors
1887-Ignore errors while reading source files.
1888-.UNINDENT
1889-.UNINDENT
1890-.UNINDENT
1891-.sp
1892-\fBreport\fP [ \fIoption\fP ... ] [ \fIMODULE\fP ... ]
1893-.INDENT 0.0
1894-.INDENT 3.5
1895-Report coverage statistics on each \fIMODULE\fP\&.
1896-.sp
1897-Options:
1898-.INDENT 0.0
1899-.TP
1900-.B \-\-fail\-under \fIMIN\fP
1901-Exit with a status of 2 if the total coverage is less than \fIMIN\fP\&.
1902-.TP
1903-.B \-i, \-\-ignore\-errors
1904-Ignore errors while reading source files.
1905-.TP
1906-.B \-m, \-\-show\-missing
1907-Show line numbers of statements in each module that weren\(aqt
1908-executed.
1909-.UNINDENT
1910-.UNINDENT
1911-.UNINDENT
1912-.sp
1913-\fBrun\fP [ \fIoptions\fP ... ] \fIPROGRAMFILE\fP [ \fIprogram_options\fP ]
1914-.INDENT 0.0
1915-.INDENT 3.5
1916-Run a Python program \fIPROGRAMFILE\fP, measuring code execution.
1917-.sp
1918-Options:
1919-.INDENT 0.0
1920-.TP
1921-.B \-a, \-\-append
1922-Append coverage data to .coverage, otherwise it is started clean
1923-with each run.
1924-.TP
1925-.B \-\-branch
1926-Measure branch coverage in addition to statement coverage.
1927-.TP
1928-.B \-\-debug \fIDEBUGOPT\fP,...
1929-Debug options \fIDEBUGOPT\fP, separated by commas
1930-.TP
1931-.B \-L, \-\-pylib
1932-Measure coverage even inside the Python installed library, which
1933-isn\(aqt done by default.
1934-.TP
1935-.B \-p, \-\-parallel\-mode
1936-Append the machine name, process id and random number to the
1937-\fB\&.coverage\fP data file name to simplify collecting data from many
1938-processes.
1939-.TP
1940-.B \-\-timid
1941-Use a simpler but slower trace method. Try this if you get
1942-seemingly impossible results!
1943-.TP
1944-.B \-\-source \fISOURCE\fP ...
1945-A list of packages or directories of code to be measured.
1946-.UNINDENT
1947-.UNINDENT
1948-.UNINDENT
1949-.sp
1950-\fBxml\fP [ \fIoptions\fP ... ] [ \fIMODULES\fP ... ]
1951-.INDENT 0.0
1952-.INDENT 3.5
1953-Generate an XML report of coverage results on each \fIMODULE\fP\&.
1954-.sp
1955-Options:
1956-.INDENT 0.0
1957-.TP
1958-.B \-\-fail\-under \fIMIN\fP
1959-Exit with a status of 2 if the total coverage is less than \fIMIN\fP\&.
1960-.TP
1961-.B \-i, \-\-ignore\-errors
1962-Ignore errors while reading source files.
1963-.TP
1964-.B \-o \fIOUTFILE\fP
1965-Write the XML report to \fIOUTFILE\fP\&. Defaults to \fBcoverage.xml\fP\&.
1966-.UNINDENT
1967-.UNINDENT
1968-.UNINDENT
1969-.SH ENVIRONMENT VARIABLES
1970-.sp
1971-COVERAGE_FILE
1972-.INDENT 0.0
1973-.INDENT 3.5
1974-Path to the file where coverage measurements are collected to and
1975-reported from. Default: \fB\&.coverage\fP in the current working directory.
1976-.UNINDENT
1977-.UNINDENT
1978-.sp
1979-COVERAGE_OPTIONS
1980-.INDENT 0.0
1981-.INDENT 3.5
1982-Space\-separated series of command\-line options to \fBpython\-coverage\fP\&. Default:
1983-empty.
1984-.UNINDENT
1985-.UNINDENT
1986-.SH HISTORY
1987-.sp
1988-The \fBpython\-coverage\fP command is a Python program which calls the \fBcoverage\fP
1989-Python library to do all the work.
1990-.sp
1991-The library was originally developed by Gareth Rees, and is now developed
1992-by Ned Batchelder.
1993-.sp
1994-This manual page was written by Ben Finney <\fI\%ben+python@benfinney.id.au\fP>\&.
1995-.\" Local variables:
1996-.\" mode: rst
1997-.\" coding: utf-8
1998-.\" time-stamp-format: "%:y-%02m-%02d"
1999-.\" time-stamp-start: "^:Date:[ ]+"
2000-.\" time-stamp-end: "$"
2001-.\" time-stamp-line-limit: 20
2002-.\" End:
2003-.\" vim: filetype=rst fileencoding=utf-8 :
2004-.
2005-.SH AUTHOR
2006-Ben Finney <ben+python@benfinney.id.au>
2007-.SH COPYRIGHT
2008-BSD license, attribution and disclaimer required.
2009-.\" Generated by docutils manpage writer.
2010-.
2011
2012=== removed file 'debian/python-coverage.1.tmp'
2013--- debian/python-coverage.1.tmp 2014-02-19 20:37:07 +0000
2014+++ debian/python-coverage.1.tmp 1970-01-01 00:00:00 +0000
2015@@ -1,278 +0,0 @@
2016-.\" Man page generated from reStructuredText.
2017-.
2018-.TH PYTHON-COVERAGE 1 "2013-10-07" "" "Coverage"
2019-.SH NAME
2020-python-coverage \- measure code coverage of Python program execution
2021-.
2022-.nr rst2man-indent-level 0
2023-.
2024-.de1 rstReportMargin
2025-\\$1 \\n[an-margin]
2026-level \\n[rst2man-indent-level]
2027-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
2028--
2029-\\n[rst2man-indent0]
2030-\\n[rst2man-indent1]
2031-\\n[rst2man-indent2]
2032-..
2033-.de1 INDENT
2034-.\" .rstReportMargin pre:
2035-. RS \\$1
2036-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
2037-. nr rst2man-indent-level +1
2038-.\" .rstReportMargin post:
2039-..
2040-.de UNINDENT
2041-. RE
2042-.\" indent \\n[an-margin]
2043-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
2044-.nr rst2man-indent-level -1
2045-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
2046-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
2047-..
2048-.SH SYNOPSIS
2049-.nf
2050-\fBpython\-coverage\fP \fIcommand\fP [ \fIoption\fP ... ]
2051-\fBpython\-coverage\fP \fBhelp\fP [ \fIcommand\fP ]
2052-\fBpython\-coverage\fP \fBhelp\fP \fBclassic\fP
2053-.fi
2054-.sp
2055-.SH DESCRIPTION
2056-.sp
2057-\fBpython\-coverage\fP executes a Python program, measures which of its statements are
2058-executed and which are not, and reports these coverage measurements.
2059-.SH COMMAND OVERVIEW
2060-.INDENT 0.0
2061-.TP
2062-.B \fBpython\-coverage\fP \fBannotate\fP
2063-Annotate source files with execution information.
2064-.TP
2065-.B \fBpython\-coverage\fP \fBcombine\fP
2066-Combine a number of data files.
2067-.TP
2068-.B \fBpython\-coverage\fP \fBerase\fP
2069-Erase previously collected coverage data.
2070-.TP
2071-.B \fBpython\-coverage\fP \fBhelp\fP
2072-Get help on using coverage.py.
2073-.TP
2074-.B \fBpython\-coverage\fP \fBhtml\fP
2075-Create an HTML report.
2076-.TP
2077-.B \fBpython\-coverage\fP \fBreport\fP
2078-Report coverage stats on modules.
2079-.TP
2080-.B \fBpython\-coverage\fP \fBrun\fP
2081-Run a Python program and measure code execution.
2082-.TP
2083-.B \fBpython\-coverage\fP \fBxml\fP
2084-Create an XML report of coverage results.
2085-.UNINDENT
2086-.SH GLOBAL OPTIONS
2087-.INDENT 0.0
2088-.TP
2089-.B \fB\-\-help\fP, \fB\-h\fP
2090-Describe how to use Coverage, in general or a command.
2091-.TP
2092-.B \fB\-\-rcfile\fP \fIRCFILE\fP
2093-Specify configuration file \fIRCFILE\fP\&. Defaults to \fB\&.coveragerc\fP\&.
2094-.TP
2095-.B \fB\-\-omit\fP \fIPATTERN\fP ...
2096-Omit files when their filename matches one of these PATTERNs.
2097-Usually needs quoting on the command line.
2098-.TP
2099-.B \fB\-\-include\fP \fIPATTERN\fP ...
2100-Include files only when their filename path matches one of these
2101-PATTERNs. Usually needs quoting on the command line.
2102-.UNINDENT
2103-.SH COMMAND REFERENCE
2104-.sp
2105-\fBannotate\fP
2106-.INDENT 0.0
2107-.INDENT 3.5
2108-Options:
2109-.INDENT 0.0
2110-.TP
2111-.B \-d \fIDIR\fP, \-\-directory \fIDIR\fP
2112-Write the output files to DIR.
2113-.TP
2114-.B \-i, \-\-ignore\-errors
2115-Ignore errors while reading source files.
2116-.UNINDENT
2117-.UNINDENT
2118-.UNINDENT
2119-.sp
2120-\fBcombine\fP
2121-.INDENT 0.0
2122-.INDENT 3.5
2123-Combine data from multiple coverage files collected with \fBrun \-p\fP\&.
2124-The combined results are written to a single file representing the
2125-union of the data.
2126-.UNINDENT
2127-.UNINDENT
2128-.sp
2129-\fBerase\fP
2130-.INDENT 0.0
2131-.INDENT 3.5
2132-Erase previously collected coverage data.
2133-.UNINDENT
2134-.UNINDENT
2135-.sp
2136-\fBhelp\fP [ \fIcommand\fP ]
2137-.INDENT 0.0
2138-.INDENT 3.5
2139-Describe how to use Coverage.
2140-.UNINDENT
2141-.UNINDENT
2142-.sp
2143-\fBhelp\fP \fBclassic\fP
2144-.INDENT 0.0
2145-.INDENT 3.5
2146-Describe help on older command syntax.
2147-.UNINDENT
2148-.UNINDENT
2149-.sp
2150-\fBhtml\fP [ \fIoption\fP ... ] [ \fIMODULE\fP ... ]
2151-.INDENT 0.0
2152-.INDENT 3.5
2153-Create an HTML report of the coverage of each \fIMODULE\fP file. Each file
2154-gets its own page, with the source decorated to show executed,
2155-excluded, and missed lines.
2156-.sp
2157-Options:
2158-.INDENT 0.0
2159-.TP
2160-.B \-d \fIDIR\fP, \-\-directory \fIDIR\fP
2161-Write the output files to \fIDIR\fP\&.
2162-.TP
2163-.B \-\-title \fITITLE\fP
2164-Use the text string \fITITLE\fP as the title on the HTML.
2165-.TP
2166-.B \-\-fail\-under \fIMIN\fP
2167-Exit with a status of 2 if the total coverage is less than \fIMIN\fP\&.
2168-.TP
2169-.B \-i, \-\-ignore\-errors
2170-Ignore errors while reading source files.
2171-.UNINDENT
2172-.UNINDENT
2173-.UNINDENT
2174-.sp
2175-\fBreport\fP [ \fIoption\fP ... ] [ \fIMODULE\fP ... ]
2176-.INDENT 0.0
2177-.INDENT 3.5
2178-Report coverage statistics on each \fIMODULE\fP\&.
2179-.sp
2180-Options:
2181-.INDENT 0.0
2182-.TP
2183-.B \-\-fail\-under \fIMIN\fP
2184-Exit with a status of 2 if the total coverage is less than \fIMIN\fP\&.
2185-.TP
2186-.B \-i, \-\-ignore\-errors
2187-Ignore errors while reading source files.
2188-.TP
2189-.B \-m, \-\-show\-missing
2190-Show line numbers of statements in each module that weren\(aqt
2191-executed.
2192-.UNINDENT
2193-.UNINDENT
2194-.UNINDENT
2195-.sp
2196-\fBrun\fP [ \fIoptions\fP ... ] \fIPROGRAMFILE\fP [ \fIprogram_options\fP ]
2197-.INDENT 0.0
2198-.INDENT 3.5
2199-Run a Python program \fIPROGRAMFILE\fP, measuring code execution.
2200-.sp
2201-Options:
2202-.INDENT 0.0
2203-.TP
2204-.B \-a, \-\-append
2205-Append coverage data to .coverage, otherwise it is started clean
2206-with each run.
2207-.TP
2208-.B \-\-branch
2209-Measure branch coverage in addition to statement coverage.
2210-.TP
2211-.B \-\-debug \fIDEBUGOPT\fP,...
2212-Debug options \fIDEBUGOPT\fP, separated by commas
2213-.TP
2214-.B \-L, \-\-pylib
2215-Measure coverage even inside the Python installed library, which
2216-isn\(aqt done by default.
2217-.TP
2218-.B \-p, \-\-parallel\-mode
2219-Append the machine name, process id and random number to the
2220-\fB\&.coverage\fP data file name to simplify collecting data from many
2221-processes.
2222-.TP
2223-.B \-\-timid
2224-Use a simpler but slower trace method. Try this if you get
2225-seemingly impossible results!
2226-.TP
2227-.B \-\-source \fISOURCE\fP ...
2228-A list of packages or directories of code to be measured.
2229-.UNINDENT
2230-.UNINDENT
2231-.UNINDENT
2232-.sp
2233-\fBxml\fP [ \fIoptions\fP ... ] [ \fIMODULES\fP ... ]
2234-.INDENT 0.0
2235-.INDENT 3.5
2236-Generate an XML report of coverage results on each \fIMODULE\fP\&.
2237-.sp
2238-Options:
2239-.INDENT 0.0
2240-.TP
2241-.B \-\-fail\-under \fIMIN\fP
2242-Exit with a status of 2 if the total coverage is less than \fIMIN\fP\&.
2243-.TP
2244-.B \-i, \-\-ignore\-errors
2245-Ignore errors while reading source files.
2246-.TP
2247-.B \-o \fIOUTFILE\fP
2248-Write the XML report to \fIOUTFILE\fP\&. Defaults to \fBcoverage.xml\fP\&.
2249-.UNINDENT
2250-.UNINDENT
2251-.UNINDENT
2252-.SH ENVIRONMENT VARIABLES
2253-.sp
2254-COVERAGE_FILE
2255-.INDENT 0.0
2256-.INDENT 3.5
2257-Path to the file where coverage measurements are collected to and
2258-reported from. Default: \fB\&.coverage\fP in the current working directory.
2259-.UNINDENT
2260-.UNINDENT
2261-.sp
2262-COVERAGE_OPTIONS
2263-.INDENT 0.0
2264-.INDENT 3.5
2265-Space\-separated series of command\-line options to \fBpython\-coverage\fP\&. Default:
2266-empty.
2267-.UNINDENT
2268-.UNINDENT
2269-.SH HISTORY
2270-.sp
2271-The \fBpython\-coverage\fP command is a Python program which calls the \fBcoverage\fP
2272-Python library to do all the work.
2273-.sp
2274-The library was originally developed by Gareth Rees, and is now developed
2275-by Ned Batchelder.
2276-.sp
2277-This manual page was written by Ben Finney <\fI\%ben+python@benfinney.id.au\fP>\&.
2278-.\" Local variables:
2279-.\" mode: rst
2280-.\" coding: utf-8
2281-.\" time-stamp-format: "%:y-%02m-%02d"
2282-.\" time-stamp-start: "^:Date:[ ]+"
2283-.\" time-stamp-end: "$"
2284-.\" time-stamp-line-limit: 20
2285-.\" End:
2286-.\" vim: filetype=rst fileencoding=utf-8 :
2287-.
2288-.SH AUTHOR
2289-Ben Finney <ben+python@benfinney.id.au>
2290-.SH COPYRIGHT
2291-BSD license, attribution and disclaimer required.
2292-.\" Generated by docutils manpage writer.
2293-.
2294
2295=== modified file 'debian/repack'
2296--- debian/repack 2014-01-13 14:51:52 +0000
2297+++ debian/repack 2014-03-07 05:33:58 +0000
2298@@ -7,11 +7,11 @@
2299 set -o nounset
2300
2301 function usage() {
2302- progname=$(basename $0)
2303+ progname="$(basename "$0")"
2304 printf "$progname --upstream-version VERSION FILENAME\n"
2305 }
2306
2307-if [ "$#" -ne "3" ] ; then
2308+if [ $# -ne 3 ] ; then
2309 usage
2310 exit 1
2311 fi
2312
2313=== modified file 'debian/rules'
2314--- debian/rules 2014-01-13 14:51:52 +0000
2315+++ debian/rules 2014-03-07 05:33:58 +0000
2316@@ -22,13 +22,22 @@
2317 PYTHON3_VERSIONS = $(shell py3versions -vr)
2318 PYTHON_VERSIONS = ${PYTHON2_VERSIONS} ${PYTHON3_VERSIONS}
2319
2320+# For some ‘/usr/bin/*-coverage’ programs (and not others), we need the
2321+# default, not current, Python interpreter in the shebang. The ‘dh-python’
2322+# helpers can re-write shebangs, but they are too eager, converting every
2323+# program's shebang without allowing us to specify different interpreters
2324+# per program. We resort to ‘sed’.
2325+python3_program = ${package_install_bin_dir}/python3-coverage
2326+shebang_line_regex = ^\#!.*$$
2327+python3_shebang_interpreter = /usr/bin/python3
2328+
2329 debug_object_exclude += _d.so
2330
2331 # Unfortunately, ‘dh_install --exclude’ requires a substring, and a glob
2332 # wildcard will *not* work. So we need to specify the exact substring for
2333 # the Python 3 debug module filenames.
2334 python3_objcode_versions = $(shell \
2335- printf "%s" ${PYTHON3_VERSIONS} | sed -e 's/\.//g')
2336+ printf "%s\n" ${PYTHON3_VERSIONS} | sed -e 's/\.//g')
2337 DEB_HOST_MULTIARCH = $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
2338 debug_object_exclude += $(foreach ver,${python3_objcode_versions}, \
2339 .cpython-${ver}dm-${DEB_HOST_MULTIARCH}.so)
2340@@ -112,14 +121,14 @@
2341 manpage-symlinks: ${versioned_manpage_paths}
2342
2343 ${DOCUMENTATION_DIR}/${default_manpage_name}: ${package_working_root}/${default_manpage_name}
2344- $(INSTALL) --mode=u=rw,go=r $< $@
2345+ $(INSTALL) --mode=u=rw,go=r "$<" "$@"
2346
2347 ${DOCUMENTATION_DIR}/%${MANPAGE_SUFFIX}: ${DOCUMENTATION_DIR}/${default_manpage_name}
2348- ln -s ${default_manpage_name} $@
2349+ ln -s ${default_manpage_name} "$@"
2350
2351 %.1: %.1${RST_SUFFIX}
2352- $(RST2MAN) $< > $@.tmp
2353- cat debian/manpage_encoding_declaration.UTF-8 $@.tmp > $@
2354+ $(RST2MAN) $< > "$@".tmp
2355+ cat debian/manpage_encoding_declaration.UTF-8 "$@".tmp > "$@"
2356
2357
2358
2359 .PHONY: clean
2360@@ -158,22 +167,27 @@
2361 --exclude ${exclude_part})
2362
2363 ${package_install_resources_root}:
2364- $(INSTALL) -d $@
2365+ $(INSTALL) -d "$@"
2366
2367 ${package_install_resources_root}/${htmlfiles_dirname}: ${HTMLFILES_DIR} ${package_install_resources_root}
2368- $(INSTALL) -d $@
2369- $(INSTALL) --mode=u=rw,go=r $</* $@
2370+ $(INSTALL) -d "$@"
2371+ $(INSTALL) --mode=u=rw,go=r "$<"/* "$@"
2372
2373 .PHONY: install-resource-files
2374 install-resource-files: ${package_install_resources_root}
2375 install-resource-files: ${package_install_resources_root}/${htmlfiles_dirname}
2376
2377 .PHONY: install-python%
2378+install-python%: python_version_major = $(firstword $(subst ., ,$*))
2379 install-python%:
2380 python$*-dbg setup.py install --install-layout=deb \
2381 --root=$(CURDIR)/${package_install_root}
2382 python$* setup.py install --install-layout=deb \
2383 --root=$(CURDIR)/${package_install_root}
2384+ if [ "${python_version_major}" = "3" ] ; then \
2385+ sed -i -e '1 s,${shebang_line_regex},#! ${python3_shebang_interpreter},' \
2386+ "${python3_program}" ; \
2387+ fi
2388
2389
2390
2391 .PHONY: binary-indep
2392
2393=== modified file 'doc/changes.rst'
2394--- doc/changes.rst 2014-01-06 21:48:50 +0000
2395+++ doc/changes.rst 2014-03-07 05:33:58 +0000
2396@@ -26,6 +26,7 @@
2397 :history: 20121223T180600, updated for 3.6b2.
2398 :history: 20130105T173500, updated for 3.6
2399 :history: 20131005T205700, updated for 3.7
2400+:history: 20131212T213100, updated for 3.7.1
2401
2402
2403 These are the major changes for coverage.py. For a more complete change
2404@@ -34,11 +35,25 @@
2405 .. _CHANGES.txt: http://bitbucket.org/ned/coveragepy/src/tip/CHANGES.txt
2406
2407
2408+.. _changes_371:
2409+
2410+Version 3.7.1 --- 13 December 2013
2411+----------------------------------
2412+
2413+- Improved the speed of HTML report generation by about 20%.
2414+
2415+- Fixed the mechanism for finding OS-installed static files for the HTML report
2416+ so that it will actually find OS-installed static files.
2417+
2418+
2419+.. _changes_37:
2420+
2421 Version 3.7 --- 6 October 2013
2422 ------------------------------
2423
2424 - Added the ``--debug`` switch to ``coverage run``. It accepts a list of
2425- options indicating the type of internal activity to log to stderr.
2426+ options indicating the type of internal activity to log to stderr. For
2427+ details, see :ref:`the run --debug options <cmd_run_debug>`.
2428
2429 - Improved the branch coverage facility, fixing `issue 92`_ and `issue 175`_.
2430
2431
2432=== modified file 'doc/cmd.rst'
2433--- doc/cmd.rst 2014-01-06 21:48:50 +0000
2434+++ doc/cmd.rst 2014-03-07 05:33:58 +0000
2435@@ -152,9 +152,9 @@
2436
2437 * ``sys``: before starting, dump all the system and environment information,
2438 as with :ref:`coverage debug sys <cmd_debug>`.
2439-
2440+
2441 * ``dataio``: log when reading or writing any data file.
2442-
2443+
2444 * ``pid``: annotate all debug output with the process id.
2445
2446
2447
2448=== modified file 'doc/index.rst'
2449--- doc/index.rst 2014-01-06 21:48:50 +0000
2450+++ doc/index.rst 2014-03-07 05:33:58 +0000
2451@@ -30,6 +30,7 @@
2452 :history: 20121229T112300, Updated for 3.6b3.
2453 :history: 20130105T174000, Updated for 3.6
2454 :history: 20131005T210000, Updated for 3.7
2455+:history: 20131212T213300, Updated for 3.7.1
2456
2457
2458 Coverage.py is a tool for measuring code coverage of Python programs. It
2459@@ -42,7 +43,7 @@
2460
2461 .. ifconfig:: not prerelease
2462
2463- The latest version is coverage.py 3.7, released 6 October 2013.
2464+ The latest version is coverage.py 3.7.1, released 13 December 2013.
2465 It is supported on Python versions 2.3 through 3.4, and PyPy 2.1.
2466
2467 .. ifconfig:: prerelease
2468
2469=== modified file 'doc/install.rst'
2470--- doc/install.rst 2014-01-06 21:48:50 +0000
2471+++ doc/install.rst 2014-03-07 05:33:58 +0000
2472@@ -19,6 +19,7 @@
2473 :history: 20121229T112400, updated for 3.6b3.
2474 :history: 20130105T174400, updated for 3.6.
2475 :history: 20131005T210600, updated for 3.7.
2476+:history: 20131212T213500, updated for 3.7.1.
2477
2478
2479 .. highlight:: console
2480@@ -74,9 +75,9 @@
2481 installed properly::
2482
2483 $ coverage --version
2484- Coverage.py, version 3.7. http://nedbatchelder.com/code/coverage
2485+ Coverage.py, version 3.7.1. http://nedbatchelder.com/code/coverage
2486
2487 You can also invoke coverage as a module::
2488
2489 $ python -m coverage --version
2490- Coverage.py, version 3.7. http://nedbatchelder.com/code/coverage
2491+ Coverage.py, version 3.7.1. http://nedbatchelder.com/code/coverage
2492
2493=== modified file 'tests/coveragetest.py'
2494--- tests/coveragetest.py 2013-10-15 13:16:36 +0000
2495+++ tests/coveragetest.py 2014-03-07 05:33:58 +0000
2496@@ -355,20 +355,21 @@
2497
2498 # Get the analysis results, and check that they are right.
2499 analysis = cov._analyze(mod)
2500+ statements = sorted(analysis.statements)
2501 if lines is not None:
2502 if type(lines[0]) == type(1):
2503 # lines is just a list of numbers, it must match the statements
2504 # found in the code.
2505- self.assertEqual(analysis.statements, lines)
2506+ self.assertEqual(statements, lines)
2507 else:
2508 # lines is a list of possible line number lists, one of them
2509 # must match.
2510 for line_list in lines:
2511- if analysis.statements == line_list:
2512+ if statements == line_list:
2513 break
2514 else:
2515 self.fail("None of the lines choices matched %r" %
2516- analysis.statements
2517+ statements
2518 )
2519
2520 if type(missing) == type(""):
2521
2522=== modified file 'tests/test_html.py'
2523--- tests/test_html.py 2013-10-15 13:16:36 +0000
2524+++ tests/test_html.py 2014-03-07 05:33:58 +0000
2525@@ -317,9 +317,32 @@
2526 cov = coverage.coverage()
2527 self.start_import_stop(cov, "main")
2528 cov.html_report()
2529+
2530 jquery = open("htmlcov/jquery.min.js").read()
2531 self.assertEqual(jquery, "Not Really JQuery!")
2532
2533+ def test_copying_static_files_from_system_in_dir(self):
2534+ # Make a new place for static files.
2535+ INSTALLED = [
2536+ "jquery/jquery.min.js",
2537+ "jquery-hotkeys/jquery.hotkeys.js",
2538+ "jquery-isonscreen/jquery.isonscreen.js",
2539+ "jquery-tablesorter/jquery.tablesorter.min.js",
2540+ ]
2541+ for fpath in INSTALLED:
2542+ self.make_file(os.path.join("static_here", fpath), "Not real.")
2543+ coverage.html.STATIC_PATH.insert(0, "static_here")
2544+
2545+ self.make_file("main.py", "print(17)")
2546+ cov = coverage.coverage()
2547+ self.start_import_stop(cov, "main")
2548+ cov.html_report()
2549+
2550+ for fpath in INSTALLED:
2551+ the_file = os.path.basename(fpath)
2552+ contents = open(os.path.join("htmlcov", the_file)).read()
2553+ self.assertEqual(contents, "Not real.")
2554+
2555 def test_cant_find_static_files(self):
2556 # Make the path point to useless places.
2557 coverage.html.STATIC_PATH = ["/xyzzy"]
2558
2559=== modified file 'tests/test_templite.py'
2560--- tests/test_templite.py 2013-10-15 13:16:36 +0000
2561+++ tests/test_templite.py 2014-03-07 05:33:58 +0000
2562@@ -1,7 +1,7 @@
2563 """Tests for coverage.templite."""
2564
2565 from coverage.templite import Templite
2566-import unittest
2567+from tests.coveragetest import CoverageTest
2568
2569 # pylint: disable=W0612,E1101
2570 # Disable W0612 (Unused variable) and
2571@@ -18,9 +18,11 @@
2572 setattr(self, n, v)
2573
2574
2575-class TempliteTest(unittest.TestCase):
2576+class TempliteTest(CoverageTest):
2577 """Tests for Templite."""
2578
2579+ run_in_temp_dir = False
2580+
2581 def try_render(self, text, ctx, result):
2582 """Render `text` through `ctx`, and it had better be `result`."""
2583 self.assertEqual(Templite(text).render(ctx), result)
2584@@ -37,6 +39,14 @@
2585 # Variables use {{var}} syntax.
2586 self.try_render("Hello, {{name}}!", {'name':'Ned'}, "Hello, Ned!")
2587
2588+ def test_undefined_variables(self):
2589+ # Using undefined names is an error.
2590+ self.assertRaises(
2591+ Exception,
2592+ self.try_render,
2593+ "Hi, {{name}}!", {}, "xyz"
2594+ )
2595+
2596 def test_pipes(self):
2597 # Variables can be filtered with pipes.
2598 data = {
2599@@ -165,6 +175,23 @@
2600 "Hi, NEDBEN!"
2601 )
2602
2603+ def test_complex_if(self):
2604+ class Complex(AnyOldObject):
2605+ """A class to try out complex data access."""
2606+ def getit(self):
2607+ """Return it."""
2608+ return self.it
2609+ obj = Complex(it={'x':"Hello", 'y': 0})
2610+ self.try_render(
2611+ "@"
2612+ "{% if obj.getit.x %}X{% endif %}"
2613+ "{% if obj.getit.y %}Y{% endif %}"
2614+ "{% if obj.getit.y|str %}S{% endif %}"
2615+ "!",
2616+ { 'obj': obj, 'str': str },
2617+ "@XS!"
2618+ )
2619+
2620 def test_loop_if(self):
2621 self.try_render(
2622 "@{% for n in nums %}{% if n %}Z{% endif %}{{n}}{% endfor %}!",
2623@@ -184,9 +211,11 @@
2624
2625 def test_nested_loops(self):
2626 self.try_render(
2627- "@{% for n in nums %}"
2628+ "@"
2629+ "{% for n in nums %}"
2630 "{% for a in abc %}{{a}}{{n}}{% endfor %}"
2631- "{% endfor %}!",
2632+ "{% endfor %}"
2633+ "!",
2634 {'nums': [0,1,2], 'abc': ['a', 'b', 'c']},
2635 "@a0b0c0a1b1c1a2b2c2!"
2636 )
2637@@ -199,6 +228,20 @@
2638 )
2639
2640 def test_bogus_tag_syntax(self):
2641- self.assertRaises(SyntaxError, self.try_render,
2642+ self.assertRaisesRegexp(
2643+ SyntaxError, "Don't understand tag: 'bogus'",
2644+ self.try_render,
2645 "Huh: {% bogus %}!!{% endbogus %}??", {}, ""
2646 )
2647+
2648+ def test_bad_nesting(self):
2649+ self.assertRaisesRegexp(
2650+ SyntaxError, "Unmatched action tag: 'if'",
2651+ self.try_render,
2652+ "{% if x %}X", {}, ""
2653+ )
2654+ self.assertRaisesRegexp(
2655+ SyntaxError, "Mismatched end tag: 'for'",
2656+ self.try_render,
2657+ "{% if x %}X{% endfor %}", {}, ""
2658+ )

Subscribers

People subscribed via source and target branches